Esc

Authentication

Every request carries your API key, the current timestamp and an HMAC SHA-256 signature made with your secret key.

Your business must be verified

The API only accepts requests once your business has passed verification in the dashboard. Until then, every request returns 401 Business not verified.

Required headers

Send these headers with every request. GET requests have no body, so they don't need x-signature.

x-api-key string required
Your API key, from the dashboard.
x-signature string required with a body
HMAC SHA-256 signature of the request body and timestamp, as a hex string. See Signing a request.
x-timestamp string required
The current Unix time in milliseconds, for example 1678901234567. It must be within 5 minutes of the server's time.

Signing a request

Build the string to sign by joining the request body, serialized as compact JSON (no spaces), and the timestamp. Then compute an HMAC SHA-256 of that string with your secret key and send the hex digest as x-signature.

String to sign
signature = HMAC_SHA256(secret_key, JSON.stringify(body) + timestamp)
const crypto = require('crypto');

function generateSignature(requestBody, timestamp, secretKey) {
  const signatureString = `${JSON.stringify(requestBody)}${timestamp}`;
  return crypto
    .createHmac('sha256', secretKey)
    .update(signatureString)
    .digest('hex');
}

// Example usage
const requestBody = {
  name: "John Doe",
  email: "john@example.com"
};
const timestamp = Date.now();
const signature = generateSignature(requestBody, timestamp, 'your_secret_key');

// Headers for the request
const headers = {
  'x-api-key': 'your_api_key',
  'x-signature': signature,
  'x-timestamp': timestamp,
  'Content-Type': 'application/json'
};
Sign the exact bytes you send

Serialize the body once and send that same string. If you re-serialize it with different spacing or key order, the signature won't match.

Example request

curl -X POST 'https://api.gafiapay.com/api/v1/external/account/generate' \
  -H 'Content-Type: application/json' \
  -H 'x-api-key: your_api_key' \
  -H 'x-timestamp: 1678901234567' \
  -H 'x-signature: generated_signature' \
  -d '{
    "name": "John Doe",
    "email": "john@example.com"
  }'

What the API checks

A request is rejected unless all of these pass:

  • API key. The key must be valid and active. Inactive or revoked keys are rejected.
  • Business verification. Your business must be verified. Businesses still in review, rejected, or that haven't submitted verification are rejected.
  • Timestamp. It must be within 5 minutes of the server's time, which prevents replay attacks.
  • Signature. It must match the request body and timestamp. Changing anything in the request invalidates it.

Errors

Authentication failures return 401 Unauthorized with one of these messages:

MessageCause
Missing required header: x-api-keyA required header is missing. The message names it.
Invalid API keyThe key doesn't exist, or it's inactive or revoked.
Business not verifiedYour business hasn't passed verification yet.
Request timestamp expiredx-timestamp is more than 5 minutes from the server's time.
Invalid signatureThe signature doesn't match the body and timestamp.
401 Unauthorized
{
  "status": "fail",
  "message": "Invalid signature"
}

Response format

Successful responses use this shape:

JSON
{
  "status": "success",
  "data": {
    // Response data specific to the endpoint
  }
}

Error responses use this shape. status is fail for a 4xx error and error for a 5xx error. Show or log message.

JSON
{
  "status": "fail",
  "message": "Error description"
}
GafiaPayGafiaPayDocs
Guide for AI agents Dashboard