Skip to content

Language

Documentation menu

REST API

Manage domains, scans and consent records over HTTPS, export proof of consent and receive signed webhooks. Authentication, pagination, errors and limits.

Last updated October 29, 2026

The REST API lets you automate everything you can do in the dashboard: add domains, start scans, search and export consent records, and receive webhooks when something changes. It is available on Pro and Enterprise plans.

Authentication

Create an API key under Settings → API keys. Keys are scoped to one workspace and are either read-only or read-write. Send the key as a bearer token:

curl https://api.cookiesh.eu/v1/domains \
  -H "Authorization: Bearer ck_sk_live_..."

Secret keys start with ck_sk_. Never use them in browser code. The script tag only needs your public site ID.

All requests use HTTPS and JSON. The base URL is https://api.cookiesh.eu/v1. Timestamps are ISO 8601 strings in UTC.

Endpoints

Method Path Description
GET /domains List domains in the workspace
POST /domains Add a domain
GET /domains/{id} Get a domain and its settings
DELETE /domains/{id} Remove a domain
POST /domains/{id}/scans Start a cookie scan
GET /domains/{id}/scans/{scan_id} Get a scan’s status and results
GET /domains/{id}/cookies List classified cookies and trackers
GET /consents Search consent records
GET /consents/{consent_id} Get one consent record with its hash-chain proof
POST /exports Start a CSV or JSON export of consent records
GET /exports/{id} Get an export’s status and download URL
DELETE /subjects/{user_id} Delete all consent records linked to a user ID

Example: find the consent records for one visitor.

curl "https://api.cookiesh.eu/v1/consents?domain_id=dom_29xk&consent_id=cns_8Hq2Lw" \
  -H "Authorization: Bearer ck_sk_live_..."
{
  "data": [
    {
      "id": "cns_8Hq2Lw",
      "domain_id": "dom_29xk",
      "created_at": "2026-10-12T09:41:07Z",
      "region": "DE",
      "method": "banner",
      "banner_version": "v14",
      "categories": { "necessary": true, "preferences": false, "analytics": true, "marketing": false },
      "hash": "9f2c…e71a"
    }
  ],
  "next_cursor": null
}

Pagination

List endpoints return up to 100 items. Pass limit to ask for fewer. If more results exist, the response includes a next_cursor. Pass it back as cursor to get the next page. Cursors stay valid for 24 hours.

Errors

Errors use standard HTTP status codes and a consistent body:

{
  "error": {
    "type": "invalid_request",
    "message": "domain_id is required",
    "request_id": "req_4Kd92"
  }
}
Status Meaning
400 The request is invalid. The message explains why.
401 The API key is missing or invalid.
403 The key is read-only, or your plan does not include this feature.
404 The resource does not exist in this workspace.
429 You hit the rate limit. Retry after the number of seconds in Retry-After.
5xx Something went wrong on our side. Retry with exponential backoff.

Include the request_id when you contact support.

Rate limits

Pro workspaces can make 600 requests per minute and Enterprise workspaces 3,000. Every response includes RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset headers. Exports do not count against the limit while they run.

Webhooks

Add an endpoint under Settings → Webhooks and choose the events to receive:

Event Sent when
consent.created A visitor saves a new choice
consent.updated A visitor changes an earlier choice
scan.completed A cookie scan finishes
scan.new_items A scan finds cookies or trackers that need review
usage.threshold The workspace reaches 80% or 100% of its monthly consent logs

Each delivery is a POST with a JSON body and a Cookiesh-Signature header. The header holds a timestamp and an HMAC-SHA256 signature of timestamp.body, made with your endpoint’s signing secret. Verify it before trusting the payload:

import crypto from 'node:crypto';

export function verify(rawBody: string, header: string, secret: string): boolean {
  const { t, v1 } = Object.fromEntries(header.split(',').map((part) => part.split('=')));
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // reject old deliveries
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}

Respond with a 2xx status within 10 seconds. Failed deliveries are retried with exponential backoff for up to 24 hours. Events can arrive more than once, so use the event id to ignore duplicates.