A standalone entry point that opens or closes a session is not yet available. Today you attach identity to a session with the call below, and you settle by calling
POST /v1/partner/payments. See Payments and redemption.Session states
A session moves through four states:OPEN: the session exists and carries ananonymous_session_id, but no identity is attached yet.IDENTIFIED: a credential resolved the customer. The session now carries the customer’s wallet context.AUTHORIZED: written only by a successful session payment. You never set this directly.CLOSED: settled into a receipt of record.ABANDONED: the interaction ended without settlement. A session that times out auto-abandons at its expiry.
OPEN to IDENTIFIED. It accepts a session that is OPEN or already IDENTIFIED (a re-identify on an identified session is allowed and idempotent). A session that is CLOSED or ABANDONED cannot be identified and returns VALIDATION_ERROR.
The session lifecycle runs identify, then settle, then close:
Attach identity to a session
POST /v1/partner/checkout/sessions/{id}/identify attaches a customer identity to an open session and returns their CustomerContext. It dispatches on a credential discriminated union (phone, card_fingerprint, short_code, provider_customer_id, or qr) and merges the session’s anonymous_session_id into the resolved customer (a one-way, idempotent merge).
Request
This call is authenticated with a POS terminal JWT. Send exactly one credential block per call;credential_type is the discriminator.
required
Partner-generated UUID, the one dedup key. Stable for this one logical operation, 24h TTL. Same key plus same payload replays the original response; same key plus a different payload returns
IDEMPOTENCY_KEY_REUSED (HTTP 422).required
The checkout session id (UUID) to attach identity to. The session must be
OPEN or IDENTIFIED. When you also send context.feddi_session_id, it must match this path value.required
Request context. Carries
merchant_id (UUID, required) and the optional branch_id, terminal_id, cashier_id, partner_session_id, and feddi_session_id. The merchant_id is enforced against the calling credential’s authority, not trusted as free text.required
One credential block, discriminated by
credential_type. One of phone, card_fingerprint, short_code, provider_customer_id, or qr. Set the matching value field: phone (E.164), provider_customer_id, card_fingerprint (masked-PAN hash), short_code (5-character wallet code), or qr (raw QR payload). The enabled set for your integration is in GET /v1/partner/capabilities under supported_identify_credential_types.required
Disambiguate when the identity resolves to multiple wallet programs. Omit it on the first call; if the response is
WALLET_PROGRAM_AMBIGUOUS, re-send with the chosen wallet_program_id.required
Request metadata. Carries
partner_request_id (UUID, your correlation id, never the dedup basis), api_version, and the optional occurred_at and sent_at timestamps.Response
A200 returns a CustomerContext. It never contains a debit token.
required
One of
registered, pending_proof, not_found. registered is fully enrolled, balance readable and spendable. pending_proof is enrolled but not yet identity-verified; balance is visible but spend may be gated by the POS. not_found means no wallet user resolved for this credential under this enterprise, returned as HTTP 200 to distinguish it from a server error. Render a register CTA, not an error.required
Opaque correlation id for this identity resolution. Persist it and echo it where a call accepts one.
The customer’s
wallet_user_id. Null when resolution_state is not_found.The wallet program binding. Null when
resolution_state is not_found. An ambiguous match returns WALLET_PROGRAM_AMBIGUOUS (HTTP 422) rather than this field.The canonical
wallet_id (ledger row). Null when resolution_state is not_found or the customer’s wallet has not been created yet (no credits).Customer display name. Null when unknown.
Loyalty tier badge label. Null when the program has no tiering.
Wallet balance breakdown, or null when
resolution_state is not_found. Carries the two money classes and a per-grant breakdown.balance object splits the wallet into two money classes. See Money: actual vs promotional for the full model.
The response meta block carries two opaque fields. data_completeness_score is an opaque integer Feddi may use internally; no action required. decision_trace_id is an opaque correlation id; persist and echo it where a call accepts one.
Example
Errors
Inspecterror.code, not the HTTP status, for programmatic branching. See Idempotency and errors for the full catalog.
Invariants
Hold these when you render the result:- Identify never returns a debit token. To debit, call
POST /v1/partner/paymentswith a payment credential. not_foundis HTTP200, not404, withresolution_state: not_found. Render a register CTA, not an error.- A
card_fingerprintlink is probationary until the phone is verified. A masked-PAN collision never auto-acts on money. - Identify never releases a
LOCKEDpromotional grant on its own. Promo release stays gated on a verified identity plus the claim flow. See PENDING_PROOF and promo release. - A customer’s balance is scoped per merchant.
Next
Money: actual vs promotional
The two-class balance model that runs through every endpoint.
Identity and credentials
The credential types and how a call resolves who the customer is.
Payments and redemption
The promo-first wallet-debit endpoint you settle against.
Make your first payment
The identify-to-pay loop, worked end to end.