# Cin7 Core Webhooks

Cin7 Core (formerly DEAR Inventory) sends a JSON webhook when something changes in your account: a sale is created or authorised, a purchase is created, a product is updated. Webhooks need the **Automation** module. Requests are **not signed**. Instead, you give the webhook a secret header when you create it, and your endpoint checks that header on every request.

## When to Use This Skill

* How do I receive Cin7 Core webhooks?
* How do I authenticate Cin7 Core (DEAR Inventory) webhook requests?
* Does Cin7 Core sign webhook payloads? (No: use a Bearer or custom header)
* How do I handle `Sale/Created`, `Sale/OrderAuthorised` or `Purchase/OrderAuthorised` events?
* Why does my Cin7 Core webhook endpoint return 401?

## Setup note

Use a custom header (ExternalHeaders) or Bearer auth on the Cin7 Core webhook, and paste the exact header name and whole value, e.g. "authorization" and "Bearer <token>".

Basic auth (`basicauth`) is not covered by Hookdeck's Cin7 Core source, so use `bearerauth` or `ExternalHeaders`. See [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/cin7-core-webhooks/references/setup.md).

## Verification (core)

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

// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
  const ha = crypto.createHash('sha256').update(String(a)).digest();
  const hb = crypto.createHash('sha256').update(String(b)).digest();
  return crypto.timingSafeEqual(ha, hb);
}

function verifyCin7CoreRequest(headers, {
  name = process.env.CIN7_CORE_WEBHOOK_HEADER || 'Authorization',
  secret = process.env.CIN7_CORE_WEBHOOK_SECRET,
} = {}) {
  if (!secret) return false;                      // never accept when unconfigured
  const received = headers[name.toLowerCase()];   // Node lowercases header names
  return Boolean(received) && safeEqual(received, secret); // WHOLE value, no 'Bearer ' parsing
}

```

* The header name is whatever you configured (`Authorization` for `bearerauth`, or your own name in `ExternalHeaders`). Look it up case-insensitively.
* Compare the **whole** value exactly as typed, including any `Bearer ` prefix. Do not strip or parse it.
* Return `401` for a missing header or mismatch, `400` for a body that isn't a JSON object, `200` once handled.
* Read the body raw (`express.raw({ type: '*/*' })`) and authenticate before parsing it.

> **Security note:** a static shared credential authenticates the *sender*, not the *body*, and gives no replay protection (no signature, no timestamp). Use HTTPS only, and rotate the credential if it leaks.

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

## Event Types

The payload has an `EventType` field, one per webhook `Type` you register.

| EventType | Sent When |
| --- | --- |
| `Sale/Created` | A sale is created |
| `Sale/OrderAuthorised` | A sale order is authorised |
| `Sale/ShipmentAuthorised` | A sale shipment is authorised |
| `Purchase/OrderAuthorised` | A purchase order is authorised |
| `Product/Updated` | A product is updated |

This is not the full list; see the [Cin7 Core API docs](https://dearinventory.docs.apiary.io/) for every webhook `Type`. Unknown events should be acknowledged with `200`.

## Delivery

Cin7 Core makes 6 attempts. After that the webhook is deactivated, so keep returning `200` quickly and re-enable it in Cin7 Core if it was switched off.

## Environment Variables

```bash
CIN7_CORE_WEBHOOK_HEADER=Authorization            # header name you configured (default Authorization)
CIN7_CORE_WEBHOOK_SECRET="Bearer your-token"      # exact, whole header value

```

## Local Development

Cin7 Core can't reach `localhost`. Tunnel with the Hookdeck CLI (no account required):

```bash
npx hookdeck-cli listen 3000 cin7-core --path /webhooks/cin7-core

```

Use the printed URL as the webhook URL in Cin7 Core.

## Resources

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/cin7-core-webhooks/references/overview.md) - What Cin7 Core webhooks are, events and payload shape
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/cin7-core-webhooks/references/setup.md) - Create the webhook with Bearer or a custom header
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/cin7-core-webhooks/references/verification.md) - Header check details, gotchas, debugging 401s
* [examples/](https://github.com/hookdeck/webhook-skills/tree/main/skills/cin7-core-webhooks/examples/) - Runnable Express, Next.js and FastAPI handlers with tests