# Ramp Webhooks

## When to Use This Skill

* How do I receive Ramp webhooks?
* How do I verify the Ramp `X-Ramp-Signature` header?
* How do I complete the Ramp `webhooks.verification` challenge and activate a subscription?
* How do I handle `transactions.authorized` or `transactions.cleared` events?
* Why is my Ramp webhook signature verification failing?

## Verification (core)

Ramp sends `X-Ramp-Signature`: the **hex-encoded HMAC-SHA256 of the raw request body**, keyed with the `secret` returned when you create the subscription (use the string as-is; do not base64-decode it). There is no timestamp or prefix. Verify against the **raw body**, before parsing JSON.

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

function verifyRampSignature(rawBody, signature, secret) {
  if (!signature || !secret) return false;
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  try {
    return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
  } catch {
    return false; // length mismatch
  }
}

```

Ramp has no webhook verification SDK; the manual check above is the supported approach in every framework.

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

## Activating a Subscription (verification challenge)

New subscriptions start as `pending_verification` and receive no business events. Ramp POSTs a signed `{"type": "webhooks.verification", "challenge": ...}` to your URL. Return a 2xx, then submit the challenge to `POST /developer/v1/webhooks/{id}/verify` to activate. See [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/ramp-webhooks/references/setup.md).

## Common Event Types

| Event | Description |
| --- | --- |
| `webhooks.verification` | One-time challenge sent when a subscription is created |
| `transactions.authorized` | Card transaction authorized |
| `transactions.cleared` | Transaction settled (also fires for refunds and reversals) |
| `transactions.declined` | Card transaction declined |
| `reimbursements.ready_for_review` | Reimbursement needs review |
| `bills.created` | Bill created |
| `bills.approved` | Bill approved |
| `bills.paid` | Bill payment completed |
| `tests.test_event` | Mock event from `POST /developer/v1/webhooks/mock-webhook-event` |

Payloads contain `id`, `type`, `created_at`, `business_id`, and `object` (resource ID only); fetch the resource from the Ramp API for details.

> **For the full event list**, see [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/ramp-webhooks/references/overview.md) and [Ramp's webhook guide](https://docs.ramp.com/developer-api/v1/guides/webhooks).

## Important Headers

| Header | Description |
| --- | --- |
| `X-Ramp-Signature` | Hex HMAC-SHA256 of the raw body |
| `X-Ramp-Webhook-ID` | Event ID (same as payload `id`, constant across retries); not on verification requests |

## Environment Variables

```bash
RAMP_WEBHOOK_SECRET=your_subscription_secret   # "secret" from POST /developer/v1/webhooks

```

## Local Development

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

```

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/ramp-webhooks/references/overview.md) - Ramp webhook events, payload structure, retries
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/ramp-webhooks/references/setup.md) - Create a subscription and complete the challenge
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/ramp-webhooks/references/verification.md) - Signature details and debugging