Capay Global

Authentication

The API authenticates with secret bearer keys. Sandbox keys start with sk_test_, production keys with sk_live_. The environment is derived from the key alone: there is no environment switch anywhere else.

Get your test keys

Create a free account, then open Developers > API keys in the dashboard. Sandbox keys start with sk_test_ and are shown once, so store the key the moment it appears. Sandbox keys never touch a real provider and never move real money.

Bearer keys

Send your secret key in the Authorization header on every request. Keys are shown once at creation and never re-displayed; store them in your secret manager, never in client-side code or repositories.

header
Authorization: Bearer sk_test_XXXXXXXXXXXX   # sandbox
Authorization: Bearer sk_live_XXXXXXXXXXXX   # production
curl
curl https://api.capayglobal.com/v1/whoami \
  -H "Authorization: Bearer sk_test_XXXXXXXXXXXX"

Authentication failures

A missing, malformed, revoked, expired or wrong-environment key always returns the same opaque 401 invalid_api_key error. The specific reason stays in our logs on purpose.

401 response
{
  "status": "error",
  "error": {
    "type": "authentication_error",
    "code": "invalid_api_key",
    "message": "The API key is missing, malformed, revoked, expired, or not valid for this environment.",
    "next_step": "Send your secret key as \"Authorization: Bearer sk_test_...\" (sandbox) or \"Bearer sk_live_...\" (production). Manage keys in the dashboard under Developers > API keys.",
    "request_id": "7e1f0a92-6c53-4b8d-a041-2f9c8e7d6b5a"
  }
}

Webhook signatures

Events we deliver to your webhook endpoints are signed with the X-Capay-Signature header: a unix timestamp t and one or more v1 signatures, each a hex HMAC-SHA256 of "{t}.{raw_body}" using your endpoint secret. During secret rotation two v1 entries are present; accept the header if any matches. Reject deliveries older than 300 seconds and always compare in constant time against the RAW request body, before any JSON parsing.

delivery headers
X-Capay-Signature: t=1767225600,v1=6f8b4c2d9e0a1b3c5d7e9f0a2b4c6d8e0f1a3b5c7d9e1f0a2b4c6d8e0f1a3b5c
X-Capay-Event-Id: evt_2n4Kq8Zr41Xa
X-Capay-Event-Type: collection.completed

Verifying a signature

curl
# T is the t= value from X-Capay-Signature; delivery.json is the raw body as received.
T=1767225600
SECRET=whsec_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

{ printf '%s.' "$T"; cat delivery.json; } \
  | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1
# Compare the printed hex with each v1= value in the header (either may match
# during a secret rotation), and reject the delivery if |now - T| > 300 s.

Registering endpoints, the retry schedule, replay and how to write an idempotent receiver are covered in the webhooks guide: Webhooks