Newton Labs Pay

Documentation

Newton Labs Pay is an M-Pesa payments gateway for Kenyan apps. Create payment links, trigger STK push prompts, settle funds straight to your till, and get a signed webhook for every result. This reference covers the REST API.

The API is organised around predictable, resource-oriented URLs and returns JSON for every response. All requests go to the base URL:

Base URL https://api.newtonlabs.ke/v1

If you've never touched the API, start in the dashboard — payment links work without writing a line of code. When you're ready to automate, the whole surface is three things: a key, REST endpoints, and signed webhooks.

New here? Run through the merchant onboarding flow first — then create an app on the dashboard's Apps page to get your API credentials.

Authentication

The API authenticates with a secret key. Send it as a bearer token in the Authorization header on every request:

Authorization: Bearer nlp_live_8fK2x...

Each account has two key pairs. They never mix — a request fails if the key doesn't match the mode you're calling.

Key prefixModeBehaviour
nlp_test_ Test Simulates the full payment lifecycle. No real M-Pesa prompt, no money moves. Use it freely while building.
nlp_live_ Live Triggers real STK push prompts and settles real funds. Available once your KYC is approved.

Create an app and copy its key on the Apps page in the dashboard. Treat the secret like a password — keep it server-side, never ship it in client code or commit it to a repo. Requests over plain HTTP are rejected; the API is TLS only.

The payment object

Every link returns a payment object. The same shape is sent on the payload of each webhook, so you can store it once and update it as the status changes.

FieldTypeDescription
id string Unique link identifier, prefixed lnk_. Use it to look the link up later.
url string The hosted M-Pesa checkout page. This is what you share with the customer.
status string One of pending, paid, failed, or expired.
amount integer Amount charged, in whole shillings.
reference string The identifier you supplied when creating the link.
receipt string The M-Pesa confirmation code (e.g. SGH7X2K9Q1). null until the link is paid.
created_at string ISO 8601 timestamp (UTC) of when the link was created.

Webhooks

Payments are asynchronous — the customer approves the STK prompt on their phone, which can take seconds or minutes. Rather than poll, give us a webhook_url and we'll POST a JSON event the moment something changes.

Event types

EventFires when
link.paidThe customer completed payment. Funds are settling to your till.
link.failedThe STK push was declined, timed out, or the customer had insufficient funds.
link.expiredThe link reached its expiry window without being paid.
refund.completedA refund you issued has landed back with the customer.

Verifying the signature

Every webhook carries an X-Newton-Signature header — an HMAC-SHA256 of the raw request body, keyed with your webhook secret. Always verify it before trusting an event, and compare against the raw body, not a re-serialised object.

// Verify the X-Newton-Signature header before trusting an event
import express from "express";
import crypto from "node:crypto";

const SECRET = process.env.NEWTON_WEBHOOK_SECRET;

app.post("/paid", express.raw({ type: "*/*" }), (req, res) => {
  const signature = req.headers["x-newton-signature"];
  const expected = "sha256=" + crypto
    .createHmac("sha256", SECRET)
    .update(req.body)
    .digest("hex");

  if (signature !== expected) {
    return res.status(401).end();
  }

  const event = JSON.parse(req.body);
  if (event.type === "link.paid") {
    markOrderPaid(event.data.reference);
  }

  // Reply 2xx within 10s, or we retry.
  res.json({ received: true });
});

Respond with any 2xx status within 10 seconds. If we don't get one, we retry with exponential backoff for up to 24 hours.

Going live

Build and test against nlp_test_ keys for as long as you like — test mode runs the exact same API and webhook flow, it just never touches real money. Switching to live is a key swap, nothing more.

  1. Integrate and test end-to-end with your nlp_test_ key — create a link, pay it in the test checkout, confirm your webhook handler runs.
  2. Complete merchant onboarding and KYC so your account can be approved for live payments.
  3. Once approved, create an app on the dashboard's Apps page and copy its app_ key + sk_ secret.
  4. Swap the key in your server environment and point webhook_url at your production endpoint.
  5. Run one small real transaction to confirm settlement reaches your till, then you're live.

Errors

The API uses conventional HTTP status codes. A 2xx means success, a 4xx means a problem with the request (and the JSON body explains what), and a 5xx means something failed on our side.

CodeMeaning
400 Bad RequestThe request was malformed — invalid JSON or a missing required field.
401 UnauthorizedThe API key is missing, invalid, or revoked.
404 Not FoundThe requested resource — usually a link ID — doesn't exist.
422 UnprocessableThe request was well-formed but a value was rejected, e.g. an amount below the minimum.
429 Too Many RequestsYou've exceeded the rate limit. Back off and retry after the window resets.
500 Server ErrorSomething broke on our end. Safe to retry; if it persists, check status.newtonlabs.ke.

Stuck on something this page doesn't cover? Email hello@newtonlabs.ke — a real engineer answers.