Skip to content
Docs
Concept

A buyer in a native app

A native app ships the channel's publishable key in its binary, which is what publishable means, and keeps its Live session by a session token the app stores. The rest is the website flow over HTTPS.

A native app has no same-origin story, so the publishable key ships in the app binary. That is what publishable means: it identifies one channel and grants nothing beyond the low-trust surface. The app talks to Live over HTTPS and the sequence is the one on A buyer on your website.

The credential. The publishable key (tlpk_, low trust) is in the app and on every request to Live; the channel is the key's. Session state lives in the Live session, keyed by a session token the app stores and sends as X-Live-Session. The customer token, once the buyer has signed in, is kept in that Live session and escalates it; the app never holds a customer token of its own.

A buyer in a native app: the publishable key ships in the binary1 List what is on sale: Native app to Live: GET /sales/listings (tlpk_ (in the app binary), X-Live-Session); Live to Backstage API: GET /internal/live/listings (Live's service token, tlpk_ (held by Live), channel: the key's); Backstage API to Native app: what the channel sells, in the low-trust view shape. 2 Make an order: Native app to Live: POST /sales/carts, items, POST /sales/carts/{id}/checkout (tlpk_ (in the app binary), X-Live-Session, X-Live-Session); Live to Backstage API: POST /internal/live/carts, items, .../checkout (Live's service token, tlpk_ (held by Live), channel: the key's); Live to Backstage API: POST /internal/live/orders/{id}/payments (Live's service token, tlpk_ (held by Live)); Backstage API to Native app: the order; Live keeps its order access token in the session. 3 Authenticate a customer: Native app to Live: POST /sales/customer-auth/magic-link (tlpk_ (in the app binary), X-Live-Session); Live to Backstage API: POST /internal/live/customer-auth/magic-link (Live's service token, tlpk_ (held by Live)); A 15 minute code is emailed to the buyer.; Native app to Live: POST /sales/customer-tokens { magic_link, code } (tlpk_ (in the app binary), X-Live-Session); Live to Backstage API: POST /internal/live/customer-tokens { magic_link, code } (Live's service token, tlpk_ (held by Live)); Backstage API to Live: customer token, 30 minutes (customer token); Live keeps the customer token in the session; the client never holds it. It escalates the session. The publishable key still authenticates every call.. 4 Read the order back: Native app to Live: GET /sales/my-orders (tlpk_ (in the app binary), X-Live-Session, X-Live-Session); Live to Backstage API: GET /internal/live/my-orders (Live's service token, tlpk_ (held by Live), customer token); Live to Backstage API: GET /internal/live/orders/{id} (Live's service token, tlpk_ (held by Live), order access token); Backstage API to Native app: the buyer's orders, or the one order the session may see1 List what is on sale2 Make an order3 Authenticate a customer4 Read the order backNative appLiveBackstage APIA 15 minute code is emailed to the buyer.Live keeps the customer token in the session; the client never holds it.It escalates the session. The publishable key still authenticates every call.GET /sales/listingstlpk_ (in the app binary)X-Live-SessionGET /internal/live/listingsLive's service tokentlpk_ (held by Live)channel: the key'swhat the channel sells, in the low-trust view shapePOST /sales/carts, items, POST /sales/carts/{id}/checkouttlpk_ (in the app binary)X-Live-SessionX-Live-SessionPOST /internal/live/carts, items, .../checkoutLive's service tokentlpk_ (held by Live)channel: the key'sPOST /internal/live/orders/{id}/paymentsLive's service tokentlpk_ (held by Live)the order; Live keeps its order access token in the sessionPOST /sales/customer-auth/magic-linktlpk_ (in the app binary)X-Live-SessionPOST /internal/live/customer-auth/magic-linkLive's service tokentlpk_ (held by Live)POST /sales/customer-tokens { magic_link, code }tlpk_ (in the app binary)X-Live-SessionPOST /internal/live/customer-tokens { magic_link, code }Live's service tokentlpk_ (held by Live)customer token, 30 minutescustomer tokenGET /sales/my-orderstlpk_ (in the app binary)X-Live-SessionX-Live-SessionGET /internal/live/my-ordersLive's service tokentlpk_ (held by Live)customer tokenGET /internal/live/orders/{id}Live's service tokentlpk_ (held by Live)order access tokenthe buyer's orders, or the one order the session may seeLow trust: publishable keyHigh trust: staff, organisation key, deviceWhere the channel comes fromCarried on top: escalates, never authenticatesNot a credential

The four moments#

MomentWhat happensCredential
List what is on saleGET /sales/listings to Live under the publishable key.tlpk_, in the app; the channel is the key's
Make an orderThe first cart creates a Live session; the app stores the session token and sends it as X-Live-Session. POST /sales/carts, items, checkout, then POST /sales/orders/{id}/payments.tlpk_ plus the session token
Authenticate a customerPOST /sales/customer-auth/magic-link, the buyer types the 15 minute code into the app, POST /sales/customer-tokens with { type: "magic_link", code }. Live stores the customer token in the session.tlpk_ authenticates; the customer token is carried on top
Read the order backGET /sales/my-orders under the escalated session, or GET /sales/orders/{id} where the session holds the order access token.tlpk_, plus the token carried on top

What the key in the binary can and cannot do#

  • It can be extracted, and that is fine. Anyone who reads it out of the app can do exactly what the app can do: list what the channel sells, open a cart, start a checkout and, with a buyer's own magic link code, see that buyer's orders. It reaches the low-trust surface only, whose responses are declared views.
  • It cannot reach anything administrative. Cost, allocation, other customers, other channels and every write to the catalogue are on the high-trust surface, which a publishable key cannot authenticate to.
  • The session token is not a credential either. It names a Live session. Losing it loses a cart; it does not grant anything the key did not.

The queue gate and the rate limiter sit in front of Live for an app exactly as for a browser, so a gated on-sale is one implementation for both.