Skip to main content
Some Waftpay APIs, notably Payouts and Remittance, require a request header called 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
  1. Get a Bearer token (Auth token).
  2. Generate an RSA 2048 key pair; register your public key with Waftpay.
  3. Build the signing string: transaction.reference + transaction.amount + originator.country + transaction.service_code with no separators.
  4. 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
These endpoints also require Bearer auth in the Authorization header.

Environments

Use separate key pairs for Sandbox and Production. Register the correct public key for each environment.

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.
Example signing string

Generate X-Custom-Signature

Keep your private key outside the repo, for example ./keys/client_private.pem, and load it at runtime.
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: KE vs KEN or 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.

OpenSSL (Linux/macOS/Windows with OpenSSL)