Skip to content
NOSTREL
Nairobi · pre-launch

Take the money.
Send the money.
Keep books that balance.

One API for M-Pesa collections and payouts, with double-entry accounting underneath, signed webhooks on top, and a statement your accountant will accept without a phone call.

No sales call to see the docs. No sales call to see the pricing either.

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, in about 90ms
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.

01Why this exists

Anyone can take one payment. The trouble starts at ten thousand.

Taking money on M-Pesa is a solved problem for about a week. Then the questions arrive, and they are all the same question wearing different clothes: which record do I believe?

The 2am reconciliation

Safaricom says 412 payments. Your database says 409. Nobody has written down which three, or when, and the statement goes out at nine.

What we doWe post both sides of every movement into a ledger that has to balance, and run reconciliation against the provider on a schedule. When the two disagree, it becomes a row in an exceptions table with a timestamp and a reason, instead of a feeling.

The payment that happened twice

Your client timed out, so it retried. Politely. Three times. The payer got three prompts and is now on the phone to you, not to Safaricom.

What we doEvery write takes an idempotency key. Send the same one a hundred times and you get the same single payment and the same response body, because the first answer is stored and replayed.

The callback you believed

A success callback arrived, you credited the balance, and the money never landed. Now your float is short and your books say it is not.

What we doWe park a success instead of trusting it, ask Safaricom whether that receipt is genuine, and credit only on a yes. A callback we cannot verify stays parked rather than becoming money.

02The money path

What actually happens when someone pays you

Seven steps. Five of them are the same anywhere. Step five is the one worth reading, because it is where most integrations quietly lose money.

  • Your server
  • NOSTREL
  • Safaricom
  • The payer
  1. 01 · Your server

    You ask for 1,850 shillings

    One POST with the phone number, the amount and your own reference. We hand back an id straight away and get on with it.

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

    We check it before anyone is bothered

    Amount is a whole number of shillings, the phone number is a real Safaricom one, the idempotency key has not been used, your tier has room. A request that was going to fail fails here, in milliseconds, not on the payer’s phone.

    state: pending
  3. 03 · Safaricom

    Safaricom pushes the prompt

    The STK prompt lands on the payer’s handset. Nothing in your code is waiting on this, because it depends on a person finding their phone.

    STK push
  4. 04 · The payer

    The payer types their PIN

    Or they do not, because they are driving, or the handset is off, or they changed their mind. All three are normal and all three are handled.

    on the handset
  5. 05 · NOSTREL

    A callback arrives, and we do not believe it yet

    This is the step most integrations skip. A success callback parks the payment instead of crediting it, and we ask Safaricom directly whether that receipt is real. Only then does your balance move.

    state: awaiting_confirmation
  6. 06 · NOSTREL

    The ledger posts, both sides

    Debit the clearing account, credit your balance, book the fee. Double entry, one transaction, no partial writes. If the posting cannot complete, nothing moved.

    state: success
  7. 07 · Your server

    Your webhook fires

    Signed with HMAC and your secret, retried with backoff if your server is having a moment, and replayable from the console when it was having a longer one.

    collection.succeeded

03The console

The whole thing, not a landing page for it

These are screenshots of the running product against a development database. No mockups, no artist's impressions, no numbers typed into Figma.

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 one list, because reconciling two lists is how the discrepancy starts. 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, in, out, closing. The document an accountant asks for, generated rather than assembled by hand.
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, which is the only page most of your customers will ever see of us.
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.

04Who is reading this

You are probably one of two people

And you want completely different things, which is why this page keeps changing register. Here is the short version for each of you.

You write the code

You have integrated a payment provider before and you are looking for the part where it goes wrong.

Idempotency on every write
A key per request. Replays return the stored response, not a second payment.
Failures you can branch on
One envelope for every error, with a stable machine code and a message that is explicitly not part of the contract. Your retry logic reads an identifier instead of pattern-matching English that we might reword.
Webhooks that behave
HMAC signed, timestamped against replay, retried with exponential backoff, replayable by hand, and dead-lettered when your endpoint is genuinely gone.
A sandbox that lies usefully
Test numbers that fail on purpose: insufficient funds, wrong PIN, timeout, cancelled. You cannot handle what you cannot reproduce.
State you can reason about
Seven states for a collection, six for a payout. One direction, documented transitions, no field called `status2`.
Read the developer pages

You sign the cheques

You do not care how it works. You care what happens on the day it does not.

Two people to move money out
One raises a payout, another approves it. On by default, because the alternative is one compromised laptop away from a bad afternoon.
A statement, not a CSV dump
Opening balance, money in, money out, fees, closing balance. It reconciles, and it prints.
Fees stated before the fact
Shown on the transaction, in the statement and in the API response. Not discovered at month end.
Your float is not our float
Merchant balances are tracked as liabilities against a settlement account, not mixed into one number and apportioned later.
An audit trail that cannot be edited
Every decision is appended and hash-chained, so a changed record is a detectable record.
Read how it is secured

05Evidence, not adjectives

Things we can show you rather than claim

A payments company saying it is secure and reliable is a payments company saying nothing. These are facts about the codebase, checkable by anyone we give access to.

269
unit testsAcross the API, the design system and both consoles.
223
end-to-end testsReal HTTP against a real database, money path included.
0
axe violationsWCAG 2.2 AA, across 58 combinations of page, theme and width.
6
adversarial auditsWritten up with what was found, including what we got wrong.

06Fit

If money moves in two directions, this is for you

Taking payments is half a business. Most of the businesses we built this for also have to send money back out, and that is the half that usually gets held together with a spreadsheet and a prayer.

  • Retail and e-commerceCollect on checkout, refund without a support ticket.
  • Logistics and deliveryTake the delivery fee, pay the rider the same evening.
  • Lenders and saccosDisburse to a phone, collect repayments, reconcile both.
  • MarketplacesHold, split and release. Two sides, one ledger.
  • Services and agenciesInvoice with a link, chase less, get paid faster.
  • AgribusinessPay a thousand farmers from one CSV, with approval.
A Kenyan produce market stall stacked with bananas, mangoes, papaya and pumpkins.
Most Kenyan commerce already runs on a phone number. The question was never whether people would pay this way. It was whether your books would survive it.

07Pricing

Per transaction. No monthly fee for the privilege of existing.

You pay when money moves and not otherwise. There is no platform fee, no seat licence and no annual commitment, which means we have to keep earning it every month rather than once.

  • Nothing to startNo setup fee, no minimum, no card on file to read the docs.
  • A rate per transactionConfirmed in writing when you onboard, based on volume and risk. Shown on every transaction and in every statement.
  • Settlement on a schedule you pickDaily or weekly to your bank or your own M-Pesa shortcode.
  • No charge for a failed paymentIf the payer never pays, there is nothing to take a percentage of.
How pricing works

08Questions

The ones people actually ask

Usually in the first ten minutes of the first call.

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, and every figure in a screenshot on this site is seeded development data from a local environment. We would rather you read that on the first page than work it out later from a changelog.

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 costs a moment and removes an entire category of fraud, which we think is a reasonable trade. 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.

Start with the quickstart. It is ten minutes and it is free.

Take a test payment, watch the webhook arrive, break it on purpose with a failing test number. If it does not convince you, nothing on this page will.

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