{"openapi_spec":"GET /api/v1/openapi.json, machine-readable OpenAPI 3.0 spec of this whole API, for Postman import or client SDK generation.","payments":{"statuses":{"pending":"Payment record created, checkout not yet completed.","processing":"Charge submitted to the processor, awaiting confirmation.","succeeded":"Charge confirmed, full amount collected, nothing refunded.","partially_refunded":"Charge succeeded and part of it has since been refunded via POST /api/merchant/payments/{id}/refund. refunded_amount on the payment (in GET /api/v1/payments) shows how much.","refunded":"Charge succeeded and the full amount has since been refunded.","failed":"The charge attempt was declined or otherwise did not complete, no money was collected.","cancelled":"Voided via POST /api/v1/payments/{id}/cancel while still pending, never reached the processor."},"refund_amount_semantics":"refunded_amount tracks cumulative refunds. A payment can be refunded more than once as long as the total refunded never exceeds the original amount (validated server-side on every refund call).","refunds":"POST /api/v1/payments/{id}/refund, omit amount for a full refund (refunds:create_full), pass amount for a partial refund (refunds:create_partial). Refunds above a per-currency threshold on live keys return 202 with requiresApproval: true and go through the same maker-checker flow as dashboard refunds; sandbox keys skip approval entirely (no real money moves).","cancel":"POST /api/v1/payments/{id}/cancel (payments:cancel), only works while the payment is still pending. A payment already submitted to the processor can't be voided from here; refund it once it succeeds instead."},"payouts":{"statuses":{"pending":"Payout request created, not yet submitted.","processing":"Submitted, awaiting transfer.completed/transfer.failed.","completed":"The transfer landed, ledger debited.","failed":"The transfer failed. No ledger entry was posted.","cancelled":"Cancelled via POST /api/v1/payouts/{id}/cancel while still pending, no money ever moved."},"overview":"POST /api/v1/payouts (payouts:create) and GET /api/v1/payouts (payouts:read). Live keys only, payouts move real settlement funds and are rejected outright on sandbox keys. Bank payouts go only to the settlement account saved in the dashboard (bankDetails, if sent, must match it). Send an Idempotency-Key header (8 to 64 characters) to make retries safe. Supports olympus_account and provider_bank destinations. Payouts above a per-currency threshold return 202 with requiresApproval: true (same maker-checker flow as dashboard payouts)."},"webhooks":{"delivery":"POST to your registered URL (see POST /api/merchant/webhooks) with header X-Olympus-Event naming the event type, X-Olympus-Signature = hex HMAC-SHA256 of \"{X-Olympus-Timestamp}.{raw body}\" using your webhook secret, and X-Olympus-Timestamp = unix seconds at send time. Verify the signature over the exact timestamp+body before trusting a delivery, and reject anything outside a ~5 minute tolerance window to prevent replay. Body shape: {\"type\": \"<event>\", \"created_at\": \"<ISO8601>\", \"data\": {...}}.","event_types":{"payment.succeeded":"A QR code or payment link charge succeeded. data includes id, amount, currency, fee_amount, net_amount, status, qr_code_id, payment_link_id (subscription charges instead include subscription_id).","payment.failed":"A checkout attempt was declined or otherwise failed to complete, fires for QR/payment-link charges and for a failed subscription renewal charge (subscription renewal instead sets the subscription to past_due). data includes amount, currency, status, failure_reason, and either the payment identifiers or subscription_id.","payment.refunded":"A refund (full or partial) was executed. data includes id, amount (original), refunded_amount (cumulative), refund_amount (this refund), currency, status (partially_refunded or refunded), fully_refunded (boolean).","payout.completed":"A payout to your settlement bank account was confirmed. data includes id, amount, currency, status.","payout.failed":"A payout to your settlement bank account failed. data includes id, amount, currency, status, failure_reason.","mandate.created":"A mandate request was created.","mandate.authorized":"The customer authorised the mandate.","mandate.activated":"The mandate became active and can now be debited.","mandate.suspended":"The mandate was suspended (collection paused, not ended).","mandate.revoked":"The customer or the business revoked the mandate.","mandate.expired":"The mandate expired.","mandate.rejected":"The mandate request was rejected before authorisation.","debit_order.created":"A debit order was created.","debit_order.review_required":"A debit order is held pending approval.","debit_order.authorized":"A debit order passed authorisation and is ready to send.","debit_order.declined":"A debit order was declined and will not be sent.","debit_order.submitted":"A debit order was sent to its payment rail.","debit_order.accepted":"The payment rail accepted the debit order.","debit_order.processing":"The debit order is in clearing.","debit_order.succeeded":"The debit was collected. Not yet settled, see debit_order.settled.","debit_order.settled":"The collected funds are now available to withdraw.","debit_order.failed":"The debit order failed. failure_code and retryable say why and whether Olympus will retry it.","debit_order.returned":"A cleared debit was returned by the debtor's bank.","debit_order.reversed":"A settled debit was reversed.","debit_order.cancelled":"A debit order was cancelled before it was sent.","collection.created":"A collection was created.","collection.submitted":"A collection's debit order was sent.","collection.succeeded":"A collection was collected in full.","collection.retry_scheduled":"A collection failed retryably and another attempt is scheduled.","collection.failed":"A collection failed and no further attempt will be made.","collection.returned":"A collected amount was returned.","collection.reversed":"A collected amount was reversed.","collection.cancelled":"A collection was cancelled.","collection.declined":"A collection was declined by authorisation or by a reviewer.","collection_batch.created":"A bulk collection batch was created.","collection_batch.approved":"A bulk collection batch was approved.","collection_batch.processing":"A bulk collection batch started processing.","collection_batch.completed":"A bulk collection batch finished (see the batch for any partial failures).","collection_batch.rejected":"A bulk collection batch was rejected.","collection_batch.cancelled":"A bulk collection batch was cancelled.","recurring_payment.created":"A recurring payment schedule was created.","recurring_payment.paused":"A recurring payment schedule was paused.","recurring_payment.resumed":"A recurring payment schedule was resumed.","recurring_payment.suspended":"A recurring payment schedule was suspended after too many consecutive failures.","recurring_payment.completed":"A recurring payment schedule reached its end date.","recurring_payment.cancelled":"A recurring payment schedule was cancelled.","approval.requested":"A debit order, batch, rule or mandate change is waiting for approval.","approval.approved":"An approval was granted.","approval.rejected":"An approval was rejected.","approval.expired":"An approval request expired unanswered.","settlement.created":"A provider settlement report was received.","settlement.completed":"A provider settlement was matched and applied in full.","settlement.failed":"A provider reported a settlement failure."},"retries":"Each delivery attempt (success or failure) is logged, see GET /api/v1/webhooks/{id}/deliveries (webhooks:read_logs), which includes attempt_number, dead_lettered, and next_retry_at. A failed delivery is automatically retried with exponential backoff (1 min, 5 min, 30 min, 2 hr after the initial attempt) up to 5 attempts total, after which it is marked dead_lettered and not retried again. Call POST /api/v1/webhooks/deliveries/{id}/retry (webhooks:retry) at any point to force an immediate retry instead of waiting for the schedule. Poll GET /api/v1/payments or GET /api/v1/settlements as a fallback source of truth if a delivery is ultimately dead-lettered.","manage":"POST /api/v1/webhooks and DELETE /api/v1/webhooks/{id} (webhooks:manage) let an API key register/remove its own webhook endpoints, in addition to the dashboard."},"payment_links":{"idempotency":"POST /api/v1/payment-links accepts either an Idempotency-Key header or an idempotencyKey body field. Retrying the same key returns the original link instead of creating a duplicate.","return_url":"POST /api/v1/payment-links accepts an optional returnUrl. When set, the payer is redirected there after checkout with ?payment_id={id}&status=success or &status=cancelled appended, instead of Olympus's own generic confirmation page."},"split_payments":{"overview":"POST /api/v1/split-recipients registers a real provider subaccount for a landlord/contractor (idempotent per externalReference, retrying the same externalReference returns the existing recipient rather than creating a duplicate Subaccount). Pass the returned id as splitRecipientId on POST /api/v1/payment-links or POST /api/v1/qr-codes to settle that specific charge directly to the recipient's own Subaccount, the payment provider splits the platform fee off automatically at charge time, in the same transaction, rather than Olympus collecting the full amount and paying the recipient out separately.","verify_and_disable":"PATCH /api/v1/split-recipients/{id} with { verified: true } (recipients:verify) or { disabled: true } (recipients:disable). Disabling is enforced at checkout, not cosmetic, a payment link/QR code assigned to a disabled recipient falls back to settling into your own default account instead of the recipient's."},"settlements":{"overview":"GET /api/v1/settlements?from=<ISO date>&to=<ISO date>&splitRecipientId=<optional> returns every payment that settled in the range (gross, fee, net, refunded, net_settled per payment) plus a per-currency summary, pass splitRecipientId to scope it to one recipient's settlements only.","export":"Add &format=csv (reports:export, in addition to settlements:read) to get the same data as a downloadable CSV instead of JSON."},"debit_orders":{"overview":"Mandates authorise a business to debit a customer; debit orders are individual instructions against an active mandate; collections are the merchant's intent to collect (one-off or recurring) and drive one or more debit orders; recurring payments create collections on a schedule. Amounts are integers in minor units.","mandate_statuses":{"draft":"Created, not yet requested.","pending_authorization":"Waiting for the customer to authorise it in Olympus Web Banking, or for signed-document evidence on a bank debtor.","authorized":"Authorised; being registered with the payment rail.","active":"Can be debited.","suspended":"Collection paused. Can be resumed only by whoever suspended it (the business, the customer, or Olympus).","revoked":"Ended by the customer or the business. Never executable again.","expired":"Ended by its own end date or expiry. Never executable again.","rejected":"The customer declined the request, or it expired unanswered."},"debit_order_statuses":{"created":"Authorisation is being evaluated.","pending_review":"Held for approval before it can be sent.","authorized":"Passed authorisation, not yet sent.","submitted":"Sent to the payment rail.","accepted":"The rail accepted it.","processing":"In clearing.","successful":"Collected. Not yet settled, funds are not withdrawable.","settled":"Settled: the funds are now available.","failed":"Did not succeed. failure_code and retryable explain why.","returned":"A cleared or settled debit was returned by the bank.","reversed":"A settled debit was reversed.","cancelled":"Cancelled before it was sent.","declined":"Declined by authorisation and never sent."},"idempotency":"POST /mandates, /debit-orders, /collections, /recurring-payments and /collection-batches all accept an Idempotency-Key header (8 to 64 characters). Retrying the same key returns the original result.","sandbox":"A test key (olp_test_...) runs every debit order through the Olympus sandbox rail, never real clearing. Choose a scenario by including one of insufficient_funds, revoked_mandate, expired_mandate, limit_exceeded, bank_error, provider_timeout, returned, reversed, delayed_settlement, duplicate, invalid_mandate or blocked_account in the reference you send; anything else succeeds and settles.","bulk_collections":"POST /collection-batches validates and schedules every item independently (one bad item never corrupts the batch). A batch above your approval threshold needs a second reviewer (GET its approvals the same way as any other) before POST /collection-batches/{id}/submit will do anything; a small batch is auto-approved and can be submitted immediately. Submitting sends each item's debit order one at a time, so a single failure never stops the rest — check item statuses with GET /collection-batches/{id}."},"account":{"overview":"GET /api/v1/account (account:read or kyc:read) returns your business profile plus verification status, kyc_status, submitted document count, verified UBO count, and whether a settlement destination is on file.","verification":"POST /api/v1/account/verification (kyc:write) starts a real Didit hosted identity/liveness session and returns { verificationUrl, sessionId }, send the business owner to verificationUrl to complete it. KYC on this platform is a hosted verification flow, not raw document upload. Poll GET /api/v1/account afterward to see kyc_status move past in_review."}}