Consent API reference
Create, list, retrieve, and withdraw consent records programmatically using the Ghost REST API and bearer-token authentication.
Authentication
Every API request must include a valid API key in the Authorization header using the Bearer scheme:
Authorization: Bearer gst_your_key_hereAPI keys are created in Settings > API Keys. Each key has one or more scopes that control which endpoints it can access:
consent:read— list and retrieve consent records.consent:write— create consent records and record withdrawals.
Keys are prefixed with gst_ and shown in full only once at creation time. Ghost stores a SHA-256 hash, so lost keys cannot be recovered — revoke and create a new one instead.
Base URL
All endpoints are served from:
https://ghostredact.app/api/v1Requests must use HTTPS and include Content-Type: application/json for request bodies.
Create a consent record
POST /api/v1/consent
Scope required: consent:write
Request body
| Field | Type | Required | Description |
|---|---|---|---|
subject_identifier | string | Yes | The data subject (e.g. email address). Max 500 chars. |
purpose | string | Yes | Why consent was collected. Max 1 000 chars. |
consent_at | string | No | ISO 8601 timestamp. Defaults to now. |
collection_method | string | No | One of: web_form, paper_form, verbal, email, api, sdk, implied, other. |
expires_at | string | No | ISO 8601 expiry date for the consent. |
client_id | string | No | Target matter UUID. Defaults to the organisation's first active matter. |
collection_reference | string | No | Your external reference. Max 200 chars. |
notice_version | string | No | Privacy notice version shown. Max 200 chars. |
wording_version | string | No | Consent wording version. Max 200 chars. |
Example request
curl -X POST https://ghostredact.app/api/v1/consent \
-H "Authorization: Bearer gst_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"subject_identifier": "user@example.com",
"purpose": "Marketing emails",
"consent_at": "2026-04-18T10:00:00Z",
"collection_method": "web_form"
}'Example response
Returns 201 Created:
{
"data": {
"id": "cns_8f3a1b2c-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
"subject_identifier": "user@example.com",
"purpose": "Marketing emails",
"status": "active",
"consent_at": "2026-04-18T10:00:00Z",
"collection_method": "web_form",
"collection_reference": null,
"notice_version": null,
"wording_version": null,
"expires_at": null,
"created_at": "2026-04-18T10:05:32Z"
}
}List consent records
GET /api/v1/consent
Scope required: consent:read
Query parameters
| Param | Type | Default | Description |
|---|---|---|---|
status | string | — | Filter by status (e.g. active, withdrawn). |
subject | string | — | Case-insensitive substring match on subject_identifier. Max 200 chars. |
limit | integer | 100 | Page size, 1–500. |
offset | integer | 0 | Number of records to skip. |
Example request
curl https://ghostredact.app/api/v1/consent?status=active&limit=10 \
-H "Authorization: Bearer gst_your_key_here"Example response
Returns 200 OK:
{
"data": [
{
"id": "cns_8f3a1b2c-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
"subject_identifier": "user@example.com",
"purpose": "Marketing emails",
"status": "active",
"consent_at": "2026-04-18T10:00:00Z",
"collection_method": "web_form",
"expires_at": null,
"created_at": "2026-04-18T10:05:32Z"
}
],
"total": 1,
"limit": 10,
"offset": 0
}Get a consent record
GET /api/v1/consent/{id}
Scope required: consent:read
Returns the full consent record including its withdrawals array.
Example request
curl https://ghostredact.app/api/v1/consent/cns_8f3a1b2c-4d5e-6f7a-8b9c-0d1e2f3a4b5c \
-H "Authorization: Bearer gst_your_key_here"Example response
Returns 200 OK, or 404 if the record does not exist or belongs to a different organisation:
{
"data": {
"id": "cns_8f3a1b2c-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
"subject_identifier": "user@example.com",
"purpose": "Marketing emails",
"status": "active",
"consent_at": "2026-04-18T10:00:00Z",
"collection_method": "web_form",
"collection_reference": null,
"notice_version": null,
"wording_version": null,
"expires_at": null,
"created_at": "2026-04-18T10:05:32Z",
"withdrawals": []
}
}Withdraw consent
POST /api/v1/consent/{id}/withdraw
Scope required: consent:write
Marks the consent record as withdrawn and creates a withdrawal entry with a timestamp.
Request body (optional)
| Field | Type | Description |
|---|---|---|
method | string | How the withdrawal was collected. Defaults to api. |
reference | string | Your external reference for the withdrawal. |
notes | string | Free-text notes. |
downstream_notified | boolean | Whether downstream systems have been notified. Defaults to false. |
Example request
curl -X POST https://ghostredact.app/api/v1/consent/cns_8f3a1b2c-4d5e-6f7a-8b9c-0d1e2f3a4b5c/withdraw \
-H "Authorization: Bearer gst_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"method": "email",
"notes": "User requested via support ticket"
}'Example response
Returns 200 OK with the withdrawal record, or 409 Conflict if the consent has already been withdrawn:
{
"data": {
"id": "wdr_1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"consent_id": "cns_8f3a1b2c-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
"method": "email",
"reference": null,
"notes": "User requested via support ticket",
"downstream_notified": false,
"withdrawn_at": "2026-04-18T14:30:00Z"
}
}Error handling
All error responses return a JSON body with an error string:
{
"error": "subject_identifier and purpose are required."
}| Status | Meaning |
|---|---|
400 | Invalid or missing request parameters. |
401 | Missing or invalid API key. |
403 | Key lacks the required scope, or the organisation has reached its plan limit. |
404 | Record not found or belongs to a different organisation. |
409 | Conflict (e.g. consent already withdrawn). |
413 | Request body exceeds the size limit. |
429 | Rate limit exceeded — wait and retry. |
500 | Server error — contact support if persistent. |
Example: insufficient scope
curl -X POST https://ghostredact.app/api/v1/consent \
-H "Authorization: Bearer gst_read_only_key" \
-H "Content-Type: application/json" \
-d '{ "subject_identifier": "user@example.com", "purpose": "Analytics" }'{
"error": "Forbidden: key does not have the consent:write scope."
}Rate limits
Each API key has a per-minute request limit (RPM) set by your plan. There is also an organisation-wide RPM cap. When either limit is hit the API returns 429 Too Many Requests:
{
"error": "Rate limit exceeded. Try again in 60 seconds."
}Wait for the current window to elapse (one minute) before retrying. If you need higher limits, contact support or upgrade your plan.