Documentation · API

ConsentLab public API

Automate your compliance: consent statistics, exportable register, banner configuration and scans, over key-authenticated REST/JSON.

1. 5-minute quickstart

The ConsentLab public API (/v1) exposes your consent statistics, your register, the banner configuration and scan triggering — all in REST/JSON, authenticated by key. It is included on the Business (site ≥ 500,000 sessions) and Agency plans.

The full interactive reference lives at /v1/docs ; the raw OpenAPI schema at /v1/openapi.json. This page is a hand-written guide: if you are unsure about an exact field, those two resources are authoritative.

Create an API key

From your dashboard → Settings → API, create a key by choosing the scopes (permissions) you need and the Site(s) it may reach. The key, in the format cl_live_…, is shown only once: copy it immediately into a secrets manager.

First authenticated call

Every request carries the Authorization: Bearer cl_live_…header. Let's list your sites:

bash
# Authenticate every request with the Authorization: Bearer header
curl "https://api.consentlab.eu/v1/sites" \
  -H "Authorization: Bearer $CONSENTLAB_API_KEY"

Response:

json
[
  { "id": "3f2a9c7e-1b4d-4e8a-9f2c-7a1b2c3d4e5f", "name": "mysite.com", "created_at": "2026-01-15T09:24:00Z" }
]

The site id (e.g. 3f2a9c7e-1b4d-4e8a-9f2c-7a1b2c3d4e5f) is the parameter for every other endpoint. A staging environment is available for your tests:

bash
# Production
https://api.consentlab.eu/v1
# Staging (pré-production)
https://dev.api.consentlab.eu/v1

2. Recipes by use case

Agency reporting

For a client dashboard, combine the aggregates (consent-stats) — acceptance rate, volumes — with the detail when needed (consents):

bash
# Consent aggregates for a site (acceptance rate, volumes…)
curl "https://api.consentlab.eu/v1/sites/3f2a9c7e-1b4d-4e8a-9f2c-7a1b2c3d4e5f/consent-stats" \
  -H "Authorization: Bearer $CONSENTLAB_API_KEY"   # scope: read:stats

Archiving the register

To keep the consent proof in your own warehouse, paginate consents by cursor (limit ≤ 100) until it is exhausted:

bash
#!/usr/bin/env bash
# Exports the whole consent register as JSON Lines, page by page.
# The key lives in an environment variable — never hard-coded in the script.
SITE="3f2a9c7e-1b4d-4e8a-9f2c-7a1b2c3d4e5f"
cursor=""
while : ; do
  resp=$(curl -s "https://api.consentlab.eu/v1/sites/$SITE/consents?limit=100&cursor=$cursor" \
    -H "Authorization: Bearer $CONSENTLAB_API_KEY")   # scope: read:consents
  echo "$resp" | jq -c '.data[]' >> register.jsonl
  # Exact pagination field: see /v1/openapi.json
  cursor=$(echo "$resp" | jq -r '.next_cursor // empty')
  [ -z "$cursor" ] && break
done

Banner config in CI/CD

Version your banner configurations and apply them from your pipeline with PUT banner-config:

bash
# Apply the banner config from your pipeline (scope: write:config).
# The JSON body is illustrative — exact schema: /v1/openapi.json
curl -X PUT "https://api.consentlab.eu/v1/sites/3f2a9c7e-1b4d-4e8a-9f2c-7a1b2c3d4e5f/banner-config" \
  -H "Authorization: Bearer $CONSENTLAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "primary_color": "#0F766E", "position": "bottom" }'
Store the key in your CI's encrypted environment (a secret), never in the repo. A write:config key can read or delete nothing else: that is least privilege.

3. Endpoint reference

Six endpoints cover the whole v1 surface. Parameter and response-schema details are on the interactive docs.

MethodPathRequired scopeDescription
GET/v1/sitesList the sites the key can access.
GET/v1/sites/:id/consent-statsread:statsConsent aggregates (rates, volumes).
GET/v1/sites/:id/consentsread:consentsConsent register, cursor-paginated.
GET/v1/sites/:id/banner-configread:config / write:configCurrent banner configuration.
PUT/v1/sites/:id/banner-configwrite:configUpdate the banner configuration.
POST/v1/sites/:id/scanstrigger:scanTrigger a cookie scan for the site.

The scopes shown match the v1 contract; the interactive docs are authoritative if an endpoint evolves.

4. Limits & errors

Error codes

Errors follow standard HTTP statuses, with a stable machine code in the JSON body:

HTTPError codeCause
401INVALID_API_KEYMissing, malformed, revoked or expired key.
403INSUFFICIENT_SCOPEThe key lacks the scope required by the endpoint.
403API_ACCESS_REQUIREDThe plan does not include API access (Business ≥ 500k or Agency).
403IP_NOT_ALLOWEDCall made from an IP outside the key's configured allowlist.
404SITE_NOT_FOUNDSite does not exist or is outside the key scope.
404CONFIG_NOT_FOUNDNo banner configuration exists yet for this site.
429RATE_LIMIT_EXCEEDEDRate exceeded — see Retry-After.

Rate limiting

Every response carries the quota headers. The guaranteed floors are 300 requests/min/IP and 600 requests/min/key; X-RateLimit-Reset is expressed in seconds remaining. Counters are shared across all our instances, so the limits are exact and enforced against the real client IP, with no drift from load balancing.

http
HTTP/1.1 200 OK
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 598
X-RateLimit-Reset: 42          # seconds until the window resets
# On a 429, the Retry-After header tells you how long to wait.
On a 429 RATE_LIMIT_EXCEEDED, honour the Retry-After header before retrying (backoff). Do not loop without waiting: you would stay blocked.

Cursor pagination

Large collections (consents) are walked by cursor: ?cursor=&limit= with limit capped at 100. Pass the previous page's cursor until it comes back empty (see the archiving recipe above). The cursor is opaque: never build it by hand.

5. Versioning & deprecation

The API is versioned in the path (/v1/). Backwards-compatible changes (new fields, new endpoints) ship without a version bump. Any breaking change would go through a new version (/v2/).

Deprecation policy: 12 months. If a version or endpoint is deprecated, it keeps being served for at least 12 months after the announcement, giving you time to migrate calmly.

Changelog

  • v1 — Initial release: per-Site scoped API keys, endpoints sites, consent-stats, consents, banner-config, scans, cursor pagination and rate limiting.

6. Key security

  • Format & one-time display: a cl_live_… key is shown only at creation. We store only a fingerprint of it: it can never be shown again.
  • Least privilege: grant only the scopes you need among the five available (read:stats, read:consents, read:config, write:config, trigger:scan). A reporting job only needs read access; to read the banner config without being able to change it, read:config alone is enough.
  • Per-Site scoping: a key can only reach the Sites you attached it to. Create one key per integration rather than a single catch-all key.
  • Time-to-live (TTL): at creation, set an expiry (expiresInDays, from 1 to 3650 days). Past that point the key is automatically rejected (401 INVALID_API_KEY): ideal for temporary access (audit, contractor, POC).
  • Two-step rotation: create the new key, deploy it, then revoke the old one. A 24 h grace period lets your services switch over without downtime.
  • Immediate revocation: a revoked key stops working instantly (401 INVALID_API_KEY). Revoke at the slightest doubt.
  • Auto-revocation on leak: our key prefixes (cl_live_…) are registered with GitHub secret scanning. If a key is accidentally pushed to a public repository, we are alerted and the key is revoked automatically — a leaked key does not stay usable.
  • Never commit a key: environment variable or secrets manager, never in the repo nor in front-end code (keys are server-to-server).

Restrict a key to IPs

You can restrict a key to a list of source IP addresses or CIDR ranges (IPv4 and IPv6). Any call coming from an IP outside that allowlist is rejected with a 403 IP_NOT_ALLOWED, however valid the key is. Ideal for pinning a server integration to the fixed egress IP of your infrastructure or CI runner.

Ready to automate your compliance?

Create a key from your dashboard, or explore the interactive API reference.