Developer First

API Documentation

Paylor provides a robust, secure, and developer-friendly API for processing M-Pesa payments. Our platform abstracts the complexity of direct integration, allowing you to go live in minutes.

Global Language:

Authentication

The Paylor API uses high-security Bearer tokens (API Keys) for authentication. Requests are made using HTTPS to ensure data privacy. Refer to the table below for the required integration credentials found in your dashboard.

Field NameUsageExample
Merchant IDYour unique account identifier used for internal reference.MERCH-A9X2Z...
API KeyYour active Secret Key (Bearer token). Keep this extremely secure.pk_live_...
Key ID / Webhook SecretThe unique ID of your API key. Used to verify HMAC signatures for webhooks.6995fe04...
Channel ID / AliasThe unique alias of your payment channel (Found in Settings).PAYL-XJ7K2P
http
1// Set this header on every request
2Authorization: Bearer YOUR_API_KEY
nodejs
1import axios from 'axios';
2
3const PAYLOR_API_KEY = process.env.PAYLOR_API_KEY;
4const PAYLOR_BASE_URL = 'https://api.paylorke.com/api/v1';
5
6const response = await axios.post(
7 `${PAYLOR_BASE_URL}/merchants/payments/stk-push`,
8 {
9 phone: '254712345678',
10 amount: 1000,
11 reference: 'ORDER-12345',
12 channelId: 'PAYL-XXXXXX',
13 description: 'Payment for Service',
14 },
15 {
16 headers: {
17 'Authorization': `Bearer ${PAYLOR_API_KEY}`,
18 'Content-Type': 'application/json',
19 },
20 }
21);
nodejs
1const response = await fetch(
2 'https://api.paylorke.com/api/v1/merchants/payments/stk-push',
3 {
4 method: 'POST',
5 headers: {
6 'Authorization': `Bearer ${process.env.PAYLOR_API_KEY}`,
7 'Content-Type': 'application/json',
8 },
9 body: JSON.stringify({
10 phone: '254712345678',
11 amount: 1000,
12 }),
13 }
14);

Base URLs

Live Environment

Use this URL for production transactions.

https://api.paylorke.com/api/v1

Sandbox Environment

Coming soon for testing purposes.

https://api.paylorke.com/api/v1/sandbox

Initiate STK Push

The STK Push (Lipa na M-Pesa Online) allows you to trigger a payment request directly to a customer's phone. The customer will be prompted to enter their M-Pesa PIN to authorize the transaction.

Request Body

  • phoneRequired

    Customer phone number in international format (e.g., 254712345678).

  • amountRequired

    Amount to charge in KES. Minimum is 1.

  • channelIdRequired

    The unique ID (alias) of your payment channel (Found in your API Settings).

  • referenceRequired

    A unique internal reference used for tracking top-ups or specific orders.

  • callbackUrlHighly Recommended

    A public URL to receive instant payment notifications. Securely signed with your Webhook Secret (Key ID).

POST/merchants/payments/stk-push
json
1{
2 "phone": "254712345678",
3 "amount": 1000,
4 "reference": "ORDER-12345",
5 "channelId": "PAYL-XJ7K2P",
6 "callbackUrl": "https://apiskan.com/api/callback",
7 "description": "Payment for Service"
8}
Success Response (201)
json
1{
2 "transactionId": "TR_A98F2...",
3 "status": "SENT",
4 "message": "STK Push sent successfully"
5}

Implementation Example

nodejs
1const axios = require('axios');
2
3const response = await axios.post(
4 'https://api.paylorke.com/api/v1/merchants/payments/stk-push',
5 {
6 phone: '254712345678',
7 amount: 1000,
8 reference: 'ORDER-123',
9 channelId: 'PAYL-XXXXXX',
10 description: 'Payment for Service'
11 },
12 {
13 headers: {
14 'Authorization': 'Bearer YOUR_API_KEY',
15 'Content-Type': 'application/json'
16 }
17 }
18);

Query Transaction

Check the status of a transaction at any time using our query endpoint. This is particularly useful for verifying payments if you miss a webhook callback.

GET/merchants/payments/transactions/:id
bash
1GET https://api.paylorke.com/api/v1/merchants/payments/transactions/TR_A98F2...

Error Codes

CodeDescription
401 UNAUTHORIZEDInvalid Auth Token or Credentials.
402 PAYMENT_REQUIREDInsufficient wallet balance to process the fee.
404 NOT_FOUNDThe requested transaction or channel ID does not exist.

Webhooks Overview

Webhooks allow your application to receive real-time notifications when a transaction status changes. Instead of polling the status API, Paylor will push data to your server as soon as the event occurs.

Steps to Integrate

  1. Create an HTTP POST endpoint on your server (e.g., /api/paylor-webhook).
  2. Go to your Dashboard > Developers > API Keys.
  3. Copy the Key ID (this is your Webhook Secret).
  4. Provide a callbackUrl in your STK Push request body.
  5. Secure your endpoint by verifying the X-Webhook-Signature header using your Secret.

Payload Structure

POST Webhook Payload
json
1{
2 "event": "payment.success",
3 "transaction": {
4 "id": "TR_A98F2...",
5 "reference": "ORDER-12345",
6 "amount": 1000,
7 "status": "COMPLETED",
8 "providerRef": "...",
9 "metadata": {
10 "mpesaReceipt": "RFL8S2K9P0"
11 }
12 }
13}
NestJS — Callback Endpoint
nodejs
1import { Controller, Post, Headers, Body, HttpCode } from '@nestjs/common';
2import * as crypto from 'crypto';
3
4@Controller('api')
5export class PaylorController {
6 @Post('paylor-callback')
7 @HttpCode(200)
8 handleCallback(
9 @Headers('x-webhook-signature') signature: string,
10 @Body() body: any,
11 ) {
12 const secret = process.env.PAYLOR_WEBHOOK_SECRET;
13 if (secret) {
14 const expected = crypto
15 .createHmac('sha256', secret)
16 .update(JSON.stringify(body))
17 .digest('hex');
18 if (signature !== expected) {
19 return { status: 'invalid signature' };
20 }
21 }
22 const { event, transaction } = body;
23 if (event === 'payment.success') {
24 console.log('Payment received:', transaction.reference);
25 }
26 return { received: true };
27 }
28}
Express — Callback Endpoint
nodejs
1import express from 'express';
2import crypto from 'crypto';
3
4const router = express.Router();
5
6router.post('/paylor-callback', (req, res) => {
7 const signature = req.headers['x-webhook-signature'];
8 const secret = process.env.PAYLOR_WEBHOOK_SECRET;
9 if (secret) {
10 const expected = crypto
11 .createHmac('sha256', secret)
12 .update(JSON.stringify(req.body))
13 .digest('hex');
14 if (signature !== expected) {
15 return res.status(401).json({ error: 'Invalid signature' });
16 }
17 }
18 const { event, transaction } = req.body;
19 if (event === 'payment.success') {
20 console.log('Payment received:', transaction.reference);
21 }
22 res.json({ received: true });
23});