Authentication

Every request to the CoinPay API is authenticated the same way, whether you're using an official SDK or calling the API directly.

Authenticating Requests

Send your API key as-is in the Authorization header. Do not prefix it with "Bearer" - CoinPay's API expects the raw key value.

Treat your API key like a password: keep it server-side, never expose it in client-side code, and never commit it to version control.

Verifying Webhooks

CoinPay supports two ways to verify that a webhook request genuinely came from CoinPay. New integrations should use the HMAC scheme; the legacy scheme remains supported for backward compatibility.

HMAC-SHA256 (recommended)

Every webhook request carries three headers used to verify its authenticity and freshness:

HeaderDescription
X-Coinpay-SignatureThe signature, in the form v1=<hex digest>.
X-Coinpay-TimestampUnix timestamp (seconds) of when the request was signed.
X-Coinpay-DeliveryA unique identifier for this specific delivery attempt. Use it to detect and ignore duplicate deliveries.

Recompute the signature over the exact raw request body - before any JSON parsing - using this formula:

The signature covers the raw, unmodified request body bytes. If your framework parses JSON before your code runs, read the raw body first (most HTTP frameworks expose this separately from the parsed body) and verify against that, not a re-serialized version of the parsed object - re-encoding can change whitespace or key order and will break the signature check.

Reject any request whose X-Coinpay-Timestamp is more than 300 seconds away from the current time, in either direction. This limits how long a captured request could be replayed.

Static Secret (legacy)

Older integrations may still receive webhooks signed with a static shared secret instead of the HMAC scheme above.

This method does not verify the payload itself or protect against replay - it only confirms the sender knows the shared secret. Prefer the HMAC scheme for any new integration.