StoreBay Developers

Authentication

Bearer credentials, credential types, OAuth2 flows, and scopes.

Authentication

Every request carries a Bearer credential. Pick the one that fits, keep it server-side, and scope it tightly.

curl https://api.storebay.co.uk/v1/sites \
  -H "Authorization: Bearer sb_live_9c1f4b7e2a6d40f8b1c3e5a7d9f0b2c4"

Credential types

TypeBest forHowNotes
API keyServer-to-server integrationsAuthorization: Bearer sb_live_… / sb_test_…Only a SHA-256 hash is stored; plaintext shown once at creation. Rotate/revoke are first-class and take effect immediately.
OAuth2 client-credentialsMachine clients that prefer short-lived tokensPOST /oauth/token → Bearer access tokenFor an operator's own backend integrations.
OAuth2 authorization-code + PKCEMarketplace apps acting on behalf of an operator/oauth/authorize → code → /oauth/token2-hour access tokens; refresh tokens rotate. PKCE (S256) is required.
Static tokenSimple, low-risk integrationsAuthorization: Bearer …Convenience credential; scope-gated like the rest.

Keys and tokens are environment-bound — a sb_live_… key only ever touches live data and a sb_test_… key only ever touches test data (see Sandbox). Never store a key or client secret in plaintext at rest.

Warning

Never embed a key in a browser, mobile app, or any client you don't control — treat it like a password.

Note

The plaintext key is shown once at creation. StoreBay stores only a SHA-256 hash.

API keys

The simplest path for a server-to-server integration. Generate a key in your StoreBay developer settings (https://app.storebay.co.uk); it is shown once, in full, at creation. Send it as a Bearer credential on every request:

curl https://api.storebay.co.uk/v1/sites \
  -H "Authorization: Bearer sb_test_…"

Rotating or revoking a key takes effect immediately — the next request with the old key gets 401 unauthorized.

OAuth2 client-credentials

For your own backend talking to your own StoreBay account, without managing a long-lived key.

  1. Your backend calls POST /oauth/token with grant_type=client_credentials, your client_id, client_secret, and the scope you need.
  2. StoreBay returns a Bearer access_token, valid for expires_in seconds.
  3. Use it as Authorization: Bearer sb_at_… on /v1 calls until it expires, then request a new one the same way.
curl -X POST https://api.storebay.co.uk/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d grant_type=client_credentials \
  -d client_id=app_018f9c2a7b3e7c1a9f2d3a5b6c7d8e9f \
  -d client_secret=… \
  -d 'scope=agreements:read billing:read'
const method = 'POST'
const res = await fetch('https://api.storebay.co.uk/oauth/token', {
  method,
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'client_credentials',
    client_id: 'app_018f9c2a7b3e7c1a9f2d3a5b6c7d8e9f',
    client_secret: '…',
    scope: 'agreements:read billing:read',
  }),
})
const { access_token, expires_in } = await res.json()
import requests

res = requests.post(
    "https://api.storebay.co.uk/oauth/token",
    data={
        "grant_type": "client_credentials",
        "client_id": "app_018f9c2a7b3e7c1a9f2d3a5b6c7d8e9f",
        "client_secret": "…",
        "scope": "agreements:read billing:read",
    },
)
access_token = res.json()["access_token"]
{
  "access_token": "sb_at_1f2e3d…",
  "token_type": "Bearer",
  "expires_in": 7200,
  "scope": "agreements:read billing:read"
}

OAuth2 authorization-code + PKCE (marketplace apps)

The flow for a marketplace app acting on behalf of an operator who installs it — the operator authorizes your app once, and you act within the scopes they granted.

  1. Redirect the operator to /oauth/authorize with your client_id, redirect_uri, the scope you're requesting, and a PKCE code_challenge (method S256).
  2. The operator signs in to StoreBay and approves the request; StoreBay redirects back to your redirect_uri with a one-time authorization code.
  3. Exchange the code — together with your PKCE code_verifier — at POST /oauth/token for a 2-hour access token (sb_at_…) and a refresh token (sb_rt_…).
  4. When the access token expires, call POST /oauth/token with grant_type=refresh_token to rotate a fresh access/refresh pair. The old refresh token is revoked in the same transaction — replaying it returns invalid_grant.

sb_rt_… is never a valid /v1 credential — only sb_at_… access tokens are accepted there. Create an OAuth client for your app in your StoreBay developer settings (https://app.storebay.co.uk).

An authenticated call

curl https://api.storebay.co.uk/v1/agreements \
  -H "Authorization: Bearer sb_test_…"
const res = await fetch('https://api.storebay.co.uk/v1/agreements', {
  headers: { Authorization: 'Bearer sb_test_…' },
})
const { data, meta } = await res.json()
import requests

res = requests.get(
    "https://api.storebay.co.uk/v1/agreements",
    headers={"Authorization": "Bearer sb_test_…"},
)
data = res.json()["data"]

Scopes

Scopes gate resources. A credential is granted a set of scopes at issue time; a request that touches a resource without the matching scope is rejected 403 insufficient_scope. Scopes are resource:action (read or write); write implies the ability to create, update, and (where supported) delete a resource — it does not imply read, so a credential wanting both requests both explicitly. Request the narrowest set you need.

ScopeGrants access to
sites:read / sites:writesites, widgets (read-only)
units:read / units:writeunit-types, units, availability
catalogue:read / catalogue:writeproducts, coverage-products, coupons, rate-plans, price-rules
contacts:read / contacts:writecontacts
reservations:read / reservations:writereservations, orders
agreements:read / agreements:writeagreements, subscriptions
billing:read / billing:writeinvoices, credit-notes, payments, payment-methods, mandates
crm:read / crm:writedeals, tasks, notes
compliance:read / compliance:writecontracts, identity-verifications
access:read / access:writeaccess-credentials, access-events, hardware-devices
integrations:read / integrations:writeintegration-connections
messages:read / messages:writemessages
webhooks:read / webhooks:writewebhooks (endpoint management)
events:readevents (event log, read-only)
reports:readreports
imports:read / imports:writeimports

The scope catalogue is platform-defined and stable; new resources add new scopes additively (see the Changelog). Enforcement is exact-match: an endpoint refuses a request without exactly the scope it needs, rather than trying to infer a looser match — so a credential scoped billing:write alone still can't GET /v1/payments without billing:read too.

Environments and going live

sb_live_… and sb_test_… credentials share the same base URL and the same endpoints — the environment is selected by the credential, never by a different host. Build and test everything against sb_test_… in the sandbox, then swap the credential to go live. Nothing else in your integration changes.

On this page