What you build here
You make your first authenticated calls against the Feddi Partner API. By the end you have probed the platform with no key, read what your integration can transact in, exchanged yourx-api-key for a short-lived terminal JWT, and resolved a customer credential to a profile.
This is the on-ramp. When you are ready to charge a wallet, continue to Make your first payment.
Every path lives under
/v1/partner. The base URL is https://api.feddi.io/v1/partner for production and https://api.dev.feddi.io/v1/partner for dev. Build and self-test against dev first.Before you start
You need a Feddi API key. Request sandbox credentials from your Feddi contact. The key is platform- or merchant-scoped and formatted asfddi_.... The tenant boundary is enforced on it: context.merchant_id must resolve to a merchant the key is authorized for. A merchant_id outside the key’s authority is rejected, never honored.
The four steps
GET /health and GET /capabilities are GA (stable). The token exchange and identify are Beta: callable today, contract may still change additively. Treat GET /capabilities and GET /openapi as the runtime source of truth for what is mounted and enabled for your integration.
Probe health, no key required
GET /health is unauthenticated. It returns immediately with no database read or tenant resolution, so a POS terminal can gate feature availability without holding a key.Read your capabilities
GET /capabilities takes your x-api-key and returns the currencies, credential types, features, and rate limits enabled for your integration. Read this before assuming any enum is enabled.Exchange your key for a terminal JWT
POST /auth/token exchanges your x-api-key for a short-lived terminal-scoped bearer token. Refresh it before any money call.Identify a customer
POST /identify resolves a customer credential (phone, card fingerprint, short code, QR, or provider customer id) to a profile carrying wallet state and balance. Identify never returns a debit token.Step 1: Probe health
Call health first. A200 with status of up and an empty degraded_subsystems array means the platform is nominal. A non-empty array signals a partial outage: surface a warning at the POS, but you may continue operations that are not degraded.
required
Always
true on HTTP 200. The platform is reachable.required
One of
up (all subsystems nominal) or degraded (one or more subsystems impaired, platform still serving).required
Named subsystems currently impaired, for example
['promo_engine', 'webhook_fanout']. Empty when status is up.Health carries no tenant data by design. It is mounted ahead of the auth layer, so it is the cheapest liveness probe.
Step 2: Read capabilities
Capabilities is the authoritative descriptor of what your integration can transact in. Send yourx-api-key. Read this before assuming any currency, credential type, or feature flag exists.
required
Your platform- or merchant-scoped API key (
fddi_...). The integration is resolved from this key.required
ISO 4217 codes of currencies this integration can transact in. An empty array means no wallet program is provisioned yet, not that currencies are unsupported globally.
required
The credential types this integration accepts for identity resolution (who the customer is), for example
phone, short_code, and qr. This array is the authoritative enabled set. Sending an unsupported type returns CREDENTIAL_TYPE_UNSUPPORTED with the supported set in error.details.required
The credential types this integration accepts as wallet-debit authorization (proof the customer approves this payment), for example
otp and qr. Identity resolution and payment authorization are separate sets: a type listed in one is not implicitly enabled in the other.required
Named feature flags keyed by operation. A missing key or
false means the feature is not available for your integration.required
Per-operation rate limit descriptors with a
per_minute integer. These match the X-RateLimit-* headers enforced on each endpoint.Step 3: Exchange your key for a terminal JWT
Per-terminal money and identity calls use a POS terminal JWT, not the rawx-api-key. You exchange the key for a JWT, then send Authorization: Bearer <jwt> on those calls.
The JWT is short-lived: TTL 600 seconds. It carries scope claims (integration_id, enterprise_id, brand_id, branch_id) derived from the key, plus an optional server-validated cashier_id. Refresh it before any money call.
The exchange takes the canonical envelope. Send meta and context, and an Idempotency-Key header. A retried exchange within the validity window replays the same JWT.
required
A partner-generated UUID. The single deduplication key for mutating endpoints (24h TTL). Same key with the same payload replays a byte-identical response. Same key with a different payload returns
IDEMPOTENCY_KEY_REUSED (HTTP 422).required
Request metadata. Requires
partner_request_id (a UUID, for correlation only, never the deduplication key) and api_version. Send occurred_at and sent_at when you have them.required
Request context. Requires
merchant_id (key-enforced, never free-text). branch_id, terminal_id, and cashier_id are optional.required
Optional cashier identifier to sign into the JWT scope. Server-validated: 64 characters or fewer, no control characters.
required
The compact JWT string. Send it as
Authorization: Bearer <token> on subsequent terminal-scoped calls.required
Always
Bearer.required
Seconds until expiry (600).
required
Absolute expiry timestamp (RFC 3339).
required
Scope claims derived from the key:
integration_id, enterprise_id, and the nullable brand_id, branch_id, cashier_id.required
Mirrors the originating key’s sandbox flag, carried as a JWT claim.
Step 4: Identify a customer
Identify resolves one customer credential to a profile. Send the terminal JWT from Step 3 asAuthorization: Bearer <jwt>. The standalone endpoint also accepts x-api-key for balance-check panels and cashier lookup screens where there is no active checkout.
The call carries the canonical envelope and an Idempotency-Key header. The credential is a discriminated union on credential_type. The enabled set is gated by GET /capabilities.
required
Exactly one credential, discriminated by
credential_type. One of phone, card_fingerprint, short_code, qr, or provider_customer_id. Provide the matching value field alongside it, for example phone for +97433001122.required
Request metadata. Requires
partner_request_id and api_version.required
Request context. Requires
merchant_id.data.resolution_state field returns one of three states:
required
One of
registered (balance readable and spendable), pending_proof (enrolled but not yet identity-verified, balance visible, spend may be gated by the POS), or not_found (no wallet user for this credential, returned as HTTP 200 with a null balance so polling UIs distinguish it from a server error).A credential that matches a customer enrolled in multiple active programs is not a resolution state. It returns the typed
WALLET_PROGRAM_AMBIGUOUS error (HTTP 422) with the candidate wallet_program_id values in error.details.candidates.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. An ambiguous match returns WALLET_PROGRAM_AMBIGUOUS (HTTP 422) instead of this field.required
The ledger id. Null when
resolution_state is not_found or the wallet has no credits yet.required
Opaque correlation id for this identity resolution. Persist and echo it where a call accepts one.
required
The wallet balance breakdown, or null when
resolution_state is not_found. Money is always minor units with an explicit currency: actual_minor (real money), promo_available_minor (spendable promotional credit), promo_locked_minor (promotional credit gated on an activation condition), pending_topups_minor (in-flight, not spendable), and currency (ISO 4217).credential_type. Read Identity and credentials for the full set, and confirm what your integration accepts via GET /capabilities.
Where to go next
Make your first payment
Identify, charge promo-first, and recover from insufficient funds, worked end to end.
Authentication
The two API credentials, the key lifecycle, and JWT scope claims.
Build with an AI agent
Read capabilities and the OpenAPI spec at runtime, validate bodies, and handle every typed error.
Idempotency and errors
The deduplication contract, typed error codes, and recovery actions.