API Reference
Technical reference for ConsentStack's edge function API endpoints.
ConsentStack exposes three edge-function endpoints that power the consent banner on your site. The SDK calls these automatically — you only need this reference if you are building a custom integration or debugging network requests.
Base URL: https://origins.consentstack.io
All endpoints return JSON and include CORS headers. Every request that references a site must include a valid Origin header matching a domain registered in your dashboard.
GET /functions/v1/config
Returns the published consent configuration for a site, resolved for the visitor's region and language.
GET https://origins.consentstack.io/functions/v1/config?site_key=YOUR_SITE_KEYQuery parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
site_key | string | Yes | Your public site key (found in the dashboard under Site Settings). |
region | string | No | Override automatic geo-detection with a specific region ID (e.g. gdpr, us_state_laws). |
lang | string | No | Override automatic language detection with a language code (e.g. de, fr). |
Request headers
| Header | Required | Description |
|---|---|---|
Origin | Yes | The requesting domain. Must match a domain registered for this site. |
Response (200)
{
"config": { ... },
"sdkCategories": [
{
"id": "essential",
"name": "Essential",
"description": "Required for the website to function properly.",
"consentModel": "exempt"
},
{
"id": "analytics",
"name": "Analytics",
"description": "Help us understand how visitors interact with our website.",
"consentModel": "opt_in"
}
],
"resolvedRegion": "gdpr",
"resolvedLanguage": "en",
"resolvedContent": {
"title": "We value your privacy",
"description": "...",
"acceptAllText": "Accept All",
"rejectAllText": "Reject All",
"customizeText": "Customize",
"savePreferencesText": "Save Preferences",
"closeText": "Close"
},
"resolvedUi": {
"required": "Required"
},
"showBranding": true,
"version": 3,
"site_name": "My Website",
"scriptRules": [
{ "pattern": "google-analytics.com", "category": "analytics" }
]
}Response fields
| Field | Type | Description |
|---|---|---|
config | object | The full published config object (appearance, content, categories, regions, consent rules). Falls back to built-in defaults if no config is published. |
sdkCategories | array | Consent categories resolved for the visitor's region. Each entry includes id, name, description, and the applicable consentModel (opt_in, opt_out, notice_only, or exempt). |
resolvedRegion | string | null | The region ID matched from the visitor's location (e.g. gdpr, us_state_laws, default). null if no region matched. |
resolvedLanguage | string | The language code used for content (e.g. en, de). Determined by the lang parameter, the visitor's browser language, or the region's default language. |
resolvedContent | object | Banner and preferences UI strings in the resolved language, with consent-model-specific overrides applied. |
resolvedUi | object | Translated UI labels (e.g. the "Required" badge text). |
showBranding | boolean | Whether to display ConsentStack branding. true for free-tier sites. |
version | number | The published config version number. 0 if no config has been published. |
site_name | string | The site name from your dashboard. |
scriptRules | array | Active script blocking rules. Each entry has a pattern (domain pattern to match) and a category (the consent category that governs it). |
Error responses
| Status | Body | Condition |
|---|---|---|
| 400 | {"error": "site_key is required"} | Missing or invalid site_key parameter. |
| 403 | {"error": "Site is paused"} | The site has been paused in the dashboard. |
| 403 | {"error": "Missing Origin header"} | No Origin header in the request. |
| 403 | {"error": "Origin not registered for this site"} | The requesting domain is not registered for this site key. |
| 404 | {"error": "Site not found"} | No site matches the provided site_key. |
| 500 | {"error": "Internal server error"} | Unexpected server error. |
Caching
Responses include Cache-Control: private, no-store. Config changes take effect immediately — there is no CDN caching layer between publish and delivery.
Usage cap
Free-tier sites that exceed their monthly active user cap receive a 200 response with {"disabled": true, "reason": "cap_exceeded"} instead of the config payload. The SDK gracefully hides the banner when it receives this response.
POST /functions/v1/consent
Records a visitor's consent decision. The SDK calls this endpoint when a visitor interacts with the banner (accept, reject, customize) and on page load for returning visitors.
POST https://origins.consentstack.io/functions/v1/consent
Content-Type: application/jsonRequest body
| Field | Type | Required | Description |
|---|---|---|---|
site_key | string | Yes | Your public site key. |
anon_id | string | Yes | A client-generated anonymous visitor ID (max 100 chars). Hashed server-side before storage. |
categories | object | Yes | A map of category IDs to booleans representing the visitor's consent choices. Example: {"essential": true, "analytics": false, "marketing": false}. Max 50 entries. |
event_type | string | Yes | One of initial (first interaction), update (changed preferences), or impression (banner was shown). |
event_action | string | No | The action taken: opt_in, opt_out, partial, or acknowledge. |
config_version | number | No | The config version the visitor saw (for audit trail). |
region | string | No | The resolved region ID at the time of consent. |
consent_model | string | No | The active consent model: opt_in, opt_out, or notice_only. |
regulation | string | No | The regulation that applied (e.g. gdpr, us_state_laws). |
time_to_action_ms | number | No | Milliseconds between banner display and visitor action. |
interaction | string | No | How the visitor interacted (e.g. accept_all, reject_all, save_preferences). Max 50 chars. |
preferences_opened | boolean | No | Whether the visitor opened the preferences panel before deciding. |
banner_load_time_ms | number | No | Milliseconds the banner took to render. |
banner_position | string | No | Banner layout position (e.g. bottom-full, center-modal). |
page_url | string | No | The page URL where consent was given. Max 2000 chars. |
sdk_version | string | No | The SDK version that sent the request. |
language | string | No | The visitor's browser language. |
resolved_language | string | No | The language the banner was displayed in. |
Request headers
| Header | Required | Description |
|---|---|---|
Origin | Yes | Must match a registered domain for the site. |
Example request
{
"site_key": "sk_live_abc123",
"anon_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"categories": {
"essential": true,
"analytics": true,
"marketing": false
},
"event_type": "initial",
"event_action": "partial",
"config_version": 3,
"region": "gdpr",
"consent_model": "opt_in",
"time_to_action_ms": 4200,
"interaction": "save_preferences",
"preferences_opened": true,
"page_url": "https://example.com/pricing",
"sdk_version": "0.1.0"
}Server-side enrichment
The server adds several fields before storing the consent log:
- Visitor ID hashing -- The
anon_idis hashed with SHA-256 before storage. The raw ID is never persisted. - User-Agent parsing -- Browser name, OS, and device type (desktop, mobile, tablet) are extracted from the
User-Agentheader. - Country detection -- The visitor's country code is read from infrastructure headers at the network edge.
- Config linkage -- The consent log is linked to the published config ID for a complete audit trail.
Response codes
| Status | Body | Condition |
|---|---|---|
| 201 | {"success": true} | Consent log recorded successfully. |
| 400 | {"error": "..."} | Validation error (missing fields, unknown category IDs, invalid JSON). |
| 403 | {"error": "Site is paused"} | The site is paused. |
| 403 | {"error": "Origin not registered for this site"} | Domain not registered. |
| 404 | {"error": "Site not found"} | No site matches the provided site_key. |
| 405 | {"error": "Method not allowed"} | Request used a method other than POST. |
| 500 | {"error": "Internal server error"} | Unexpected server error. |
Category IDs in the categories object are validated against your published config. Sending an unknown category ID returns a 400 error with the invalid IDs listed in the message. Impression events skip this validation.
POST /functions/v1/scripts
Reports third-party scripts detected on the page. The SDK scans the DOM for script elements, extracts their domains, and sends them to this endpoint. The server upserts each script and auto-categorizes known tracker domains.
POST https://origins.consentstack.io/functions/v1/scripts
Content-Type: application/jsonQuery parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
sync_id | string (UUID) | No | Links this report to a tracker sync session initiated from the dashboard. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
site_key | string | Yes | Your public site key. |
page_url | string | Yes | The page URL where scripts were detected. Max 500 chars. |
scripts | array | Yes | Array of detected scripts (max 100). Each entry contains the fields below. |
timestamp | number | Yes | Unix timestamp of the detection. |
sync_id | string | No | UUID linking to a dashboard-initiated sync (deprecated in body; use query parameter). |
error | string | No | Error message if the scan encountered issues. Max 500 chars. |
Script entry fields
| Field | Type | Description |
|---|---|---|
src | string | The full script src URL. Max 2000 chars. |
domain | string | The extracted domain of the script (e.g. google-analytics.com). Max 500 chars. |
category | string | null | The SDK's best-guess category from its local known-tracker list, or null if unknown. |
blocked | boolean | Whether the SDK blocked this script from executing. |
Example request
{
"site_key": "sk_live_abc123",
"page_url": "https://example.com/",
"scripts": [
{
"src": "https://www.google-analytics.com/analytics.js",
"domain": "google-analytics.com",
"category": "analytics",
"blocked": true
},
{
"src": "https://cdn.example.com/widget.js",
"domain": "cdn.example.com",
"category": null,
"blocked": false
}
],
"timestamp": 1709654400
}Server-side behavior
For each reported script, the server:
- Upserts by domain -- If the domain has been seen before for this site, the existing record is updated. New domains are inserted.
- Auto-categorizes -- Known tracker domains are matched against a built-in database and assigned a category automatically.
- Updates sync status -- If a
sync_idis provided, the corresponding tracker sync record is marked as complete (or failed, if anerrorwas included).
Response codes
| Status | Body | Condition |
|---|---|---|
| 200 | {"success": true} | Scripts processed successfully. |
| 400 | {"error": "..."} | Validation error (invalid body or sync_id format). |
| 404 | {"error": "Site not found"} | No site matches the provided site_key. |
| 405 | {"error": "Method not allowed"} | Request used a method other than POST. |
| 500 | {"error": "Internal server error"} | Unexpected server error. |
Rate limiting
All endpoints are rate-limited at the CDN edge: 20 requests per 10 seconds per IP address (~120 requests/minute). This limit is for abuse prevention and should not affect normal SDK usage.
What's next
- Getting started -- Install the SDK and publish your first config.
- Dashboard guide -- Manage sites, configs, and view consent logs.