Build it with an AI agent
Give this to your AI agent
Paste it into Claude Code, Cursor or any agent that can read a URL. It reads the OpenAPI contract and the signing recipe, and follows the rules for handling real funds.
Read https://www.payaries.com/ai-agents.md and integrate Payaries crypto payments into this project. Follow its rules for handling real funds.Never paste your API secret into an agent. Keep it in an environment variable.
01What arrives
X-Gateway-Signature: <hex HMAC-SHA256>
X-Gateway-Timestamp: <unix ms>
Signature = HMAC-SHA256(webhookSecret, timestamp + rawBody)The signature covers the timestamp and the raw request body. Verify against the raw string, before any JSON parsing — re-serialising a parsed body can change whitespace or key order and break the match.
02Verify it
function verifyWebhook(rawBody, signature, timestamp, webhookSecret, toleranceMs = 5 * 60 * 1000) {
if (Math.abs(Date.now() - timestamp) > toleranceMs) return false;
const expected = crypto
.createHmac('sha256', webhookSecret)
.update(timestamp + rawBody)
.digest('hex');
const a = Buffer.from(expected, 'hex');
const b = Buffer.from(signature, 'hex');
return a.length === b.length && crypto.timingSafeEqual(a, b);
}- The timestamp check rejects old deliveries, which limits replay of a captured request.
timingSafeEqualcompares in constant time; check the buffer lengths first, because it throws on a length mismatch.
03A complete handler
app.post('/webhooks/payaries', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.header('X-Gateway-Signature');
const timestamp = Number(req.header('X-Gateway-Timestamp'));
const rawBody = req.body.toString('utf8'); // the RAW body — before any JSON parsing
if (!verifyWebhook(rawBody, signature, timestamp, process.env.WEBHOOK_SECRET)) return res.sendStatus(401);
const event = JSON.parse(rawBody);
res.sendStatus(200); // acknowledge first, do the slow work after
queue.add(event); // dedupe on event.idempotencyKey
});Acknowledge with a 2xx quickly — the endpoint has 10 seconds to respond — and do the slow work afterwards. A slow handler risks a needless retry even though you already received the event.
04Retries and duplicates
| Attempt | When |
|---|---|
| 1 | Immediately |
| 2 | After 1 minute |
| 3 | After 5 minutes |
| 4 | After 15 minutes |
| 5 | After 1 hour |
| Final | After 6 hours |
Because a delivery can repeat, deduplicate on the event's idempotencyKey and make your handler idempotent on the invoice and status. After the last failed attempt an invoice's webhookStatus becomes failed; re-fire it with POST /v1/invoices/:id/resend-webhook.
05Endpoint requirements
- A public
https://URL.http://, localhost, private or link-local addresses and internal hostnames are refused, and re-checked on every delivery. - Redirects aren't followed — register the final URL.
- Don't rely on the source address for authenticity. Always verify the signature.
Questions
- Why must I verify the webhook against the raw body?
- The signature is computed over the exact bytes sent. Parsing and re-serialising the JSON can change whitespace or key order, so the recomputed HMAC no longer matches.
- How do I handle duplicate webhook deliveries?
- Deliveries are retried on failure, so the same event can arrive more than once. Deduplicate on the idempotencyKey and make your handler idempotent on the invoice and status.
- How fast must my webhook endpoint respond?
- Within 10 seconds with a 2xx status. Acknowledge first and process afterwards.
Keep reading
- A crypto payment API that behaves like the rest of your stackA REST crypto payment API: create an invoice, get a one-time deposit address, receive signed webhooks and withdraw on demand. HMAC-signed and idempotent.
- Crypto payment confirmations, explained for merchantsHow blockchain confirmations work for merchants: what each invoice status means, when a payment counts as paid, and credited vs withdrawable.
- Handling underpaid, late and unmatched crypto paymentsWhat to do when a customer underpays, pays after an invoice expires or sends money that matches no invoice — and how to build order logic for each.
- A crypto payment gateway for forex brokers and trading firmsAccept USDT and other supported crypto for client deposits with a one-time address per deposit, signed webhooks to credit accounts, and on-demand withdrawals. An API for brokers and trading firms.
- Build a crypto payment integration with an AI agent, safelyEverything an AI agent or coding assistant needs to integrate Payaries: an OpenAPI spec, llms.txt, markdown docs, signing test vectors and the rules for handling real funds safely.
- How to sign crypto payment API requests (HMAC-SHA256)How to sign Payaries API requests with HMAC-SHA256, with working Node.js, Python, Go and shell code and published test vectors to check your implementation against.
- Hosted crypto checkout: send the customer, get them backUse Payaries's hosted payment page without building a checkout: create an invoice with a success and cancel URL, redirect the customer, verify the signed return and fulfil from the webhook.