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_purchase_optionsPre-order and subscription selling plans
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_online_store_pages, write_online_store_pagesStorefront pages, internal-link writes
read_online_store_navigationMenu and navigation structure
read_publications, write_publicationsSales-channel publication of products and collections
read_files, write_filesGenerated images + media
read_translations, write_translationsTranslations engine
write_legal_policiesPolicy/compliance content
read_themesTheme-publish correlation on SEO crawl-regression incidents

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.

read_themes requires re-consent on existing installs

read_themes was added in 2026-08 for the themes/publish and themes/update webhooks that correlate theme deploys with SEO crawl regressions. It is read-only and non-PCD, but adding any scope forces re-authorisation: every existing install must re-consent before those webhooks start delivering.

Until an install re-consents, theme changes stay invisible to the regression incident drawer — which reports that state explicitly rather than implying the site was untouched. The granted scope is read per shop from the stored session, so the app never claims coverage it does not have.

Ship the scope with shopify app deploy, then confirm re-auth completes; the app/scopes_update subscription is what tells you it did.

Reading the granted scope list

Shopify collapses read_X into write_X when both are granted, so a stored session's scope string will look shorter than the declared list. Diff on the implied set, not on the literal string, before concluding a scope is missing.

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