Authentication & scopes
Three auth surfaces
Each surface has a distinct auth model — appropriate to its threat surface and use case.
| Surface | Auth method | Granularity |
|---|---|---|
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 signature | Per-source secret |
| Webhooks (outbound) | HMAC SHA-256 signature you sign | Per-endpoint secret |
Admin API — bearer tokens
Token format
copt_live_aB3xY9zKqMnPwR2vT4uH7jL5sQ8eFc1g
copt_prefix identifies as Sumeru Systems API tokenlive_(ortest_) 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
| Scope | What it grants |
|---|---|
customers:read | Read Customer 360 profiles |
customers:write | Update profiles, tag, enroll in journeys |
orders:read | Read order history |
orders:write | Create / cancel / refund orders |
attribution:read | Read attribution data |
campaigns:read | Read campaigns |
campaigns:write | Create / launch / pause campaigns |
journeys:read | Read journeys + enrollments |
journeys:write | Create / edit / pause journeys |
products:read | Read product catalog |
products:write | Update products, prices, inventory |
analytics:read | Read dashboards + DIE recommendations |
webhooks:read | List webhook subscriptions |
webhooks:write | Create / update webhook subscriptions |
audit:read | Read 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-Sha256header - WhatsApp (Meta): HMAC-SHA256 with app secret,
X-Hub-Signature-256header - Marketplaces: per-marketplace contract (per webhooks)
Signature mismatches reject with 401.
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:
| Scope | Powers |
|---|---|
read_customers, write_customers | Customer 360, journeys, loyalty enrolment |
read_orders, read_all_orders | Order history + extended retention for predictive LTV |
read_products, write_products | Catalog operations, product pipeline |
read_inventory, write_inventory | Inventory forecast + write-inventory |
read_locations | Location-scoped inventory writes |
write_discounts, read_discounts | Loyalty redemption + campaign discount codes |
read_own_subscription_contracts | Subscription MRR / churn / dunning in the Revenue Hub |
write_purchase_options | Pre-order and subscription selling plans |
write_pixels, read_customer_events | Web Pixel + UTM attribution capture |
read_markets | Markets & currency routing |
write_marketing_events | Campaign + marketing-event reporting |
read_content, write_content | Blog engine + content publishing |
read_online_store_pages, write_online_store_pages | Storefront pages, internal-link writes |
read_online_store_navigation | Menu and navigation structure |
read_publications, write_publications | Sales-channel publication of products and collections |
read_files, write_files | Generated images + media |
read_translations, write_translations | Translations engine |
write_legal_policies | Policy/compliance content |
read_themes | Theme-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.
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 installsread_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.
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.