Skip to content
NOSTREL
Nairobi · pre-launch

M-Pesa collections
and payouts,
on a ledger that balances.

One API for taking M-Pesa payments and sending them back out. Double-entry accounting underneath, signed webhooks on top, and a statement you can hand to an auditor.

Documentation, pricing and the sandbox are public. No account required to read them.

take a paymentbash
curl https://api.nostrel.com/v1/api/collections \  -H "Authorization: Bearer sk_live_••••••••" \  -H "Idempotency-Key: order-1042" \  -d phone=254712345678 \  -d amount=1850 \  -d reference=SC-1042
202 Accepted
what comes backjson
{  "id": "col_7dj4a2zeialj",  "state": "pending",  "amount": 1850,  "currency": "KES",  "reference": "SC-1042",  "checkout_request_id": "ws_CO_02102026...",  "created_at": "2026-10-02T09:14:22Z"}
  • Double entry, always

    Every movement posts two sides or neither.

  • Idempotent by key

    Retry the same request all day. One payment.

  • Signed webhooks

    HMAC, exponential backoff, replay from the console.

  • Two people to send

    Maker and checker on money leaving, by default.

The problem

Three failure modes that appear at volume

A single M-Pesa integration is straightforward. These three are what break at scale, and each one has a specific cause.

Records that disagree

Safaricom reports 412 payments. Your database holds 409. Nothing records which three differ, or when.

What we doA double-entry ledger that must balance, reconciled on a schedule. Each disagreement is written as a dated exception row with the receipt attached.

Duplicate payments

A client times out and retries. The payer receives three prompts for one order.

What we doEvery write takes an idempotency key. Repeat requests return the first response instead of creating a second payment.

Unverified callbacks

Safaricom does not sign its callbacks. A success message credited on arrival may describe money that never moved.

What we doA success callback parks the payment. We query Safaricom for the receipt and credit only on a confirmed result.

The money path

What actually happens when someone pays you

Seven steps, from your API call to a posted ledger entry. Step five is the provider confirmation.

Every payment posts to both columns at once. Money in and money out are one ledger, kept level. That is what lets the statement reconcile.
Read the seven steps
  • Your server
  • NOSTREL
  • Safaricom
  • The payer
  1. 01 · Your server

    You ask for 1,850 shillings

    One POST: phone, amount, your reference. An id comes straight back.

    POST /v1/api/collections
  2. 02 · NOSTREL

    We check it before anyone is bothered

    Whole shillings, a real Safaricom number, an unused idempotency key, room in your tier. A doomed request fails here, not on the payer’s phone.

    state: pending
  3. 03 · Safaricom

    Safaricom pushes the prompt

    The prompt lands on the handset. Nothing in your code waits on a person finding their phone.

    STK push
  4. 04 · The payer

    The payer types their PIN

    Or they do not. Driving, phone off, changed their mind. All handled.

    on the handset
  5. 05 · NOSTREL

    A callback arrives, and we do not believe it yet

    The step most integrations skip. A success callback parks the payment. We ask Safaricom if the receipt is real, and only then does your balance move.

    state: awaiting_confirmation
  6. 06 · NOSTREL

    The ledger posts, both sides

    Debit clearing, credit you, book the fee. One transaction, no partial writes.

    state: success
  7. 07 · Your server

    Your webhook fires

    HMAC signed, retried with backoff, replayable from the console.

    collection.succeeded

The console

Screens from the running product

Captured from the console itself, not mocked up. The data shown is seeded development data.

app.nostrel.com/transactions
The transactions screen, listing collections and payouts together with money in, money out, net and success rate across the top.
Collections and payouts in a single list, with money in, money out, net and success rate. Demonstration data.
app.nostrel.com/statement
The statement screen showing opening balance, money in, money out and closing balance with a line-by-line ledger beneath.
Opening balance, money in, money out, closing balance, with the ledger lines beneath. Exportable for any date range.
The hosted checkout on a phone, showing an order for 2kg of Arabica coffee at 1,850 shillings with a field for an M-Pesa number.
The hosted checkout, on a phone. For most payers this is the only NOSTREL page they see.
Two more screens
app.nostrel.com/payouts
The payouts screen showing available to send, paid out, awaiting approval and a list of recipients with their approval state.
Money leaving has its own screen, its own approval state and its own PIN. Demonstration data.
app.nostrel.com/developers
The developers screen showing live and test API keys, webhook endpoints and a quickstart panel.
Keys, webhook endpoints and request logs where a developer expects them, not in an email from support.

Who is reading this

You are probably one of two people

The short version for each.

You write the code

You are looking for the part where it goes wrong.

Idempotency on every write
Replays return the stored response, not a second payment.
Failures you can branch on
One envelope, a stable machine code. Branch on the code, never on the prose.
Webhooks that behave
HMAC signed, replay-proof, retried, replayable by hand, dead-lettered.
A sandbox that lies usefully
Numbers that fail on purpose: no funds, wrong PIN, timeout, cancelled.
State you can reason about
Seven states for a collection, six for a payout. One direction.
Read the developer pages

You sign the cheques

You care what happens on the day it does not work.

Two people to move money out
One raises, another approves. On by default.
A statement, not a CSV dump
Opening, in, out, fees, closing. It reconciles and it prints.
Fees stated before the fact
On the transaction, the statement and the API response. Not at month end.
Your float is not our float
Balances are liabilities against a settlement account, not one pooled number.
An audit trail that cannot be edited
Appended and hash-chained. A changed record is a detectable one.
Read how it is secured

Evidence, not adjectives

Things we can show you rather than claim

Not claims. Facts about the code, each one checkable.

269
unit testsAPI, design system, both consoles.
233
end-to-end testsReal HTTP against a real database, money path included.
0
axe violationsWCAG 2.2 AA across 58 page, theme and width combinations.
6
adversarial auditsFindings published in full on the security page.

Use cases

Businesses that move money in both directions

Six shapes NOSTREL is built for. Each needs collections and payouts reconciled against one another.

  • Retail and e-commerceCollect at checkout. Issue refunds from the same balance.
  • Logistics and deliveryCollect delivery fees and pay riders from the proceeds.
  • Lenders and saccosDisburse to a phone number, collect repayments, reconcile both sides.
  • MarketplacesHold, split and release funds across two parties on one ledger.
  • Services and agenciesIssue invoices as payment links and track settlement against them.
  • AgribusinessBulk payouts from a CSV, with two-person approval on the batch.
A Kenyan produce market stall stacked with bananas, mangoes, papaya and pumpkins.
M-Pesa had 40.99 million one-month-active customers in Kenya in Safaricom's FY26 results. The payment rail is established. The accounting around it is the part most businesses end up building twice.

Pricing

Charged per transaction

No platform fee, no seat licence, no annual commitment and no minimum.

  • No setup costNo setup fee, no minimum and no payment details required to read the documentation or use the sandbox.
  • A rate per transaction, per railPercentage, fixed component, floor and optional ceiling. Confirmed in writing at onboarding and shown on every transaction and statement.
  • Settlement on your scheduleDaily or weekly, to a bank account or your own M-Pesa shortcode.
  • Failed payments are not chargedA fee is posted only when a payment is confirmed by Safaricom.
How pricing works

Questions

Common questions

The ones that come up most often during evaluation.

20 more on the questions page
Are you live yet?

No. NOSTREL is pre-launch. The API, both consoles, the ledger, reconciliation, settlement and webhook delivery are built and under test against Safaricom's sandbox. Every figure in a screenshot on this site is seeded development data from a local environment.

How quickly does money actually arrive?

A collection reaches a confirmed state once Safaricom confirms it, usually seconds after your customer enters their PIN. We do not credit the ledger on the callback alone: we ask the provider to confirm the transaction independently first. That adds a short delay and removes an entire category of forged-callback fraud. Payouts leave as soon as they are approved, subject to Safaricom's own processing on the other side.

How do you stop a retry from charging someone twice?

An Idempotency-Key header is required on every call that moves money, not merely honoured if you send one. Replay the same key with the same body and you get the first result back rather than a second payment. Replay it with a different body and you get an error, because that is a bug in your code and guessing which of the two you meant would be worse than telling you.

How do I know a webhook really came from you?

Every delivery carries a NOSTREL-Signature header holding a timestamp and an HMAC over the exact body, in the shape most webhook libraries already recognise. Verify the HMAC with your endpoint secret, reject a timestamp outside your tolerance, and compare in constant time. The webhooks page works through it, including the mistake almost everyone makes once, which is verifying a re-serialised object instead of the bytes that actually arrived.

How does your pricing work?

Per rail, as a percentage plus a fixed component, with a floor and an optional ceiling, and dated so that a change applies from a day forward and never retroactively. Each merchant has their own schedule. The shape of it is set out on the pricing page; the numbers come out of a short conversation about your volume and your mix, because a card-style flat rate would be wrong for almost everyone.

Is there a sandbox, and is it any good?

Yes, and it runs against Safaricom's own sandbox rather than a mock we wrote, which means the failure modes are the real ones instead of the ones we imagined. You can trigger a timeout, a cancelled prompt, an insufficient-balance result and a deliberately lost callback. Testing those four is the step nearly everyone skips, and then meets for the first time on a Friday afternoon.

Why should I believe any of this?

Because most of it is checkable. The test counts on the landing page come from a real run and not a marketing round-up, the screenshots are the actual consoles rather than a design file, and the security page lists the findings we went looking for and what each fix was. Where something is not finished, it is written in the future tense. That is the whole trick.

Take a test payment in the sandbox

The quickstart runs against Safaricom's own sandbox. Take a test payment, receive the webhook, and trigger a failure with a test number. No account and no payment details are required.

Photograph: developers at Random Hacks of Kindness, Nairobi. Credit on the credits page.