Getting Started
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
Authentication
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_.
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.
Configuration
Base URL & Versioning
The current API version is v1. All endpoints are prefixed with /api/v1.
https://pay.archietech.app/api/v1
GET https://pay.archietech.app/health
// Response
{
"status": "ok",
"service": "ArchPay",
"version": "2.0.0",
"ts": "2025-01-01T12:00:00.000Z"
}
Endpoints
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
| Parameter | Type | Description |
| phonerequired | string | Safaricom number in 254XXXXXXXXX format (12 digits, starts with 254) |
| amountrequired | integer | Amount in KSH. Must be a whole number. Min: 1, Max: 300,000 |
| accountReferenceoptional | string | Reference shown on M-Pesa statement. Max 12 chars. Defaults to "Payment" |
| descriptionoptional | string | Transaction description shown in prompt. Max 20 chars |
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": true,
"message": "STK push sent",
"checkoutRequestId": "ws_CO_271020231234567890",
"merchantRequestId": "29115-34620561-1",
"creditsRemaining": 9
}
{
"success": false,
"error": "Insufficient credits"
}
Endpoints
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
| Parameter | Type | Description |
| checkoutRequestIdrequired | string | The checkoutRequestId returned by the /stkpush call |
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" }'
{
"success": true,
"status": "completed",
"mpesaReceiptNumber": "RFP20XXXXXXX",
"amount": 1500,
"phone": "254712345678",
"transactionDate": "20250101120000"
}
Endpoints
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 Parameter | Type | Description |
| limitoptional | integer | Max results to return. Default: 50, Max: 200 |
| statusoptional | string | Filter by status: completed, pending, failed, cancelled, timeout |
curl https://pay.archietech.app/api/v1/transactions?limit=50 \
-H "x-api-key: apk_your_key_here"
{
"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
}
Endpoints
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
curl https://pay.archietech.app/api/v1/balance \
-H "x-api-key: apk_your_key_here"
{
"success": true,
"credits": 47,
"businessName": "Acme Ltd"
}
Webhooks
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.
{
"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.
Webhooks
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.
Webhooks
Configuring Your Webhook
Set your webhook URL from the Settings tab in your dashboard. It must be a publicly accessible HTTPS endpoint.
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
});
Getting Started
Error Handling
All errors return JSON with "success": false and an "error" message. HTTP status codes follow standard conventions.
| Status | Meaning | Common cause |
| 400 | Bad Request | Missing required fields, invalid phone format, or non-integer amount |
| 401 | Unauthorized | Missing or invalid x-api-key header |
| 402 | Payment Required | Account has zero credits — top up to continue |
| 403 | Forbidden | Account is suspended |
| 404 | Not Found | Transaction ID not found |
| 500 | Server Error | M-Pesa API error or internal issue — retry after a moment |
Guides
Quick Start
From zero to your first STK push in 3 steps.
# Sign up at https://pay.archietech.app → Get Started
# Your API key appears immediately on the dashboard (starts with apk_)
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 }'
# In your dashboard → Settings → Webhook URL
# Enter: https://yourapp.com/webhooks/mpesa
# Then handle the POST in your server (see Webhook section above)
Guides
Node.js Integration
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
Guides
Python Integration
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