Cairn Developer API and webhooks

Last updated 2 November 2026

Every Cairn plan includes a REST API and signed webhooks. Use them to show points in a headless storefront, answer "how many points do I have?" inside a help desk, post points from your own systems, or start automations in Zapier, Make, n8n or your own code. Create the API token and the webhook endpoint in the app under Integrations.

Authentication

Send the token as a bearer token. It identifies your shop, so no other header is needed. Keep it on a server: the API sends no CORS headers because the token must never reach a browser. Creating a new token replaces the old one immediately.

curl https://cairn.keelcroft.com/api/v1/members?email=ada@example.com \
  -H "Authorization: Bearer cairn_..."

Responses are JSON. Errors look like {"error":{"code":"member_not_found","message":"..."}}. Limit: 300 requests a minute per store.

Endpoints

Customers join the program when they create an account or order, exactly as in the Shopify admin. The API reads and changes existing members; it does not create them.

Webhooks

Cairn sends an HTTPS POST for each event below. Failed deliveries are retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours; after that you can retry from the app. Respond with any 2xx status within 8 seconds. Redirects are not followed.

{
  "id": "evt_6f1c...",
  "type": "points.earned",
  "created_at": "2026-11-02T09:30:00.000Z",
  "shop": "your-store.myshopify.com",
  "data": {
    "customer": { "id": 123456, "gid": "gid://shopify/Customer/123456",
                  "email": "ada@example.com", "first_name": "Ada",
                  "accepts_marketing": true },
    "points": 120, "balance": 620, "points_name": "stars",
    "tier": "Silver", "referral_link": "https://your-store.com/?ref=ADA1"
  }
}

An event can arrive more than once; use id to ignore repeats. Order is not guaranteed.

Verifying the signature

Each request carries Cairn-Signature: t=<unix seconds>,v1=<hex>. The value is HMAC-SHA256 of t + "." + raw body using your webhook secret (the whsec_ value shown once when you save the endpoint). Reject requests whose timestamp is more than five minutes old.

import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const expected = createHmac("sha256", secret)
    .update(parts.t + "." + rawBody)
    .digest("hex");
  const ok = timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
  return ok && Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
}

Headers also include Cairn-Event (the type) and Cairn-Delivery (the event id).

Help desks and automation tools

Tools that call a URL with a header (Gorgias HTTP integrations, Zapier "Webhooks", Make HTTP, n8n) work with the member endpoint and the token above. Tools that receive a URL (Zapier "Catch Hook", Make webhooks, n8n) can take the webhook endpoint; they do not need to check the signature, but anyone who learns the URL could post to it, so prefer tools that let you verify it.