Overview
You resolve a customer withGET /customers/lookup. Pass a phone or a POS customer id, and get back one at-a-glance projection: the identity state, the customer name, both money classes, lifetime activity, and a derived badge.
The single rule that shapes this page: a stranger is not an error. All four identity states resolve to HTTP 200 with a status field, never a 404. Your point-of-sale can render a “register this customer” call to action instead of routing a normal lookup through an error path.
You typically call this at the start of an order, before payment.
This page covers the cashier-panel read. To attach identity inside an active checkout, or to
resolve a credential with no panel, see Identity and credentials.
The four-state model
GET /customers/lookup returns one of four identity states in data.status. Branch your panel UI on the state, not on the HTTP code.
registered
A verified customer. You get name, member-since, both money classes, lifetime activity, and a
derived badge. Greet them and show the balance.
pending_proof
A proto-wallet that has accrued
LOCKED cashback but has not verified a phone. The response
carries unclaimed_cashback. Prompt the customer to register and claim it.not_found
A stranger.
customer is null. Render the register call to action. This is a valid outcome at
HTTP 200, not an error.disabled
An operator-shut-off record. It surfaces as
not_found with no PII and no register affordance,
so only three values appear in status.Cross-enterprise reads resolve to not_found
Every lookup is scoped to the enterprise resolved from the verified POS-terminal JWT, never from request input. The same phone at a different enterprise resolves a different record set, so a cross-enterprise read surfaces asnot_found at HTTP 200, not a 403. A balance is scoped per merchant.
A 403 is reserved for a token that is valid but not authorized for the merchant or branch scope it claims.
Look up a customer
GET /customers/lookup is callable today. Authenticate with a POS-terminal JWT. Pass at least one of phone or provider_customer_id.
required
E.164 phone to resolve the customer. The typed phone is authoritative. It is normalized (trimmed)
the same way the top-up write path normalizes, so the read key byte-matches the stored record. At
least one of
phone or provider_customer_id is required.required
The POS provider’s own customer id. The no-phone fallback. Resolved within the calling
integration’s enterprise only.
Response fields
The lookup returns identity, both money classes, lifetime activity, and a badge in one payload.required
The identity state. One of
registered, pending_proof, not_found. A disabled record reports
as not_found. Branch your panel on this.The customer projection, or
null when status is not_found.Customer name, or
null when unknown.required
The E.164 phone the summary resolved to.
required
The underlying identity state machine value. One of
VERIFIED, PENDING_PROOF. A DISABLED
identity is never returned in a customer body, it surfaces as top-level status of not_found
with a null customer.required
ISO-8601 date-time of when this customer first became known at the merchant.
required
Actual (cashable) balance the customer can spend now, in minor units. The customer’s claim against
the merchant’s deferred-revenue liability.
required
Sum of
LOCKED promotional credit awaiting register-to-claim, in minor units. Surfaced, not
spendable until the customer verifies. See Money: actual vs promotional.ISO-4217 code for the money above, or
null when the customer has no money of either kind.required
Lifetime completed spend (payments out of the wallet), in minor units.
required
Lifetime count of completed purchases.
required
Completed purchases in the last 90 days.
ISO-8601 date-time of the most recent completed activity (pay or top-up), or
null.required
A derived classification. One of
new, regular, vip, lapsed. Precedence is fixed: the lapse
check is evaluated before the VIP check, so a high-value but inactive customer reads lapsed
rather than vip.Present (non-null) only when
LOCKED cashback is waiting, typically with status of
pending_proof. Carries amount_minor (integer, minor units) and currency (string or null).Echoed from the POS-terminal JWT scope, never from request input.
null for enterprise or
brand-scoped tokens.required
ISO-8601 date-time of when this projection was read.
This read surfaces a proto-wallet. It never credits, releases, or moves money. The
pending_proof
state gates every money side effect elsewhere, lookup is read-only.Badge values
Errors
Neither
phone nor provider_customer_id was supplied, or a value is malformed. Send at least one
valid credential.The terminal JWT is missing, expired, malformed, or has the wrong audience. Re-mint the token. See
Authentication.
The token is valid but not authorized for the merchant or branch scope it claims. A cross-enterprise
customer is not a 403, it resolves to
not_found at HTTP 200 to avoid a PII leak.Resolving identity without the panel
GET /customers/lookup is the cashier-panel read. To resolve a customer credential directly, or to attach identity to a checkout, use one of the identify resources. Both return a CustomerContext and never return a debit or authorize token. Resolving who the customer is never grants the right to move their balance.
Standalone identify
POST /identify resolves a customer for a balance-check or POS-display panel with no active
checkout. It accepts a discriminated credential and returns a CustomerContext.Session identify
POST /checkout/sessions/{id}/identify attaches identity to an open session and merges the
anonymous session into the customer.POST /identify
The standalone identify accepts one credential, discriminated bycredential_type, and returns a CustomerContext. Supported credential_type values are phone, card_fingerprint, short_code, provider_customer_id, and qr. Call GET /capabilities for the enabled set. Authenticate with an API key or a POS-terminal JWT. Send the Idempotency-Key header on this call.
required
Request metadata. See Conventions.
required
The merchant, branch, terminal, and cashier context for this call.
required
Exactly one credential, discriminated by
credential.credential_type. A credential_type not in
the supported set returns CREDENTIAL_TYPE_UNSUPPORTED listing the supported values.required
One of
registered (fully enrolled, balance readable and spendable), pending_proof (enrolled but
not yet identity-verified, balance visible, spend may be gated by the POS), not_found (no customer
for this credential under this enterprise). not_found is HTTP 200 with a null balance, not a
404, so polling UIs can distinguish “no wallet” from a server error.The POS-facing identity id.
null when resolution_state is not_found.The wallet program binding.
null when resolution_state is not_found. Ambiguity raises HTTP 422
WALLET_PROGRAM_AMBIGUOUS instead of returning this field.The ledger id.
null when resolution_state is not_found or the wallet has no credits yet.required
An opaque correlation id. Persist it and echo it where a call accepts one.
Customer display name, or
null when unknown.Current loyalty tier label, or
null when the program has no tiering.Wallet balance breakdown, or
null when resolution_state is not_found.Where to go next
Identity and credentials
The credential model, and why identify never returns a debit token.
Enrollment and signup
Turn a not_found stranger or a pending_proof proto-wallet into a verified customer.
Money: actual vs promotional
The two-class balance the lookup surfaces: spendable actual and LOCKED promo.
The checkout session
Group the calls of one customer interaction and attach identity to it.