Skip to main content
All requests require authentication. You can get your API credentials (Access ID and Access Secret) from the Tender dashboard. See Getting API Credentials. Tender uses two authentication methods, depending on the endpoint: Each endpoint page shows which method it uses in a note at the top.
Your access ID must belong to an API key that is linked to an outlet (agent). Requests made with a key that has no outlet are rejected with 401 INVALID_REQUEST_SIGNATURE, on both Signed and Basic endpoints.

Basic (Access ID) Authentication

Payment and System endpoints (/v1/api/payment/* and /v1/api/system/*) only require your access ID:
No request signing is needed for these endpoints, and your Access Secret is never sent. Treat your access ID as a credential anyway: anyone who has it can call these endpoints on your behalf.

Signed (HMAC) Authentication

All other endpoints require an HMAC-SHA256 signature. Each request must include these headers:

What is signed

The signature covers a JSON string built from the three header values, with the keys in exactly this order and no whitespace:
This is the output of JavaScript’s JSON.stringify({ timeStamp, requestId, accessId }). Use the exact strings you send in the headers: if you sign one timestamp and send another, the signature won’t match.
Only these three values are signed. The request body, HTTP method and path are not part of the signature. Always call the API over HTTPS, and keep your Access Secret on your server.

Signature algorithm

The output is standard Base64 (with +, / and = padding), not hex and not URL-safe Base64.

Signed request example

Each example below signs a request and registers a webhook with POST /v1/api/webhook.

Authentication errors

Authentication failures use the standard error envelope. Example:

Troubleshooting INVALID_REQUEST_SIGNATURE

  • Check your clock. Sync your server with NTP; a drift of more than 5 minutes fails every request.
  • Sign the exact header values. Build the timestamp once and use the same string in the header and in the signed JSON.
  • Check the JSON string. It must be {"timeStamp":…,"requestId":…,"accessId":…} in that order, with no spaces. In Python use separators=(',', ':').
  • Use Base64, not hex. In Node.js, digest('base64'); in PHP, pass true as the fourth argument to hash_hmac before Base64-encoding.
  • Use the right environment. Sandbox credentials only work against https://sandbox-api.tender.cash, and production credentials only against https://secureapi.tender.cash.

Important notes

  • Keep your Access Secret secure. Never expose it in client-side code or public repositories.
  • Generate a new request ID (UUID v4) for every request.
  • Rate limits apply per merchant. See Rate limits.
  • Payout requests need an Idempotency-Key header in addition to the signed headers. See Idempotency.
For other error responses, see Errors.