REST API v1

Integrate payments with one HTTP call

Authentication via Api-Key. Base URL: https://api.payzeno.io

REST API v1

3-step flow

From order to confirmed webhook.

1 Create session

Send amount, currency, customer and return URLs.

2 Pay

Redirect to checkout_url or use direct payment via API.

3 Webhook

Receive HTTP notification when status changes.

Api-Key

Authentication

All routes require the Api-Key header with a key created at app.payzeno.io → Integration → API Keys.

  • Format: pk_live_… (production) or pk_test_… (sandbox, when available)
  • Merchant account must be active with approved KYC
  • Configure the webhook URL on the API key before creating sessions
cURL
curl https://api.payzeno.io/v1/checkout/sessions/abc123/status \
  -H "Api-Key: pk_live_xxx"
Idempotency-Key

Idempotency

On all POST requests, send Idempotency-Key with a unique value per operation (e.g. your store order ID).

  • Repeating the same key + same body returns the original response (no duplicate charge)
  • Different key with same body → 409 error
  • Recommended: UUID or -checkout
cURL
curl -X POST https://api.payzeno.io/v1/checkout/sessions \
  -H "Api-Key: pk_live_xxx" \
  -H "Idempotency-Key: order-123-unique" \
  -H "Content-Type: application/json" \
  -d '{ ... }'
REST API v1

Main endpoints

Essential routes for hosted checkout and direct payments.

POST /v1/checkout/sessions

Creates checkout session and returns checkout_url and checkout_id.

GET /v1/checkout/sessions/:id/status

Query session status (pending, paid, expired, etc.).

POST /v1/payment/mpesa

Direct M-Pesa payment with checkout_id and phone (+258…).

POST /v1/payment/emola

Direct e-Mola payment with checkout_id and phone.

POST /v1/checkout/sessions

Create checkout session

amount in minor units (cents): 2500 = 25.00 MZN.

Required fields: amount, currency, customer (name, phone E.164; email optional), success_url.

Optional: reference, description, cancel_url, payment_methods (mpesa, emola, …).

Full schema in Swagger →
Request
curl -X POST https://api.payzeno.io/v1/checkout/sessions \
  -H "Api-Key: pk_live_xxx" \
  -H "Idempotency-Key: order-123-unique" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 2500,
    "currency": "MZN",
    "reference": "order-123",
    "description": "Order #123",
    "payment_methods": ["mpesa", "emola"],
    "customer": {
      "name": "Maria Silva",
      "email": "maria@example.com",
      "phone": "+258840000000"
    },
    "success_url": "https://store.com/success",
    "cancel_url": "https://store.com/cancelled"
  }'

Response (201)

JSON
{
  "success": true,
  "checkout_id": "674a1b2c3d4e5f678901234",
  "checkout_url": "https://checkout.payzeno.io/payment/674a1b2c...",
  "status": "pending",
  "amount": 2500,
  "currency": "MZN",
  "expires_at": "2026-06-16T14:30:00+02:00"
}
POST /v1/payment/*

Direct API payment

Triggers STK/USSD on the customer phone without opening the browser. Use after creating the session.

Phone: Mozambique format +25884… or 84… (normalised automatically).

Request
curl -X POST https://api.payzeno.io/v1/payment/mpesa \
  -H "Api-Key: pk_live_xxx" \
  -H "Idempotency-Key: order-123-mpesa" \
  -H "Content-Type: application/json" \
  -d '{
    "checkout_id": "674a1b2c3d4e5f678901234",
    "phone": "+258840000000"
  }'

Async response (202)

JSON
{
  "success": true,
  "checkout_id": "674a1b2c3d4e5f678901234",
  "status": "pending",
  "message": "Payment initiated. Await webhook or poll status."
}

Confirm payment via payment.succeeded webhook or polling GET …/status.

REST API v1

Webhooks

POST JSON to the URL configured on the API key. Respond 2xx quickly; retries on failure.

event

payment.succeeded

Payment confirmed. Status succeeded.

event

payment.refunded

Refund processed.

event

payment.chargeback

Chargeback / dispute recorded.

Example — payment.succeeded

Webhook payload
{
  "event": "payment.succeeded",
  "payment_id": "674a1b2c3d4e5f678901234",
  "checkout_id": "674a1b2c3d4e5f678901234",
  "reference": "order-123",
  "amount": 2500,
  "currency": "MZN",
  "status": "succeeded",
  "paid_at": "2026-06-15T12:34:56+02:00",
  "payment_method": "mpesa",
  "customer": {
    "name": "Maria Silva",
    "email": "maria@example.com"
  }
}

amount is always in minor units. reference is the value you sent in reference when creating the session.

REST API v1

Errors and HTTP codes

JSON body: { "success": false, "message": "…" }

HTTP Situation
400 Invalid body, amount ≤ 0, invalid phone, expired session
401 Missing or invalid Api-Key
404 checkout_id not found or belongs to another merchant
409 Idempotency conflict (same key, different body)
422 Gateway declined payment (balance, timeout, etc.)

Session statuses

  • pending — awaiting payment
  • paid — paid successfully
  • expired — session expired
  • cancelled — cancelled by customer
  • refunded — refunded

Payment links (/l/pl_xxx) in the dashboard do not require the API — ideal for quick tests.

cURL
curl https://api.payzeno.io/v1/checkout/sessions/674a1b2c/status \
  -H "Api-Key: pk_live_xxx"
OpenAPI

Interactive Swagger

Explore all endpoints, schemas and test calls in the OpenAPI documentation.

Open Swagger
WhatsApp