Skip to content
NOSTREL
Developers

Small enough to
hold in your head.

Three endpoints move money. Everything else reads. There is no resource hierarchy to learn, no builder pattern, no concept of a "session", and no sixty-page conceptual overview standing between you and a payment.

REST over HTTPS, JSON in and JSON out, bearer keys in a header, everything under /v1. If you have integrated a payment provider before, you already know how to read the rest of this page.

authentication, and the simplest callbash
# Keys are server-side secrets.# One in a browser is one you have given away.curl https://api.nostrel.com/v1/api/balance \  -H "Authorization: Bearer sk_live_••••••••" {  "available": 10245000,  "held": 120000,  "currency": "KES"}

01Surface

The whole public API

Not a selection. Seven routes, and this is all of them.

Every public NOSTREL endpoint, with the HTTP method, path, required API key scope and whether an Idempotency-Key header is required.
RouteScopeKey
POST/v1/api/collections

Raise an STK prompt on a payer's handset.

collections:writerequired
GET/v1/api/collections

List your collections.

collections:readnot used
GET/v1/api/collections/{id}

Fetch one, including its current state.

collections:readnot used
POST/v1/api/payouts

Create a payout. It is queued, not sent: a second person releases it.

payouts:writerequired
GET/v1/api/payouts

List your payouts.

payouts:readnot used
GET/v1/api/payouts/{id}

Fetch one, including its approval state.

payouts:readnot used
GET/v1/api/balance

Available, held and the currency it is all in.

balance:readnot used

Everything the merchant console does sits on the same API behind a session rather than a key, so if there is something you can see in a browser and cannot automate, that is a gap we would like to hear about rather than a deliberate boundary.

02Keys

Scoped, so a leak is smaller than a catastrophe

A key that can only read balances cannot move money. Give your monitoring that one and the blast radius of a leaked environment variable changes shape entirely.

  • collections:read

    Read collections. Safe for a dashboard or a reporting job.

  • collections:write

    Raise prompts. This one takes money from people.

  • payouts:read

    Read payouts and their approval state.

  • payouts:write

    Create payouts. Creating is not sending: a second person still has to release it.

  • balance:read

    Read balances. The scope to give your monitoring.

Server side only

There is no publishable key and no browser SDK, deliberately. A payments key in client-side JavaScript is a key published to everyone who opens dev tools, and the usual workaround, a restricted key with an origin allowlist, is a comfort rather than a control.

If you want to take a payment without a server, that is what the hosted checkout is for. It is a page we run, with no key of yours anywhere near the browser.

03Idempotency

Required, not offered

Every call that moves money takes an Idempotency-Key header, and a call without one is refused before it reaches anything.

Same key, same body

You get the first response back. No second payment, no second prompt on the payer's phone.

Same key, different body

409, with a message saying the key was reused with different parameters. That combination is a bug on your side, and we would rather name it than silently pick one of your two intentions.

Same key, still in flight

409, because the first call has not finished. Wait and ask again rather than starting a second payment alongside the first.

Keyed per merchant

Your keys are yours. Another merchant choosing the same string as you is not a collision.

Use something from your own domain: an order number, an invoice id, a payroll run identifier. A random UUID generated at the call site defeats the purpose, because a retry generates a different one and the two requests stop being the same request.

the whole contractbash
# Send it once.curl https://api.nostrel.com/v1/api/collections \  -H "Authorization: Bearer sk_live_••••••••" \  -H "Idempotency-Key: order-1042" \  -d phone=254712345678 -d amount=1850 # Send it again, same key, same body. Same answer. One payment.# Send it again with a different body and you get 409, because that is a# bug in your code and guessing which one you meant would be worse.

04Straight answers

What is rough, and what is not built

You would find all of this in an afternoon. Better you find it here.

The error catalogue is young

Every error carries a stable code, and the coarse ones are deliberately coarse: a console route that refuses you answers permission_denied rather than naming the exact rule, because the exact rule is a question for whoever administers your team. Finer codes will appear in place of general ones over time. That is an addition, not a break, as long as you treat an unrecognised code as its type.

No official client libraries

Three endpoints and an HMAC check make a thin wrapper in any language, and a stale SDK is worse than no SDK. There is an OpenAPI document if you want to generate one.

No partial refunds through the API

Reversals are recorded when the provider reverses a payment. Initiating one yourself is console work for now.

Rate limits are at the edge, not per key

They exist and they are deliberately not published in detail, because a published limit is a published budget for anyone probing. If you hit one in normal use, tell us and we will raise it.