Merchants

Gateway API

Everything the payment-link dashboard does, over JSON: create links and payments, read status, and receive signed webhooks. No SDK required — plain HTTPS and an API key.

On this page

Getting started

Authentication

Authenticated endpoints take an API key as a bearer token. Create keys in the partner dashboard under API keys — the full key (it starts with ae_) is shown exactly once at creation and cannot be retrieved again. Send it on every request:

Header
Authorization: Bearer ae_your_api_key

Keys are meant for server-to-server use. Never embed one in a website, app, or repository — anyone holding the key can act on your links. Revoke a leaked key from the dashboard; revocation is immediate.

Base URL

All authenticated endpoints live under:

Base URL
https://api.anonexch.io/v1

Public endpoints (no key needed) live on the main origin under https://anonexch.io/api/pay. Requests and responses are JSON; amounts are decimal strings, timestamps are RFC 3339 / ISO 8601. Unknown request fields are rejected.

Rate limits

The authenticated gateway API allows 60 requests per minute per key. On the public endpoints, link and payment creation allow 10 per minute and quotes 30 per minute, per client. Rate-limited requests receive HTTP 429 — back off and retry.

Public endpoints

No account, no key: these endpoints power anonymous links and the customer-facing checkout. They live on the main origin under https://anonexch.io/api/pay.

Quote a payment

POST/api/pay/links/{id}/quote

Price a payment against a link before creating it. Quotes are indicative — the binding send amount is re-computed server-side when the payment is created.

Body fields
FieldTypeRequiredDescription
fromCoinstringrequiredThe coin the customer pays with.
amountstringoptionalDonation links only: what the customer chooses to give, denominated in the link's receive coin. Must be omitted on fixed-amount links, which carry their own.
Request
curl -X POST https://anonexch.io/api/pay/links/QK7W3MZP9T/quote \
  -H 'Content-Type: application/json' \
  -d '{"fromCoin": "eth"}'
Response 200
{
  "fromCoin": "eth",
  "sendAmount": "0.081",
  "quotedReceive": "0.005",
  "validUntil": "2026-08-16T12:05:00Z"
}

Create a payment

POST/api/pay/links/{id}/payments

Start a checkout against a link. The pair is re-priced server-side (the quote the client saw is never trusted) and the response carries the deposit address plus the exact amount the customer must send in fromCoin.

Body fields
FieldTypeRequiredDescription
fromCoinstringrequiredThe coin the customer pays with.
amountstringoptionalDonation links only: the amount the customer gives, in the link's receive coin. Must be omitted on fixed-amount links.
refundAddressstringoptionalOptional but recommended — where the deposit returns if the conversion fails.
Request
curl -X POST https://anonexch.io/api/pay/links/QK7W3MZP9T/payments \
  -H 'Content-Type: application/json' \
  -d '{
    "fromCoin": "eth",
    "refundAddress": "0xcustomer-refund-address"
  }'
Response 200
{
  "id": "XV5R8NDJ2M",
  "url": "https://pay.anonexch.io/QK7W3MZP9T?pmt=XV5R8NDJ2M",
  "linkId": "QK7W3MZP9T",
  "status": "awaiting_deposit",
  "depositAddress": "0xdeposit-address",
  "fromCoin": "eth",
  "toCoin": "btc",
  "sendAmount": "0.081",
  "quotedReceive": "0.005",
  "createdAt": "2026-08-16T12:00:00Z"
}

A depositMemo field is included when the deposit coin requires a memo or destination tag.

Payment status

GET/api/pay/payments/{id}

The customer-facing status of a payment — the same shape the create call returned, with status advancing through the payment statuses. Poll it, or use the events stream below.

Request
curl https://anonexch.io/api/pay/payments/XV5R8NDJ2M
Response 200
{
  "id": "XV5R8NDJ2M",
  "url": "https://pay.anonexch.io/QK7W3MZP9T?pmt=XV5R8NDJ2M",
  "linkId": "QK7W3MZP9T",
  "status": "confirming",
  "depositAddress": "0xdeposit-address",
  "fromCoin": "eth",
  "toCoin": "btc",
  "sendAmount": "0.081",
  "quotedReceive": "0.005",
  "createdAt": "2026-08-16T12:00:00Z"
}

Events stream (SSE)

GET/api/pay/payments/{id}/events

Every payment has a public Server-Sent Events stream on the main origin — no key needed, so your checkout page can subscribe directly. Each event's data is the same JSON as the payment status endpoint; a new event is sent whenever the projection changes, and the stream ends after a terminal state.

Example — browser
const es = new EventSource('https://anonexch.io/api/pay/payments/PAYMENT_ID/events')
es.onmessage = event => {
  const payment = JSON.parse(event.data)
  if (payment.status === 'complete') es.close()
}

Partner endpoints

With an API key, links belong to your account: they can be listed, edited, and archived, they appear in the dashboard alongside links created there, and they can carry a webhook URL. All endpoints in this group live under https://api.anonexch.io/v1 and require the Authorization header. A 404 means the resource does not exist or is not yours — the API does not distinguish the two.

Rotate webhook secret

POST/v1/links/{id}/webhook-secret

Issues a new signing secret for the link's webhooks. The new secret is returned exactly once; rotating invalidates the old secret immediately.

Request
curl -X POST https://api.anonexch.io/v1/links/QK7W3MZP9T/webhook-secret \
  -H 'Authorization: Bearer ae_your_api_key'
Response 200
{
  "webhookSecret": "whsec_9c1b7f…a230"
}

List payments

GET/v1/payments

Every payment on your account, across all links and one-off payments, newest first. linkId identifies the link a payment was made against; one-off payments omit it.

Request
curl https://api.anonexch.io/v1/payments \
  -H 'Authorization: Bearer ae_your_api_key'
Response 200
[
  {
    "id": "XV5R8NDJ2M",
    "url": "https://pay.anonexch.io/QK7W3MZP9T?pmt=XV5R8NDJ2M",
    "linkId": "QK7W3MZP9T",
    "status": "complete",
    "fromCoin": "eth",
    "toCoin": "btc",
    "sendAmount": "0.081",
    "quotedReceive": "0.005",
    "createdAt": "2026-08-16T12:00:00Z",
    "completedAt": "2026-08-16T12:19:02Z"
  }
]

One-off payment

POST/v1/payments

A payment can also be created directly, without a stored link — useful when your backend generates a charge per checkout. The response carries the deposit address and the amount the customer must send in fromCoin. One-off payments carry no webhook — create a link if you want callbacks.

Body fields
FieldTypeRequiredDescription
toCoinstringrequiredThe coin you receive.
toAddressstringrequiredYour payout address for toCoin.
amountstringrequiredThe amount to receive, in toCoin.
fromCoinstringrequiredThe coin the customer pays with.
refundAddressstringoptionalOptional but recommended — where the deposit returns if the conversion fails.
Request
curl -X POST https://api.anonexch.io/v1/payments \
  -H 'Authorization: Bearer ae_your_api_key' \
  -H 'Content-Type: application/json' \
  -d '{
    "toCoin": "btc",
    "toAddress": "bc1qyour-btc-address",
    "amount": "0.005",
    "fromCoin": "eth",
    "refundAddress": "0xcustomer-refund-address"
  }'
Response 200
{
  "id": "XV5R8NDJ2M",
  "status": "awaiting_deposit",
  "depositAddress": "0xdeposit-address",
  "fromCoin": "eth",
  "toCoin": "btc",
  "sendAmount": "0.081",
  "quotedReceive": "0.005",
  "createdAt": "2026-08-16T12:00:00Z"
}

Get a payment

GET/v1/payments/{id}

Poll a payment's status with your key, or subscribe to the public SSE stream — it needs no key, so your checkout page can listen directly.

Request
curl https://api.anonexch.io/v1/payments/XV5R8NDJ2M \
  -H 'Authorization: Bearer ae_your_api_key'
Response 200
{
  "id": "XV5R8NDJ2M",
  "url": "https://pay.anonexch.io/QK7W3MZP9T?pmt=XV5R8NDJ2M",
  "linkId": "QK7W3MZP9T",
  "status": "sending",
  "depositAddress": "0xdeposit-address",
  "fromCoin": "eth",
  "toCoin": "btc",
  "sendAmount": "0.081",
  "quotedReceive": "0.005",
  "createdAt": "2026-08-16T12:00:00Z"
}

Webhooks

Partner-only: a webhook URL can be set on links created from the partner dashboard or through this API — never on anonymous links. When a payment against such a link settles, we POST to the URL. Webhook URLs must be HTTPS.

Payload

Events: payment.completed and payment.failed. The body:

Delivery body
{
  "event": "payment.completed",
  "paymentId": "…",
  "linkId": "…",
  "status": "complete",
  "fromCoin": "eth",
  "toCoin": "btc",
  "sendAmount": "0.081",
  "quotedReceive": "0.005",
  "createdAt": "2026-08-16T12:00:00Z",
  "completedAt": "2026-08-16T12:19:02Z"
}

Signature

Each delivery is signed with your link's secret (it starts with whsec_):

Header
X-AnonExch-Signature: t=1755345542,v1=5f8a2c…e4d1

To verify: concatenate the t value, a literal ., and the raw request body; compute HMAC-SHA256 over that string keyed with your whsec_ secret; hex-encode and constant-time compare against v1. Reject the delivery if the signature doesn't match or t is older than your tolerance (5 minutes is a sensible default) — the timestamp is what makes captured deliveries unreplayable. Always verify against the raw bytes, not a re-serialized parse.

Retries

Respond with any 2xx quickly; anything else is retried after 1 minute, 5 minutes, 30 minutes, 2 hours, and 6 hours before the delivery is dropped.

Payment statuses

Payments move through these states, in order, ending in exactly one terminal state:

awaiting_depositWaiting for the customer to send funds to the deposit address.
confirmingThe deposit was seen and is gathering network confirmations.
exchangingThe deposit is being converted into the receive coin.
sendingThe converted funds are on their way to the payout address.
completeTerminal — the payout was delivered.
failedTerminal — the conversion failed; a refund address, if given, is used.
expiredTerminal — no deposit arrived within the payment window.
Gateway API: Accept Crypto Payments Programmatically — AnonExch