{"openapi":"3.1.0","paths":{"/v1/payment_intents":{"post":{"description":"Returns a client secret and, once confirmed, whatever the customer must do next. Send an `Idempotency-Key` header — a replay returns the same payment rather than creating a second.","operationId":"PaymentsController_createIntent","parameters":[{"name":"idempotency-key","required":false,"in":"header","description":"Any unique string you generate per logical payment. A replay returns the payment the first call created rather than taking the money twice. Reusing a key with a different body is rejected with 412 — that is a client bug, and answering with the first result would charge the wrong figure silently.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePaymentIntentDto"}}}},"responses":{"201":{"description":"Created, awaiting confirmation. `clientSecret` is present exactly here.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedPaymentIntentResponseDto"}}}},"400":{"description":"Validation failed, or the amount is not positive.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}},"401":{"description":"Missing, malformed, revoked or IP-refused key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}},"404":{"description":"The customer or wallet referenced does not exist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}},"412":{"description":"The idempotency key was reused with a different body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}},"429":{"description":"Per-key request budget exhausted. Honour `retry-after`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}}},"security":[{"apiKey":[]}],"summary":"Create a payment","tags":["Payments"]},"get":{"description":"Scoped to the environment of the key you authenticate with. A TEST key never sees LIVE payments.","operationId":"PaymentsController_listIntents","parameters":[{"name":"limit","required":false,"in":"query","description":"Capped at 100. Larger reads belong in an export.","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","description":"One-based.","schema":{"type":"string"}},{"name":"search","required":false,"in":"query","description":"Prefix on ids; case-insensitive contains on reference, receipt email, description and the customer’s name or email.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedPaymentIntentsResponseDto"}}}},"401":{"description":"Missing, malformed, revoked or IP-refused key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}},"429":{"description":"Per-key request budget exhausted. Honour `retry-after`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}}},"security":[{"apiKey":[]}],"summary":"List payments","tags":["Payments"]}},"/v1/payment_intents/{id}/confirm":{"post":{"description":"Routes the payment to a provider and starts it. The response says what the customer must do next.","operationId":"PaymentsController_confirmIntent","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"zintle-client-secret","required":true,"in":"header","schema":{"type":"string"}},{"name":"Zintle-Client-Secret","in":"header","description":"The payment’s client secret. Required with a publishable key (here or as `clientSecret` in the body).","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmPaymentIntentDto"}}}},"responses":{"200":{"description":"Routed and started. Read `status` and `nextAction` — a DEVICE_PUSH payment is not complete when this returns.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmedPaymentIntentResponseDto"}}}},"401":{"description":"Missing, malformed, revoked or IP-refused key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}},"404":{"description":"No such payment under this merchant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}},"409":{"description":"Already expired, already confirmed, or another attempt is in flight — a customer double-tapping a pay button gets this rather than two charges.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}},"429":{"description":"Per-key request budget exhausted. Honour `retry-after`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}},"503":{"description":"No route is available for this corridor right now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}}},"security":[{"apiKey":[]}],"summary":"Confirm a payment","tags":["Payments"]}},"/v1/payment_intents/{id}":{"get":{"description":"Includes every charge attempted against it. Somebody else’s id answers 404 rather than 403, so this cannot be used to test whether an id exists.","operationId":"PaymentsController_getIntent","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"zintle-client-secret","required":true,"in":"header","schema":{"type":"string"}},{"name":"Zintle-Client-Secret","in":"header","description":"The payment’s client secret. Required with a publishable key; a header rather than a query parameter so it never lands in a URL.","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentWithChargesResponseDto"}}}},"401":{"description":"Missing, malformed, revoked or IP-refused key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}},"404":{"description":"No such payment under this merchant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}},"429":{"description":"Per-key request budget exhausted. Honour `retry-after`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}}},"security":[{"apiKey":[]}],"summary":"Retrieve a payment","tags":["Payments"]}},"/v1/customers":{"post":{"description":"Optional. A payment can name a customer or stand alone; creating one is worth it when you want the same payer across several payments.","operationId":"PaymentsController_createCustomer","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCustomerDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerResponseDto"}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}},"401":{"description":"Missing, malformed, revoked or IP-refused key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}},"429":{"description":"Per-key request budget exhausted. Honour `retry-after`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}}},"security":[{"apiKey":[]}],"summary":"Create a customer","tags":["Payments"]}},"/v1/providers":{"get":{"description":"Returns flow kinds, not provider names. A checkout UI renders from this, which is why enabling a new provider requires no client release. Pass `amount` to exclude methods whose limits exclude this payment.","operationId":"PaymentsController_availableMethods","parameters":[{"name":"country","required":true,"in":"query","schema":{"example":"CM","type":"string"}},{"name":"currency","required":true,"in":"query","schema":{"example":"XAF","type":"string"}},{"name":"amount","required":false,"in":"query","description":"Minor units, as a decimal string.","schema":{"example":"500000","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvailableMethodsResponseDto"}}}},"401":{"description":"Missing, malformed, revoked or IP-refused key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}},"429":{"description":"Per-key request budget exhausted. Honour `retry-after`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}}},"security":[{"apiKey":[]}],"summary":"Payment methods available for a corridor","tags":["Payments"]}},"/v1/payment_links":{"post":{"description":"Returns a `url` to send the customer to. The page offers every method that can take the payment. The result arrives as a `payment_intent.*` webhook carrying your `reference`.","operationId":"PaymentLinksController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePaymentLinkDto"}}}},"responses":{"201":{"description":"Created. Redirect the customer to `url`."},"400":{"description":"Validation failed, an unsafe success URL, or an expiry in the past.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}},"401":{"description":"Missing, malformed, revoked or IP-refused key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}},"403":{"description":"A publishable key, or a restricted key without the `billing.links.*` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}}},"security":[{"apiKey":[]}],"summary":"Create a hosted checkout","tags":["Payment links"]}},"/v1/payment_links/{id}":{"get":{"description":"Its status and the payments made through it, newest first. The webhook is the signal to act on; this is for reconciling.","operationId":"PaymentLinksController_retrieve","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"401":{"description":"Missing, malformed, revoked or IP-refused key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}},"403":{"description":"A publishable key, or a restricted key without the `billing.links.*` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}}},"security":[{"apiKey":[]}],"summary":"Retrieve a hosted checkout","tags":["Payment links"]}},"/v1/payment_links/{id}/deactivate":{"post":{"description":"Stops the page taking payment — for an order cancelled before it was paid. A payment already under way still completes.","operationId":"PaymentLinksController_deactivate","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"401":{"description":"Missing, malformed, revoked or IP-refused key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}},"403":{"description":"A publishable key, or a restricted key without the `billing.links.*` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}},"409":{"description":"Already paid: a completed checkout cannot be reopened or closed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponseDto"}}}}},"security":[{"apiKey":[]}],"summary":"Close a hosted checkout","tags":["Payment links"]}}},"info":{"title":"Zintle Payments API","description":"The REST surface a merchant server integrates against. The dashboard uses\nGraphQL; both are presentations of one service layer, and neither holds\nbusiness logic of its own.\n\nFour conventions run through every endpoint:\n\n- **Amounts are decimal strings of minor units.** `\"1050\"` in USD is $10.50;\n  `\"500000\"` in XAF is 500,000 francs, because XAF has no minor unit. A JSON\n  number silently loses precision above 2^53, which a settlement batch in a\n  zero-decimal currency reaches sooner than it looks.\n- **Dates are epoch milliseconds**, matching the GraphQL surface exactly.\n- **The environment is in the key**, never in a header or a request field. A\n  `zk_sk_test_…` key can neither read nor write live data.\n- **Errors carry a stable `code`.** Branch on `error.code`, never on\n  `error.message` — the message is translated to the caller’s language.\n\nMoney-moving calls take an `Idempotency-Key`. A replay returns the original\nresult rather than taking the money twice.","version":"2026-08-19","contact":{}},"tags":[{"name":"Payments","description":"Create a payment, confirm it, and read what happened. Fulfil an order on the `payment_intent.succeeded` webhook, never on the response to confirm — a mobile-money payment is not complete when that call returns."},{"name":"Payment links","description":"Hosted checkout. Create one for an order from your server and redirect the customer to its `url`; the page offers every method that can take the payment, and the outcome arrives as a `payment_intent.*` webhook carrying your `reference`."}],"servers":[{"url":"https://api.zintle.io","description":"Production"},{"url":"http://localhost:4005","description":"Local development"}],"components":{"securitySchemes":{"apiKey":{"scheme":"bearer","bearerFormat":"JWT","type":"http","description":"A secret key (`zk_sk_test_…`, `zk_sk_live_…`) or a restricted one (`zk_rk_…`) from a server. A publishable key (`zk_pk_…`) is accepted only by `GET /v1/providers` and, with the payment’s client secret, by retrieve and confirm on one payment. The environment is in the key text, never inferred from the request, so a production credential in a staging config is visible to anyone reading the file."}},"schemas":{"ApiErrorDetailDto":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error","authentication_error","rate_limit_error","api_error"]},"code":{"type":"string","enum":["invalid_request","unauthenticated","permission_denied","resource_missing","resource_conflict","idempotency_key_reused","unprocessable_entity","rate_limit_exceeded","processor_unavailable","api_error"]},"message":{"type":"string","description":"Human-readable, already translated."},"requestId":{"type":"string","description":"Echoed from the `x-request-id` response header."},"details":{"type":"object","additionalProperties":true,"description":"Field-level validation failures, when there are any."}},"required":["type","code","message"]},"ApiErrorResponseDto":{"type":"object","properties":{"error":{"$ref":"#/components/schemas/ApiErrorDetailDto"}},"required":["error"]},"CreatePaymentIntentDto":{"type":"object","properties":{"amount":{"type":"string","description":"Amount in minor units, as a decimal string. \"1050\" is $10.50.","example":"500000"},"currency":{"type":"string","example":"XAF"},"country":{"type":"string","description":"Where the payer is. Decides the corridor and the route.","example":"CM"},"methodType":{"type":"string","enum":["CARD","BANK_ACCOUNT","MOBILE_MONEY","WALLET","CRYPTO","CASH","PAYPAL","APPLE_PAY","GOOGLE_PAY"]},"walletBrand":{"type":"string","enum":["APPLE_PAY","GOOGLE_PAY","PAYPAL"],"description":"The credential source for a wallet payment. Not a provider — Apple Pay produces a token an acquirer processes."},"reference":{"type":"string","description":"Your own reference for this payment."},"description":{"type":"string"},"customer":{"type":"string","description":"Customer id (cus_…)."},"wallet":{"type":"string","description":"Wallet to credit (wlt_…). Defaults to the wallet bound to your key, then your default wallet."},"receiptEmail":{"type":"string"},"returnUrl":{"type":"string","description":"Where to send the payer after an off-site step."},"statementDescriptor":{"type":"string","description":"Shown on the customer’s statement."},"providerParams":{"type":"object","description":"Provider-specific parameters. In test mode, `simulate` forces an outcome."},"metadata":{"type":"object"}},"required":["amount","currency","country","methodType"]},"CreatedPaymentIntentResponseDto":{"type":"object","properties":{"id":{"type":"string","example":"pi_01JAY2K5RQ9M3W7ZB8XF4TC6VD"},"object":{"type":"string","enum":["payment_intent"],"example":"payment_intent"},"status":{"type":"string","enum":["REQUIRES_PAYMENT_METHOD","REQUIRES_CONFIRMATION","REQUIRES_ACTION","PROCESSING","REQUIRES_CAPTURE","SUCCEEDED","CANCELED","FAILED","EXPIRED"]},"amount":{"type":"string","description":"Minor units, as a decimal string.","example":"500000"},"currency":{"type":"string","example":"XAF"},"country":{"type":"string","example":"CM"},"methodType":{"type":"string","enum":["CARD","BANK_ACCOUNT","MOBILE_MONEY","WALLET","CRYPTO","CASH","PAYPAL","APPLE_PAY","GOOGLE_PAY"]},"provider":{"type":"string","enum":["SIMULATOR","FLUTTERWAVE","PAYSTACK","STRIPE","MTN_MOMO","ORANGE_MONEY","AIRTEL_MONEY","MOOV_MONEY","WAVE","MPESA","PAYPAL","BANK_TRANSFER_LOCAL"],"nullable":true,"description":"Null until the payment is confirmed and routed."},"flowKind":{"type":"string","enum":["DIRECT","REDIRECT","DEVICE_PUSH","OTP_CHALLENGE","QR_DISPLAY","VOUCHER_INSTRUCTION"],"nullable":true,"description":"What the customer must do. A client renders from this, never from the provider name — which is why enabling a new provider needs no client release."},"reference":{"type":"object","nullable":true,"example":"order-4471"},"description":{"type":"object","nullable":true},"nextAction":{"type":"object","nullable":true,"additionalProperties":true,"description":"The instruction for the client while the payment needs the customer."},"lastPaymentError":{"type":"object","nullable":true,"additionalProperties":true,"description":"The last failure, with a canonical `code` from the failure taxonomy plus the provider’s own message. Retry logic branches on `code`."},"expiresAt":{"type":"number","nullable":true,"description":"Epoch milliseconds. After this the payment moves to EXPIRED, which is not FAILED — nobody declined it."},"created":{"type":"number","description":"Epoch milliseconds."},"clientSecret":{"type":"string","description":"Returned exactly once, at creation. Only a hash is stored, so this cannot be recovered — a client that loses it creates a new payment. It lets a browser confirm this one payment without holding a secret key."}},"required":["id","object","status","amount","currency","country","methodType","provider","flowKind","reference","description","nextAction","lastPaymentError","expiresAt","created"]},"ConfirmPaymentIntentDto":{"type":"object","properties":{"clientSecret":{"type":"string","description":"The `clientSecret` returned when the payment was created. Required with a publishable key, ignored otherwise. May be sent as the `Zintle-Client-Secret` header instead.","example":"pi_01JAY…_secret_…"}}},"ChargeResponseDto":{"type":"object","properties":{"id":{"type":"string","example":"ch_01JAY2K5RQ9M3W7ZB8XF4TC6VD"},"object":{"type":"string","enum":["charge"],"example":"charge"},"status":{"type":"string","enum":["PENDING","AUTHORIZED","SUCCEEDED","FAILED","CANCELED","EXPIRED","REFUNDED","PARTIALLY_REFUNDED","DISPUTED"]},"amount":{"type":"string","example":"500000"},"amountCaptured":{"type":"string","example":"500000","description":"What the provider actually took, which is not always what was asked for."},"currency":{"type":"string","example":"XAF"},"provider":{"type":"string","enum":["SIMULATOR","FLUTTERWAVE","PAYSTACK","STRIPE","MTN_MOMO","ORANGE_MONEY","AIRTEL_MONEY","MOOV_MONEY","WAVE","MPESA","PAYPAL","BANK_TRANSFER_LOCAL"]},"failureCode":{"type":"string","enum":["INSUFFICIENT_FUNDS","INVALID_ACCOUNT","ACCOUNT_INACTIVE","LIMIT_EXCEEDED","EXPIRED_INSTRUMENT","AUTHENTICATION_FAILED","DECLINED_BY_ISSUER","USER_CANCELLED","TIMEOUT","DUPLICATE","FRAUD_SUSPECTED","CURRENCY_NOT_SUPPORTED","PROVIDER_UNAVAILABLE","PROVIDER_ERROR","CONFIGURATION_ERROR"],"nullable":true},"failureMessage":{"type":"object","nullable":true,"description":"The provider’s own prose. Never branch on it."},"created":{"type":"number"}},"required":["id","object","status","amount","amountCaptured","currency","provider","failureCode","failureMessage","created"]},"ConfirmedPaymentIntentResponseDto":{"type":"object","properties":{"id":{"type":"string","example":"pi_01JAY2K5RQ9M3W7ZB8XF4TC6VD"},"object":{"type":"string","enum":["payment_intent"],"example":"payment_intent"},"status":{"type":"string","enum":["REQUIRES_PAYMENT_METHOD","REQUIRES_CONFIRMATION","REQUIRES_ACTION","PROCESSING","REQUIRES_CAPTURE","SUCCEEDED","CANCELED","FAILED","EXPIRED"]},"amount":{"type":"string","description":"Minor units, as a decimal string.","example":"500000"},"currency":{"type":"string","example":"XAF"},"country":{"type":"string","example":"CM"},"methodType":{"type":"string","enum":["CARD","BANK_ACCOUNT","MOBILE_MONEY","WALLET","CRYPTO","CASH","PAYPAL","APPLE_PAY","GOOGLE_PAY"]},"provider":{"type":"string","enum":["SIMULATOR","FLUTTERWAVE","PAYSTACK","STRIPE","MTN_MOMO","ORANGE_MONEY","AIRTEL_MONEY","MOOV_MONEY","WAVE","MPESA","PAYPAL","BANK_TRANSFER_LOCAL"],"nullable":true,"description":"Null until the payment is confirmed and routed."},"flowKind":{"type":"string","enum":["DIRECT","REDIRECT","DEVICE_PUSH","OTP_CHALLENGE","QR_DISPLAY","VOUCHER_INSTRUCTION"],"nullable":true,"description":"What the customer must do. A client renders from this, never from the provider name — which is why enabling a new provider needs no client release."},"reference":{"type":"object","nullable":true,"example":"order-4471"},"description":{"type":"object","nullable":true},"nextAction":{"type":"object","nullable":true,"additionalProperties":true,"description":"The instruction for the client while the payment needs the customer."},"lastPaymentError":{"type":"object","nullable":true,"additionalProperties":true,"description":"The last failure, with a canonical `code` from the failure taxonomy plus the provider’s own message. Retry logic branches on `code`."},"expiresAt":{"type":"number","nullable":true,"description":"Epoch milliseconds. After this the payment moves to EXPIRED, which is not FAILED — nobody declined it."},"created":{"type":"number","description":"Epoch milliseconds."},"latestCharge":{"$ref":"#/components/schemas/ChargeResponseDto"}},"required":["id","object","status","amount","currency","country","methodType","provider","flowKind","reference","description","nextAction","lastPaymentError","expiresAt","created","latestCharge"]},"PaymentIntentWithChargesResponseDto":{"type":"object","properties":{"id":{"type":"string","example":"pi_01JAY2K5RQ9M3W7ZB8XF4TC6VD"},"object":{"type":"string","enum":["payment_intent"],"example":"payment_intent"},"status":{"type":"string","enum":["REQUIRES_PAYMENT_METHOD","REQUIRES_CONFIRMATION","REQUIRES_ACTION","PROCESSING","REQUIRES_CAPTURE","SUCCEEDED","CANCELED","FAILED","EXPIRED"]},"amount":{"type":"string","description":"Minor units, as a decimal string.","example":"500000"},"currency":{"type":"string","example":"XAF"},"country":{"type":"string","example":"CM"},"methodType":{"type":"string","enum":["CARD","BANK_ACCOUNT","MOBILE_MONEY","WALLET","CRYPTO","CASH","PAYPAL","APPLE_PAY","GOOGLE_PAY"]},"provider":{"type":"string","enum":["SIMULATOR","FLUTTERWAVE","PAYSTACK","STRIPE","MTN_MOMO","ORANGE_MONEY","AIRTEL_MONEY","MOOV_MONEY","WAVE","MPESA","PAYPAL","BANK_TRANSFER_LOCAL"],"nullable":true,"description":"Null until the payment is confirmed and routed."},"flowKind":{"type":"string","enum":["DIRECT","REDIRECT","DEVICE_PUSH","OTP_CHALLENGE","QR_DISPLAY","VOUCHER_INSTRUCTION"],"nullable":true,"description":"What the customer must do. A client renders from this, never from the provider name — which is why enabling a new provider needs no client release."},"reference":{"type":"object","nullable":true,"example":"order-4471"},"description":{"type":"object","nullable":true},"nextAction":{"type":"object","nullable":true,"additionalProperties":true,"description":"The instruction for the client while the payment needs the customer."},"lastPaymentError":{"type":"object","nullable":true,"additionalProperties":true,"description":"The last failure, with a canonical `code` from the failure taxonomy plus the provider’s own message. Retry logic branches on `code`."},"expiresAt":{"type":"number","nullable":true,"description":"Epoch milliseconds. After this the payment moves to EXPIRED, which is not FAILED — nobody declined it."},"created":{"type":"number","description":"Epoch milliseconds."},"charges":{"description":"Every attempt against this payment. A declined card followed by a successful mobile-money payment is two charges under one payment.","type":"array","items":{"$ref":"#/components/schemas/ChargeResponseDto"}}},"required":["id","object","status","amount","currency","country","methodType","provider","flowKind","reference","description","nextAction","lastPaymentError","expiresAt","created","charges"]},"PaymentIntentResponseDto":{"type":"object","properties":{"id":{"type":"string","example":"pi_01JAY2K5RQ9M3W7ZB8XF4TC6VD"},"object":{"type":"string","enum":["payment_intent"],"example":"payment_intent"},"status":{"type":"string","enum":["REQUIRES_PAYMENT_METHOD","REQUIRES_CONFIRMATION","REQUIRES_ACTION","PROCESSING","REQUIRES_CAPTURE","SUCCEEDED","CANCELED","FAILED","EXPIRED"]},"amount":{"type":"string","description":"Minor units, as a decimal string.","example":"500000"},"currency":{"type":"string","example":"XAF"},"country":{"type":"string","example":"CM"},"methodType":{"type":"string","enum":["CARD","BANK_ACCOUNT","MOBILE_MONEY","WALLET","CRYPTO","CASH","PAYPAL","APPLE_PAY","GOOGLE_PAY"]},"provider":{"type":"string","enum":["SIMULATOR","FLUTTERWAVE","PAYSTACK","STRIPE","MTN_MOMO","ORANGE_MONEY","AIRTEL_MONEY","MOOV_MONEY","WAVE","MPESA","PAYPAL","BANK_TRANSFER_LOCAL"],"nullable":true,"description":"Null until the payment is confirmed and routed."},"flowKind":{"type":"string","enum":["DIRECT","REDIRECT","DEVICE_PUSH","OTP_CHALLENGE","QR_DISPLAY","VOUCHER_INSTRUCTION"],"nullable":true,"description":"What the customer must do. A client renders from this, never from the provider name — which is why enabling a new provider needs no client release."},"reference":{"type":"object","nullable":true,"example":"order-4471"},"description":{"type":"object","nullable":true},"nextAction":{"type":"object","nullable":true,"additionalProperties":true,"description":"The instruction for the client while the payment needs the customer."},"lastPaymentError":{"type":"object","nullable":true,"additionalProperties":true,"description":"The last failure, with a canonical `code` from the failure taxonomy plus the provider’s own message. Retry logic branches on `code`."},"expiresAt":{"type":"number","nullable":true,"description":"Epoch milliseconds. After this the payment moves to EXPIRED, which is not FAILED — nobody declined it."},"created":{"type":"number","description":"Epoch milliseconds."}},"required":["id","object","status","amount","currency","country","methodType","provider","flowKind","reference","description","nextAction","lastPaymentError","expiresAt","created"]},"PaginatedPaymentIntentsResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PaymentIntentResponseDto"}},"total":{"type":"number","example":137},"page":{"type":"number","example":1},"limit":{"type":"number","example":20,"description":"Capped at 100."},"hasMore":{"type":"boolean","description":"From the server, never inferred from page × limit < total — the two disagree the moment a row is inserted between the count and the fetch."}},"required":["items","total","page","limit","hasMore"]},"CreateCustomerDto":{"type":"object","properties":{"email":{"type":"string"},"phone":{"type":"string"},"name":{"type":"string"},"externalRef":{"type":"string","description":"Your own id for this customer."},"country":{"type":"string"},"metadata":{"type":"object"}}},"CustomerResponseDto":{"type":"object","properties":{"id":{"type":"string","example":"cus_01JAY2K5RQ9M3W7ZB8XF4TC6VD"},"object":{"type":"string","enum":["customer"],"example":"customer"},"email":{"type":"object","nullable":true},"phone":{"type":"object","nullable":true},"name":{"type":"object","nullable":true},"externalRef":{"type":"object","nullable":true,"description":"Your own id for this customer."},"created":{"type":"number"}},"required":["id","object","email","phone","name","externalRef","created"]},"ProviderMethodDto":{"type":"object","properties":{"methodType":{"type":"string","enum":["CARD","BANK_ACCOUNT","MOBILE_MONEY","WALLET","CRYPTO","CASH","PAYPAL","APPLE_PAY","GOOGLE_PAY"]},"flowKind":{"type":"string","enum":["DIRECT","REDIRECT","DEVICE_PUSH","OTP_CHALLENGE","QR_DISPLAY","VOUCHER_INSTRUCTION"]},"minAmount":{"type":"object","nullable":true,"example":"100"},"maxAmount":{"type":"object","nullable":true,"example":"5000000"}},"required":["methodType","flowKind","minAmount","maxAmount"]},"AvailableMethodsResponseDto":{"type":"object","properties":{"country":{"type":"string","example":"CM"},"currency":{"type":"string","example":"XAF"},"methods":{"type":"array","items":{"$ref":"#/components/schemas/ProviderMethodDto"}}},"required":["country","currency","methods"]},"CheckoutLineItemDto":{"type":"object","properties":{"name":{"type":"string","example":"Wireless headphones"},"quantity":{"type":"number","example":2,"minimum":1,"maximum":1000},"unitAmount":{"type":"string","description":"Price of one, in minor units, as a decimal string.","example":"15000"}},"required":["name","quantity","unitAmount"]},"CheckoutAppearanceVariablesDto":{"type":"object","properties":{"colorPrimary":{"type":"string","example":"#0a7cff","description":"Buttons, the selected method, focus rings."},"colorPrimaryText":{"type":"string","example":"#ffffff","description":"Text on the primary colour."},"colorBackground":{"type":"string","example":"#ffffff","description":"The page behind the form."},"colorSurface":{"type":"string","example":"#f7f7f8","description":"The form card and the inputs."},"colorText":{"type":"string","example":"#111827"},"colorMutedText":{"type":"string","example":"#6b7280","description":"Labels and hints."},"colorBorder":{"type":"string","example":"#e5e7eb","description":"Input and card borders."},"colorDanger":{"type":"string","example":"#dc2626","description":"Errors."},"fontFamily":{"type":"string","enum":["system","Inter","Roboto","Open Sans","Lato","Montserrat","Poppins","Nunito","Source Sans 3","Work Sans","DM Sans","Manrope","IBM Plex Sans","Merriweather","Playfair Display"],"example":"Poppins"},"fontSizeBase":{"type":"number","minimum":12,"maximum":20,"example":16,"description":"Base text size in px."},"borderRadius":{"type":"number","minimum":0,"maximum":24,"example":8,"description":"Corner radius in px."}}},"CheckoutAppearanceLayoutDto":{"type":"object","properties":{"methodLayout":{"type":"string","enum":["grid","list"],"default":"grid"},"showMerchant":{"type":"boolean","default":true,"description":"Your logo and name above the form. Turn off when your own page already shows them."},"showDescription":{"type":"boolean","default":true},"showLineItems":{"type":"boolean","default":true}}},"CheckoutAppearanceLabelsDto":{"type":"object","properties":{"payButton":{"type":"string","example":"Complete order","maxLength":40,"description":"Replaces \"Pay\" on the button, in every language."}}},"CheckoutAppearanceDto":{"type":"object","properties":{"theme":{"type":"string","enum":["auto","light","dark"],"default":"auto"},"variables":{"$ref":"#/components/schemas/CheckoutAppearanceVariablesDto"},"layout":{"$ref":"#/components/schemas/CheckoutAppearanceLayoutDto"},"labels":{"$ref":"#/components/schemas/CheckoutAppearanceLabelsDto"}}},"CreatePaymentLinkDto":{"type":"object","properties":{"amount":{"type":"string","description":"Amount in minor units, as a decimal string. Send this or `lineItems`, not both.","example":"500000"},"lineItems":{"description":"What is being bought, shown line by line. The total is their sum. Send this or `amount`, not both.","type":"array","items":{"$ref":"#/components/schemas/CheckoutLineItemDto"}},"currency":{"type":"string","example":"XAF"},"title":{"type":"string","description":"What is being paid for. Shown at the top of the page.","example":"Order #4471"},"description":{"type":"string","description":"A line under the title."},"imageUrl":{"type":"string","description":"HTTPS URL of a product image, shown beside the title.","example":"https://shop.example.com/images/headphones.jpg"},"reference":{"type":"string","description":"Your own id for this checkout, such as an order number. Copied onto the payment, so `payment_intent.succeeded` carries it.","example":"order-4471"},"successUrl":{"type":"string","description":"HTTPS URL the payer is sent to once the payment succeeds. Confirm the payment from the webhook, never from this redirect.","example":"https://shop.example.com/orders/4471/thanks"},"expiresAt":{"type":"number","description":"Epoch milliseconds after which the page stops taking payment.","example":1785869000000},"paymentMethods":{"type":"array","description":"Limit the methods offered. Omit to offer every method that can take this payment.","items":{"type":"string","enum":["CARD","BANK_ACCOUNT","MOBILE_MONEY","WALLET","CRYPTO","CASH","PAYPAL","APPLE_PAY","GOOGLE_PAY"]}},"singleUse":{"type":"boolean","description":"Accept one payment and then close. Defaults to true: a checkout is for one order.","default":true},"collectEmail":{"type":"boolean","description":"Ask the payer for an email address. Defaults to true.","default":true},"collectPhoneNumber":{"type":"boolean","description":"Ask for a phone number even when paying by card.","default":false},"embedOrigins":{"description":"Origins allowed to show this checkout in an iframe, exactly as the browser reports them: `https://shop.example.com`. HTTPS only, except `http://localhost` on a test key. Omit to allow none.","example":["https://shop.example.com"],"maxItems":10,"type":"array","items":{"type":"string"}},"appearance":{"description":"Colours, font, corner radius and labels, so the checkout matches your site. Applies to the hosted page and the embed alike.","allOf":[{"$ref":"#/components/schemas/CheckoutAppearanceDto"}]},"metadata":{"type":"object","description":"Copied onto the payment’s metadata."}},"required":["currency","title"]}}}}