Registry API v1

Webhooks

Instead of polling, receive signed HTTP callbacks when registry records change. Endpoints are configured per organisation: send us the receiving URL and the events you want, and you receive a signing secret once.

Events

EventMeaning
artwork.createdA new artwork entered the public registry projection.
artwork.updatedPublic fields of an artwork changed.
artwork.soldAn artwork was marked sold.
artwork.removedAn artwork left the public projection (withdrawn, made private or removed).
artwork.ownership_changedThe artwork moved to another collection profile.
certificate.createdA certificate of registration was issued.
gallery.updatedA gallery or institutional profile changed.

Payloads carry only public identifiers and metadata — collect24_id, resource type and id, the organisation slug and the names of changed fields. Never owner ids, private notes, non-public prices or key material.

Delivery

Writes only enqueue an event, so a slow receiver never slows a Collect24 write. A worker then delivers pending events. Respond 2xx quickly — do the work asynchronously on your side.

Request headers
POST /your/endpoint HTTP/1.1
content-type: application/json
x-collect24-event: artwork.created
x-collect24-timestamp: 1770000000
x-collect24-delivery: 8f2b1c4e-…      # stable across retries
x-collect24-attempt: 1
x-collect24-signature: sha256=<hmac>
Example payload
{
  "object": "event",
  "id": "8f2b1c4e-6d21-4f0e-9f8a-1c2b3d4e5f60",
  "type": "artwork.updated",
  "created_at": "2026-08-14T07:41:02.005Z",
  "organization": { "slug": "gallery-example" },
  "resource": { "type": "artwork", "id": "6f1b1f24-…", "collect24_id": "C24-4711" },
  "changed_fields": ["availability", "price"]
}

Verifying the signature

The signature is sha256= plus an HMAC-SHA256 over timestamp + "." + rawBody, keyed with your signing secret. Compute it over the raw body — before any JSON parsing or reserialisation — compare in constant time, and reject timestamps more than a few minutes old to prevent replay.

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

export function verifyCollect24Signature(rawBody, headers, signingSecret) {
  const signature = headers["x-collect24-signature"] ?? "";
  const timestamp = headers["x-collect24-timestamp"] ?? "";

  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;

  const expected =
    "sha256=" + createHmac("sha256", signingSecret).update(`${timestamp}.${rawBody}`).digest("hex");

  const a = Buffer.from(signature);
  const b = Buffer.from(expected);
  return a.length === b.length && timingSafeEqual(a, b);
}
Python
import hmac, hashlib, time

def verify_collect24_signature(raw_body: bytes, headers, signing_secret: str) -> bool:
    signature = headers.get("x-collect24-signature", "")
    timestamp = headers.get("x-collect24-timestamp", "")
    try:
        if abs(time.time() - float(timestamp)) > 300:
            return False
    except ValueError:
        return False

    payload = f"{timestamp}.".encode() + raw_body
    expected = "sha256=" + hmac.new(signing_secret.encode(), payload, hashlib.sha256).hexdigest()
    return hmac.compare_digest(signature, expected)

Retries and idempotent receivers

Failed deliveries are retried with exponential backoff, up to six attempts. Retries reuse the same x-collect24-delivery id, so deduplicate on that value and make your handler idempotent — the same event may arrive more than once. x-collect24-attempt tells you which attempt you are seeing.

An endpoint that keeps failing is paused automatically after ten consecutive failures. Collect24 then resumes it manually, which resets the counter — so monitor your receiver and let us know once it is healthy again.

Rotating the signing secret

A rotation issues a new whsec_… secret, shows it once and applies it to the next delivery; the previous secret stops validating immediately. Plan a rotation together with us in a short maintenance window, or make your receiver accept two secrets during the switch.

Checklist

DoWhy
Verify the signature on every requestThe URL alone is not a credential.
Use the raw request bodyReserialised JSON produces a different HMAC.
Reject old timestampsPrevents replay of a captured delivery.
Deduplicate on x-collect24-deliveryRetries repeat the same event id.
Return 2xx fast, then processSlow responses count as failures and trigger retries.
Serve HTTPSDeliveries are only sent to HTTPS endpoints.