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
| Event | Meaning |
|---|---|
artwork.created | A new artwork entered the public registry projection. |
artwork.updated | Public fields of an artwork changed. |
artwork.sold | An artwork was marked sold. |
artwork.removed | An artwork left the public projection (withdrawn, made private or removed). |
artwork.ownership_changed | The artwork moved to another collection profile. |
certificate.created | A certificate of registration was issued. |
gallery.updated | A 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.
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>{
"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.
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);
}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
| Do | Why |
|---|---|
| Verify the signature on every request | The URL alone is not a credential. |
| Use the raw request body | Reserialised JSON produces a different HMAC. |
| Reject old timestamps | Prevents replay of a captured delivery. |
Deduplicate on x-collect24-delivery | Retries repeat the same event id. |
| Return 2xx fast, then process | Slow responses count as failures and trigger retries. |
| Serve HTTPS | Deliveries are only sent to HTTPS endpoints. |

