{"openapi":"3.1.0","info":{"title":"EverydayMoney","description":"Payouts into Africa, settled from a USDC wallet.\n\nAmounts are decimal strings, never floats — `\"1250.00\"`, not `1250.0`. Each corridor states its own accepted fields, methods and bounds at `/v1/corridors/{country}/requirements`, so one generic form can serve every destination.\n\nIdentity comes from the key. No endpoint takes a merchant id: a caller states what they want done, not who they are.","license":{"name":"UNLICENSED","identifier":"UNLICENSED"},"version":"1.0.0"},"paths":{"/v1/balance":{"get":{"tags":["Wallet"],"summary":"`GET /v1/balance`","description":"Readable by anybody who may read the merchant — their own balance is not a\nprivileged fact about them.","operationId":"getBalance","responses":{"200":{"description":"Available and reserved, per currency","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletBalance"}}}}},"security":[{"api_key":[]}]}},"/v1/cases":{"get":{"tags":["Cases"],"summary":"`GET /v1/cases`","operationId":"listCases","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":["string","null"]}},{"name":"payout_id","in":"query","required":false,"schema":{"type":["string","null"]}},{"name":"payment_id","in":"query","required":false,"schema":{"type":["string","null"]}},{"name":"subject","in":"query","description":"`payout` or `payment`. Absent is both, because the queue is one queue.","required":false,"schema":{"type":["string","null"]}},{"name":"limit","in":"query","required":false,"schema":{"type":["integer","null"],"format":"int64"}}],"responses":{"200":{"description":"Cases on this account","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CaseView"}}}}}},"security":[{"api_key":[]}]},"post":{"tags":["Cases"],"summary":"`POST /v1/cases` — say a payout did not arrive as recorded.","operationId":"raiseCase","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RaiseCase"}}},"required":true},"responses":{"200":{"description":"The case as recorded. Raising one never moves money.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaseView"}}}}},"security":[{"api_key":[]}]}},"/v1/cases/{id}/resolve":{"post":{"tags":["Cases"],"summary":"`POST /v1/cases/{id}/resolve` — record what was found.","operationId":"resolveCase","parameters":[{"name":"id","in":"path","description":"The case identifier","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolveCase"}}},"required":true},"responses":{"200":{"description":"The case, closed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaseView"}}}}},"security":[{"api_key":[]}]}},"/v1/collections/settlements":{"get":{"tags":["Payments"],"summary":"`GET /v1/collections/settlements`","description":"Every settlement of your collections, newest first: what was taken off what\nyou are owed, what was sent, at what rate, and the transaction that sent it.","operationId":"listCollectionSettlements","responses":{"200":{"description":"Settlements, newest first","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MerchantSettlement"}}}}}},"security":[{"api_key":[]}]}},"/v1/collections/summary":{"get":{"tags":["Payments"],"summary":"`GET /v1/collections/summary`","description":"What your collections come to, per currency, after our fee — and what of it\nhas been paid to you and what is still to come.","operationId":"getCollectionSummary","parameters":[{"name":"from","in":"query","description":"`YYYY-MM-DD`. Absent is from the first collection.","required":false,"schema":{"type":["string","null"]}},{"name":"to","in":"query","description":"`YYYY-MM-DD`, inclusive. Absent is today.","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"description":"One entry per collection currency","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CollectionSummary"}}}}}},"security":[{"api_key":[]}]}},"/v1/corridors":{"get":{"tags":["Corridors"],"summary":"The corridors *this merchant* can pay into.","description":"Scoped, never global: two merchants can get different answers for the same\ncountry, because enablement and overrides are per merchant.","operationId":"listCorridors","responses":{"200":{"description":"The corridors this account may pay into","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Corridor"}}}}}},"security":[{"api_key":[]}]}},"/v1/corridors/{country}/requirements":{"get":{"tags":["Corridors"],"summary":"What to collect for a corridor, and what values are valid.","description":"The endpoint an integrator builds one generic form against.","operationId":"getRequirements","parameters":[{"name":"country","in":"path","description":"ISO-3166 alpha-2 code","required":true,"schema":{"type":"string"}},{"name":"method","in":"query","description":"`bank_account` | `mobile_money` | `card`","required":false,"schema":{"type":["string","null"]}},{"name":"direction","in":"query","description":"`payout` (the default) or `collection`.\n\nAbsent means payout, because that is what this platform does. A caller\nthat has never heard of collections asks exactly as it always did.","required":false,"schema":{"type":["string","null"]}},{"name":"amount","in":"query","description":"The destination amount this recipient is being collected for.\n\nOptional, and it does not change the fields — it changes which\nacquirer's bank list is offered. Routing filters candidates on a route's\nlimits, so a corridor can hand a large payout to a different rail than a\nsmall one, and the two do not share bank codes. A caller who knows the\namount gets the list belonging to the rail that will actually carry it.\n\nA decimal string, like every amount on this API.","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"description":"Everything needed to build a valid recipient here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Requirements"}}}}},"security":[{"api_key":[]}]}},"/v1/corridors/{country}/validate":{"post":{"tags":["Corridors"],"summary":"`POST /v1/corridors/{country}/validate`","description":"Who owns this account, before you pay it.\n\n# A failed lookup is not a failed account\n\nMost corridors cannot answer. Only some acquirers offer name lookup, and\nwhich acquirer carries a corridor is decided at payout time. So this returns\n`checked: false` rather than an error when nobody could look, and the caller\ndecides what that means to them.\n\nThe failure mode worth avoiding is the opposite of the obvious one. It is\nnot \"a bad account got through\" — this endpoint blocks nothing. It is an\nintegrator reading `verified: false` as \"wrong account\" and refusing a\nperfectly good payout on every corridor with no lookup at all, which is most\nof them. Hence two fields where one would have been shorter.","operationId":"validateAccount","parameters":[{"name":"country","in":"path","description":"ISO-3166 alpha-2","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateAccount"}}},"required":true},"responses":{"200":{"description":"What the rail said, if anything","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountCheck"}}}}},"security":[{"api_key":[]}]}},"/v1/customers":{"get":{"tags":["Payouts"],"summary":"`GET /v1/customers`","description":"Everybody this merchant has paid, most recent first. Not a directory we\nmaintain — it is their payout history grouped by destination, which is the\nonly customer list we can honestly claim to have.","operationId":"listCustomers","parameters":[{"name":"q","in":"query","description":"Search by destination, customer id or name.","required":false,"schema":{"type":["string","null"]}},{"name":"limit","in":"query","required":false,"schema":{"type":["integer","null"],"format":"int64"}}],"responses":{"200":{"description":"The merchant's own customers, by their reference","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Customer"}}}}}},"security":[{"api_key":[]}]}},"/v1/payments":{"get":{"tags":["Payments"],"summary":"`GET /v1/payments`","operationId":"listPayments","parameters":[{"name":"limit","in":"path","required":true,"schema":{"type":["integer","null"],"format":"int64"}},{"name":"state","in":"path","description":"`pending`, `settled` and so on. Empty is every state.\n\nA list, because the states somebody wants together are rarely one:\n\"anything that did not settle\" is `failed` and `expired`, and\n\"still owed money\" is `pending` and `processing`. Matched as any of.","required":true,"schema":{"type":"array","items":{"type":"string"}}},{"name":"q","in":"path","description":"The identifiers somebody has to hand: our payment id, their own\nreference, the payer's email or phone, the name the bank gave for\nwhoever actually paid.\n\nA list, matched as any of, each a substring — the same shape the payout\nlisting takes, because it is the same job: a column of references\npasted out of a spreadsheet.","required":true,"schema":{"type":"array","items":{"type":"string"}}},{"name":"from","in":"path","description":"Inclusive, ISO-8601 dates or timestamps.","required":true,"schema":{"type":["string","null"]}},{"name":"to","in":"path","required":true,"schema":{"type":["string","null"]}},{"name":"cursor","in":"path","description":"The last row of the previous page, by id. Keyset, for the reason the\npayout listing gives: this list grows at the newest end.","required":true,"schema":{"type":["string","null"]}}],"responses":{"200":{"description":"Payments, newest first","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PaymentView"}}}}}},"security":[{"api_key":[]}]},"post":{"tags":["Payments"],"summary":"`POST /v1/payments`","description":"Ask one of this merchant's customers to pay.","operationId":"createPayment","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePayment"}}},"required":true},"responses":{"200":{"description":"This idempotency key already made a payment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentView"}}}},"201":{"description":"Created; the payer has something to do","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentView"}}}},"400":{"description":"Refused"},"403":{"description":"This corridor is not open to this account for collections"}},"security":[{"api_key":[]}]}},"/v1/payments/{id}":{"get":{"tags":["Payments"],"summary":"`GET /v1/payments/{id}`","operationId":"getPayment","parameters":[{"name":"id","in":"path","description":"Payment id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The payment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentView"}}}}},"security":[{"api_key":[]}]}},"/v1/payouts":{"get":{"tags":["Payouts"],"operationId":"listPayouts","parameters":[{"name":"reference","in":"path","description":"Look up by the caller's own reference — the reconciliation primitive.","required":true,"schema":{"type":["string","null"]}},{"name":"q","in":"path","description":"Free-text search across the identifiers somebody actually has to hand:\ntheir reference, ours, the destination number, their customer id.\n\nSeparate from `reference`, which is an exact lookup that returns one\npayout. Somebody with a partial phone number is asking a different\nquestion and would otherwise get nothing.","required":true,"schema":{"type":["string","null"]}},{"name":"recipient","in":"path","description":"Everything sent to one destination.","required":true,"schema":{"type":["string","null"]}},{"name":"customer_reference","in":"path","description":"Everything for one of the merchant's own customers.","required":true,"schema":{"type":["string","null"]}},{"name":"funds","in":"path","description":"Narrow to where the money is: `held`, `delivered`, `returned`.\n\n`held` is the one that matters — money out of the wallet, not delivered\nand not returned. A merchant chasing a problem wants exactly that list\nand has no way to build it from statuses without knowing which of ours\nreturn funds.","required":true,"schema":{"type":["string","null"]}},{"name":"from","in":"path","description":"Inclusive date bounds, ISO-8601.","required":true,"schema":{"type":["string","null"]}},{"name":"to","in":"path","required":true,"schema":{"type":["string","null"]}},{"name":"limit","in":"path","required":true,"schema":{"type":["integer","null"],"minimum":0}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PayoutView"}}}}}},"security":[{"api_key":[]}]},"post":{"tags":["Payouts"],"summary":"Accept a payout.","description":"Returns **202 Accepted** with `pending`: we have taken responsibility, not\ncompleted the transfer. A `201` would imply the money has moved.","operationId":"createPayout","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePayout"}}},"required":true},"responses":{"200":{"description":"The idempotency key was seen before; the original payout is replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayoutView"}}}},"202":{"description":"Accepted and durably recorded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayoutView"}}}}},"security":[{"api_key":[]}]}},"/v1/payouts/{id}":{"get":{"tags":["Payouts"],"operationId":"getPayout","parameters":[{"name":"id","in":"path","description":"The payout identifier","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayoutView"}}}}},"security":[{"api_key":[]}]}},"/v1/pricing":{"get":{"tags":["Reporting"],"summary":"`GET /v1/pricing?country=KE`","description":"Readable by anybody who may read the merchant. What they are charged is\ntheirs to know, and making them ask for it is the problem this solves.","operationId":"getPricing","parameters":[{"name":"country","in":"query","description":"Narrow to one country, ISO-3166 alpha-2.","required":false,"schema":{"type":["string","null"]}},{"name":"direction","in":"query","description":"`payout` (the default) or `collection`.\n\nTwo price lists rather than one with a column: a merchant who does not\ncollect should not be shown collection rates, and one who does is asking\na different question on a different screen.","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"description":"What this account is charged, per corridor","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tariff"}}}}},"security":[{"api_key":[]}]}},"/v1/quotes":{"post":{"tags":["Payouts"],"summary":"`POST /v1/quotes`","description":"What a payout would cost. Reserves nothing, moves nothing, and can be\ncalled as often as a screen needs it.","operationId":"createQuote","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteRequest"}}},"required":true},"responses":{"200":{"description":"A price, valid until it expires","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteView"}}}},"400":{"description":"The corridor cannot carry this"}},"security":[{"api_key":[]}]}},"/v1/stats":{"get":{"tags":["Reporting"],"summary":"`GET /v1/stats?days=7&country=KE`","description":"Readable by any credential that may read, including a read-only agent token.\nThis is the endpoint most agents will live on.","operationId":"getStats","parameters":[{"name":"days","in":"query","description":"How far back to look. Defaults to a week, capped at a year.","required":false,"schema":{"type":["integer","null"],"format":"int32"}},{"name":"country","in":"query","description":"Restrict to one destination country, ISO-3166 alpha-2.","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"description":"Totals, per-country breakdown and failure reasons","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Stats"}}}}},"security":[{"api_key":[]}]}},"/v1/wallet/funding-accounts":{"get":{"tags":["Wallet"],"summary":"`GET /v1/wallet/funding-accounts`","description":"Where to send money to fund your wallet. Empty until one is arranged with\nyou.","operationId":"listFundingAccounts","responses":{"200":{"description":"Enabled funding accounts, in the order to show them","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FundingAccount"}}}}}},"security":[{"api_key":[]}]}},"/v1/wallet/ledger":{"get":{"tags":["Wallet"],"summary":"`GET /v1/wallet/ledger`","description":"Every movement against this merchant's wallet, newest first. The statement a\nmerchant reconciles against their own books, and the first thing anybody\nlooks at when a balance is not what they expected.","operationId":"listLedgerEntries","parameters":[{"name":"from","in":"query","description":"Inclusive date bounds, ISO-8601.","required":false,"schema":{"type":["string","null"]}},{"name":"to","in":"query","required":false,"schema":{"type":["string","null"]}},{"name":"q","in":"query","description":"Match a reference — a payout id or a deposit hash.","required":false,"schema":{"type":["string","null"]}},{"name":"limit","in":"query","required":false,"schema":{"type":["integer","null"],"format":"int64"}}],"responses":{"200":{"description":"Every movement, newest first","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/LedgerEntry"}}}}}},"security":[{"api_key":[]}]}}},"components":{"schemas":{"AccountCheck":{"type":"object","description":"What the rail said.","required":["verified","checked"],"properties":{"account_name":{"type":["string","null"],"description":"The name the rail holds. Absent when nobody could look, and never the\ncaller's own input handed back — echoing it would read as confirmation."},"checked":{"type":"boolean","description":"Whether anybody was actually able to look. False means nobody could, and\n`verified: false` then carries no information about the account."},"session_id":{"type":["string","null"],"description":"Short-lived handle to send back on the payout where the rail issues one,\nso the lookup is reused rather than repeated."},"verified":{"type":"boolean","description":"True only when a rail vouched for the name. False covers two different\nsituations — the rail said no, and no rail could say — which is why\n`checked` sits beside it."}}},"AllowedValue":{"type":"object","description":"A permitted value for an enum field, with a label for display.","required":["value","label"],"properties":{"label":{"type":"string"},"label_key":{"type":["string","null"],"description":"Stable key, when the label is ours to translate.\n\n`None` for a value whose label is a *name* rather than prose — an\noperator or a bank. Translating \"MTN Mobile Money\" would be translating a\nbrand, and a payer looks for the words on their own phone."},"value":{"type":"string"}}},"AmountLimits":{"type":"object","description":"What a corridor will accept, in the destination currency.\n\nEither bound may be absent, and absent means *not stated* rather than\nunbounded — an operator that has never told us its ceiling is different from\none that has none, and quoting infinity for the first would be inventing a\npromise on their behalf.","required":["currency"],"properties":{"currency":{"type":"string","description":"The currency the bounds are expressed in — always the destination\ncurrency, said explicitly so nobody reads them as settlement amounts."},"max":{"type":["string","null"]},"min":{"type":["string","null"],"description":"Decimal strings, like every other amount on the wire. A float here would\nreintroduce exactly the error the rest of the API avoids."}}},"AmountView":{"type":"object","required":["amount","currency"],"properties":{"amount":{"type":"string"},"country":{"type":["string","null"]},"currency":{"type":"string"}}},"CaseView":{"type":"object","required":["id","subject","subject_id","kind","status","summary","detail","opened_by","created_at","updated_at"],"properties":{"already_open":{"type":"boolean","description":"True when this case already existed and was returned rather than made.\n\nAn agent reporting the same complaint twice should be told it is already\nknown, not handed a second row — and should be able to tell the\ndifference without comparing timestamps."},"created_at":{"type":"string"},"detail":{},"id":{"type":"string"},"kind":{"type":"string"},"opened_by":{"type":"string"},"opened_for":{"type":["string","null"]},"payout_id":{"type":["string","null"],"description":"The payout, where it is one.\n\nPresent for a payout case and absent for a collection, so a caller\nwritten before collections keeps working and one written after is not\ntold a collection has a payout id. Prefer `subject_id`."},"resolution":{"type":["string","null"]},"resolved_at":{"type":["string","null"]},"resolved_by":{"type":["string","null"]},"status":{"type":"string"},"subject":{"type":"string","description":"What this case is about: `payout` or `payment`.\n\nA case is about a transaction, and since collections that is not always\na payout — the same four complaints arrive in the other direction and\nbelong in the same queue."},"subject_id":{"type":"string"},"summary":{"type":"string"},"updated_at":{"type":"string"}}},"CollectedFunding":{"type":"object","description":"Collected funds a merchant may actually fund a payout from.\n\n# Why this is visible when the collected balance is not\n\n`collected.rs` hides the collected balance from merchants, and gives the\nreason: a number that looks like money but cannot be spent is a support\nconversation every time it is displayed.\n\nNetting made that untrue in one place. Where a merchant has been granted\nit, the collected balance *is* spendable — that is the whole of the\nfeature — and hiding it left them discovering the figure by being refused,\nwhich disclosed it anyway and only when they overdrew.\n\nSo this carries exactly the currencies they can spend and no others. A\nmerchant with no grant sees nothing here, and the original reasoning still\nholds everywhere it still applies.","required":["currency","available","reserved","updated_at"],"properties":{"available":{"type":"string","description":"What a payout with `funded_from: \"collections\"` can draw on right now."},"currency":{"type":"string","description":"The local currency. One entry per currency, never summed — a franc is\nnot a naira and no rate has been agreed to make them one."},"reserved":{"type":"string","description":"Claimed by your own payouts funded from collections that have not yet\nsettled or failed. It returns to `available` if one fails.\n\nThe same meaning `reserved` has on the wallet above, deliberately: a\nmerchant reconciling one already knows how to read the other."},"updated_at":{"type":"string","description":"When this balance last moved."}}},"CollectionFlow":{"type":"object","description":"How a payer completes a collection on this corridor.","required":["kind","label","returns","next_step"],"properties":{"kind":{"type":"string","description":"`hosted_page` | `momo_prompt` | `bank_transfer`"},"label":{"type":"string","description":"What happens, in a sentence somebody can act on."},"next_step":{"type":"string","description":"What the integrator does next. Prose on purpose: this is the step that\nwas skipped, and a machine-readable enum nobody reads would have been\nskipped identically."},"returns":{"type":"array","items":{"type":"string"},"description":"Which fields of the response carry the thing you need. Named rather than\nimplied, because the one that broke was a caller reading neither."}}},"CollectionSummary":{"type":"object","description":"A merchant's collections in one currency, as what they are owed.\n\n# Settled amounts, never gross\n\nEvery total here is after our fee. A gross total invites reconciling the\nmoney that arrives against money that was never going to: our fee is in it,\nand the gap reads as something missing. The gross of a single collection is\non that collection, where it is needed to match a customer's receipt; it is\nnot summed anywhere a merchant looks for what they will be paid.\n\nNo USDC figure for what is still owed, either. What they will receive in\nUSDC depends on the rate on the day it is sent, and an estimate that moves\ndaily is a number to be disappointed by. USDC appears where it is a fact:\nagainst a settlement that happened.","required":["currency","collections","settled_amount","awaiting_fee","paid_out","paid_out_usdc","awaiting_settlement"],"properties":{"awaiting_fee":{"type":"integer","format":"int64","description":"Settled collections in the period whose fee is still being worked out,\nand so are not in `settled_amount` yet. Zero in the ordinary case."},"awaiting_settlement":{"type":"string","description":"Owed to you and not yet settled, as of now — whatever dates were asked\nfor, because what is owed is not a property of a period."},"collections":{"type":"integer","format":"int64","description":"Settled collections in the period."},"currency":{"type":"string","description":"The collection currency. One entry per currency, never summed."},"last_settled_at":{"type":["string","null"],"description":"When you were last settled in this currency. Absent if never."},"paid_out":{"type":"string","description":"Settlements paid to you in the period, in this currency."},"paid_out_usdc":{"type":"string","description":"What those settlements sent, in USDC."},"settled_amount":{"type":"string","description":"What those collections are owed to you: each one's amount received less\nour fee, added up."}}},"Corridor":{"type":"object","description":"A corridor the merchant can pay into.","required":["country","currency","direction","methods","status"],"properties":{"country":{"type":"string"},"currency":{"$ref":"#/components/schemas/CurrencyInfo"},"direction":{"type":"string","description":"Which leg this describes — `payout` or `collection`.\n\nEchoed because the query parameter sets the meaning of the whole\nresponse and the two are structurally identical: the same country, the\nsame currency, a `methods` list of the same shape. A client that caches\none, logs one, or holds both had nothing in the payload to tell them\napart, and \"is Nigeria on for this account\" has two answers.\n\nAlways present, including when the caller omitted the parameter and got\nthe payout default — a response that states what it is beats one the\nreader has to reconstruct from the request that produced it."},"methods":{"type":"array","items":{"$ref":"#/components/schemas/RailOption"},"description":"Selectable transfer methods, each with its own speed and cost."},"status":{"type":"string","description":"`live` | `paused`"}}},"CorridorTariff":{"type":"object","required":["country","country_name","destination_currency","flat","percent_bps","effective_from","is_default"],"properties":{"country":{"type":"string"},"country_name":{"type":"string","description":"Human name, so a list can be read without a lookup table."},"destination_currency":{"type":"string","description":"What the recipient is paid in."},"effective_from":{"type":"string","description":"When this price took effect. A merchant asking why a fee changed is\nasking this."},"flat":{"type":"string","description":"Charged on every payout regardless of size."},"is_default":{"type":"boolean","description":"True when this is the platform default rather than a price negotiated\nfor this merchant. Said plainly, because \"why am I on the standard rate\"\nis a conversation worth having with the right facts."},"max_amount":{"type":["string","null"]},"max_fee":{"type":["string","null"]},"method":{"type":["string","null"],"description":"The transfer method this price applies to. Absent means it applies to\nevery method in the country."},"min_amount":{"type":["string","null"],"description":"What the corridor will accept, in the destination currency. The other\nhalf of \"can I send this\" — repeated here so a finance question does not\nneed a second endpoint."},"min_fee":{"type":["string","null"],"description":"Floor and ceiling on the fee itself, where the contract sets them."},"percent_bps":{"type":"integer","format":"int32","description":"Of the amount sent, in basis points — 200 is 2%."}}},"CountryStats":{"type":"object","required":["country","payouts","settled","failed","sent"],"properties":{"country":{"type":"string"},"failed":{"type":"integer","format":"int64"},"payouts":{"type":"integer","format":"int64"},"sent":{"type":"array","items":{"$ref":"#/components/schemas/CurrencyTotal"}},"settled":{"type":"integer","format":"int64"}}},"CreatePayment":{"type":"object","description":"What a merchant asks for.","required":["amount","currency","country"],"properties":{"amount":{"type":"string","description":"What the payer is asked for, as a decimal string in the local currency.\nNever a float — a float cannot hold 0.1, and this is a figure somebody\nis charged."},"cancel_url":{"type":["string","null"],"description":"Where to return a payer who abandoned a hosted flow. Same rules."},"country":{"type":"string"},"currency":{"type":"string"},"customer_reference":{"type":["string","null"],"description":"The merchant's own id for the person paying.\n\nA different handle from `reference`: that one names the transaction,\nthis one names the customer. Payouts have carried both since the\nbeginning and collections carried only the first, so a merchant could\nsay which invoice a payment was for and not whose customer it was from —\non the leg where knowing who is paying decides the price.\n\nAuthoritative where sent. It decides whether this payer is a first-time\ndepositor in preference to anything we derive from a phone or an email,\nbecause two of their customers sharing a family phone are two customers\nand they are the ones who know."},"idempotency_key":{"type":["string","null"],"description":"Sent twice, charged once. Absent means the reference, and failing that\na key we mint — which makes a retry a second payment, so a caller that\nretries should send one."},"method":{"type":["string","null"],"description":"`mobile_money`, `bank_account`, `card`, `wallet`. Absent is mobile\nmoney, which is how most of Africa pays — the opposite default to\npayouts, where the bank is the common case."},"narration":{"type":["string","null"],"description":"What the payer sees this called, where the rail shows anything."},"payer":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Payer","description":"Who is paying, as far as the merchant knows. Optional throughout: a\nhosted page collects this itself, and demanding it up front would refuse\nthe flow that converts best."}]},"reference":{"type":["string","null"],"description":"The merchant's own handle for this payment. Opaque to us, and what they\nreconcile on."},"sender":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Sender","description":"Who is paying, for the record.\n\nThe same shape a payout carries, and deliberately the same name: a\ncollection has a sender too, and it is the person paying. A report\nasking \"who sent us money, and who did we send money to\" should not\nhave to learn two vocabularies for one fact.\n\nDistinct from `payer`, which stays what it was — the phone, the email\nand the operator are rail inputs that tell an acquirer where to send a\nprompt, and they have no payout equivalent. This is the identity half:\ntheir name, your id for them, where they are, and one document.\n\nOptional, everywhere, and not passed to any acquirer today. It is\nrecorded the way a payout's sender is recorded — because the corridors\nthat will eventually demand it are not the ones you are using now, and\na record that starts on the day somebody asks is a record with a hole\nin it.\n\n`sender.name` and `payer.name` are the same person; send either. Where\nboth arrive, this one is kept.\n\n**`sender.reference` is not read here.** Your own id for this person is\n`customer_reference`, and there is one field for it because there is one\nperson: on a collection the payer, the sender and your customer are the\nsame human. A payout needs both because its sender and its customer are\ntwo people — whoever the money came from, and whoever it went to.\n\nSending it anyway is not an error and is not stored; it is logged, so\nan id that matters is never lost silently. Put it in\n`customer_reference`, which is also what decides whether this payer is\npriced as a first-time depositor."}]},"success_url":{"type":["string","null"],"description":"Where to return the payer after a hosted flow completes.\n\nOptional, unlike the platforms that require it: most of our flows send\nnobody anywhere. A mobile-money prompt is answered on a handset with no\nbrowser involved, and a Nigerian bank transfer is made from the payer's\nown banking app hours later — demanding a return URL would refuse every\none of them for a redirect that never happens.\n\nhttps only. A payer is returned here carrying the fact that they just\npaid, and plain HTTP puts that on the wire for whoever is in between.\n\n**Not evidence.** Landing here does not mean the payment succeeded — a\npayer can type the address, and a rail can redirect before its own\nwebhook fires. It is where to send somebody. What settles a collection\nis the rail saying so."}}},"CreatePayout":{"type":"object","description":"A payout instruction.","required":["amount","currency","country"],"properties":{"amount":{"type":"string","description":"Destination amount, as a decimal string — never a float."},"country":{"type":"string"},"currency":{"type":"string"},"customer_reference":{"type":["string","null"],"description":"The merchant's own id for whoever is being paid.\n\nOptional and never interpreted by us. It exists so \"everything we sent\nthis customer\" is answerable correctly rather than by assuming a phone\nnumber is a person."},"funded_from":{"type":["string","null"],"description":"Where to take the money from: `wallet` (the default) or `collections`.\n\n`collections` funds the payout from what you have collected in the\ndestination currency, with no conversion: a naira payout paid from\nnaira you took in. It is priced at the corridor's usual rate plus a\nsmall netting fee, and it is available only where it has been agreed\nfor your account and that country — elsewhere the payout is refused,\nnot quietly funded from the wallet instead. You will not be charged\na conversion you did not ask for, and you will not be charged a\nnetting fee you did not ask for either."},"idempotency_key":{"type":["string","null"]},"method":{"type":["string","null"]},"narration":{"type":["string","null"]},"quote_id":{"type":["string","null"],"description":"A price obtained earlier from `POST /v1/quotes`.\n\nSend it where you showed somebody a figure and are now acting on it:\nthe payout is charged exactly what the quote said, rather than\nre-priced at whatever the rate has done since. A quote past its expiry\nis refused rather than silently re-quoted — being charged a number\nnobody was shown is the failure this exists to prevent."},"recipient":{"type":"object","description":"The destination account, as flat `field: value` pairs.\n\nDeliberately untyped here: the fields a corridor needs are data, served\nby `GET /v1/corridors/{country}/requirements`, and validated against that\nsame definition on the way in. A typed struct would mean the shape a\nmerchant is told to send and the shape we accept could drift apart.","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}},"reference":{"type":["string","null"]},"sender":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Sender","description":"Who is sending the money.\n\nRequired when the merchant pays out on behalf of their own users, and\nrefused as unnecessary when the merchant is themselves the originator —\nwe already know them. Which applies is a property of their account, not\nof this request, so nobody has to remember it per payout.\n\nCarried, never interpreted: we do not verify identity documents, and the\nschema does not pretend otherwise."}]},"session_id":{"type":["string","null"],"description":"A handle from a prior `POST /v1/corridors/{country}/validate`.\n\nSend it where you resolved the account and showed the name to somebody:\nit makes the payout go to the account you actually looked at, rather than\nto whatever that number resolves to at submission time. Accounts change\nhands and businesses get renamed.\n\nOptional everywhere. On a corridor whose rail insists on a lookup, one is\nperformed for you if you send none — the session is an improvement on\nthat path, not a precondition for it."}}},"CurrencyInfo":{"type":"object","description":"A destination currency, in the shape a client renders.\n\nCode *and* symbol *and* scale, so an amount input can be built without the\nclient shipping its own currency table — and without it rendering \"1,00 FRw\"\nfor a currency that has no minor unit.","required":["code","symbol","decimals"],"properties":{"code":{"type":"string","description":"ISO-4217 three-letter code."},"decimals":{"type":"integer","format":"int32","description":"Decimal places this currency actually supports.","minimum":0},"symbol":{"type":"string","description":"Symbol a payer recognises — `₦`, `FRw`, `€`."}}},"CurrencyTotal":{"type":"object","required":["currency","amount","count"],"properties":{"amount":{"type":"string","description":"A decimal string, never a float. The wire format for money everywhere\nelse here, and for the same reason: 0.1 + 0.2 is not 0.3."},"count":{"type":"integer","format":"int64"},"currency":{"type":"string"}}},"Customer":{"type":"object","description":"One person a merchant has paid, and what they have sent them.","required":["recipient_key","country","payouts","settled","failed","funds_held","sent","first_paid_at","last_paid_at"],"properties":{"country":{"type":"string"},"customer_reference":{"type":["string","null"],"description":"The merchant's own id for them, where they have sent one. Absent means\nwe know the destination and not who it belongs to."},"failed":{"type":"integer","format":"int64"},"first_paid_at":{"type":"string"},"funds_held":{"type":"integer","format":"int64","description":"Money out and unaccounted for, for this person specifically. The reason\nsomebody looks a customer up is usually that one of these is not zero."},"last_paid_at":{"type":"string"},"name":{"type":["string","null"],"description":"The most recent name a rail gave us for this destination, where one was\nreturned. Never authoritative — corridors that cannot validate a name\nsend back whatever was typed."},"payouts":{"type":"integer","format":"int64"},"recipient_key":{"type":"string","description":"The destination identifier — a phone or account number."},"sent":{"type":"array","items":{"$ref":"#/components/schemas/CurrencyTotal"},"description":"Total sent, per destination currency. Never summed across them."},"settled":{"type":"integer","format":"int64"}}},"Delivery":{"type":"object","description":"Settlement speed, in the shape an integrator renders.","required":["typical_seconds","max_seconds","label_key","label"],"properties":{"label":{"type":"string","description":"Ready-made label — \"Instant\", \"Today\" — so every client does not invent\nits own wording for the same number."},"label_key":{"type":"string","description":"Stable key, so a client can use its own wording."},"max_seconds":{"type":"integer","format":"int32","minimum":0},"typical_seconds":{"type":"integer","format":"int32","minimum":0}}},"EurApprox":{"type":"object","description":"A balance restated in euro, for reading rather than for reckoning.","required":["available","total","rate","published"],"properties":{"available":{"type":"string"},"published":{"type":"string"},"rate":{"type":"string","description":"The rate used, and the day the ECB published it. A Friday rate read on\na Sunday is still a Friday rate, and a screen that hides that invites\nsomebody to reconcile against the wrong day."},"total":{"type":"string"}}},"ExternalStatus":{"type":"string","description":"What a merchant sees. Stable — the compatibility surface depends on it.","enum":["pending","processing","completed","failed","reversed"]},"FailureStats":{"type":"object","required":["reason","count"],"properties":{"count":{"type":"integer","format":"int64"},"reason":{"type":"string","description":"The reason as the rail gave it, or `unknown` where none was recorded."}}},"FeeQuote":{"type":"object","description":"The merchant's contracted price for a corridor, ready to display.\n\nThis is what the *merchant* pays under their agreement (plan §8) — never our\nrail cost. Shown beside the delivery estimate because speed and price are one\ndecision: a payer choosing a method is trading one against the other.","required":["amount","currency","free"],"properties":{"amount":{"type":"string","description":"Decimal string, to avoid any float representation on the wire."},"currency":{"type":"string"},"free":{"type":"boolean","description":"True when the charge is zero.\n\nSurfaced as a flag rather than a rendered label because \"Free\" /\n\"Sans frais\" is the client's word in the client's language — and\nformatting `0,20 €` versus `€0.20` is locale work we would get wrong."}}},"FieldKind":{"type":"string","description":"How a field should be collected and validated.","enum":["text","msisdn","enum"]},"FieldScope":{"type":"string","description":"What a field describes.\n\nCorridors need more than recipient details: a SWIFT payout also needs the\n**charge bearer** — who absorbs correspondent-bank fees. That is a property\nof the *payment*, not the recipient, and clients group the two separately.","enum":["recipient","payer","payment"]},"FieldSpec":{"type":"object","description":"One field an integrator must collect.","required":["name","label_key","label","kind","required","scope"],"properties":{"allowed_values":{"type":"array","items":{"$ref":"#/components/schemas/AllowedValue"},"description":"Populated for [`FieldKind::Enum`]; empty otherwise."},"example":{"type":["string","null"]},"kind":{"$ref":"#/components/schemas/FieldKind"},"label":{"type":"string","description":"Human label for a form, in the payer's language."},"label_key":{"type":"string","description":"Stable key for the label.\n\nServed alongside the text so a client with its own translations — or a\nlanguage we do not carry — matches on this and ignores ours. Adding a\nlanguage then never becomes a prerequisite for somebody's launch."},"name":{"type":"string","description":"Key to send back in the payout request."},"regex":{"type":["string","null"],"description":"Anchored pattern the value must satisfy, where one is known."},"required":{"type":"boolean"},"resolved":{"type":"boolean","description":"This corridor can tell you this rather than you telling us.\n\nTrue for the beneficiary's name where the rail resolves it — do not ask\nyour user for it, and do not require it before calling\n`POST /v1/corridors/{country}/validate`, because it is what that call\n*returns*.\n\nSet only where resolution is compulsory, never merely available. On a\ncorridor where the lookup is optional the answer may not come back, and\ntelling somebody not to collect a field they then have no way to fill is\nworse than asking for it unnecessarily."},"scope":{"$ref":"#/components/schemas/FieldScope","description":"Recipient detail, or payment option."}}},"FundedFrom":{"type":"string","description":"Which of the merchant's balances a payout was debited from.\n\nOn the payout rather than derived from its currencies: a payout debited in\nnaira *is* one funded from collections today, but \"the debit currency is\nnot the settlement currency\" is an inference about the deployment, and the\nday a merchant holds a payout wallet in naira it becomes a wrong one.","enum":["wallet","collections"]},"FundingAccount":{"type":"object","description":"A funding account as the merchant reads it.","required":["id","method","currency","wallet_currency","details","created_at"],"properties":{"created_at":{"type":"string"},"currency":{"type":"string","description":"What to send: `EUR`, `USD`, `USDC`."},"details":{"description":"The fields to send to, by method. For `sepa`: `beneficiary_name`, `iban`,\n`bic`, and optionally `bank_name`, `bank_address`, `reference`. For\n`ach`: `beneficiary_name`, `routing_number`, `account_number`,\n`bank_name`, optionally `account_type`, `bank_address`,\n`beneficiary_address`, `reference`. For `wire`: `beneficiary_name`,\n`account_number`, `bank_name`, `bank_address`, and `routing_number` or\n`swift_bic`. For `local_bank`: `beneficiary_name`, `account_number`,\n`bank_name`, `country`, optionally `bank_code`, `reference`. For\n`crypto`: `network` (`evm` or `solana`), `asset`, `address`, optionally\n`chain` and `memo`."},"id":{"type":"string"},"instructions":{"type":["string","null"],"description":"Anything else to know when sending — a reference to quote, a cut-off."},"label":{"type":["string","null"]},"method":{"type":"string","description":"`sepa`, `ach`, `wire`, `local_bank` or `crypto`."},"wallet_currency":{"type":"string","description":"The wallet it funds."}}},"FundsState":{"type":"string","description":"Where a payout's money currently is, from the merchant's side.","enum":["untouched","delivered","returned","held"]},"FxView":{"type":"object","description":"The conversion, as the merchant needs to see it.\n\n`rate` is the whole of it, deliberately. It is what this payout actually\nconverted at, and it is what a platform reselling this corridor prices its\nown customers from.\n\n# Why there is no spread figure here yet\n\nThere was one, briefly, and it would have understated the truth on every\ntransaction. It reported *our* contracted markup — which is a real number\nand the one a merchant agreed to — while the rate it sat beside already\ncarried the rail's own spread inside it, a flat 99 basis points on every\ncorridor at the time of writing.\n\nSo a merchant priced at zero markup would have read `0` and concluded they\nhad been given the mid-market rate. They had not. Publishing a spread that\nexcludes the part of it we did not set is worse than publishing none: the\nrate can at least be checked against any public source, whereas a number\nlabelled \"spread\" is trusted precisely because we said it.\n\nReporting it honestly means reporting it inclusive of the rail, and the\nrail's figure is not currently captured — `RateSource::reference_rate`\nreturns a bare rate, and the quote token that carries the spread is\ndiscarded before it reaches storage. When that is threaded through, this\ngains one field: the all-in spread against the benchmark, rail included.\n\nWhat is deliberately not here in any case: which acquirer supplied the\nrate. That is ours, on this surface as on every other.","required":["rate","from","to"],"properties":{"from":{"type":"string"},"rate":{"type":"string","description":"Destination currency per unit of the settlement currency, as a decimal\nstring — never a float."},"to":{"type":"string"}}},"LedgerEntry":{"type":"object","description":"One movement against the wallet, as a merchant reads it.","required":["id","kind","direction","amount","currency","balance_after","occurred_at"],"properties":{"amount":{"type":"string"},"balance_after":{"type":"string","description":"The balance immediately after this movement.\n\nRecorded at the time rather than recomputed now, so a statement printed\ntoday and one printed next year agree — a running total derived from a\nfiltered list would not."},"currency":{"type":"string"},"direction":{"type":"string","description":"`credit` money in, `debit` money out."},"id":{"type":"string"},"kind":{"type":"string","description":"What caused it: `deposit`, `payout.reserve`, `payout.settle`,\n`payout.release`. The vocabulary of the ledger rather than of the UI,\nbecause a merchant reconciling against their own records needs the term\nthat appears everywhere else."},"occurred_at":{"type":"string"},"reference":{"type":["string","null"],"description":"What it was for. A payout id, or a deposit's transaction hash."}}},"MerchantSettlement":{"type":"object","description":"A settlement, as the merchant it was paid to reads it.\n\nNot [`SettlementView`] with fields hidden by the caller: a type that holds\nwhat an administrator sees, trimmed at a call site, is one forgotten trim\nfrom showing a merchant who in our office recorded it and what the custodian\ncharged us to convert. The custodian's fee is our cost; the note is ours.","required":["id","currency","amount","settled_amount","asset","rate","network","destination_address","tx_hash","created_at"],"properties":{"amount":{"type":"string","description":"What was taken off what you are owed, in `currency`."},"asset":{"type":"string","description":"`USDC`."},"created_at":{"type":"string"},"currency":{"type":"string","description":"The collection currency settled."},"destination_address":{"type":"string","description":"Your wallet it was sent to."},"id":{"type":"string"},"network":{"type":"string","description":"`evm` or `solana`."},"period_from":{"type":["string","null"],"description":"The dates it covered, `YYYY-MM-DD`, where they were stated."},"period_to":{"type":["string","null"]},"rate":{"type":"string","description":"Units of `currency` per one unit of `asset`, as applied."},"settled_amount":{"type":"string","description":"What was sent to you."},"tx_hash":{"type":"string","description":"The on-chain transaction, so you can see it arrive."}}},"Payer":{"type":"object","description":"The person paying, as stated. Carried, never interpreted.","properties":{"email":{"type":["string","null"]},"msisdn":{"type":["string","null"],"description":"E.164 where it is a phone, which for mobile money it is."},"name":{"type":["string","null"],"description":"Also accepted as `account_name`.\n\nThe requirements call this `name` on a collection now — one field with\ntwo spellings was a client sending `account_name` and reading `name`\nback off the same payment, wondering which of them was real. The alias\nstays because a form built before that change still sends the old one,\nand breaking their integration would punish them for our tidying."},"network":{"type":["string","null"],"description":"Which wallet or bank the money comes from — `mpesa`, `mtn`, `opay`.\n\nOptional, and needed more often than it looks: a corridor with one\noperator does not need telling, and Nigeria, where a payer chooses\nbetween OPay and PalmPay, does. Where a rail wants it and it is absent,\nthe rail decides — which is a worse experience than being asked."}}},"PaymentView":{"type":"object","description":"A payment, as a merchant reads it.","required":["id","state","country","method","amount","currency","flow","created_at"],"properties":{"amount":{"type":"string"},"amount_received":{"type":["string","null"],"description":"What the payer actually sent, once it has settled.\n\nBeside `amount` rather than replacing it, because the two are different\nfacts: `amount` is what you asked for, and a payer given a bank reference\ncan send a different figure. `fee` is charged on this one."},"cancel_url":{"type":["string","null"]},"country":{"type":"string"},"created_at":{"type":"string"},"currency":{"type":"string"},"customer_reference":{"type":["string","null"],"description":"Your own id for the person paying, as you sent it."},"customer_tier":{"type":["string","null"],"description":"Whether this payer was a first-time user of yours: `ftd`, `whitelisted`.\n\nAbsent where nothing identified them, and absent means *unknown* rather\nthan new — the payer fields are asked for on every collection and\nrequired on almost none, so a merchant may name nobody. An unidentified\npayer is still charged the first-time rate, because that is the rate\nthat cannot undercharge; what is missing is the claim about the person,\nnot the price.\n\nTheir customer's standing, which is theirs. What it *cost us* to carry\nis not, and is not here."},"expires_at":{"type":["string","null"],"description":"When we stop expecting it. The acquirer's window, not ours."},"failure_reason":{"type":["string","null"]},"fee":{"type":["string","null"],"description":"What we charge for carrying it. Their price, never our cost.\n\nAbsent until the payment settles: the fee is taken from what actually\narrived, and a payer given a bank reference can send a figure other than\nthe one asked for. Absent means not yet known, and is not zero."},"fee_currency":{"type":["string","null"]},"flow":{"type":"string","description":"How this payer completes it: `hosted_page`, `momo_prompt` or\n`bank_transfer`.\n\nStated on every payment, not only where there is a URL. An integrator\nwho has to infer the flow from whether a field is present will infer it\nwrong the first time a corridor changes — which has already happened\nonce, to somebody else, and is why `0040` put it in data."},"id":{"type":"string"},"instructions":{"description":"What to show them instead: a till number, an account, a reference to\nquote. Shape differs per flow, which is why it is not columns."},"method":{"type":"string"},"paid_by":{"type":["string","null"],"description":"Who the money actually came from, in the rail's own words.\n\nThe sending account's name on a bank transfer, the cardholder on a card.\nNot merged into `payer`, which is what *you* told us: they disagree\nlegitimately — somebody else settling an invoice is normal — and both\nare worth having when a customer asks about a payment.\n\nAbsent where the rail did not say, and never inferred from anything."},"payer":{"description":"Who the merchant said was paying, back as they sent it.\n\nRead back rather than only written, because the whole reason it is\nasked for is to tie a payment to one of the merchant's own customers —\nand a field that goes in and never comes out cannot be reconciled\nagainst anything. It is also what decides whether this payer is a\nfirst-time depositor or a known one, which is a price.\n\nNever what the *bank* said. An inward transfer names whoever made it,\nand that name is evidence about the transfer, not a claim the merchant\nmade about their customer. Conflating the two would let a payer rename\nsomebody else's customer by sending them money."},"redirect_url":{"type":["string","null"],"description":"Where to send the payer. Absent for a flow with nowhere to send them."},"reference":{"type":["string","null"]},"sender":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Sender","description":"Who paid, as you stated them. The same shape a payout returns.\n\nRead back for the reason `payer` is: a field that goes in and never\ncomes out cannot be reconciled against anything. Absent where nothing\nwas stated, which most collections do not."}]},"settled_amount":{"type":["string","null"],"description":"What you are owed from this collection: `amount_received` less `fee`.\n\nThe figure you are settled on, and the one to reconcile against. Absent\nuntil the collection settles, and absent while it is held because its\nfee could not yet be worked out — which is not zero."},"state":{"type":"string","description":"`pending`, `processing`, `settled`, `failed`, `expired`,\n`indeterminate`."},"success_url":{"type":["string","null"],"description":"Where the payer is returned after a hosted flow, as you gave them.\n\nEchoed so an integrator can see the payment carries what they sent —\na redirect that goes to the wrong place is otherwise only discovered by\na customer who ends up somewhere unexpected."}}},"PayoutView":{"type":"object","description":"What a merchant is shown.\n\nNote what is absent: the acquirer, its rail identifier, our cost, the margin.\nThose exist on the domain object and stop here.","required":["id","status","destination","debit","funds"],"properties":{"created_at":{"type":["string","null"],"description":"When this payout was accepted, ISO-8601 in UTC.\n\nEvery screen showing a payout was showing it without a time, which made\ntwo transfers to the same recipient for the same amount impossible to\ntell apart — the question \"is this the one from Tuesday\" had no answer\non the page it was being asked on.\n\nAbsent, not defaulted, on the one path that has no row behind it: the\nresponse to creating a payout. A stored payout always has one."},"customer_reference":{"type":["string","null"],"description":"The merchant's own id for whoever was paid, where they sent one."},"debit":{"$ref":"#/components/schemas/AmountView","description":"What leaves the merchant's wallet — the contracted price."},"destination":{"$ref":"#/components/schemas/AmountView"},"funded_from":{"$ref":"#/components/schemas/FundedFrom","description":"Where the debit came from: `wallet` or `collections`.\n\nAlways present, because it decides how to read `debit`: from the\nwallet it is a USDC figure converted at `fx`; from collections it is\nin the destination currency and there is no `fx` at all."},"funds":{"$ref":"#/components/schemas/FundsState","description":"Where the merchant's money is.\n\nThe status says what happened to the *payout*; this says what happened\nto the *money*, and they are not the same question. A merchant chasing a\nproblem is asking the second one — \"did I get it back?\" — and answering\nit from a status means knowing which states return funds and which hold\nthem, which is our knowledge and not theirs."},"fx":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/FxView","description":"The rate this payout was converted at, where a conversion happened.\n\nAbsent when none did — a payout in the currency the balance is held in\nconverts nothing, and quoting a rate of 1 would be inventing a fact.\n\nThis is the number a platform prices its own customers from. Without it\na merchant reselling this corridor has to guess what they were charged\nbefore deciding what to charge, and a guess in that position is either\nmargin they did not take or a loss they did not intend."}]},"id":{"type":"string"},"netting_fee":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AmountView","description":"The netting fee, where the payout was funded from collections.\n\nAlready inside `debit`; shown beside it so the merchant can see what\nfunding from collections cost, apart from what the corridor cost.\nAbsent on a wallet-funded payout, which was charged none."}]},"recipient_key":{"type":["string","null"],"description":"The destination identifier, so a client can group without re-parsing the\nrecipient object it may not fully understand."},"reference":{"type":["string","null"]},"status":{"$ref":"#/components/schemas/ExternalStatus"}}},"Period":{"type":"object","required":["days","from","to"],"properties":{"days":{"type":"integer","format":"int32"},"from":{"type":"string"},"to":{"type":"string"}}},"QuoteRequest":{"type":"object","required":["amount","currency","country"],"properties":{"amount":{"type":"string","description":"Destination amount, as a decimal string — never a float."},"country":{"type":"string"},"currency":{"type":"string"},"customer_reference":{"type":["string","null"],"description":"The merchant's own id for whoever is being paid.\n\nSend it. A quote without one is priced at the first-payment tier,\nbecause that is the only honest answer when we cannot tell whether this\npayee has been paid before — and a quote that reads low because it\nassumed otherwise would be charged high."},"method":{"type":["string","null"],"description":"`bank_account` | `mobile_money`. Defaults the way a payout does."}}},"QuoteView":{"type":"object","description":"A price, and the id that makes it binding.","required":["id","destination","debit","tier","expires_at"],"properties":{"debit":{"$ref":"#/components/schemas/AmountView","description":"What leaves the wallet if this quote is used."},"destination":{"$ref":"#/components/schemas/AmountView","description":"What the recipient receives."},"expires_at":{"type":"string","description":"After this, the quote is refused rather than re-priced."},"fx":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/FxView","description":"The conversion, where one happens."}]},"id":{"type":"string"},"tier":{"type":"string","description":"Which side of the rate card priced it — `ftd`, `whitelisted` or\n`baseline`. Stated because it is the field a caller can change: send a\ncustomer reference and a returning payee prices lower."}}},"RailOption":{"type":"object","description":"One selectable transfer method for a corridor.\n\nModelled on how payers actually choose: method, then *how fast* and *how\nmuch*, side by side. A picker that omits either forces the payer to guess.","required":["method","label","validation_supported"],"properties":{"delivery":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Delivery","description":"How fast. Absent when we have not measured it honestly yet."}]},"fee":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/FeeQuote","description":"How much. Absent when the amount is not yet known (the picker is often\nrendered before an amount is entered)."}]},"label":{"type":"string","description":"Display name — \"Bank account\", \"Mobile money\"."},"method":{"type":"string","description":"Transfer method in customer vocabulary — never the acquirer behind it."},"network":{"type":["string","null"],"description":"The specific operator, when this option *is* an operator (mobile money)."},"validation_supported":{"type":"boolean","description":"Whether recipient-name verification is available on this method."}}},"RaiseCase":{"type":"object","required":["summary"],"properties":{"detail":{"description":"Structured extras: the reference they quoted, when they checked, which\nbranch they visited. Whatever the reporter had."},"kind":{"type":["string","null"],"description":"One of [`KINDS`]. Defaults to the common claim."},"payment_id":{"type":["string","null"],"description":"The collection this is about."},"payout_id":{"type":["string","null"],"description":"The payout this is about.\n\nKept for callers written before collections existed. Send `payment_id`\ninstead to report a collection; send neither and the request is refused\nrather than guessed at."},"summary":{"type":"string","description":"What the complainant said, in their words."}}},"Requirements":{"type":"object","description":"Everything needed to construct a valid request for one corridor.\n\n# One shape, both directions\n\nEvery field is always present, `null` where there is nothing to say. A key\nthat appears and disappears is a second thing to branch on: a reader of\n`limits` has to work out whether an absent one means unbounded or means\nnobody asked, and a collection response missing `sandbox` and `delivery`\nlooked like a different endpoint rather than the same one answering about\nthe other leg.\n\nSo a payout and a collection deserialise into one struct. What differs is\nthe values — a collection has no validation to offer and usually no delivery\nestimate, and says so with a null rather than by leaving the reader to\nnotice.","required":["country","method","currency","fields","validation"],"properties":{"calling_code":{"type":["string","null"],"description":"The country's dialling code, without a `+`.\n\nPublished because a client building a phone input needs it and has, until\nnow, had to dig it out of the msisdn field's regex — which means parsing\nsomebody else's pattern and assuming its shape holds. The pattern is\nthere to *validate*; this is there to *build*, and conflating the two is\nhow a corridor whose regex grows an alternation quietly breaks a form.\n\nAbsent for a corridor with no mobile rail, where there is nothing to\nprefix."},"country":{"type":"string"},"currency":{"$ref":"#/components/schemas/CurrencyInfo","description":"Destination currency, with symbol and scale for rendering."},"delivery":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Delivery","description":"How long this corridor typically takes. Rendered beside the rail choice,\nbecause \"which method?\" and \"how fast?\" are one decision for the payer.\n\nUsually `null` on a collection, and honestly so: a bank transfer arrives\nwhen the payer gets round to making it, and a figure there would be a\npromise about somebody else's behaviour."}]},"direction":{"type":["string","null"],"description":"Which way the money goes: `payout` or `collection`.\n\nAlways stated, including on a payout. It used to be omitted there on the\ngrounds that a payout-only integrator should not have to read about a\ndirection they will never use — but the two responses are otherwise the\nsame shape, and a reader holding both had nothing in the payload to tell\nthem apart. The same reasoning the corridor list follows."},"fields":{"type":"array","items":{"$ref":"#/components/schemas/FieldSpec"}},"flow":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/CollectionFlow","description":"What the payer has to do, on a collection. `null` on a payout, which has\nno payer to send anywhere.\n\nThis field exists because of an incident. Wave and Orange-style channels\nin Côte d'Ivoire and Senegal began returning a page the payer must be\nredirected to, and until that was surfaced the payments — in the\nacquirer's own words — \"were failing silently\". An integrator should\nlearn what the flow is from the endpoint whose job is to say what to\nbuild, not from a payment that quietly did nothing."}]},"limits":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AmountLimits","description":"What this corridor will accept, in the destination currency.\n\nBelongs here for the same reason the fields do: this endpoint exists to\nsay everything needed to construct a *valid* payout, and an amount\noutside the operator's bounds is not valid. Leaving it out means an\nintegrator discovers the limit from a rejection, which is both slower and\nworse — a rejection at the rail costs a round trip and sometimes a fee."}]},"method":{"type":"string","description":"The transfer method, in customer vocabulary — `bank_account`,\n`mobile_money`, `card`.\n\nNote this is the *method*, never the acquirer: which provider carried a\npayout is an internal routing detail and appears nowhere a customer can\nsee it."},"sandbox":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/SandboxDestinations","description":"Destinations that behave a particular way, on a sandbox deployment.\n\n`null` in production, and null here means none — a live deployment\npublishing these would be advertising that some real account numbers are\nspecial, which is both false and an invitation.\n\nAlso null where the flow has no field to paste one into: a bank\ncollection mints an account for the payer rather than asking for theirs."}]},"validation":{"$ref":"#/components/schemas/ValidationSupport","description":"Stated up-front so an integrator knows whether to expect a verified name.\n\nNever supported on a collection: resolving who owns an account is a\nquestion about a destination, and there is none — the payer has not been\nnamed by anybody, which is what is being asked for."}}},"ResolveCase":{"type":"object","required":["status"],"properties":{"resolution":{"type":["string","null"],"description":"What was found. Required to close, refused before."},"status":{"type":"string","description":"`upheld` if the claim was right, `rejected` if the money did arrive.\n`investigating` to say it is being looked at without deciding."}}},"SandboxDestinations":{"type":"object","description":"Destinations that behave a particular way, for a sandbox.\n\n# Why the API says this rather than a form knowing it\n\nA console had these written into it as `2540000000001` and friends, which is\nKenya's calling code with a magic suffix. Offered on a Rwandan corridor they\nfailed that corridor's own pattern — the screen suggested a value and then\nmarked it invalid, which is worse than offering nothing.\n\nThe suffixes belong to the sandbox wrapper and the prefix belongs to the\ncorridor, and neither of those is a fact a form can hold. So the corridor\ncomposes them and publishes the result: a value an integrator can paste into\nthe field they are looking at.","required":["settles","fails_later","rejected","times_out"],"properties":{"fails_later":{"type":"string","description":"Accepted, then fails on the next status check — the asynchronous failure\nmost integrations get wrong."},"rejected":{"type":"string","description":"Rejected outright, provably not sent."},"settles":{"type":"string","description":"Settles immediately."},"times_out":{"type":"string","description":"Times out. Outcome genuinely unknown, funds held."}}},"Sender":{"type":"object","description":"The originator of a payment, as the merchant states them.","required":["name"],"properties":{"country":{"type":["string","null"],"description":"Where they are, ISO-3166 alpha-2."},"identifier":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/SenderIdentifier","description":"One further identifying detail. Which one is up to the merchant and\nwhat they hold — the regimes that ask for this accept several."}]},"name":{"type":"string","description":"Their name as it appears on their own records."},"reference":{"type":["string","null"],"description":"The merchant's own id for this person, so \"everything they sent\" is\nanswerable in the merchant's vocabulary rather than by matching names.\n\n`skip_serializing_if` on all three of these, because this struct is now\nread back as well as sent — a collection returns its sender. Without it\nthe absent half of a partly-stated person comes out as `null`, which\nevery other optional field on these surfaces omits instead."}}},"SenderIdentifier":{"type":"object","required":["type","value"],"properties":{"type":{"type":"string","description":"`national_id`, `passport`, `residence_permit`, `drivers_licence`."},"value":{"type":"string"}}},"Stats":{"type":"object","required":["period","totals","by_country","failures","open_cases","funds_held"],"properties":{"by_country":{"type":"array","items":{"$ref":"#/components/schemas/CountryStats"},"description":"Per destination country, busiest first."},"failures":{"type":"array","items":{"$ref":"#/components/schemas/FailureStats"},"description":"Why payouts failed, most common first. Empty when none did."},"funds_held":{"type":"integer","format":"int64","description":"Payouts whose money left the wallet and has neither been delivered nor\ncome back.\n\nThe figure a merchant should act on. It is not the same as `failed`: a\nfailure that was reversed is closed business, and a payout whose outcome\nwe genuinely do not know is not a failure at all — but the money is out\nin both of the states counted here, and neither resolves by waiting."},"open_cases":{"type":"integer","format":"int64","description":"Open complaints. The number a support agent actually wants."},"period":{"$ref":"#/components/schemas/Period","description":"The window these numbers describe, restated so a caller relaying them\nhas the qualifier without having to remember the request."},"totals":{"$ref":"#/components/schemas/Totals"}}},"Tariff":{"type":"object","required":["currency","corridors"],"properties":{"corridors":{"type":"array","items":{"$ref":"#/components/schemas/CorridorTariff"}},"currency":{"type":"string","description":"The currency every fee below is charged in — the settlement currency,\nnot the destination one."}}},"Totals":{"type":"object","required":["payouts","settled","failed","in_flight","sent","debited"],"properties":{"debited":{"type":"array","items":{"$ref":"#/components/schemas/CurrencyTotal"},"description":"What the merchant was debited, per settlement currency."},"failed":{"type":"integer","format":"int64"},"in_flight":{"type":"integer","format":"int64","description":"Still moving: accepted, reserved, or awaiting an answer from a rail."},"payouts":{"type":"integer","format":"int64"},"sent":{"type":"array","items":{"$ref":"#/components/schemas/CurrencyTotal"},"description":"What was sent, per destination currency.\n\nNever summed across currencies — see the module note."},"settled":{"type":"integer","format":"int64"},"success_rate":{"type":["number","null"],"format":"double","description":"Settled as a share of everything that reached a terminal state.\n\nIn-flight payouts are excluded from the denominator rather than counted\nas failures — a rate that drops every time somebody submits a batch is a\nrate nobody can act on. `None` when nothing has settled or failed yet,\nbecause 0/0 is not zero percent."}}},"ValidateAccount":{"type":"object","description":"What a caller asks about an account.","properties":{"method":{"type":["string","null"],"description":"`mobile_money`, `bank_account`, `card`, `wallet`."},"recipient":{"type":"object","description":"The same flat `field: value` pairs a payout carries, checked against the\nsame corridor definition. One shape for both, so an integrator that can\nbuild a payout can validate one without learning a second format.","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}}}},"ValidationSupport":{"type":"object","description":"Whether a corridor can tell us who owns an account before we pay it.","required":["supported"],"properties":{"required":{"type":"boolean","description":"True when the rail will not move money until the account is resolved.\n\nDistinct from `supported`, and the distinction is the useful part. On a\ncorridor where this is true the lookup happens on every payout whether or\nnot the caller asks — so an integrator can show the beneficiary's name to\ntheir own user for confirmation before anybody presses send, and a payout\ncan be refused for an account that does not exist before money moves.\n\nWhere it is false and `supported` is true, the lookup is an option. A\npayout proceeds whatever it says."},"supported":{"type":"boolean","description":"True when name lookup is available here."}}},"WalletBalance":{"type":"object","description":"What a merchant has, and what of it is already spoken for.","required":["merchant_id","currency","available","reserved","total","updated_at"],"properties":{"approximate_eur":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/EurApprox","description":"The same balance in euro, for a merchant who funds and reports in it.\n\nIndicative and named so. Converted at the European Central Bank's daily\nreference rate, which prices nothing here and never will — the figures\nabove are the balance, this is a convenience beside them. Absent when\nthe reference could not be read, because no euro figure is a smaller\nfailure than a stale one."}]},"available":{"type":"string","description":"What a new payout can be funded from."},"collections":{"type":"array","items":{"$ref":"#/components/schemas/CollectedFunding"},"description":"Local-currency balances you may fund a payout from, where funding from\ncollections has been agreed for your account.\n\nAbsent entirely where it has not, rather than empty: an empty list\nreads as \"you have none\", and the true statement is that the\narrangement does not exist."},"currency":{"type":"string"},"merchant_id":{"type":"string"},"reserved":{"type":"string","description":"Claimed by payouts that have not yet settled."},"total":{"type":"string","description":"Available plus reserved. Offered because somebody always wants it, and\nnamed so it cannot be mistaken for spendable."},"updated_at":{"type":"string"}}}},"securitySchemes":{"api_key":{"type":"http","scheme":"bearer","description":"Your secret key, as `Authorization: Bearer em_live_…`. Test keys carry `em_test_` and behave identically against the sandbox, so the integration you build with one needs no changes to go live."}}},"tags":[{"name":"Corridors","description":"Where you can pay, and what each destination needs."},{"name":"Payouts","description":"Sending money, and finding it again afterwards."},{"name":"Wallet","description":"What you hold with us, and every movement of it."},{"name":"Reporting","description":"Aggregates, so nobody has to paginate payouts to answer a question."},{"name":"Cases","description":"A customer says the money never arrived and we say it did."}]}