Skip to main content

Documentation Index

Fetch the complete documentation index at: https://coinvoyage-3c99945b.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Webhooks let you receive real-time notifications in your application whenever a PayOrder changes status. Rather than polling the CoinVoyage API for updates, you register an HTTPS endpoint and CoinVoyage delivers a POST request with the event details each time something happens — payment received, confirmed on-chain, completed, refunded, and more.

Set up a webhook

Register and manage your webhook endpoints in the CoinVoyage Dashboard under the Developers section.
1

Navigate to Developers → Webhooks

Open the CoinVoyage Dashboard and select Developers in the sidebar, then click Webhooks.
2

Click Add Webhook

Click the Add Webhook button to open the registration form.
3

Enter your endpoint URL

Provide the URL of your publicly accessible webhook handler.
Your endpoint must use HTTPS in production. HTTP endpoints are not accepted.
4

Select the events to subscribe to

Choose which PayOrder lifecycle events should trigger delivery to your endpoint. Subscription event identifiers use uppercase ORDER_* format (for example, ORDER_COMPLETED).
The type field in the delivered JSON payload uses lowercase payorder_* values — for example, subscribing to ORDER_COMPLETED results in payloads with "type": "payorder_completed". See Webhook Events for the full mapping.
5

Save and store your Webhook Secret

Save the webhook. CoinVoyage generates a Webhook Secret — copy it immediately and store it securely (for example, as an environment variable). You need this secret to verify the signature on every incoming request.
Store the Webhook Secret in an environment variable, never in source code or version control.
Your endpoint must be publicly accessible and respond with a 2xx status code within 30 seconds. Responses outside this window are treated as delivery failures.

Verify webhook signatures

Every webhook request includes a CoinVoyage-Webhook-Signature header containing an HMAC-SHA256 signature of the raw request body, encoded in Base64. Always verify this signature before parsing or acting on the payload. The example below shows a complete Next.js Route Handler that verifies the signature and dispatches on the event type:
app/api/webhook/route.ts
"use server";

import { createHmac } from "crypto";

const webhookSecret = process.env.COIN_VOYAGE_WEBHOOK_SECRET!;

export const POST = async (req: Request) => {
  const rawBody = await req.text();
  const signature = req.headers.get("CoinVoyage-Webhook-Signature");

  const hmac = createHmac("sha256", webhookSecret)
    .update(rawBody)
    .digest("base64");

  if (!signature || signature !== hmac) {
    return new Response("Unauthorized", {
      status: 401,
    });
  }

  const event = JSON.parse(rawBody) as {
    type: string;
    payorder_id: string;
    status: string;
  };

  switch (event.type) {
    case "payorder_completed":
      console.log("PayOrder completed", event.payorder_id);
      break;
    case "payorder_refunded":
      console.log("PayOrder refunded", event.payorder_id);
      break;
    case "payorder_expired":
      console.log("PayOrder expired", event.payorder_id);
      break;
    default:
      console.log("Unhandled webhook", event.type, event.payorder_id);
  }

  return new Response("Webhook Received", {
    status: 200,
  });
};
Read the raw request body with req.text() before calling JSON.parse. The HMAC is computed over the raw bytes, so parsing first will not affect correctness here, but reading the body twice is not possible in the Web Fetch API — always read it once as text, verify, then parse.

Security best practices

Following these practices protects your endpoint against spoofed or replayed requests.
  • Verify the signature before parsing. Compute the HMAC-SHA256 of the raw request body and compare it to the CoinVoyage-Webhook-Signature header before you inspect any payload fields.
  • Use HTTPS in production. Plain HTTP exposes the request body and signature to interception. CoinVoyage only delivers to HTTPS endpoints in production environments.
  • Store your Webhook Secret securely. Keep it in an environment variable (for example, COIN_VOYAGE_WEBHOOK_SECRET). Never commit it to source code or expose it in client-side bundles.
  • Respond quickly. Return a 2xx response as soon as the signature is verified. Offload any heavy processing to a background job so you don’t risk hitting the 30-second timeout.
  • Implement proper error handling. Return a non-2xx status only when delivery should be retried. Return 200 even for unrecognized event types you choose to ignore.