X-Custom-Signature. This signature lets our platform verify who sent the request and that its key fields were not altered in transit.
TL;DR
- Get a Bearer token (Auth token).
- Generate an RSA 2048 key pair; register your public key with Waftpay.
- Build the signing string:
transaction.reference + transaction.amount + originator.country + transaction.service_codewith no separators.- Sign with RSA + SHA-256, Base64-encode the signature bytes, and send it in
X-Custom-Signature.
When it’s required
- Payouts:
POST /payments/v1/payouts - Remittance:
POST /payments/v1/remittance
Authorization header.
Environments
Prerequisites
- Valid Bearer token in
Authorization: Bearer <access_token>(see Auth token). - RSA 2048 key pair in PEM; public key registered with Waftpay.
- Make sure the values you sign exactly match the payload you send, including casing and formatting.
Key handling and security
- Never commit private keys to your repo or docs site. Treat them as secrets.
- Store private keys in a secure vault or encrypted filesystem and load them at runtime.
- Rotate keys periodically and re-register the public key with Waftpay.
- Use different key pairs for Sandbox and Production.
1) Create your key pair (RSA 2048)
You need an RSA 2048 key pair in PEM format:- Private key: PKCS#8 PEM (
-----BEGIN PRIVATE KEY-----) - Public key: X.509 SubjectPublicKeyInfo PEM (
-----BEGIN PUBLIC KEY-----)
2) Build the signing string
Concatenate these fields in order with no separators:- Use the exact strings you send in the JSON, for example amount
"100.00"and country"KE"vs"KEN". - Do not trim, pad, round, or change case once you build the payload.
Generate X-Custom-Signature
Keep your private key outside the repo, for example ./keys/client_private.pem, and load it at runtime.
- Node.js
- Python
- Java
- PHP
Use standard Base64, not Base64URL. The signing algorithm is RSA PKCS#1 v1.5 + SHA-256.
Example request headers
Common mistakes
- Mismatched amount formatting: if you send
"100.00"you must sign"100.00", not"100". - Country/service casing:
KEvsKENor service code casing must match exactly. - Wrong key format: use PKCS#8 (
BEGIN PRIVATE KEY), not PKCS#1 (BEGIN RSA PRIVATE KEY). - Base64URL vs Base64: use standard Base64 with
+and/, not URL-safe Base64. - Payload mismatch: any difference between the signed string and the sent JSON will fail verification.
