Developer First

API Documentation

Paylor provides a robust, secure, and developer-friendly API for processing M-Pesa payments, B2C payouts, and USDT TRC20 crypto payments. Our platform abstracts payment orchestration so you can go live faster.

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);

B2C Payout

Send money directly to any M-Pesa registered mobile number from your B2C wallet. Payouts are instant and ideal for salaries, rewards, or supplier payments.

Request Body

  • phoneRequired

    Recipient phone number (e.g., 254712345678).

  • amountRequired

    Amount to send. Min: 10, Max: 150,000 per transaction.

  • referenceRequired

    Unique tracking reference for your system.

POST/merchants/payments/b2c
json
1{
2 "phone": "254712345678",
3 "amount": 500,
4 "reference": "PAYOUT-001",
5 "description": "Commission Payment"
6}

USDT Crypto Payment

Create a USDT TRC20 payment intent and return the destination TRON address to your customer. The customer sends the exact USDT amount on the TRON network, and Paylor confirms the transaction automatically through the blockchain monitor.

Before You Start

  • Add at least one active TRON settlement wallet in Dashboard > Crypto > Wallets.
  • Use an API key with the payments:create scope. Default merchant API keys include this scope.
  • Only USDT on TRON/TRC20 is supported. Do not send ERC20, BEP20, BTC, or other assets to the returned address.

Request Body

  • amountRequired

    Exact amount in USDT. Minimum is 0.01.

  • currencyRequired

    Must be USDT.

  • referenceRequired

    Your order, invoice, or customer reference.

  • cryptoIdOptional

    Specific crypto wallet/channel ID. If omitted, Paylor selects an active crypto wallet for the merchant or project.

  • callbackUrlRecommended

    Your HTTPS endpoint for the final confirmation webhook.

POST/merchants/payments/crypto
json
1{
2 "amount": 10.5,
3 "currency": "USDT",
4 "reference": "INV-1002",
5 "description": "Subscription renewal",
6 "cryptoId": "665f4c1b9a8d2e001f2a1234",
7 "callbackUrl": "https://example.com/paylor/callback"
8}
Success Response (201)
json
1{
2 "payment_id": "6660a21f8b4d2e001f5c6789",
3 "address": "TR7NHqSfy2efYv2NpHY5YshptfA3KHxvXp",
4 "amount": 10.5,
5 "currency": "USDT",
6 "status": "INITIATED",
7 "expires_at": "2026-05-23T12:30:00.000Z"
8}
Send the customer the returned address and exact amount. Confirmation is automatic once a matching confirmed TRC20 transfer is detected. If no matching transfer is found before expires_at, the payment becomes EXPIRED.

Implementation Example

nodejs
1const axios = require('axios');
2
3const response = await axios.post(
4 'https://api.paylorke.com/api/v1/merchants/payments/crypto',
5 {
6 amount: 10.5,
7 currency: 'USDT',
8 reference: 'INV-1002',
9 description: 'Subscription renewal',
10 callbackUrl: 'https://example.com/paylor/callback'
11 },
12 {
13 headers: {
14 'Authorization': 'Bearer YOUR_API_KEY',
15 'Content-Type': 'application/json'
16 }
17 }
18);
19
20console.log(response.data.address);

Project-scoped URL

Use this form when your API key is scoped to a project.

http
1POST https://api.paylorke.com/api/v1/merchants/payments/projects/:projectId/crypto

Payment Links

Hosted payment links can also initiate crypto payments when the merchant has an active crypto wallet.

http
1POST https://api.paylorke.com/api/v1/public/pay/:slug/initiate-crypto

Check Wallet Balance

Monitor your service credits and B2C float balance programmatically to avoid transaction failures.

GET/merchants/payments/wallet
bash
1curl -H "Authorization: Bearer YOUR_API_KEY" \
2 https://api.paylorke.com/api/v1/merchants/payments/wallet

B2C Performance Metrics

Retrieve aggregated data about your payout history, including total volume and transaction counts.

GET/merchants/payments/stats/b2c
bash
1curl -H "Authorization: Bearer YOUR_API_KEY" \
2 https://api.paylorke.com/api/v1/merchants/payments/stats/b2c

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});