Permissions
Permissions are a vocabulary of scopes, each a resource:action pair. You declare what your app needs; the merchant approves it at install; we enforce it on every request. Your code never checks a permission — a call either succeeds or answers 403 with the missing scope named.
Declaring them
{
"scopes": ["transactions:read", "products:read", "inventory:write", "emails:send"],
"optional_scopes": ["emails:send"]
}scopes is everything the app can use. optional_scopes is the subset the merchant may decline at install; the app must work without them. Ask for the narrowest set that works — a consent screen listing eighteen permissions is one merchants abandon — and request an optional scope at the moment you need it with paysio.requestScope().
The vocabulary
Every scope an app can declare today, with the tier the merchant sees it under.
| Main | Grants | Tier |
|---|---|---|
account:read | View workspace name, logo and branding | Standard |
account:write | Update workspace name, logo and branding | Standard |
products:read | View products and images | Standard |
products:write | Create and edit products | Standard |
inventory:write | Adjust stock levels | Standard |
customers:read | View customers and their contact details | Sensitive |
customers:write | Create and edit customers | Sensitive |
transactions:read | View payments | Standard |
activity:read | View the activity feed | Standard |
| Payments | Grants | Tier |
|---|---|---|
payment_links:read | View payment links | Standard |
payment_links:write | Create and edit payment links | Standard |
checkout_sessions:read | View checkout sessions | Standard |
checkout_sessions:write | Start checkouts | Standard |
charges:read | View charges | Standard |
charges:write | Charge a customer | Money |
refunds:write | Refund and void payments | Money |
invoices:read | View invoices | Standard |
invoices:write | Create and send invoices | Money |
subscriptions:read | View subscriptions | Standard |
subscriptions:write | Create and change subscriptions | Money |
fulfillments:read | View fulfillments | Standard |
fulfillments:write | Mark orders fulfilled | Standard |
| Payouts | Grants | Tier |
|---|---|---|
balance:read | View balance | Standard |
payouts:read | View payouts | Standard |
payouts:write | Send payouts | Money |
payout_links:read | View payout links | Standard |
payout_links:write | Create payout links | Money |
payout_requests:read | View payout requests | Standard |
payout_requests:write | Create payout requests | Money |
| Risk | Grants | Tier |
|---|---|---|
disputes:read | View disputes | Standard |
disputes:write | Respond to disputes and submit evidence | Sensitive |
| Marketing | Grants | Tier |
|---|---|---|
emails:send | Send email as the business | Sensitive |
pixels:read | View tracking pixels | Standard |
pixels:write | Configure tracking pixels | Standard |
| Storefront | Grants | Tier |
|---|---|---|
storefronts:read | View storefronts | Standard |
files:read | Read files the app uploaded | Standard |
files:write | Upload files | Standard |
| Org | Grants | Tier |
|---|---|---|
members:read | View team member names and emails | Sensitive |
Unknown scopes are rejected when you save the config. Scopes for features not yet exposed to apps (campaigns, storefront publishing, checkout settings, risk settings and others) exist in the platform but cannot be declared until they open; they will appear here when they do.
Tiers
- Standard — non-personal reads and content writes. Listed plainly at install.
- Sensitive — personal data, or acting as the merchant (sending email). Listed with a note.
- Money — anything that moves or promises money: charges, refunds, invoices, subscriptions. Open to every app. Called out with its own warning at install, where the merchant must also tick that they understand the app can move money before Install enables, and again on the app’s page. Every charge an app makes shows in Transactions.
Some capabilities are absent from the vocabulary by construction and can never be granted to an app: API keys, members and roles, security settings, processor credentials, billing. There is no scope to ask for, which is what makes them safe.
How enforcement works
Every /v1 request resolves the intersection of three sets — what your app declares, what this merchant granted, and what the token carries — recomputed per request, so a revoked scope stops working on the very next call. A path with no scope rule is denied, never allowed, so a new endpoint can never quietly become reachable to apps.
HTTP/1.1 403 Forbidden
{
"error": "This app has not been granted the required permission: emails:send. The merchant can grant it from the app's page.",
"scope": "emails:send",
"type": "invalid_request_error"
}Webhooks are gated the same way: a topic is only delivered to apps granted the read scope its payload implies. See Storage & webhooks.
Requesting more later
Declare it as optional
Add the scope to both scopes and optional_scopes. If it is new to the app, that is a scope addition and goes through review first.
Ask at the moment of need
await paysio.requestScope('emails:send') sends the merchant to the consent page for your app, with only the new permission to decide on.
Hear the answer
An app/scopes_update webhook carries the new granted set. Tokens minted from then on carry it; existing tokens pick it up on their next request.
Changing what you need
| Change | Review? | Effect |
|---|---|---|
| Adding a scope | Yes | Inert until approved. Once approved, merchants see a diff of exactly what was added and re-consent; until they do, the app runs on the old set. |
| Making a scope optional | No | Takes effect on save. |
| Removing a scope | No | Takes effect on save; existing grants narrow to match. |
Merchants can also revoke individual scopes from your app’s page at any time. Design for a call to fail with 403 and say what to do, rather than assuming what was granted yesterday is granted today.