CookieHub Docs

Public widget API

The DSAR widget and the hosted request page both submit requests through a public HTTP API. If you want to build your own request form, for example inside a mobile app or a design system that cannot use the widget, you can call the same API directly.

The API has two endpoints: one that issues a short-lived submission token and one that creates the request. It is meant to be called from a visitor's browser or from your server. It does not read or change requests that already exist. That is done in the Dashboard.

Base URL

https://dash.cookiehub.com/api/v1/dsar

What you need

  • The account key and domain key of the website the request is for. They are the data-account-key and data-domain-key values in the embed code from Publish form in the Dashboard. See Installing the widget.
  • An account with the DSAR add-on.
  • A Referer header that matches the website. See Origin check.

The keys are public: they are visible in every page that embeds the widget. They identify the website; they do not authenticate you.

The flow

  1. Your form calls GET /init and receives a submission_token.
  2. When the visitor submits, your form calls POST /submit with the token and the fields.
  3. CookieHub creates the request as unverified and emails the data subject a verification link. If the request is on behalf of someone else, the requester gets a separate link too.
  4. The data subject opens the link and confirms. The request becomes pending and appears in your Dashboard. See After the request is submitted.

GET /init

Returns a submission token.

GET /api/v1/dsar/init?ak=<account key>&dk=<domain key>
Query parameterMeaning
akThe account key.
dkThe domain key.

Success:

{ "submission_token": "..." }

Treat the token as an opaque string. It is valid for 30 minutes and tied to the website it was issued for. A token is single use: the first POST /submit that presents it consumes it, even when that submit is then rejected. Fetch a fresh token before every submit.

Failures:

Statuserror_keyWhen
422err_invA key is missing (API keys are missing.), or the keys are not valid for the same website (Invalid API keys.).
403err_unauthThe account does not have DSAR enabled, or the origin check failed.
429Too many requests from this address.

POST /submit

Creates the request. Send JSON with the headers Content-Type: application/json and Accept: application/json. Without Accept: application/json, a validation failure is answered with a redirect instead of the JSON described below.

{
  "account_key": "ak_xxxxxxxx",
  "domain_key": "dk_yyyyyyyy",
  "submission_token": "...",
  "name": "Jane Doe",
  "email": "jane@example.com",
  "relationship": "customer",
  "request_types": ["access", "erasure"],
  "request_details": "Please send me everything you hold about me.",
  "country_code": "DE",
  "locale": "de",
  "source_url": "https://www.example.com/privacy-requests",
  "consent_token": "",
  "is_on_behalf": false
}

Fields

FieldRequiredRules
account_keyYesThe account key.
domain_keyYesThe domain key.
submission_tokenYesA token from GET /init.
nameYesThe data subject's name, up to 255 characters.
emailYesThe data subject's email address, up to 255 characters.
relationshipYesOne of customer, visitor, employee, former_employee, applicant, other.
request_typesYesA list with at least one of access, erasure, portability, rectification, restriction, object, automated, other.
request_detailsYesThe message, up to 2,000 characters.
country_codeNoA two-letter country code. The country decides the jurisdiction suggested for the request. If omitted, CookieHub uses the country detected from the request where it can.
localeNoA lowercase language code such as en, pt-br or zh-tw, up to 10 characters. It selects the language of every email the data subject receives. Unsupported codes fall back to English.
source_urlNoThe address of the page the request comes from: http or https, up to 255 characters. If omitted, the Referer header is used. Shown as the Source URL on the request.
consent_tokenNoThe visitor's consent ID, up to 64 characters. Links the request to the consent log.
is_on_behalfNotrue if someone submits for another person. Accepts true, false, 1, 0, on, yes.
requester_nameIf is_on_behalfThe requester's name, up to 255 characters.
requester_emailIf is_on_behalfThe requester's email address. Must differ from email.
requester_relationshipIf is_on_behalfOne of parent, lawyer, agent, family, other.
subject_phoneNoLeave this out, or send it empty. See Spam protection.

Leading and trailing spaces are trimmed from text fields, and a field that is empty after trimming counts as not sent.

The keys and the token can also be sent as the headers X-Account-Key, X-Domain-Key and X-Submission-Token. A browser calling the API from your website cannot send these headers, because the API's CORS policy only allows Content-Type and Accept. Put them in the body.

Success

{
  "success": true,
  "message_key": "ok_received",
  "message": "Your request has been received. Please check your inbox, including your spam folder, to verify your identity."
}

Status 201. The response does not contain a request ID: the data subject receives the link to their request in the verification email.

Failures

StatusBodyWhen
422error_key: "err_keys"account_key or domain_key is missing.
422error_key: "err_inv"The keys are invalid or belong to different websites.
422message and errorsA field failed validation. errors maps each field name to a list of messages, and message is the first of them.
403error_key: "err_unauth"DSAR is not enabled for the account, or the origin check failed.
403error_key: "err_token"The token is missing, malformed, expired or already used. Reload and fetch a new one.
429messageToo many requests. See Rate limits.
500error_key: "err_server"The request could not be stored. Nothing was created; it is safe to try again later.

Error responses with an error_key also carry a human-readable message in English. If you show your own text, key it on error_key.

If the verification email cannot be sent after the request is stored, the response is still 201 and the failure is recorded on the request. You can resend the email from the request page in the Dashboard.

Origin check

Both endpoints check where the call comes from, to stop another website from using a visitor's browser to submit on your behalf.

  • On GET /init the origin is the Referer header.
  • On POST /submit the origin is source_url if you send it, otherwise the Referer header.

It must be an http or https address of the website itself, or of one of its subdomains. A leading www. is ignored on both sides. For a website registered as example.com, https://www.example.com/privacy, https://example.com/ and https://help.example.com/ pass, and https://example.org/ does not. The address can be at most 255 characters.

The address of the website's active hosted request page also passes, for that website only.

Browsers send the Referer header themselves, so a form on your website works as long as your site's referrer policy does not remove it (for example no-referrer). Calling the API from your server is possible, but you must set a matching Referer header yourself.

Rate limits

Limits apply per minute, counted from the first request:

EndpointLimit
GET /init20 per IP address
POST /submit10 per IP address, and 60 per website across all addresses

In addition, each email address, whether the data subject's or the requester's, can receive at most 5 verification emails per hour from submissions, across all your websites. A submission that would exceed it is answered with 429 and creates nothing.

A request over a per-minute limit is answered with 429, the body {"message":"Too Many Attempts."} and a Retry-After header with the seconds to wait.

Spam protection

subject_phone is a hidden decoy field. A person never sees it, but a bot that fills in every field does. If it is present and not empty, the API answers as if the request succeeded (201) and stores nothing. Do not render this field in your form.

CORS

Every response carries these headers, so a browser can call the API from any website:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Accept
Access-Control-Max-Age: 86400

OPTIONS requests to any path under /api/v1/dsar/ are answered with 204.

After the request is submitted

The rest of the flow happens on pages that CookieHub hosts and that people open from their email. They are web pages, not JSON endpoints, and you do not need to call them, but it helps to know what the data subject will see:

AddressWhat it does
/dsar/verify/<token>The link in the verification email. Opening it shows a confirmation button; pressing the button completes the verification. Email scanners that open links therefore cannot verify a request by accident. The link is valid for 30 days.
/dsar/request/<id>The data subject's status page: the request's status and deadline, replies to your questions, the download of the disclosure file and, where the law requires it, the appeal form.

All of these live on https://dash.cookiehub.com. Verification, status and download pages are limited to a few requests per minute per address, and "send the verification email again" on the status page is limited to 3 per hour.

An on-behalf request needs both the data subject and the requester to verify before it becomes pending. Opening a link that is expired, already used or unknown shows a page that says so. A new verification link can be sent from the status page, or from the request in the Dashboard.

Next steps

On this page