Agent skill

Cin7 Core Webhooks Skill

Receive and authenticate Cin7 Core (formerly DEAR Inventory) webhooks. Use when setting up a Cin7 Core webhook endpoint, checking the Bearer or custom header, debugging 401s, or handling events like Sale/Created, Sale/OrderAuthorised, Purchase/OrderAuthorised and Product/Updated.

Install this skill

npx skills add hookdeck/webhook-skills --skill 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.

Verification (core)

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/, examples/nextjs/, examples/fastapi/.

Event Types

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

EventTypeSent When
Sale/CreatedA sale is created
Sale/OrderAuthorisedA sale order is authorised
Sale/ShipmentAuthorisedA sale shipment is authorised
Purchase/OrderAuthorisedA purchase order is authorised
Product/UpdatedA product is updated

This is not the full list; see the Cin7 Core API docs 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

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):

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

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

Resources


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 6, 2026

View on GitHub →