Overview
The platform endpoints answer three questions before you write a transactional call: is the API reachable, what is this integration allowed to do, and what does the contract say. All three are live and safe to call today. We recommend readingGET /capabilities before you assume any enum, currency, or feature flag. It is tenant-scoped and authoritative. The contract at GET /openapi is served live from the running API, so it never drifts from the surface you call.
This page also covers standalone identity resolution with POST /identify, which resolves a customer credential to their wallet state without opening a checkout session.
Operations
The authoritative list of mounted routes is always
GET /capabilities and GET /openapi, never a static table. Call them at runtime to discover what your integration can transact in.Health versus capabilities
These two endpoints look similar and serve different jobs. Use each for its own purpose.GET /health is liveness
Unauthenticated, stateless, no database read, no tenant resolution. Mounted ahead of the auth layer. Carries zero tenant data. Use it for load-balancer probes and POS terminals that gate feature availability without holding a key.
GET /capabilities is read-before-you-assume
Authenticated and tenant-scoped. Returns the supported currencies, credential types, feature flags, and rate limits for the integration your key authenticated as. Read it before you build against any enum.
GET /health
Check that the platform is reachable. The response is{ ok: true, status: "up", degraded_subsystems: [] } when everything is nominal.
A non-empty degraded_subsystems array means a partial outage. Surface a warning on the POS, and continue for operations that do not depend on the impaired subsystem.
required
Always
true on HTTP 200. The platform is reachable.required
One of
up (all subsystems nominal) or degraded (one or more subsystems impaired but the platform is still serving).required
Named subsystems currently impaired, for example
["promo_engine", "webhook_fanout"]. Empty when status is up.GET /health returns a bare liveness object, not the standard response envelope. It is mounted before auth so it can stay this lean. Every other operation on this page returns the { ok, data, error, meta } envelope.GET /capabilities
Read what your integration can transact in. This is the runtime source of truth for currencies, credential types, feature flags, and rate limits, scoped to the integration yourx-api-key resolves to.
Read this before assuming any enum. Empty collections mean a feature is not yet provisioned for this integration, not that it is unsupported globally. The descriptor is additive: it never over-claims.
required
Your integration’s API key (format
fddi_...). The descriptor returned is scoped to the integration this key authenticates as.required
The single canonical API version string, for example
2026-06-01.required
The integration UUID the key authenticated as.
required
Provider name, for example
ODOO or FOODICS.required
ISO 4217 codes this integration can transact in, derived from active wallet program assignments. Empty means none provisioned yet.
required
Credential types the integration’s wallet programs accept for identity resolution (who the customer is). This array, alongside the
openapi schema, is the authoritative enabled set. Empty means none provisioned yet.required
Credential types accepted as wallet-debit authorization (proof the customer approves this payment), for example
otp and qr. A type enabled for identification is not implicitly enabled for payment; the two sets are independent.required
Named feature flags keyed by operation slug, with boolean values. A missing key or
false means the feature is not available for this integration.required
Per-operation limits, each carrying a
per_minute integer that matches the X-RateLimit-* headers enforced on the endpoint.404 with RESOURCE_NOT_FOUND means the integration resolved from the API key is not found or not active.
GET /openapi
Fetch the CI-validated OpenAPI 3.1 contract for every mounted/v1/partner/* route. Validate your integration against this document rather than against generated TypeScript types.
Every mounted route has an entry in the spec, so it stays in sync with the live surface. It is unauthenticated so tooling (Postman, Swagger UI, custom SDK generators) can bootstrap without a key.
GET /capabilities: /openapi tells you what shapes exist, and /capabilities tells you which of them your integration can call right now.
POST /identify
Resolve a customer credential to their wallet state without opening a checkout session. Use this for balance-check panels, cashier lookup screens, and any POS-display surface where there is no active session. The request carries exactly one credential, discriminated bycredential_type. The credential types are phone, card_fingerprint, short_code, qr, and provider_customer_id. Read GET /capabilities and the openapi schema for the set enabled on your integration. The response returns the customer’s wallet state, both money classes, the wallet program binding, and an identity_trace_id for downstream correlation.
Resolution states
The response carries the outcome indata.resolution_state, one of three values. An ambiguous match does not return a resolution state: it returns a typed WALLET_PROGRAM_AMBIGUOUS error (HTTP 422) instead. See Identify ambiguity below.
registered
Fully enrolled. Balance is readable and spendable through the normal payment flow.
pending_proof
Enrolled but not yet identity-verified. Balance is visible, but spend may be gated by the POS until the customer proves their phone. See PENDING_PROOF and promo release.
not_found
No wallet user for this credential under this enterprise. Returned as HTTP 200 with a null
balance, not 404, so polling UIs can distinguish ‘no wallet’ from a server error.Request
Request fields
required
A client-generated UUID. A retry with the same key replays the original result. Deduplication is keyed exclusively on this header, never on
partner_request_id.required
Request metadata. Requires
partner_request_id (a correlation UUID, not the dedup key) and api_version. Optional occurred_at (when the event happened at the POS) and sent_at (when you sent the request); both are RFC 3339 timestamps. Send both timestamps.required
Request context. Requires
merchant_id (a UUID that the calling credential must be authorized for, never a free-text trust field). Optional branch_id, terminal_id, cashier_id, partner_session_id, and feddi_session_id. cashier_id is server-trusted only when signed into the terminal JWT.required
A discriminated union selected by The
credential_type. Exactly one credential field must be present alongside credential_type:card_fingerprint link is probationary and reversible until the phone is verified, so a masked-PAN match never auto-acts on money.Response fields
required
One of
registered, pending_proof, or not_found. See Resolution states.required
The POS-facing identity id. Null when
resolution_state is not_found.required
The wallet program binding. Null when
resolution_state is not_found. Ambiguity raises HTTP 422 WALLET_PROGRAM_AMBIGUOUS instead of returning this field.required
The ledger id. Null when
resolution_state is not_found or the customer’s wallet has not yet been created (no credits yet).required
Opaque correlation id for this identity resolution event. Persist it and echo it where a call accepts one.
required
Customer display name. Null when unknown.
required
Current loyalty tier badge label, for example
Gold. Null when the program has no tiering.required
Wallet balance breakdown. Null when
resolution_state is not_found. Fields:actual_minor(integer): real money funded by top-ups, in minor units.promo_available_minor(integer): released promotional credit, spendable, in minor units.promo_locked_minor(integer): locked promotional credit not yet spendable, in minor units.pending_topups_minor(integer): top-up value in flight, not spendable, in minor units.currency(string): ISO 4217 code for all minor-unit fields above.promo_grants(array): per-grant breakdown, sorted FIFO byexpires_at.
promo_grants carries source (one of CASHBACK, RELOAD_BONUS, SKU_TOPUP_BONUS, GATEWAY_BONUS, SIGNUP_BONUS), state (LOCKED or RELEASED), remaining_minor (integer), and expires_at (RFC 3339 timestamp, or null if the grant does not expire).balance object exposes both money classes distinctly. Treat actual_minor as real money and promo_available_minor as released spendable promotional credit; promo_locked_minor is gated on signup or first top-up. Never conflate them. Read Money: actual vs promotional for the full model.
POST /identify is the standalone, sessionless variant. The session-anchored form, which writes the resolved identity into an open checkout session, is documented under The checkout session.Errors
identify is rate-limited per integration to prevent enumeration. A 429 response carries Retry-After and X-RateLimit-* headers; back off and retry.
The
meta.data_completeness_score field, when present, is an opaque integer Feddi may use internally. No action required. The meta.decision_trace_id field is an opaque correlation id. Persist it and echo it where a call accepts one.Where to go next
Customers and identification
The cashier-panel lookup and the four-state customer read model in depth.
Authentication
Platform key versus terminal JWT, and how the tenant boundary is enforced.
Idempotency and errors
The Idempotency-Key header and the typed error codes you handle on every mutating call.
Conventions
The response envelope, money-as-minor-integers, versioning, and pagination.