# CryptoProcessing Webhooks

## When to Use This Skill

* How do I receive CryptoProcessing (CoinsPaid) webhook callbacks?
* How do I verify the CryptoProcessing `X-Processing-Signature` header?
* Why is my CryptoProcessing webhook signature verification failing?
* How do I handle `deposit`, `withdrawal`, `exchange` or `payment_request` callbacks?
* Which secret signs CryptoProcessing callbacks, and what happens when I rotate it?

## Verification (core)

CryptoProcessing sends every callback to the single callback URL set in the merchant portal. Each delivery carries:

* `X-Processing-Signature` — **lowercase hex HMAC-SHA512** (128 characters, no prefix) of the **raw request body**
* `X-Processing-Key` — the Master API key (public part) that signed the callback

The key is the **secret key of your active Master API key**, used as UTF-8 bytes. Nothing but the body is signed: no timestamp, id or other header, so there is no replay window to check.

> Paste the secret key of your active Master API key. Rotating that key changes the callback secret.

Only the active Master key signs callbacks; custom keys never do. After you activate a different Master key, verify with that key's secret.

```javascript
const crypto = require('crypto');

function verifyCryptoProcessingSignature(rawBody, signature, secret) {
  if (!signature || !secret) return false;
  // Validate length and alphabet first: Buffer.from(hex) silently drops bad characters
  if (!/^[0-9a-fA-F]{128}$/.test(signature)) return false;
  const expected = crypto.createHmac('sha512', secret).update(rawBody).digest();
  return crypto.timingSafeEqual(Buffer.from(signature.toLowerCase(), 'hex'), expected);
}

```

`rawBody` must be the exact bytes received (a `Buffer`), never re-serialized JSON. In Express use `express.raw({ type: '*/*' })`; in Next.js `await request.arrayBuffer()`; in FastAPI `await request.body()`. Answer a missing, malformed or wrong signature with `401`.

Docs test vector: secret `AbCdEfG123456`, body `{"currency":"BTC","foreign_id":"123456"}` gives `03c25fcf7cd35e7d995e402cd5d51edd72d48e1471e865907967809a0c189ba55b90815f20e2bb10f82c7a9e9d865546fda58989c2ae9e8e2ff7bc29195fa1ec`.

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/cryptoprocessing-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/cryptoprocessing-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/cryptoprocessing-webhooks/examples/fastapi/).

## Common Event Types

There is no event header. Dispatch on the body's top-level `type` (the kind of transaction); `status` is its new state (`confirmed`, `not_confirmed`, `cancelled`, `pending`, `paid`, `expired`, `failed`, … depending on the kind). A callback fires on every status change, so a router needs both.

| `type` | Meaning |
| --- | --- |
| `deposit` | Crypto received on one of your deposit addresses |
| `withdrawal` | Crypto sent out to an external address |
| `exchange` | Conversion between two currencies |
| `payment_request` | Payment request (hosted payment page) progress |
| `deposit_exchange` | Deposit that is converted on arrival |
| `deposit_wallet_client` | Deposit to a wallet client address |
| `withdrawal_exchange` | Withdrawal funded by a conversion |
| `withdrawal_instant` | Instant withdrawal |
| `withdrawal_instant_exchange` | Instant withdrawal funded by a conversion |
| `invoice` | Legacy invoice |

Handle the ones you use and log the rest. See [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/cryptoprocessing-webhooks/references/overview.md).

## Delivery

Only an HTTP `200` stops delivery. Otherwise CryptoProcessing retries 12 times after the first attempt (+1, +5, +15, +30, +60, +90, +120, +180 minutes, then +240 minutes four times); the schedule is not configurable. Answer a duplicate of a callback you already processed with `201` to stop its retries.

## Environment Variables

```bash
CRYPTOPROCESSING_WEBHOOK_SECRET=your-master-api-key-secret   # secret key of the ACTIVE Master API key

```

## Local Development

```bash
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 cryptoprocessing --path /webhooks/cryptoprocessing

```

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/cryptoprocessing-webhooks/references/overview.md) - Callback kinds, event types, payload shape, delivery and retries
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/cryptoprocessing-webhooks/references/setup.md) - Set the callback URL and find the Master key secret
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/cryptoprocessing-webhooks/references/verification.md) - X-Processing-Signature details, gotchas, debugging