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

paysio.app.json
JSON
{
  "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.

MainGrantsTier
account:readView workspace name, logo and brandingStandard
account:writeUpdate workspace name, logo and brandingStandard
products:readView products and imagesStandard
products:writeCreate and edit productsStandard
inventory:writeAdjust stock levelsStandard
customers:readView customers and their contact detailsSensitive
customers:writeCreate and edit customersSensitive
transactions:readView paymentsStandard
activity:readView the activity feedStandard
PaymentsGrantsTier
payment_links:readView payment linksStandard
payment_links:writeCreate and edit payment linksStandard
checkout_sessions:readView checkout sessionsStandard
checkout_sessions:writeStart checkoutsStandard
charges:readView chargesStandard
charges:writeCharge a customerMoney
refunds:writeRefund and void paymentsMoney
invoices:readView invoicesStandard
invoices:writeCreate and send invoicesMoney
subscriptions:readView subscriptionsStandard
subscriptions:writeCreate and change subscriptionsMoney
fulfillments:readView fulfillmentsStandard
fulfillments:writeMark orders fulfilledStandard
PayoutsGrantsTier
balance:readView balanceStandard
payouts:readView payoutsStandard
payouts:writeSend payoutsMoney
payout_links:readView payout linksStandard
payout_links:writeCreate payout linksMoney
payout_requests:readView payout requestsStandard
payout_requests:writeCreate payout requestsMoney
RiskGrantsTier
disputes:readView disputesStandard
disputes:writeRespond to disputes and submit evidenceSensitive
MarketingGrantsTier
emails:sendSend email as the businessSensitive
pixels:readView tracking pixelsStandard
pixels:writeConfigure tracking pixelsStandard
StorefrontGrantsTier
storefronts:readView storefrontsStandard
files:readRead files the app uploadedStandard
files:writeUpload filesStandard
OrgGrantsTier
members:readView team member names and emailsSensitive

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.

JSON
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

1

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.

2

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.

3

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

ChangeReview?Effect
Adding a scopeYesInert 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 optionalNoTakes effect on save.
Removing a scopeNoTakes 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.