openapi.json
is callable here — request bodies are validated client-side against the
schema before they’re sent.
The playground talks to
https://api.sandbox.zennopay.com. Use a
sandbox HMAC key ({partner}_sandbox_*) — production keys are
rejected by the sandbox host. The legacy api.sandbox.zennopay.in host is
still served if your client is pinned to it.What you’ll need
- A sandbox HMAC key (
X-Zennopay-Key-Id) and shared secret. Compute the signature with the canonical-request recipe from Authentication. - For session-token endpoints (
GET /v1/payment_intents/{intent_id}andPOST /v1/payment_intents/{intent_id}/confirm), thesession_tokenreturned by aPOST /v1/payment_intentscall — send it asAuthorization: Bearer. These are the calls the SDK normally makes; you rarely call them by hand.
Endpoints
The playground is generated from/openapi.json and groups
endpoints by tag:
- Payment Intents —
POST /v1/payment_intents,POST /v1/payment_intents/{intent_id}/session,POST /v1/payment_intents/{intent_id}/receipt_token,GET /v1/payment_intents/{intent_id},POST /v1/payment_intents/{intent_id}/confirm,POST /v1/payment_intents/{intent_id}/cancel
Open the OpenAPI spec
Raw OpenAPI 3.1 document. Drop this into Postman, Insomnia, or your own
codegen if you’d rather build a client locally.
Tips for first-time use
My HMAC request returns 401
My HMAC request returns 401
The most common causes, in order:
- Clock skew.
X-Zennopay-Timestampmust be within ±5 minutes of server time. NTP-sync your machine. - Body hash mismatch. The canonical request hashes the exact bytes of the request body. Don’t pretty-print or re-serialize between signing and sending.
- Source IP not allowlisted. The sandbox allowlist is configured per partner in the console.
My session-token call returns 401
My session-token call returns 401
In the default flow the session token is Zennopay-minted, so the usual
causes are:
- Expired.
session_expires_athas passed. Create a fresh intent (or re-mint viaPOST /v1/payment_intents/{intent_id}/session) and use the newsession_token. - Wrong intent. A token issued for intent A can’t call intent B — the
path
{intent_id}must match the intent the token was minted for. - Already consumed. The confirming call is single-use; re-mint via
POST /v1/payment_intents/{intent_id}/sessionif you need another.
My nonce keeps getting rejected
My nonce keeps getting rejected
Each
X-Zennopay-Nonce is single-use within a 10-minute window.
Generate a fresh random 32-byte hex string for every request — never
hardcode or reuse one.