Payout to Africa

The rail global fintechs plug into to move money into Africa.

Your customer sends from anywhere — dollars, euros, yen, dirhams. We land it in Kigali, instantly, in local currency, through one integration. You keep the customer; we own the African rails, the liquidity, and the last mile.

Instant local-currency settlement One API, every corridor KYC / AML built in
Settlement · live Processing
🇺🇸
New York
USD · origin
via USDC
🇳🇬
Lagos
NGN · payout
Received$250.00
FX · USD→NGN1 : 1,540
Recipient gets₦385,000
Settled locallyT+0 · 3.1s
Live on the ground with
Safe HavenSudo AfricaOrchestrate Global

The hard part isn't the sending

Getting money into Africa breaks where global rails end.

Sending is easy from almost anywhere. Landing it — in the right currency, to a real person or merchant, compliantly — is where cross-border payments into Africa fall apart. Which is why we are destination-first: the origin is a detail, the last mile is the business.

Acceptance

Foreign cards die at the POS

80%+ of Nigeria's POS terminals have no contactless — Apple Pay and foreign taps simply fail; where terminals exist, small merchants rarely use them.

Fragmentation

Every country is its own build

Different banks, mobile-money networks, regulators and formats — a separate integration for each market.

Liquidity

FX & settlement risk

Someone must hold local currency and settle instantly while reconciling the corridor.

Compliance

Local KYC you don't want to own

On-the-ground identity, AML and payout licensing per country — years of work before one transaction clears.

One integration · every last mile

Connect once. We handle Africa.

Connect

One API, both directions

Collect from foreign cards, bank or stablecoin — and pay out across Africa — through one set of endpoints and webhooks.

Move

Settled in stablecoin

Your funding arrives in euro or dollars; your recipient gets local currency. Between the two, value moves as USDC, EURC or USDT — which is why a payout to Dakar clears in seconds, not days, and why you'll see it as the unit of account on every transaction. You never hold it.

Settle

We land it on local rails

Bank transfer, mobile money, wallet or QR at the point of sale. We hold the liquidity and move money the local way.

Deliver

Paid in local currency

The merchant or recipient is settled instantly in NGN, RWF and more. You keep the customer relationship end to end.

See the payout product →

Coverage

Live across 17 African markets.

🇳🇬
Nigeria
NGN · PalmPay · OPay · Bank
Live
🇰🇪
Kenya
KES · M-PESA · Airtel Money
Live
🇬🇭
Ghana
GHS · MTN · AirtelTigo · Telecel
Live
🇿🇦
South Africa
ZAR · Bank transfer
Live
🇨🇮
Côte d'Ivoire
XOF · Wave · Orange · MTN
Live
🇸🇳
Senegal
XOF · Orange · Wave · Free
Live
🇧🇯
Benin
XOF · MTN · Moov
Live
🇹🇬
Togo
XOF · Moov · T-money
Live
🇧🇫
Burkina Faso
XOF · Orange · Moov · Mobicash
Live
🇨🇲
Cameroon
XAF · Orange · MTN
Live
🇬🇦
Gabon
XAF · Airtel Money
Live
🇹🇿
Tanzania
TZS · Airtel · Tigo · Halopesa
Live
🇺🇬
Uganda
UGX · MTN · Airtel
Live
🇿🇲
Zambia
ZMW · MTN · Airtel
Live
🇲🇼
Malawi
MWK · Airtel · TNM Mpamba
Live
🇧🇼
Botswana
BWP · Mascom MyZaka · Orange
Live
🇷🇼
Rwanda
RWF · MTN · Airtel
Licensing

Who you are contracting with

A French company, with rails we run ourselves.

Station FSchoolab

Incubated at Station F — the world's largest startup campus — with Schoolab, the Paris innovation studio.

1 API
for all of Africa
T+0
local settlement
12 live
markets across Africa
24/7
liquidity, always on

Get started

Move money into Africa without building Africa.

Tell us the corridor you need to serve. We'll get you a sandbox key and a settlement plan for your first market.

Product · Payouts

One payout API for all of Africa.

Collect however your customer pays — foreign card, bank or stablecoin — and settle recipients instantly in local currency, on rails we run on the ground.

Payouts

Every way to pay out, one contract.

Global Payouts

Pay anyone, anywhere in Africa

Send to bank accounts, mobile-money wallets and cards across every live corridor from a single endpoint.

Instant Payouts

Settle in seconds, T+0

Recipients are paid in local currency in real time — not next-day batch — with a live status webhook.

Mass Payouts

Thousands in one call

Disburse to a whole payout file — creators, suppliers, gig workers — with per-item status and retries.

Bank transferMobile moneyWalletQR at POSCard

Settlement & liquidity

Liquidity is the product.

We hold local balance sheet so your recipients are paid instantly, and reconcile the corridor behind the scenes.

Local-currency settlement

Recipients receive NGN, RWF and more — never a foreign balance they can't spend.

FX & liquidity

Transparent, locked FX at payout time. We carry the float; you get one predictable rate.

Reconciliation

Every payout reconciled to the rail, with a ledger your finance team can trust.

Platform

Compliance & controls in the flow.

KYC & compliance

Screening at the rail

Identity, sanctions and AML checks handled per country — see Security.

Fraud & risk

Stop bad payouts

Velocity, anomaly and beneficiary checks before money leaves the account.

Analytics

Every corridor, one view

Success rates, settlement times and FX per country, in real time.

Get started

Pay out to Africa in a single call.

Solutions

Whoever needs to pay into Africa.

One rail, many jobs — from a neobank sending remittances to a marketplace paying thousands of creators.

By use case

Built for how money actually moves.

Remittances

Diaspora → home

Send from any diaspora market to family and merchants in Africa, settled instantly in local currency.

Marketplace payouts

Creators & sellers

Pay thousands of African creators, drivers and sellers in one mass-payout call.

B2B supplier payments

Cross-border invoices

Settle African suppliers on time, in their currency, with full reconciliation.

Payroll & gig

Workforce payouts

Recurring or on-demand payouts to workers across multiple markets.

Visitor acceptance

Pay-in → local settle

A visitor pays with a foreign card or stablecoin; the merchant is settled in local currency.

Custom corridor

Don't see yours?

Tell us the flow — we'll map the corridor and the compliance.

By customer

Who plugs in.

Neobanks & fintechs

Add Africa without building it

Give your users an Africa corridor through one integration — you own the customer, we own the last mile.

PSPs & remittance cos

Local settlement, instant

Replace fragile country-by-country integrations with one settlement rail.

Platforms & marketplaces

Pay your African side

Disburse to sellers, creators and drivers at scale, reliably.

Get started

Tell us your corridor.

Coverage

Live where it counts — expanding across Africa.

Proven on the ground, corridor by corridor. Status shown honestly: live today, in licensing, or on the roadmap.

17 markets live

Real payout rails across Africa.

Bank and mobile-money payout, live today, on the networks people actually use in each market.

🇳🇬
Nigeria
NGN · PalmPay · OPay · Bank
Live
🇰🇪
Kenya
KES · M-PESA · Airtel Money
Live
🇬🇭
Ghana
GHS · MTN · AirtelTigo · Telecel
Live
🇿🇦
South Africa
ZAR · Bank transfer
Live
🇨🇮
Côte d'Ivoire
XOF · Wave · Orange · MTN
Live
🇸🇳
Senegal
XOF · Orange · Wave · Free
Live
🇧🇯
Benin
XOF · MTN · Moov
Live
🇹🇬
Togo
XOF · Moov · T-money
Live
🇧🇫
Burkina Faso
XOF · Orange · Moov · Mobicash
Live
🇨🇲
Cameroon
XAF · Orange · MTN
Live
🇬🇦
Gabon
XAF · Airtel Money
Live
🇹🇿
Tanzania
TZS · Airtel · Tigo · Halopesa
Live
🇺🇬
Uganda
UGX · MTN · Airtel
Live
🇿🇲
Zambia
ZMW · MTN · Airtel
Live
🇲🇼
Malawi
MWK · Airtel · TNM Mpamba
Live
🇧🇼
Botswana
BWP · Mascom MyZaka · Orange
Live
🇷🇼
Rwanda
RWF · MTN · Airtel
Licensing

Rails per market

We meet money where it already moves.

Bank

Instant transfer

Direct-to-account payout on local instant-payment schemes.

Mobile money

Wallet payout

The dominant way most of Africa gets paid.

Card

Push-to-card

Where cards are the norm, we push funds directly.

QR / POS

In-person

Merchant acceptance and settlement at the point of sale.

Get started

Don't see your corridor? We'll build it.

Developers

API-first. Payout in a single call.

Idempotent endpoints, signed webhooks, and a full sandbox with test corridors — so your ledger and ours never drift.

The spec is generated from the running service, not written beside it — so it describes the deployment answering you rather than a document that drifted.

POST /v1/payouts
# Land 25,000 naira in a Nigerian bank account.
# Idempotency-Key: inv-4821-a   ← retry with the same key, pay once
{
  "amount":    "25000.00",        // a string. always. see below
  "currency":  "NGN",
  "country":   "NG",
  "method":    "bank_account",
  "recipient": {                // fields the corridor asked for
    "account_number": "0123456789",
    "bank_code":     "058"
  },
  "reference": "invoice-4821"
}

→ 202 Accepted
{
  "id":     "pay_7c1f…",
  "status": "pending",
  "destination": { "amount": "25000.00", "currency": "NGN", "country": "NG" },
  "debit":       { "amount": "16.83", "currency": "USDC" },
  "funds":     "held",          // where your money is, not what the payout is
  "reference": "invoice-4821"
}
[1]

Amounts are strings

"25000.00", never 25000.0. A float cannot hold 0.1, and money that is nearly right is wrong.

[2]

The corridor tells you the fields

GET /v1/corridors/{country}/requirements returns what to collect, per country and method. One form serves every destination.

[3]

Retry without paying twice

Send Idempotency-Key on every create. A repeat returns the original payout rather than making a second one.

[4]

Which rail carried it is ours

You send a country and a method. We route, fall through and reconcile — and never make you branch on a provider.

Reference

The payout API, end to end.

Everything below is the live contract. The generated OpenAPI document is the authority for exact field lists; this is the shape of the thing.

Base URL

https://api.everydaymoney.eu  · production
https://sandbox-api.everydaymoney.eu  · sandbox

Auth

Authorization: Bearer em_test_… — one key, no merchant id anywhere in the API. A caller states what it wants done, not who it is.

Amounts

Decimal strings, always. "1250.00", never 1250.0. Parse as decimals, never floats.

Language

Send Accept-Language and every label we generate comes back in it — method names, field labels, operator names, delivery estimates and payer instructions. en, fr, es and de today; anything else falls back to English rather than being refused.

A header rather than a query parameter or an account setting, because it is what every HTTP client already sends: proxy your payer's browser and the right language arrives with no code at all. Quality values are honoured and region subtags dropped — fr-CI and fr-SN are both French, de-AT and de-CH both German, since the difference does not change what a bank account is called.

Errors

{ "error": { "code": "insufficient_funds", "message": "…" } }

Lifecycle

Five statuses, and a separate answer about your money.

pending

Accepted, funds reserved. Your wallet is already debited.

processing

With a rail and working. Also what an unknown outcome looks like — see below.

completed

Delivered.

failed

Definitively not sent. Funds returned to your wallet.

reversed

Reserved, then unwound. Funds returned.

Every payout also carries funds, which answers a different question: not what became of the payout, but where your money is.

untouched

Not yet debited.

held

Out of the wallet, not delivered, not returned. The one worth alerting on.

delivered

Out and correctly spent.

returned

Out and back again.

Reconcile on funds. Deriving it from status means knowing which of our states return money, which is our knowledge and not yours.

The one that costs money: an unknown outcome. A timeout, a 5xx, or the passage of time are statements about the conversation, not about the payment. A payout in that position keeps reporting processing with funds: "held", and it does not resolve by waiting — only the rail saying so, or a statement, resolves it.

So never retry a payout that is still processing, and never treat it as failed. Send a new one only after you have seen failed or reversed. An integration that retries on timeout pays twice, and this is the single most expensive mistake available.

Before you send

Ask the corridor what it needs.

Field lists are data, not a spec you hard-code. One generic form serves every destination because the corridor answers for itself.

GET /v1/corridors?direction=payout
# Accept-Language: fr    ← labels come back in French
[
  {
    "country":   "SN",
    "currency":  { "code": "XOF", "symbol": "CFA", "decimals": 0 },
    "direction": "payout",
    "status":    "live",
    "methods": [
      { "method": "mobile_money", "label": "Orange Money",
        "network": "orange", "validation_supported": true }
    ]
  }
]
GET /v1/corridors/SN/requirements?method=mobile_money
# Accept-Language: fr  →  "label": "Numéro de téléphone"
{
  "method":   "mobile_money",
  "currency": { "code": "XOF", "symbol": "CFA", "decimals": 0 },
  "fields": [
    { "name": "msisdn", "label": "Mobile number",
      "kind": "msisdn", "required": true,
      "example": "+221700000000" },
    { "name": "network", "label": "Mobile money operator",
      "kind": "enum", "required": true,
      "allowed_values": [
        { "value": "orange", "label": "Orange Money" },
        { "value": "wave",   "label": "Wave" }
      ] },
    { "name": "account_name", "label": "Account holder name",
      "kind": "text", "required": false }
  ],
  "limits": { "min": "500", "max": "300000", "currency": "XOF" }
}

# Whatever comes back in `fields` is what goes in `recipient`,
# keyed by `name`. Honour `required` — it is the same definition
# we validate against on the way in.
# Amounts outside `limits` are refused here rather than at the rail —
# no round trip, no fee, nothing to unwind.

Optional, and worth it where the rail supports it: POST /v1/corridors/{country}/validate resolves an account and returns the name on it, plus a session_id. Send that id on the payout and the money goes to the account you actually looked at — not to whatever that number resolves to at submission time. Accounts change hands and businesses get renamed.

Quoting

Ask the price before you commit to it.

A payout debits the wallet the moment it is created — that is deliberate, and it is why there is a separate call for the question “what would this cost?”. A quote reserves nothing and can be asked as often as a screen needs it.

POST /v1/quotes
{
  "amount":   "50000",
  "currency": "XOF",
  "country":  "SN",
  "method":   "mobile_money",
  // send it — see the note on tiers below
  "customer_reference": "creator_88"
}

→ 200 OK
{
  "id": "qt_4b19…",
  "destination": { "amount": "50000", "currency": "XOF", "country": "SN" },
  "debit":       { "amount": "93.84", "currency": "USDC" },
  "fx":         { "rate": "554.14", "from": "USDC", "to": "XOF" },
  "tier":       "whitelisted",
  "expires_at": "2026-09-01T14:22:10Z"
}

# Nothing is reserved. Ask again whenever you like.
POST /v1/payouts
# Present the id and you are charged what you were shown.
{
  "quote_id": "qt_4b19…",
  "amount":   "50000",
  "currency": "XOF",
  "country":  "SN",
  "method":   "mobile_money",
  "recipient": { "msisdn": "+2217•••••••",
                 "network": "orange" }
}

# The rate, the markup and the fee all come from the quote.
# None of them is recomputed against a rate that moved.

# Past its expiry:
→ 400  { "error": { "code": "quote_expired", … } }

# For a different amount, currency, country or method:
→ 400  { "error": { "code": "quote_mismatch", … } }

An expired quote is refused, never re-priced. Silently re-quoting would charge you a number nobody was shown, which is the one outcome a quote exists to prevent. Ask for a new one instead — the response always carries its own expires_at, so you never have to guess how long you have.

Send a customer reference. The price depends on whether this payee has been paid before, and without your identifier we cannot tell — so the quote is priced at the first-payment tier, which is the only honest answer. The tier field tells you which one you got. A quote that assumed otherwise would read low and be charged high, and that is worse than no quote at all.

Sending

Create, then read it back.

POST /v1/payouts
# The header is the important part.
Idempotency-Key: invoice-4821-attempt-1

# Same key twice → the original payout, not a second one.
# Send one on every create. Without it, a request that
# times out and is retried is two payments.

# Optional on the body, for callers who cannot set headers:
{ "idempotency_key": "invoice-4821-attempt-1" }
# The header wins where both are present.
GET /v1/payouts
# One, by our id
GET /v1/payouts/pay_7c1f…

# Exactly one, by your own reference
GET /v1/payouts?reference=invoice-4821

# Partial match over id, your reference, destination, customer id
GET /v1/payouts?q=254712

# Everything for one of your customers
GET /v1/payouts?customer_reference=cust_1183

# The list that matters: out, not delivered, not returned
GET /v1/payouts?funds=held

# Bounded, and paged from where the last page stopped
GET /v1/payouts?from=2026-08-01&to=2026-08-31&limit=100
GET /v1/balance

Wallet balance — available and reserved, per currency.

GET /v1/wallet/ledger

Every movement with the balance after it, recorded at the time rather than recomputed — so a statement printed today and one printed next year agree.

GET /v1/pricing

Your own rate card, corridor by corridor: flat, percentage, FX markup, bounds and effective date. Route on real numbers rather than a copied table.

GET /v1/stats?days=7

Totals, per-country breakdown, failure reasons, open cases and funds_held. One call, and cheaper than paginating payouts.

GET /v1/customers

Everyone you have paid, grouped by destination, with what is still outstanding for each.

POST /v1/cases

Raise a case when a payout marked delivered did not arrive. This is the supported route for a dispute — not a retry.

Webhooks

Every event we send, and what is in it.

Two event types on the payout leg. Both arrive at the endpoint you register in the console, signed, and retried until you take them.

POST your endpoint
Content-Type:      application/json
EM-Signature:      9f2a7c…            // HMAC-SHA256 of the raw body
EM-Event-Dialect:  everydaymoney@2026-08-01

# Sign the bytes as received. Parsing and re-serialising first will
# change them, and the signature will not match.
# Compare in constant time.
EVENT payout.completed
# Delivered. Funds correctly spent.
{
  "id":             "evt_pay_7c1f…_payout.completed_0",
  "type":           "payout.completed",
  "schema_version": "2026-08-01",
  "occurred_at":    "2026-08-31T09:14:22Z",
  "data": {
    "payout_id": "pay_7c1f…",
    "status":    "completed",
    "detail":    null
  }
}

# Fetch the payout for the money: this event carries the
# outcome, not the amounts.
EVENT payout.failed
# Provably not sent. Funds already back in your wallet.
{
  "id":             "evt_pay_3ab8…_payout.failed_0",
  "type":           "payout.failed",
  "schema_version": "2026-08-01",
  "occurred_at":    "2026-08-31T09:15:03Z",
  "data": {
    "payout_id": "pay_3ab8…",
    "status":    "failed",
    "detail":    "account does not exist"
  }
}

# `detail` is the rail's reason in our words. It is absent
# when we were given none — absent, never invented.
EVENT payout.failed · status reversed
# The case to handle explicitly. One event type, two statuses.
{
  "id":             "evt_pay_51d0…_payout.failed_2",
  "type":           "payout.failed",
  "schema_version": "2026-08-01",
  "occurred_at":    "2026-08-31T09:31:47Z",
  "data": {
    "payout_id": "pay_51d0…",
    "status":    "reversed",
    "detail":    "every route for this corridor was exhausted"
  }
}

# Branch on `data.status`, not on `type`. A payout that was
# reserved and then unwound reports `reversed`; one that never
# got that far reports `failed`. Both mean: money is back,
# nothing was delivered, you may safely send a new payout.

# The trailing _2 is the attempt it happened on. Ids are stable,
# so a redelivery carries the id you already saw.
RETRY when your endpoint does not take it
# Retried on 408, 429 and any 5xx:
attempt 1   after 10s
attempt 2   after 1m
attempt 3   after 5m
attempt 4   after 30m
attempt 5   after 2h
thereafter  every 6h

# NOT retried on any other 4xx — a body you rejected as
# malformed will not become valid on the third attempt.

# Return 2xx as soon as you have stored it. Do the work after.
# The schedule is deliberately flatter than our payout poller:
# your endpoint being down is your outage, and hammering it
# during an incident makes your recovery harder.

Expect the same event twice. Rendering is deterministic and event ids are stable, so a redelivery months later is byte-identical to the original — key your handler on id and make it idempotent. An event is written in the same transaction as the state change that caused it, so it cannot be lost because a delivery failed; the cost of that guarantee is that it can be delivered more than once.

There is no event for an unknown outcome, because there is nothing to report — nothing has happened. A payout stays processing and silent until the rail resolves it, and then you get one of the two events above. Poll GET /v1/payouts?funds=held for anything that has been quiet longer than you are comfortable with; do not infer failure from silence.

Errors

What a refusal means.

One envelope everywhere. The code is stable and safe to branch on; the message is for a human and may be reworded.

4xx error envelope
{ "error": { "code": "insufficient_funds",
            "message": "this payout needs 16.83; the wallet holds 4.20" } }
insufficient_funds

The wallet cannot cover it. We debit before asking the rail — the reverse order is how you send money you do not have.

no_wallet

No wallet in that settlement currency yet.

merchant_not_active

The account is not approved to transact.

corridor_not_enabled

That country is not open for you. method_not_enabled and direction_not_enabled are the narrower forms.

unsupported_corridor

Nobody carries it — as opposed to you not being entitled to it. unsupported_method and unsupported_currency likewise.

amount_below_minimum

Outside what the corridor accepts, refused here rather than at the rail. amount_above_maximum is the other end.

invalid_recipient

A required field is missing or malformed. Checked against the same definition /requirements served you.

invalid_amount

Not a decimal string, or not positive. invalid_country for a bad ISO code.

rate_unavailable

No reference rate right now. We refuse rather than price from a stale one.

missing_credentials

No key. invalid_key if it is unknown, revoked, or the wrong mode.

too_many

Rate limited. Back off and retry — with the same idempotency key.

A 403 is an answer, not a wrong turn. Every endpoint declares that a credential is required; none declares which one satisfies it. Two keys reading the same spec get different results, because authority belongs to the key and not to the endpoint.

A 5xx from us is not a failed payout. It says the conversation failed. Retry with the same idempotency key — that is exactly what it is for — and read the status back rather than assuming either outcome.

Quickstart

From key to first payout in an afternoon.

Key

Grab a sandbox key

Sign in to the sandbox console and issue one yourself — sandbox-app.everydaymoney.eu. No sales call to get started.

Call

Create a test payout

Hit /v1/payouts against a test corridor and watch it settle.

Ship

Go live on a corridor

Flip to live keys once your first corridor is approved.

Playground

Send a sandbox payout. No signup.

Pick a country, and the corridor tells the form what it needs — that is how the API works, and this is the API. Every call is shown underneath.

Sandbox only. No real money moves, and the recipient details go to a test rail.

GET /v1/corridors
Pick a destination to begin.

Agent skills

Let an AI agent read your payouts.

One markdown file. Works in Claude Code, Buzz, a Slack bot, or anything else that reads a skill — it is the same document everywhere.

Bot

Create a bot user

A member of your business like anybody else, so it reaches what you granted it — not a separate set of powers.

Token

Issue its token

Every token expires, and read is the default. Write has to be chosen deliberately.

Paste

Drop in the skill

Save as SKILL.md and put the token in your harness’s secret store — never in the file.

What it can do: read payouts, balances, the ledger, corridors, pricing and statistics; raise a case when a customer says a payment marked successful never arrived.

What it cannot: send a payout, fund a wallet, issue an API key or change settings. That limit is the token, not the wording — so a prompt asking for any of it is refused rather than obeyed. A token can never exceed the person who created it.

SKILL .claude/skills/everydaymoney/SKILL.md
---
name: everydaymoney
description: Read payouts, balances and settlement statistics from EverydayMoney, and raise a case when a customer says a payment marked successful never arrived. Use for questions about payout status, failure rates, wallet balance, corridor pricing, or a specific transaction.
---

# EverydayMoney

Payouts into Africa, settled from a USDC wallet. This skill reads your
organisation's own data and can open a case about a payment.

## Credential

A personal access token, as `Authorization: Bearer <token>`. Create one in the
console under **Tokens**. Two things about it that matter here:

- **It expires.** Every token carries an expiry date; there is no permanent one.
  A 401 usually means it lapsed rather than that anything is wrong.
- **It cannot exceed the person who made it.** A token issued by somebody who
  cannot see live keys cannot see live keys either, whatever it asks for. If a
  request is refused, the answer is not a different endpoint — it is that this
  token was never allowed to do that.

Never print the token, echo it into a message, or write it to a file.

## Base URL

    https://api.everydaymoney.eu          production
    https://sandbox-api.everydaymoney.eu  sandbox

Test tokens behave identically against the sandbox.

## The full reference

    GET /openapi.json

Unauthenticated, and generated from the running service rather than written
beside it — so it describes the deployment answering you, not a document that
drifted. Fetch it when you need a field this skill does not mention, an exact
response shape, or the bounds on a query parameter. What is below is the useful
subset, not the whole surface.

**It does not say what *you* may do.** Every operation declares that a
credential is required; none of them declares which role satisfies it. Two
tokens reading the same document get different answers, because authority is a
property of the token and not of the endpoint. So a 403 is not a sign you called
the wrong thing — it is the answer.

The reference also omits the console and platform endpoints entirely. Those need
a browser session or a platform admin, so they are not doors this token can
open, and describing them would only invite you to knock.

## Amounts are strings

`"1250.00"`, never `1250.0`. Read them as decimals and never as floats — a
float cannot hold `0.1` exactly, and money that is nearly right is wrong. When
reporting a figure, quote it exactly as returned. Do not round, reformat, or
convert between currencies.

## What to call

### How are payouts doing?

    GET /v1/stats?days=7

Totals, per-country breakdown, failure reasons, open cases, and `funds_held`.
Prefer this over paginating payouts — it is one call and it is what the console
shows.

`funds_held` is the figure to lead with when somebody asks whether anything
needs attention. It counts payouts whose money left the wallet and has neither
been delivered nor come back. It is **not** the same as failures: a failure that
was reversed is closed business.

### Find one payout

    GET /v1/payouts?reference=invoice-4821    exact, by your own reference
    GET /v1/payouts?q=254712                  partial: phone, id, reference
    GET /v1/payouts/{id}

### What still needs chasing

    GET /v1/payouts?funds=held

The list a person actually wants when something has gone wrong. `held` means
out of the wallet, not delivered, not returned — and it does not resolve by
waiting.

### Money

    GET /v1/balance          available and reserved
    GET /v1/wallet/ledger    every movement, newest first

### Where we can pay, and what it costs

    GET /v1/corridors
    GET /v1/corridors/{country}/requirements?method=bank_account
    GET /v1/pricing

### A customer says the money never arrived

This is the one thing this skill writes.

    POST /v1/cases
    {
      "payout_id": "pay_…",
      "summary": "customer called, says nothing arrived, checked 14:00 Tue"
    }

`summary` is what the complainant said, in their words — not your paraphrase of
what you think happened. Add `detail` for anything structured they gave you: the
reference they quoted, when they checked, which branch they visited.

    GET  /v1/cases
    POST /v1/cases/{id}/resolve
    { "status": "upheld" | "rejected" | "investigating", "resolution": "…" }

Raising a case **never moves money**. It records that somebody disputes an
outcome we call successful, so a person can investigate. Do not tell anybody a
refund is on its way — you have not caused one and cannot.

Do not resolve a case you raised on a customer's word alone. `upheld` and
`rejected` are findings, and a finding needs somebody who looked.

## Attribution

If your harness knows which person asked, pass it:

    EM-On-Behalf-Of: alice@company.com

This is recorded for audit and is not verified. It does not grant anything and
never changes what a token may do.

## Rules

- **Never invent a figure.** If a call fails, say the call failed. A plausible
  number is worse than no number, because somebody will act on it.
- **Never state that money has moved** unless a payout's own status says so.
  `pending` is not `completed`.
- **Quote the reference somebody gave you** when reporting on a payout, so they
  can check it themselves.
- If asked to send a payout, decline and say why: this token reads and works
  cases; moving money is deliberately outside what any bot may do here.

Build

Get your sandbox key.

The sandbox is live and open. Sign in, issue a test key, and send a payout against a real corridor before you talk to anybody.

Security & Compliance

Regulated by design.

We're building the compliance foundation an enterprise fintech expects — with honest status on every certification. Where a standard is in progress, we say so.

Certifications & registrations

Where we stand today.

GDPR

Compliant

EU data protection built into how we collect, store and process personal data.

ORIAS

In progress

Registration as a French payment-services intermediary.

PCI DSS

Via partners

Card data handled through PCI DSS Level 1 certified partners — we minimise scope by never storing raw card numbers.

SOC 2 Type II

In progress

Controls being built to SOC 2 standards; observation window underway.

ISO 27001

In progress

Information-security management system in implementation toward certification.

Data residency

EU-hosted

Data hosted in the EU; regional residency for African markets on the roadmap.

Security by default

How we protect money and data.

Encryption

In transit & at rest

TLS everywhere; sensitive data encrypted at rest with managed keys.

Access control

Least privilege

Role-based access, audited actions, and separation of duties on money movement.

Monitoring

Always watching

Anomaly detection and alerting across payout flows and infrastructure.

Trust

Due-diligence questions? Ask us.

Company

Built in Paris to move money into Africa.

A French company you can contract with, running payment rails we operate ourselves in Africa. Both halves matter when something goes wrong at 2am in Lagos and your legal team is in Frankfurt.

Our story

We ran these rails before we sold them.

EverydayMoney started by getting African merchants paid — live in Nigeria with QR, wallets and cards. That is the same infrastructure your payouts land on. We are not reselling somebody else's corridor and learning it alongside you.

Who we are

Nigerian founders in Paris

We hold French Passeport Talent permits as state-recognised innovative founders, and we built EverydayMoney's rails and merchant network in Nigeria — now opening them to the world.

Where we are

Paris · Lagos

A French SAS to contract with, invoice and escalate to, and a team in-market for the last mile. Support hours cover both.

Backed & incubated in Paris

Station FSchoolab

Incubated at Station F — the world's largest startup campus — with Schoolab, the Paris innovation studio.

Partners

Live on the ground with.

Safe Haven

Banking-as-a-service & local settlement infrastructure in Nigeria.

Sudo Africa

Card issuing and processing across African markets.

Orchestrate Global

Payments orchestration partner in the ecosystem.

Join us

Building the rail for money into Africa.

We're hiring across engineering, payments ops and compliance.

Pricing

Usage-based. No surprises.

You pay per payout plus a transparent FX spread. Volume brings the rate down. No setup fees, no lock-in.

Starter
Pay as you go
  • Per-payout fee + transparent FX
  • All live corridors
  • Sandbox & test corridors
  • Email support
Get API access
Growth
Volume rate
  • Lower per-payout fee at volume
  • Instant & mass payouts
  • Webhooks & analytics
  • Priority support
Talk to sales
Enterprise
Custom
  • Negotiated FX & settlement
  • Dedicated liquidity & SLAs
  • Compliance & onboarding support
  • Named account team
Contact us

FAQ

Questions, answered.

How is the FX rate set?

We lock the rate at payout time and show it up front — one predictable rate, no hidden markup beyond the disclosed spread.

Are there setup or monthly fees?

No. You pay per payout plus FX. Enterprise agreements can bundle dedicated liquidity and SLAs.

Which corridors are available?

Nigeria is live; Rwanda is in licensing; more are on the roadmap. See Coverage, or ask us to open a corridor.

How fast do recipients get paid?

Instant (T+0) in local currency on supported rails, with a real-time settlement webhook.

Get started

Get a quote for your corridor.