API Key Scopes

How API key scopes restrict what an integration can read and write on the Marketplacer GraphQL and REST APIs, and how to choose the right scopes when issuing a key.

Introduction

Every Marketplacer API key carries a scope set — a list of tokens that declares exactly which parts of the API the key can reach. A key issued for “publish adverts” should not be able to reach into refunds, payouts, or admin role management; scopes make that boundary explicit on the key itself rather than leaving it to be enforced by your own code.

Scopes apply to both the GraphQL API and the REST API. Newly-issued keys carry exactly the scopes you ticked when creating them, and nothing else.

Scope token shape

A scope token is two colon-separated parts: a resource and a level.

adverts:read
orders:write
refunds:manage

A key’s scope set is the space-delimited list of these tokens, e.g. "adverts:read orders:read sellers:read". The empty string means “no scopes” — the key can only reach surfaces that are intentionally scope-free (login, the public catalogue, etc.).

Levels are hierarchical

The three levels are nested:

LevelIncludesUsed for
readreads onlyListing, searching, fetching detail records
writeimplies readCreating and updating records
manageimplies write & readDestructive operations — deletes, irreversible state transitions

So a key holding orders:write can also satisfy any field that requires orders:read. You only ever need to grant the highest level the key actually needs; the create-key form records one token per resource.

Resources

Each resource groups a slice of the API surface that a single scope token grants access to. The following list is the full catalogue:

ResourceLevels availableCovers
Advertsread/write/manageAdverts, variants, attachments, images, mappings, advert metadata, category suggestions, AI transformations
Golden Productsread/write/manageThe operator-curated master product catalogue and its variant/option metadata
Ordersread/write/manageOrders, invoices, line items, shipments, return shipments, invoice amendments, invoice annotations
Refundsread/write/manageRefund requests and their line-item flow, including approve/deny/refund/return
Shipping Settingsread/write/manageShipping options/profiles/rates/zones, Shippit credentials and shipment booking
Paymentsread/write/managePayment events, payment methods, billing, Adyen/Braintree settings
Remittancesread/write/manageSeller remittances, remittance advices, remittance events
MPayread/write/manageMarketplacer Pay deposits, payout details, deposit reconciliations
Sellersread/write/manageSeller records, multi-store memberships, commission packages, onboarding processes, seller stats
Usersread/write/manageSeller-side user accounts under your seller record
Adminsread/write/manageOperator admin identities, admin roles, permission catalog, Rithum API keys
Webhooksread/write/manageWebhook subscriptions and webhook event delivery records
Imports and Exportsread/write/manageSpreadsheet import/export jobs across every domain (orders, sellers, commission packages, …)
Auditread onlyActivity logs, change logs, contextual history of records you can already see
Messagingread/write/manageIn-app chat
Catalog Rulesread/write/manageCatalog rules engine
Promotionsread/write/managePromotion records
Inquiriesread/write/manageCustomer inquiries directed at sellers
API Keysread/write/manageListing, creating, and revoking other API keys you own
Site Configwrite/manageOperator-only — write side of the per-vertical site configuration. Reads are intentionally scope-free.
Taxonomywrite/manageOperator-only — write side of taxons, option types/values, tax codes, custom fields, prototypes, measurement units. Reads are intentionally scope-free.

Site Config and Taxonomy have no read level because the corresponding read-side surfaces (the catalogue, the per-vertical config) are designed to be reachable without an API key scope at all — every integration needs them.

What needs no scope

Some surfaces are intentionally scope-free regardless of the key’s scope set. They fall into five categories:

  • Public catalogue — taxons, brands, models, countries, option types/values, refund reasons, prototypes, custom-field templates, etc. These describe the marketplace itself, not any seller’s records.
  • Public config — the site config tree (SiteConfig and its sub-settings), feature flags, branding, health checks, translations.
  • Auth primitives — login mutations, MFA enrolment/verification, session token operations. Requiring a scope here would be a chicken-and-egg failure — you don’t have a key yet.
  • Self lookup — the user query that resolves “who am I” from the calling credential.
  • Derived data — value-shaped types such as Money, Address, KeyValue, error/validation messages. These never carry resource data of their own; the parent object’s scope already gated them.

You don’t need to do anything to reach these — even a key with an empty scope set works.

Choosing scopes for an integration

The create-key form lets you tick exactly the scopes your integration needs. As a rule of thumb:

  1. Start from your integration’s job description. “Sync adverts and inventory each night” maps to adverts:write and probably imports_exports:write for spreadsheet uploads. A reporting integration that just pulls order data is orders:read.
  2. Use read unless you actually mutate. Most integrations are predominantly read-side. Granting write or manage you don’t need expands the blast radius if the key leaks.
  3. Skip manage unless you actually delete or destructively transition records. write covers create/update for almost every domain.
  4. Don’t tick resources you don’t touch. A separate, tightly-scoped key per integration is easier to revoke if one of those integrations is compromised.

A typical advert-sync integration ends up with three or four scopes. A back-office reporting integration ends up with one or two. Ticking a higher level visually checks the lower ones — that’s just the hierarchy showing through. Only the highest-level token per resource is recorded on the key.

Pre-existing keys without scopes

Keys created before scopes existed carry no scope set at all. By default these legacy keys keep working as they always did — they’re treated as fully-trusted so existing integrations don’t break the day scopes ship.

Operators can disable this grandfathering on a per-vertical basis once the key fleet has been audited and re-issued with explicit scopes. The vertical-level setting is Authentication Settings → Legacy unscoped API keys grant full access; setting it to false means any remaining scope-less key is rejected. Set it once you’re confident every legacy integration has been migrated to a scoped key.

Keys created from the create-key form always have a real scope set, so this grandfathering doesn’t apply to them.

What happens when a scope is missing

A request that lacks a required scope is denied rather than ignored:

  • GraphQL — the field returns null and a structured error is included in the errors array:

    {
      "errors": [
        {
          "message": "Missing required scope: orders:write",
          "extensions": {
            "code": "MISSING_SCOPE",
            "scope": "orders:write"
          },
          "path": ["orderCreate"]
        }
      ],
      "data": { "orderCreate": null }
    }
    

    The extensions.code value is always MISSING_SCOPE, and extensions.scope is the scope you’d need to add. Other GraphQL fields in the same query that the key does have access to still resolve normally.

  • REST (v2) — the response is HTTP 403 with a JSON:API error body:

    {
      "errors": [
        {
          "status": "403",
          "code": "MISSING_SCOPE",
          "title": "Missing required scope",
          "detail": "This endpoint requires the 'adverts:write' scope.",
          "meta": { "scope": "adverts:write" }
        }
      ]
    }
    

In both cases the fix is the same: re-issue the key with the missing scope ticked. (You cannot edit an existing key’s scopes; create a new key with the right set, deploy it, then revoke the old one.)

Scope descriptions in the schema

The auto-generated GraphQL schema documentation calls out the required scope for every field. Hovering over a field in GraphQL Voyager or browsing the text schema shows trailers like:

Requires API key scope adverts:write.
Requires API key scopes orders:read and imports_exports:write.
No API key scope required.

If you’re unsure what scope a particular query or mutation needs, that’s the authoritative source.

Rotating scoped keys

Scopes are part of the credential, so any time you rotate a key per the API Key Rotation playbook you can also re-evaluate its scopes. Two common reasons to re-issue with different scopes:

  • The integration’s job has narrowed (e.g. dropped a write surface) — issue the rotated key with fewer scopes.
  • The integration grew (e.g. now also processes refunds) — issue the rotated key with the additional scopes ticked.

Because you can’t edit an existing key, rotation is the natural moment to right-size scope grants.