ArchPay API Reference

ArchPay gives you a simple REST API to trigger M-Pesa STK Push payments, verify payment status, and receive real-time webhook callbacks. One API key. No SDK required.

Base URL
https://pay.archietech.app/api/v1
Content-Type — All requests must include Content-Type: application/json
Auth header — All requests must include x-api-key: YOUR_API_KEY

API Key Authentication

Every request to the API must include your API key in the x-api-key header. You receive your key immediately on account creation. Keys start with apk_.

Required headers
x-api-key: apk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Content-Type: application/json
Keep your key secret. Do not expose it in frontend JavaScript or public repositories. Rotate it immediately from your dashboard if compromised.

Base URL & Versioning

The current API version is v1. All endpoints are prefixed with /api/v1.

Base URL
https://pay.archietech.app/api/v1
Health Check
GET https://pay.archietech.app/health // Response { "status": "ok", "service": "ArchPay", "version": "2.0.0", "ts": "2025-01-01T12:00:00.000Z" }

Trigger STK Push

Sends an M-Pesa payment prompt to the customer's phone. The customer sees a PIN entry dialog. Consumes 1 credit per call.

POST /stkpush pay.archietech.app/api/v1/stkpush

Request Body

ParameterTypeDescription
phonerequiredstringSafaricom number in 254XXXXXXXXX format (12 digits, starts with 254)
amountrequiredintegerAmount in KSH. Must be a whole number. Min: 1, Max: 300,000
accountReferenceoptionalstringReference shown on M-Pesa statement. Max 12 chars. Defaults to "Payment"
descriptionoptionalstringTransaction description shown in prompt. Max 20 chars
Request — cURL
curl -X POST https://pay.archietech.app/api/v1/stkpush \ -H "x-api-key: apk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "phone": "254712345678", "amount": 1500, "accountReference": "INV-001", "description": "Order payment" }'
Success Response (200)
{ "success": true, "message": "STK push sent", "checkoutRequestId": "ws_CO_271020231234567890", "merchantRequestId": "29115-34620561-1", "creditsRemaining": 9 }
Error — Insufficient Credits (402)
{ "success": false, "error": "Insufficient credits" }

Verify Payment Status

Poll the status of a specific STK push transaction using its checkoutRequestId. Use this as a fallback — webhooks are the recommended way to receive results.

POST /verify pay.archietech.app/api/v1/verify
ParameterTypeDescription
checkoutRequestIdrequiredstringThe checkoutRequestId returned by the /stkpush call
Request
curl -X POST https://pay.archietech.app/api/v1/verify \ -H "x-api-key: apk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "checkoutRequestId": "ws_CO_271020231234567890" }'
Response (200)
{ "success": true, "status": "completed", "mpesaReceiptNumber": "RFP20XXXXXXX", "amount": 1500, "phone": "254712345678", "transactionDate": "20250101120000" }

List Transactions

Returns a paginated list of all transactions made with your API key, sorted by most recent first.

GET /transactions pay.archietech.app/api/v1/transactions
Query ParameterTypeDescription
limitoptionalintegerMax results to return. Default: 50, Max: 200
statusoptionalstringFilter by status: completed, pending, failed, cancelled, timeout
Request
curl https://pay.archietech.app/api/v1/transactions?limit=50 \ -H "x-api-key: apk_your_key_here"
Response (200)
{ "success": true, "transactions": [ { "_id": "64f...", "checkoutRequestId": "ws_CO_271020231234567890", "phone": "254712345678", "amount": 1500, "status": "completed", "mpesaReceiptNumber": "RFP20XXXXXXX", "accountReference": "INV-001", "createdAt": "2025-01-01T12:00:00.000Z" } ], "count": 1 }

Check Credit Balance

Returns the current credit balance for your account. Each successful STK push consumes 1 credit.

GET /balance pay.archietech.app/api/v1/balance
Request
curl https://pay.archietech.app/api/v1/balance \ -H "x-api-key: apk_your_key_here"
Response (200)
{ "success": true, "credits": 47, "businessName": "Acme Ltd" }

Webhook Payload

When a payment is confirmed or fails, ArchPay sends an HTTP POST to your configured webhook URL. This happens within seconds of M-Pesa processing the transaction.

Webhook POST body
{ "event": "payment.callback", "status": "completed", "checkoutRequestId": "ws_CO_271020231234567890", "mpesaReceiptNumber": "RFP20XXXXXXX", // null if not completed "amount": 1500, "phone": "254712345678", "accountReference": "INV-001", "timestamp": "2025-01-01T12:00:00.000Z" }
Your webhook endpoint must return a 2xx HTTP status within 10 seconds. ArchPay does not currently retry failed webhook deliveries — ensure your endpoint is reliable.

Payment Statuses

Every transaction has one of these statuses. Only completed means money moved.

completed
Payment confirmed. M-Pesa receipt number present. Money received.
pending
STK push sent, awaiting customer PIN entry. Poll or wait for webhook.
failed
Payment failed. Could be wrong PIN, insufficient funds, or M-Pesa error.
cancelled
Customer dismissed or cancelled the STK prompt.
timeout
Customer did not respond to the STK prompt within the allowed window.

Configuring Your Webhook

Set your webhook URL from the Settings tab in your dashboard. It must be a publicly accessible HTTPS endpoint.

Express.js webhook handler
app.post('/webhooks/mpesa', (req, res) => { const { event, status, checkoutRequestId, mpesaReceiptNumber, amount, phone } = req.body; if (status === 'completed') { // Payment confirmed — fulfill the order console.log(`Payment ${mpesaReceiptNumber}: KSH ${amount} from ${phone}`); } else if (status === 'failed' || status === 'cancelled') { // Payment did not go through console.log(`Payment ${checkoutRequestId} ${status}`); } res.status(200).json({ received: true }); // must return 2xx });

Error Handling

All errors return JSON with "success": false and an "error" message. HTTP status codes follow standard conventions.

StatusMeaningCommon cause
400Bad RequestMissing required fields, invalid phone format, or non-integer amount
401UnauthorizedMissing or invalid x-api-key header
402Payment RequiredAccount has zero credits — top up to continue
403ForbiddenAccount is suspended
404Not FoundTransaction ID not found
500Server ErrorM-Pesa API error or internal issue — retry after a moment

Quick Start

From zero to your first STK push in 3 steps.

Step 1 — Get your API key
# Sign up at https://pay.archietech.app → Get Started # Your API key appears immediately on the dashboard (starts with apk_)
Step 2 — Send a test STK push
curl -X POST https://pay.archietech.app/api/v1/stkpush \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "phone": "2547XXXXXXXX", "amount": 1 }'
Step 3 — Set up your webhook
# In your dashboard → Settings → Webhook URL # Enter: https://yourapp.com/webhooks/mpesa # Then handle the POST in your server (see Webhook section above)

Node.js Integration

Node.js / fetch
const ARCHPAY_KEY = process.env.ARCHPAY_API_KEY; const BASE = 'https://pay.archietech.app/api/v1'; async function stkPush(phone, amount, reference) { const res = await fetch(`${BASE}/stkpush`, { method: 'POST', headers: { 'x-api-key': ARCHPAY_KEY, 'Content-Type': 'application/json', }, body: JSON.stringify({ phone, amount, accountReference: reference }), }); return res.json(); } // Usage const result = await stkPush('254712345678', 500, 'ORDER-001'); console.log(result.checkoutRequestId); // save this to verify later

Python Integration

Python / requests
import requests, os ARCHPAY_KEY = os.environ['ARCHPAY_API_KEY'] BASE = 'https://pay.archietech.app/api/v1' HEADERS = {'x-api-key': ARCHPAY_KEY, 'Content-Type': 'application/json'} def stk_push(phone, amount, reference='Payment'): r = requests.post( f'{BASE}/stkpush', headers=HEADERS, json={'phone': phone, 'amount': amount, 'accountReference': reference} ) return r.json() # Usage result = stk_push('254712345678', 500, 'ORDER-001') print(result['checkoutRequestId']) # save for verification