403 FORBIDDEN_SCOPE. Grant the minimum a key needs.
Catalog
Commerce resources
The resource APIs use Shopify-parity scope names.catalog:read and catalog:write are the older names for read_products and
write_products. They remain valid and are treated as equivalent, so keys issued before the
rename keep working and either name authorizes either surface. New keys should use the
*_products names.
GET /whoami reports both: scopes is the grant exactly as issued on the key — the audit
answer — and effective_scopes is that set expanded through the aliases, which is what
authorization checks. A key created with catalog:read reports ["catalog:read"] and
["catalog:read", "read_products"] respectively.
Fulfillment
See Suppliers & 3PL.
agent:admin behaves differently on the two surfaces
Spend scopes
store:publish, ads:write, marketing:send, checkout:recover, write_discounts and
write_gift_cards move money, mint redeemable value, or contact real customers. A key created
with requires_approval: true parks these actions in pending_approval until a human resolves
them — see Approvals.
A gift card and a discount code are bearer instruments: whoever holds one can redeem it. Treat
write_gift_cards and write_discounts like ads:write — grant them to a dedicated key with
approval on, never to a general-purpose one.
offers:approve also changes live pricing, but it is not a spend scope: offer optimization
carries its own in-band confirmation, where the two gated calls require
approval_context: { "merchant_confirmed": true } in the body and otherwise return 200 with
data.status: "pending_approval". It is gated at the call, not at the key.