API Reference

The complete REST API for accepting crypto payments — checkouts, wallets, transactions, payments, projects, and signed webhooks. Every endpoint, parameter, and response, documented.

The ZyraPay API is organized around predictable, resource-oriented URLs, accepts and returns JSON, and uses standard HTTP verbs and status codes. This page is the exhaustive reference; for a guided walkthrough start with the Quickstart.

Base URL

All requests go to a single versioned base URL over HTTPS:

Base URL
https://api.zyrapay.net/v1

Versioning

The API is versioned in the path. Every endpoint below is mounted under /v1. We introduce breaking changes only behind a new version prefix, so pinning to /v1 keeps your integration stable.

Requests and responses

Send request bodies as JSON with a Content-Type: application/json header. Responses are JSON and, by convention, wrap their payload in a success flag with the data under data (or top-level fields for creation endpoints such as checkout). Errors set success: false — see Errors.

Authentication at a glance

Every endpoint authenticates with your project API key in the x-api-key header — that's all your server needs. Creation and money-moving requests also accept an Idempotency-Key header for safe retries. Full details are in Authentication.

Keep your keys secret

API keys carry full access to your project. Send them only from your server, never from browser or mobile client code, and rotate immediately if one is exposed.

Authentication

The ZyraPay API authenticates with a project API key sent in the x-api-key header. It is the only credential your server needs — there are no tokens to mint and no OAuth flow.

API key (gateway, checkout, wallets)

Server-to-server endpoints authenticate with a project API key sent in the x-api-key header (the legacy apikey header is also accepted). The key is a 64-character hex string. Each key resolves to exactly one project, so the project context is implied by the key — you never pass a project id on these requests.

Request header
x-api-key: 4f3c9a...e9

Keys are hashed at rest — store them securely

ZyraPay never stores your key in plaintext. We persist only its SHA-256 hash (the indexed lookup) plus a short non-secret 8-character prefix shown in the dashboard. The full key is revealed exactly once, at project creation (or rotation). Keep it server-side; if you lose it, rotate the key from the dashboard — there is no way to recover the original.

If the key is missing or invalid you receive:

401 — missing / invalid key
{ "success": false, "message": "API key is required" }
{ "success": false, "message": "Invalid API key get your api key from the dashboard" }

Wallets behave slightly differently

POST /v1/wallets reads the same x-api-key header but returns 401 when the key is missing and 403 when the key is invalid (rather than 401 for both).

One credential for everything

Every endpoint in this reference — checkout, wallets, transactions, payments, and key rotation — uses the same x-api-key. There is no separate token or login to manage. You can also create projects and view history visually in the dashboard.

The Idempotency-Key header

Money-moving and creation requests support an optional Idempotency-Key request header. Send a unique value (for example a UUID) and a replay returns the original response verbatim instead of performing the action twice — making retries safe over flaky networks.

FieldTypeDescription
Idempotency-KeystringA unique, client-generated key. Replays within the TTL (24h default) return the cached 2xx response.

The key is honored on the following endpoints, each scoped to a different collision namespace:

EndpointScope
POST /v1/checkoutProject
POST /v1/walletsAPI key

A request that is still in flight under the same key returns 409:

409 — duplicate in flight
{ "success": false, "message": "A request with this Idempotency-Key is still being processed." }

Checkout

A checkout is a hosted payment page for a one-off purchase. You create it with your API key, redirect the customer to checkoutUrl, and receive signed webhooks as the payment lands. Checkouts default to a 30-minute expiry and accept overpayment and partial payment.

Create a checkout

POST/v1/checkoutAPI key

Creates a checkout session, mints a deposit wallet for it, and (if a webhookUrl is supplied) fires a best-effort signed checkout.created webhook. Honors the Idempotency-Key header, scoped per project.

Body parameters

FieldTypeDescription
itemsrequiredobject[]Line items. Must be a non-empty array. See item fields below.
currencystringOne of PI, USD, USDT, DOLLAR. USD-family currencies are converted to PI at the live price. Defaults to PI.
expiresInMinutesnumberMinutes until the session expires. Must be positive. Defaults to 30.
redirectUrlstringURL the hosted page returns the customer to after payment.
successUrlstringURL to redirect to on a successful payment.
cancelUrlstringURL to redirect to if the customer cancels.
webhookUrlstringPer-checkout webhook URL. Overrides the project default for this session.
metadataobjectArbitrary key/value data echoed back on the checkout and its webhooks.

Item fields

FieldTypeDescription
namerequiredstringDisplay name of the line item.
amountrequirednumberUnit price. Must be a positive number.
quantityrequirednumberQuantity. Must be a positive number.
descriptionstringOptional line-item description.
imagestringOptional image URL shown on the hosted page.
metadataobjectOptional per-item metadata.
curl https://api.zyrapay.net/v1/checkout \
  -H "x-api-key: $ZYRA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "items": [
      { "name": "Pro plan", "amount": 5, "quantity": 1 },
      { "name": "Add-on seat", "amount": 2, "quantity": 4 }
    ],
    "currency": "PI",
    "expiresInMinutes": 30,
    "successUrl": "https://yourapp.com/success",
    "cancelUrl": "https://yourapp.com/cancel",
    "webhookUrl": "https://yourapp.com/webhooks/zyra"
  }'

Validation errors return 400

If items is empty, or any item lacks a string name / positive amount / positive quantity, you receive { "success": false, "message": "Invalid request data", "errors": [...] }.

List checkouts

GET/v1/checkoutAPI key

Lists checkouts for the project tied to your API key, with filtering, sorting, and pagination. The project scope is fixed by the key.

Query parameters

FieldTypeDescription
pageinteger1-based page number. Defaults to 1.
limitintegerPage size, capped at 100. Defaults to 20.
sortstringSort field: createdAt | totalAmount | status. Defaults to createdAt.
orderstringasc | desc. Defaults to desc.
statusstringpending | expired | cancelled | completed | partial | overpaid. Use all (or omit) for no filter.
dateFromISO dateInclude checkouts created on or after this timestamp.
dateToISO dateInclude checkouts created on or before this timestamp (inclusive).
minAmountnumberFilter on totalAmount ≥ this value.
maxAmountnumberFilter on totalAmount ≤ this value.
qstringCase-insensitive search over checkoutId and embedded items.name.
curl "https://api.zyrapay.net/v1/checkout?status=completed&sort=totalAmount&order=desc&page=1&limit=20" \
  -H "x-api-key: $ZYRA_API_KEY"

Retrieve a checkout

GET/v1/checkout/{checkoutId}Public

Returns a single checkout populated with its project.name and the checkout's deposit address. This route is public (no auth) so the hosted checkout page can read it directly.

curl https://api.zyrapay.net/v1/checkout/chk_8f2a1c9b4d6e7f

Cancel a checkout

DELETE/v1/checkout/{checkoutId}API key

Cancels a checkout owned by your project. Only a pending checkout can be cancelled.

curl -X DELETE https://api.zyrapay.net/v1/checkout/chk_8f2a1c9b4d6e7f \
  -H "x-api-key: $ZYRA_API_KEY"

Wallets

A wallet is a per-customer deposit address. Generate one for each user or invoice, hand them the address, and receive a signed deposit webhook whenever Pi arrives on it.

Create a wallet

POST/v1/walletsAPI key

Generates a new deposit address for the project tied to your API key. Takes no body. Emits a wallet.created webhook to the project's configured webhookUrl if one is set. Honors the Idempotency-Key header, scoped per API key.

Rate limit

Wallet creation is limited to 200 requests per 15 minutes per API key (or IP), with a one-hour block when the limit is breached.

curl -X POST https://api.zyrapay.net/v1/wallets \
  -H "x-api-key: $ZYRA_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

Auth errors

A missing x-api-key header returns 401; an invalid key returns 403.

Transactions

Transactions are the ledger of every money movement on your project — deposits, withdrawals, payments, refunds, and fees. These endpoints authenticate with your x-api-key and return your project's transactions.

List transactions

GET/v1/transactionsx-api-key

Returns a paginated list of the caller's transactions, each with its referenced project populated (name, type).

Query parameters

FieldTypeDescription
pageinteger1-based page number. Defaults to 1.
limitintegerPage size, capped at 100. Defaults to 20.
sortstringSort field: createdAt | amount | status. Defaults to createdAt.
orderstringasc | desc. Defaults to desc.
statusstringpending | completed | failed | cancelled. Use all (or omit) for no filter.
typestringdeposit | withdrawal | transfer | fee | payment | refund. Use all (or omit) for no filter.
projectIdstringFilter by a specific project id. Use all (or omit) for no filter.
dateFromISO dateInclude transactions created on or after this timestamp.
dateToISO dateInclude transactions created on or before this timestamp (inclusive).
minAmountnumberFilter on amount ≥ this value.
maxAmountnumberFilter on amount ≤ this value.
qstringCase-insensitive search over trx_id and title.
curl "https://api.zyrapay.net/v1/transactions?type=deposit&status=completed&sort=amount&order=desc&page=1&limit=20" \
  -H "x-api-key: $ZYRA_API_KEY"

Retrieve a transaction

GET/v1/transactions/{id}x-api-key

Returns a single transaction owned by the caller.

curl https://api.zyrapay.net/v1/transactions/trx_a1b2c3d4 \
  -H "x-api-key: $ZYRA_API_KEY"

Payments

Payment endpoints move Pi off your balance — sending funds to a recipient (withdraw) or returning a previously received payment (refund). Authenticate with your x-api-key like every other endpoint. Both honor the Idempotency-Key header, and on-chain transfers are serialized behind a lock so concurrent calls cannot double-spend.

Withdraw

POST/v1/payments/withdrawx-api-key

Debits your balance atomically (a conditional update guarantees two concurrent withdrawals can't both pass a stale balance check), submits a Pi payment to recipient, and records a completed withdrawal transaction. If the on-chain transfer fails, the reserved balance is refunded automatically.

Body parameters

FieldTypeDescription
recipientrequiredstringThe destination Pi address to send funds to.
amountrequirednumberAmount of Pi to withdraw. Must not exceed your available balance.
curl -X POST https://api.zyrapay.net/v1/payments/withdraw \
  -H "x-api-key: $ZYRA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "recipient": "GBVYHIK3X4VQ2...",
    "amount": 10
  }'

Errors

400 — missing/invalid input or insufficient balance. 401 — no authenticated user. 500 — on-chain failure (the debited balance is restored).

Refund

POST/v1/payments/refundx-api-key

Refunds a previously received payment. The original transaction must belong to you, be a payment or deposit, and be completed. Double refunds are blocked. Because the on-chain payer address is not stored on the transaction, you supply the recipient to refund to. Omit amount for a full refund, or pass a smaller value for a partial refund.

Body parameters

FieldTypeDescription
trxIdrequiredstringThe trx_id of the original completed transaction to refund.
recipientrequiredstringThe address to send the refund to (the on-chain payer address).
amountnumberOptional partial-refund amount. Defaults to the full original amount. Cannot exceed it.
curl -X POST https://api.zyrapay.net/v1/payments/refund \
  -H "x-api-key: $ZYRA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "trxId": "trx_a1b2c3d4",
    "recipient": "GBVYHIK3X4VQ2...",
    "amount": 5
  }'

Errors

400 — missing/invalid input, not refundable, or amount too large. 401 — no user. 404 — original transaction not found. 409 — already refunded. 500 — on-chain failure.

Projects

A project owns an API key and groups its checkouts, wallets, and transactions. You can create projects in the dashboard; the endpoint below rotates a project's key and authenticates with your x-api-key.

Rotate an API key

POST/v1/projects/{projectId}/rotate-keyx-api-key

Generates a fresh API key for the project, stores only its new SHA-256 hash and 8-character prefix, and returns the new plaintext key exactly once. The previous key stops working immediately — rotation is also how you revoke a leaked key.

The key is shown only once

Copy the returned apiKey and store it securely. It is never displayed again; if you lose it you must rotate again.

curl -X POST https://api.zyrapay.net/v1/projects/650f1c.../rotate-key \
  -H "x-api-key: $ZYRA_API_KEY"

Webhook Events

ZyraPay delivers events to the webhookUrl configured on your project (or, for checkouts, the per-session webhookUrl). Every delivery is an HTTP POST with a JSON body in the SimpleWebhookPayload envelope and an HMAC-SHA256 signature you should verify before trusting it.

Event types

TypeDescription
depositPi arrived on a deposit wallet.
wallet.createdA new deposit wallet was minted via POST /v1/wallets.
checkout.createdA checkout session was created via POST /v1/checkout.
checkout.paidA checkout was paid in full.
checkout.underpaidA checkout received a partial payment (less than the total).
checkout.overpaidA checkout received more than its total amount.
checkout.expiredA pending checkout reached its expiry without full payment.
checkout.cancelledA pending checkout was cancelled.

Payload shape

Every event uses the same envelope. type is one of the values above, timestamp is a Unix epoch (milliseconds), projectId identifies your project, and signature is the HMAC-SHA256 hex digest of the payload (also sent in the header). The data object varies by event type.

SimpleWebhookPayload
{
  "type": "checkout.paid",
  "data": {
    "projectId": "650f1c...",
    "checkoutId": "chk_8f2a1c9b4d6e7f",
    "transactionId": "trx_a1b2c3d4",
    "from": "GBVYHIK3X4VQ2...",
    "amount": 13,
    "metadata": {}
  },
  "timestamp": 1749902700000,
  "projectId": "650f1c...",
  "signature": "3a7f...hmac-sha256-hex"
}

The data fields differ per family: deposit carries address, transactionId, and amount; the checkout.paid / underpaid / overpaid events carry checkoutId, from, transactionId, and amount; and the lifecycle events (checkout.expired, checkout.cancelled) carry checkoutId and amount.

Verifying the signature

Each request carries an X-Zyra-Signature header of the form sha256=<hex>. Compute the HMAC-SHA256 of the raw request body using your webhook secret and compare it to the header. ZyraPay also sends X-Project-ID, X-Event-Type, and X-Timestamp headers for convenience.

Verify in Node.js
import crypto from "crypto";

function verify(rawBody, signatureHeader, secret) {
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signatureHeader)
  );
}

// Express: capture the raw body, then
app.post("/webhooks/zyra", (req, res) => {
  const ok = verify(req.rawBody, req.headers["x-zyra-signature"], process.env.WEBHOOK_SECRET);
  if (!ok) return res.status(400).send("bad signature");
  // handle req.body.type ...
  res.sendStatus(200);
});

Retries and delivery

ZyraPay retries failed deliveries up to 3 times with increasing back-off (1s, 5s, 15s). Respond with a 2xx status as quickly as possible and process asynchronously; treat events as at-least-once and deduplicate on data.transactionId.

Errors

ZyraPay uses conventional HTTP status codes: 2xx for success, 4xx for problems with the request (these are safe to surface to the caller), and 5xx for errors on ZyraPay's side.

Status codes

StatusMeaning
200 OKThe request succeeded.
201 CreatedA resource was created (checkout, wallet, refund).
400 Bad RequestInvalid or missing parameters, or a non-refundable / insufficient-balance condition.
401 UnauthorizedMissing or invalid API key,.
403 ForbiddenInvalid API key on the wallets endpoint.
404 Not FoundThe resource does not exist or is not owned by the caller.
409 ConflictA duplicate idempotent request is in flight, or a payment has already been refunded.
429 Too Many RequestsRate limit exceeded (for example, wallet creation).
500 Server ErrorAn unexpected or on-chain failure. Withdrawals restore the reserved balance.

Error response shape

Error responses carry a success: false flag and a human-readable message. Some errors also include a machine-readable error code, the statusCode, and a details object with field-level information.

Error response
{
  "success": false,
  "error": "VALIDATION_ERROR",
  "message": "Invalid request data",
  "statusCode": 400,
  "details": {
    "errors": [
      { "field": "items", "message": "items must be a non-empty array" }
    ]
  }
}

Search docs

Search the documentation

Ask the AI assistant