Exchange a Shopify customer access token for a session token
POST /session-tokens/exchange| Scope | None: called without an API key |
| Customer session token | Not needed |
| Rate limit class | read (limits) |
| Idempotency key | Not needed: this call has no side effect |
| MCP tool | Not exposed: the exchange runs on the shopper’s device |
For headless storefronts with no server of their own: called from the browser or app, without an API key. Send the signed-in shopper’s Customer Account API access token; Charm asks Shopify who it belongs to and mints a session token for that customer, exactly like POST /customers/{customer_id}/session-tokens. Classic Storefront API customerAccessToken values are not accepted. Limited to 20 exchanges per minute per IP address. Theme stores get the same token from /apps/charm/session-token on their own storefront.
Request body
Section titled “Request body”| Field | Type | Description |
|---|---|---|
shop | string | The store’s .myshopify.com domain. |
customer_access_token | string | The shopper’s Customer Account API access token (Hydrogen: customerAccount). |
scopes | array of string: read, write | Defaults to ["read"]. (optional) |
ttl_seconds | integer | (optional) |
Response 201
Section titled “Response 201”Returns SessionToken.
| Field | Type | Description |
|---|---|---|
token | string | chrm_st_…; send as Authorization: Bearer <token>. |
token_type | "Bearer" | |
customer_id | string | Shopify customer GID, e.g. gid://shopify/Customer/123. Accepted by every endpoint that takes a customer id, as is the numeric legacy_id. |
scopes | array of string: read, write | |
api_scopes | array of string | |
expires_in | integer | |
expires_at | string |
{ "token": "chrm_st_eyJ2IjoxLCJzaG9wIjoi…", "token_type": "Bearer", "customer_id": "gid://shopify/Customer/7712345", "scopes": [ "read" ], "api_scopes": [ "api scopes" ], "expires_in": 1, "expires_at": "2026-10-01T09:30:00.000Z"}Example
Section titled “Example”curl -X POST "https://charm.appfleece.app/api/v1/session-tokens/exchange" \ -H "Authorization: Bearer chrm_live_..." \ -H "Content-Type: application/json" \ -d '{"shop":"your-store.myshopify.com","customer_access_token":"customer access token"}'Errors
Section titled “Errors”unauthorized · invalid_token · insufficient_scope · access_required · validation_error · rate_limited
Every error body carries code, message, request_id and a doc_url pointing at the matching entry in the error catalog.