Skip to main content

Authentication & scopes

Three auth surfaces

Each surface has a distinct auth model — appropriate to its threat surface and use case.

SurfaceAuth methodGranularity
Admin API (/api/v1/...)Bearer token (copt_...)Per-token scopes
Public endpoints (/api/public/...)None (CORS allow-list) or API key (pk_...)Per-shop
Webhooks (inbound)HMAC SHA-256 signaturePer-source secret
Webhooks (outbound)HMAC SHA-256 signature you signPer-endpoint secret

Admin API — bearer tokens

Token format

copt_live_aB3xY9zKqMnPwR2vT4uH7jL5sQ8eFc1g
  • copt_ prefix identifies as Sumeru Systems API token
  • live_ (or test_) environment marker
  • 32-character random body (base62)

Tokens are stored as SHA-256 hashes; the raw token is shown once at creation and never displayed again.

Creating tokens

Admin UI: Settings → API tokens → Create. Pick scopes (see catalog below). Token shown once.

Programmatic creation (for managing many shops): use the /api/v1/api-keys endpoint with a master token (Enterprise).

Sending the token

GET /api/v1/customers/cus_abc123 HTTP/1.1
Host: api.sumeru.systems
Authorization: Bearer copt_live_aB3xY9zKqMnPwR2vT4uH7jL5sQ8eFc1g

Authorization header is required on every admin API call.

Token security

  • Tokens are stored hashed (SHA-256); raw never persisted
  • All token comparisons use crypto.timingSafeEqual
  • Tokens can be rotated anytime (old token revoked immediately)
  • Tokens are scope-gated; minimum-privilege principle
  • Admin UI shows last-used timestamp + IP per token

Scopes catalog

ScopeWhat it grants
customers:readRead Customer 360 profiles
customers:writeUpdate profiles, tag, enroll in journeys
orders:readRead order history
orders:writeCreate / cancel / refund orders
attribution:readRead attribution data
campaigns:readRead campaigns
campaigns:writeCreate / launch / pause campaigns
journeys:readRead journeys + enrollments
journeys:writeCreate / edit / pause journeys
products:readRead product catalog
products:writeUpdate products, prices, inventory
analytics:readRead dashboards + DIE recommendations
webhooks:readList webhook subscriptions
webhooks:writeCreate / update webhook subscriptions
audit:readRead audit log
*All scopes (admin only — discouraged)

Tokens declare their scopes at creation; the API enforces on every call. Calling an endpoint outside scope returns 403 with code scope_required.

Public endpoints — CORS or API key

Public endpoints are storefront-facing (called from your shop's JavaScript). They use:

CORS allow-list (default)

Each shop has a configured allow-list of domains (yourshop.com, www.yourshop.com). Requests from those origins succeed without an API key.

fetch('https://api.sumeru.systems/api/public/reviews', {
headers: { 'X-Shop-Domain': 'yourshop.com' }
})

API key (for headless / custom platforms)

For storefronts not on standard Shopify domains (Hydrogen, custom Next.js, etc.):

pk_live_xY7aB3cKz9...

Sent as X-Public-Key header. Public keys are scoped to public endpoints only — cannot access admin API.

Webhooks — HMAC signatures

Inbound (Sumeru receiving)

When Shopify / WhatsApp / marketplace fires a webhook to Sumeru, the body is HMAC-signed by the source. We verify:

  • Shopify: HMAC-SHA256 with shop secret, X-Shopify-Hmac-Sha256 header
  • WhatsApp (Meta): HMAC-SHA256 with app secret, X-Hub-Signature-256 header
  • Marketplaces: per-marketplace contract (per webhooks)

Signature mismatches reject with 401.

Providers that don't HMAC-sign

Some messaging providers (MSG91, Gupshup) do not HMAC-sign their delivery callbacks. For those, authentication is a shared secret token on the callback URL, and the handler fails closed (403) on a missing/incorrect token. See Messaging-provider webhooks.

Outbound (Sumeru notifying you)

When Sumeru sends events to your endpoint (Zapier, custom integration), we HMAC-sign the body. You verify:

const crypto = require('crypto');

function verifyWebhook(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}

Header: X-Sumeru-Signature: sha256=<hex>

Your endpoint secret is shown once at webhook subscription creation. Rotate via the admin or API.

Shopify scopes (the underlying)

Sumeru as a Shopify app declares these scopes; you'll see them at install. The set was pruned in 2026-05 to match actual usage:

ScopePowers
read_customers, write_customersCustomer 360, journeys, loyalty enrolment
read_orders, read_all_ordersOrder history + extended retention for predictive LTV
read_products, write_productsCatalog operations, product pipeline
read_inventory, write_inventoryInventory forecast + write-inventory
read_locationsLocation-scoped inventory writes
write_discounts, read_discountsLoyalty redemption + campaign discount codes
read_own_subscription_contractsSubscription MRR / churn / dunning in the Revenue Hub
write_pixels, read_customer_eventsWeb Pixel + UTM attribution capture
read_marketsMarkets & currency routing
write_marketing_eventsCampaign + marketing-event reporting
read_content, write_contentBlog engine + content publishing
read_files, write_filesGenerated images + media
read_translations, write_translationsTranslations engine
write_legal_policiesPolicy/compliance content

These are Shopify-side scopes the merchant approves at install. The Sumeru admin token scopes (above) gate Sumeru API access on top of whatever Shopify-side access the app has.

Subscription scope is deploy-gated

read_own_subscription_contracts is a protected scope — the subscription economics in the Revenue Hub stay hidden until it's approved for the app. Adding subscription scopes also triggers a Shopify re-review.

Token rotation

POST /api/v1/api-keys/<id>/rotate HTTP/1.1
Authorization: Bearer copt_live_...

Returns the new token in the response. The old token is revoked immediately. Plan for ~5 seconds of overlap if your client is mid-flight when rotation fires.

Brute-force / rate-limit protection

  • Failed auth attempts rate-limited at 10/min per IP
  • 100 failed attempts in 24h: IP blocked for 1h
  • 1000 failed attempts in 24h: alert to admin

Successful auth resets counters per IP.

See also