# Helcim Webhooks

[Helcim](https://www.helcim.com) is a Canadian and US card payment processor. When a payment is processed it POSTs a signed JSON event to the one HTTPS Deliver URL you set under **All Tools > Integrations > Webhooks**. Signatures follow Standard Webhooks, so each delivery carries `webhook-id`, `webhook-timestamp` and `webhook-signature` headers.

## When to Use This Skill

* How do I receive Helcim webhooks?
* How do I verify a Helcim webhook signature (`webhook-signature`) with the `verifierToken`?
* Where do I find my Helcim `verifierToken`?
* How do I handle `cardTransaction` and `terminalCancel` events?
* How do I get the amount and card details when `cardTransaction` only has an `id`?
* Why is my Helcim webhook verification failing, or failing only on retries and replays?
* How do I receive Helcim webhooks through Hookdeck?

## Verification (core)

| Header | Value |
| --- | --- |
| `webhook-id` | Identifies the message; retries reuse it. Your idempotency key (the body has no event id) |
| `webhook-timestamp` | Unix seconds |
| `webhook-signature` | One or more space-separated `v1,<base64>` entries |

Signed content is `{webhook-id}.{webhook-timestamp}.{raw body}`, HMAC-SHA256, base64 output. The key is the base64-**decoded** `verifierToken`. Helcim shows it as bare base64 with no `whsec_` prefix; the `standardwebhooks` library accepts it as shown, with or without a `whsec_` prefix. Compare against every `v1,` entry; ignore other versions. If `webhook-id` is absent, read `svix-id`, `svix-timestamp` and `svix-signature` instead, as Hookdeck does. The examples use the `standardwebhooks` library (npm and PyPI):

```javascript
const { Webhook } = require('standardwebhooks');

// rawBody: exact request body (Buffer or string), NOT re-serialised JSON
function verifyHelcim(rawBody, headers, secret = process.env.HELCIM_WEBHOOK_SECRET) {
  const p = headers['webhook-id'] ? 'webhook' : 'svix'; // fall back to the svix-* names
  new Webhook(secret).verify(rawBody, {
    'webhook-id': headers[`${p}-id`],
    'webhook-timestamp': headers[`${p}-timestamp`],
    'webhook-signature': headers[`${p}-signature`],
  }, { jsonParse: false }); // throws on a bad signature or stale timestamp → respond 401
}

```

The library always enforces a fixed **5-minute timestamp window**, but Helcim documents none. Hookdeck retries and replays resend the original `webhook-timestamp`, so the library rejects them once they are more than 5 minutes old. Helcim's own retries also wait up to 10 hours. If you need late retries to pass, use the manual verifier in [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/helcim-webhooks/references/verification.md), which can drop or widen the window. In Express, read the body with `express.raw({ type: '*/*' })` so any content type verifies, and parse JSON only after the check.

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

## Hookdeck: Paste the verifierToken With a `whsec_` Prefix

Hookdeck base64-decodes only secrets that start with `whsec_`, and Helcim's `verifierToken` has no prefix. When you add the Helcim source in Hookdeck, paste the token as `whsec_` followed by the token. Hookdeck's configuration alert for this source reads exactly:

> Paste your verifierToken as whsec_ followed by the token, or verification fails.

Your own handler needs no prefix: set `HELCIM_WEBHOOK_SECRET` to the value exactly as Helcim shows it.

## Event Type and Payload

The event name is the top-level `type`. Helcim documents two events, and both bodies are small:

```json
{ "id": "25764674", "type": "cardTransaction" }

```

```json
{
  "data": {
    "cancelledAt": "2026-01-14T18:38:18Z",
    "currency": "CAD",
    "customerCode": "",
    "deviceCode": "NBL7",
    "invoiceNumber": "",
    "transactionAmount": 1
  },
  "type": "terminalCancel"
}

```

`cardTransaction` is a thin notification: `id` is the card transaction ID, and the amount, card type and status come from [Get Card Transaction by ID](https://devdocs.helcim.com/reference/getcardtransaction).

## Common Event Types

| Event | Fired when |
| --- | --- |
| `cardTransaction` | A card transaction occurs on the merchant account (approved or declined) |
| `terminalCancel` | A payment is cancelled on a payment device in API mode before processing |

Helcim documents only these two (the list may not be exhaustive): ignore types you don't recognise. See [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/helcim-webhooks/references/overview.md).

## Delivery Semantics

Return a `2xx` (200-299), then process asynchronously. Anything else is retried until a success code: immediately, then after 5 s, 5 min, 30 min, 2 h, 5 h, 10 h and 10 h, each period starting when the previous attempt fails. Deduplicate on `webhook-id`. Return `400` for missing headers and `401` for a bad signature. An event is only sent if its toggle is on at **Integrations > Webhooks**.

## Environment Variables

```bash
HELCIM_WEBHOOK_SECRET=your_verifier_token_here   # verifierToken exactly as Helcim shows it; handlers fail closed (500) without it

```

## Local Development

```bash
# Express / Next.js (use 8000 for FastAPI). No install or account required.
npx hookdeck-cli listen 3000 helcim --path /webhooks/helcim

```

Set the printed URL as the Deliver URL. Helcim requires `https` and rejects a URL containing "Helcim", so use a tunnel or Hookdeck URL that doesn't.

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/helcim-webhooks/references/overview.md) - Events, payloads and retry schedule
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/helcim-webhooks/references/setup.md) - Enable webhooks, find the verifierToken, Hookdeck setup
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/helcim-webhooks/references/verification.md) - Library and manual verification, gotchas, debugging