# Onramper Webhooks

Onramper (the fiat-to-crypto on-ramp aggregator) sends a webhook each time a transaction's status changes. Every delivery is the transaction's current state as one JSON object, signed with HMAC-SHA256 in the `X-Onramper-Webhook-Signature` header.

> Onramper turns on webhooks through your Customer Success Manager. Send them the Hookdeck URL and paste the secret key they issue.

## When to Use This Skill

* How do I receive Onramper webhooks?
* How do I verify the `X-Onramper-Webhook-Signature` header?
* How do I handle `completed`, `paid`, `pending`, `new`, `failed` and `canceled` transactions?
* Why is my Onramper webhook signature verification failing?
* How do I tell a `buy` from an `offramp` transaction in the payload?

## Verification (core)

* Header: `X-Onramper-Webhook-Signature`, the bare lowercase hex digest (no prefix). Compare the whole value.
* Algorithm: HMAC-SHA256 with the secret Onramper issues, keyed with the secret's UTF-8 bytes as-is, over the **raw request body bytes**. Nothing else is signed (no timestamp or id), so there is no replay window to check.

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

function verifyOnramperSignature(rawBody, signature, secret) {
  if (!secret || typeof signature !== 'string') return false;
  if (!/^[0-9a-fA-F]{64}$/.test(signature)) return false; // validate before decoding
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest(); // rawBody: Buffer
  const received = Buffer.from(signature.toLowerCase(), 'hex'); // 32 bytes, safe to decode now
  return crypto.timingSafeEqual(received, expected);
}

```

Read the body as raw bytes (Express: `express.raw({ type: '*/*' })`; Next.js: `await request.arrayBuffer()`; FastAPI: `await request.body()`) and parse JSON only after the check. Return `401` for a missing, malformed or mismatched signature. Onramper's own sample compares with `===`; use a constant-time compare instead.

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

## Event Types

Onramper sends no event name and no header carries a type. Dispatch on the body's `status` field. Its six documented values (Onramper's "Webhook Status" table) are `new`, `pending`, `paid`, `completed`, `failed` and `canceled`.

`transactionType` (`buy` or `offramp`) gives the direction. Handle the statuses you need and log the rest. Other fields include `transactionId`, `onrampTransactionId`, `onramp`, `sourceCurrency`, `targetCurrency`, `inAmount`, `outAmount`, `paymentMethod`, `walletAddress`, `transactionHash`, `partnerContext` and `statusDate`.

## Environment Variables

```bash
ONRAMPER_WEBHOOK_SECRET=your_onramper_webhook_secret  # issued by Onramper at registration

```

## Local Development

```bash
npx hookdeck-cli listen 3000 onramper --path /webhooks/onramper

```

No account required. Give the printed URL to your Onramper Customer Success Manager.

## Resources

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/onramper-webhooks/references/overview.md) - How the webhooks work, statuses, payload
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/onramper-webhooks/references/setup.md) - Getting webhooks enabled and the secret issued
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/onramper-webhooks/references/verification.md) - Signature details, Node and Python verifiers, debugging
* [examples/](https://github.com/hookdeck/webhook-skills/tree/main/skills/onramper-webhooks/examples/) - Runnable Express, Next.js and FastAPI handlers with tests