Skip to Content
Your subscription data now goes where you need it: the Curobi Developer API feeds your warehouse, ERP or CRM, and webhooks tell you the moment something changes — read the API docs →
Developer APIAuthentication & Scopes

Authentication & scopes

Every call to the Curobi Admin API carries a secret key as a bearer token.

Authorization: Bearer curobi_sk_live_7f3aQ2…

There is no other credential type on this surface. A publishable key (curobi_pk_…, reserved for the future browser-safe Storefront API) is refused here with invalid_credential.

Minting a key

Keys are created in your Curobi admin on the API keys page.

Name it after the system that will use it

“Netsuite nightly sync” tells you what breaks when you revoke it. “Key 1” does not.

Grant only the scopes that system needs

Scopes are checked per endpoint. A BI pull that only reads subscriptions has no reason to hold read:customers.

Copy the key immediately

It is shown once. Curobi stores only sha256(pepper + token), so nobody — including us — can recover the original. Lost a key? Revoke it and mint a new one.

Set an expiry if the key is for a contractor or a one-off migration

An expired key fails with credential_expired instead of quietly outliving the engagement.

A secret key is a full-power read credential for your subscription data. Keep it in a secrets manager or an environment variable — never in client-side JavaScript, a mobile app bundle, a theme file, or a committed .env. Anything a browser can read, a customer can read.

What gets checked, and in what order

Five facts are checked on every single request, in this order. The order matters: the auth ladder runs before an endpoint looks at its own scope, so a downgraded shop with a fully-scoped key sees plan_upgrade_required rather than insufficient_scope.

#CheckFailure codeStatus
1A Authorization: Bearer header is presentmissing_credential401
2The key resolves, and is neither revoked nor expiredinvalid_credential · credential_revoked · credential_expired401
3Curobi is still installed on the shopshop_unavailable401
4The rate-limit bucket has a token leftrate_limited429
5The shop’s plan includes the APIplan_upgrade_required403
6The key holds the scope this endpoint requiresinsufficient_scope403

The plan is re-read on every request, cached for at most 60 seconds. A shop that downgrades from Pro loses API access within the minute, not at the next key rotation. Plan and scope are separate facts and neither substitutes for the other.

Scopes

Exact string matching. No wildcards, no hierarchy, and write: does not imply read:.

read:subscriptions read:plans read:bundles read:mixmatch read:gifts read:winback read:notifications read:analytics read:customers manage:webhooks
ScopeUnlocks
read:subscriptionsGET /subscriptions, GET /subscriptions/{contract_gid}
read:plansGET /plans
read:bundlesGET /bundles, GET /bundles/{id}/boxes
read:mixmatchGET /mix-match, GET /mix-match/{id}
read:giftsGET /gifts
read:winbackGET /winback/campaigns, GET /winback/campaigns/{id}/sends
read:notificationsGET /notifications
read:analyticsGET /analytics/mrr, /churn, /cohorts, /recovery
read:customersCustomer names, email addresses and gift messages on the resources above
manage:webhooksThe whole /webhooks/* family — the only write in v1

The write:* scopes named in the API design are published vocabulary for a later phase. They are not offered when you mint a key, because a checkbox for an endpoint that does not exist would mint keys whose stated power is fiction.

read:customers is separate on purpose

It is the only scope that returns Protected Customer Data: customer names, email addresses, and gift messages.

A reporting pull can page through every subscription, gift and notification without it — those resources are keyed by Shopify customer GID, which identifies a customer without describing them. Keep read:customers off the BI key, and the warehouse gets the full picture with none of the liability.

Without read:customers, PCD fields come back as null rather than as a "[redacted]" placeholder. That’s deliberate: paired with the scopes you know your own key holds, you can tell “there is no value” from “the value is withheld”.

Gift claim tokens are never serialized, at any scope. A claim token is a bearer credential for the recipient’s claim page — returning it would turn a merchant’s read key into the ability to accept or decline gifts on a customer’s behalf.

Revoking

Revoke a key from the API keys page at any time. The next request with it fails with credential_revoked; there is no grace period.

Uninstalling Curobi revokes every key for the shop immediately. An uninstalled merchant can’t reach the admin to revoke their own keys, so a key that outlived an uninstall would be a live credential nobody could turn off. Uninstalling also disables every webhook endpoint — revoking credentials stops other people calling us, disabling endpoints stops us calling other people.

Key hygiene

  • One key per integration. Shared keys can’t be revoked without collateral damage, and “last used” tells you nothing when four systems share one credential.
  • Rotate by overlap: mint the new key, deploy it, confirm traffic on it, then revoke the old one. Revoking first means downtime.
  • The API keys page shows each key’s prefix and last used time, so you can find the dead one before you revoke the live one. Last-used is written at most once a minute per key.
Last updated on