Getting StartedQuickstart

Quickstart

Make your first authenticated call to the Feddi Partner API in a few minutes: probe health, read capabilities, exchange your key for a terminal JWT, and identify a customer.

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 your x-api-key for a terminal JWT, and resolved a customer credential to a profile.

This is the on-ramp, not the full loop. For the complete open → identify → basket → offers → pay → close flow, go to The Golden Path.

Base URL is https://api.feddi.io for production and https://sandbox.api.feddi.io for sandbox. Every path lives under /v1/partner. Build and self-test in sandbox first: sandbox rows are flagged and never touch production money or PII.

What you need first

You need one of two API credentials before step 3.

The raw_key from key provisioning is shown once. Store it. A lost key is regenerated, never recovered. See Authentication for the full key lifecycle.

The five steps

This sequence mixes maturities. Health and capabilities are GA. The terminal JWT exchange and identify are Beta (callable today, shapes may still tighten). Sandbox key provisioning is Planned. Treat GET /v1/partner/capabilities and GET /v1/partner/openapi as the runtime source of truth for what is actually mounted.

Get a sandbox key (or use your provisioned key)

POST /v1/partner/auth/keys/sandbox returns sandbox credentials. This is Planned, so until it is mounted, use your provisioned platform x-api-key (fddi_...) against the sandbox base URL.

Probe health, no key required

GET /v1/partner/health is GA and unauthenticated. It returns immediately with no DB read or tenant resolution, so a POS terminal can gate feature availability without holding a key.

Read your capabilities

GET /v1/partner/capabilities is GA. Send your x-api-key. It returns the supported currencies, credential types, and feature flags for your integration. Read this before assuming any enum is enabled.

Exchange your key for a terminal JWT

POST /v1/partner/auth/token is Beta. It exchanges your x-api-key for a short-lived (600s TTL) terminal-scoped bearer token. Refresh it before any money call.

Make a first identify call

POST /v1/partner/identify is Beta. Resolve a customer credential (phone, card fingerprint, short code, or QR) to a profile carrying wallet state and promo balance. Identify never returns a debit token or any artifact that permits a ledger mutation.

Step 2: Probe health (GA, no key)

Call health first. A 200 with status: "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 for non-degraded operations.

curl https://sandbox.api.feddi.io/v1/partner/health

Health carries no tenant data by design. It is mounted before the auth middleware, so it is the cheapest possible liveness probe.

Step 3: Read capabilities (GA, with x-api-key)

Capabilities is the authoritative descriptor of what your integration can transact in. An integrating agent (POS adapter, SDK, or automation) reads this before assuming any currency, credential type, or feature flag exists.

header
x-api-keystring
Required

Your platform-scoped API key (fddi_...). The integration is resolved from this key.

curl https://sandbox.api.feddi.io/v1/partner/capabilities \
  -H "x-api-key: fddi_your_key_here"

Key fields to read off the response:

supported_currenciesarray

The currencies your integration's active wallet programs transact in. An empty array means no program is provisioned yet, not that currencies are unsupported globally.

supported_credential_typesarray

The customer credential types this integration accepts. The core types are phone, card_fingerprint, and short_code; additional types (provider_customer_id, and qr in QR-capable contexts) exist under the same discriminated-union pattern. This array is the authoritative enabled set, alongside the openapi schema. Sending an unsupported type returns CREDENTIAL_TYPE_UNSUPPORTED with the supported set in the error details.

featuresobject

Named feature flags keyed by operation. The descriptor grows additively as endpoint bites land; it never over-claims.

Cache this response, honor its Cache-Control header, and re-fetch on a CREDENTIAL_TYPE_UNSUPPORTED or CURRENCY_NOT_SUPPORTED typed error. See Availability & Maturity for the full runtime-discovery model.

Step 4: Exchange your key for a terminal JWT (Beta)

Per-terminal money and session calls use a POS terminal JWT, not the raw x-api-key. You exchange the key for a JWT, then send Authorization: Bearer <jwt> on session, payment, and identify 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 a 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 window replays the same JWT).

header
Idempotency-Keystring
Required

A partner-generated UUID. A retried exchange within the window replays the same JWT.

curl -X POST https://sandbox.api.feddi.io/v1/partner/auth/token \
  -H "x-api-key: fddi_your_key_here" \
  -H "Idempotency-Key: 6f1d2c3a-0000-4abc-9def-000000000001" \
  -H "Content-Type: application/json" \
  -d '{
    "meta": { "partner_request_id": "aa07...", "occurred_at": "2026-06-05T09:00:00Z", "sent_at": "2026-06-05T09:00:00Z", "api_version": "2026-06-01" },
    "context": { "merchant_id": "11111111-1111-1111-1111-111111111111", "branch_id": "22222222-2222-2222-2222-222222222222", "terminal_id": "POS-360-0007", "cashier_id": "cashier-42" },
    "cashier_id": "cashier-42"
  }'

Step 5: Make a first identify call (Beta)

Identify resolves one customer credential to a profile. Use the terminal JWT you minted in step 4 (the standalone endpoint also accepts x-api-key for balance-check panels and cashier lookup screens where there is no active session).

The call carries the canonical envelope and an Idempotency-Key header. The data.resolution_state field returns one of three states: registered (balance readable and spendable), pending_proof (enrolled but not yet identity-verified, balance visible, spend 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.

curl -X POST https://sandbox.api.feddi.io/v1/partner/identify \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...signature" \
  -H "Idempotency-Key: 6f1d2c3a-0000-4abc-9def-000000000002" \
  -H "Content-Type: application/json" \
  -d '{
    "meta": { "partner_request_id": "bb08...", "occurred_at": "2026-06-05T09:01:00Z", "sent_at": "2026-06-05T09:01:00Z", "api_version": "2026-06-01" },
    "context": { "merchant_id": "11111111-1111-1111-1111-111111111111", "branch_id": "22222222-2222-2222-2222-222222222222", "terminal_id": "POS-360-0007", "cashier_id": "cashier-42" },
    "credential": { "credential_type": "phone", "phone": "+97433001122" }
  }'

Identify never returns a debit token, an authorization token, or any artifact that permits a ledger mutation. It reads identity and balance only. A spend happens on a separate, idempotent payment call. See The Decisioning Model.

The credential types follow a discriminated-union pattern on credential_type. The core types are phone, card fingerprint, and short code; additional types exist in the contract under the same pattern. Read Identity & Credentials for the full set, and confirm what your integration accepts via GET /capabilities.

You are now authenticated

You have probed the platform, read your capabilities, minted a terminal JWT, and resolved a customer. The next move is the full session loop: open a checkout_session, attach the basket, resolve offers, pay promo-first, and close with a receipt of record.

Where to go next