REST API
DPOKit REST API reference — scanner, data map, consent, DSAR, retention, and developer endpoints under the dpo-kit/v1 namespace.
REST API
Version: 1.9.0
DPOKit exposes a REST API under the namespace dpo-kit/v1. The base URL is your site's REST root followed by the namespace:
https://example.com/wp-json/dpo-kit/v1
Authentication
| Auth Type | Description |
|---|---|
| Admin | Requires a logged-in WordPress user with the manage_options capability. Use WordPress Application Passwords or a cookie + X-WP-Nonce header. |
| Public | No authentication required. Available to unauthenticated visitors. |
Using Application Passwords (recommended for headless setups)
- Go to Users → Profile in wp-admin and generate an Application Password.
- Send requests with HTTP Basic authentication:
Authorization: Basic base64(username:application_password)
Using Cookie + Nonce (JavaScript on the same domain)
fetch( '/wp-json/dpo-kit/v1/consent/stats', {
headers: {
'X-WP-Nonce': wpApiSettings.nonce,
},
credentials: 'same-origin',
} );Endpoints
Scanner
POST /dpo-kit/v1/scanner/start
Auth: Admin
Start a new site scan. Returns a scan_id and the list of URLs to crawl.
Request body:
{
"scan_depth": 50
}Response:
{
"scan_id": 42,
"urls": [ "https://example.com/", "https://example.com/about/" ],
"total": 2
}POST /dpo-kit/v1/scanner/scan-page
Auth: Admin
Scan a single URL and save findings.
Request body:
{
"scan_id": 42,
"url": "https://example.com/shop/"
}Response:
{
"url": "https://example.com/shop/",
"findings": [ "..." ],
"count": 3
}POST /dpo-kit/v1/scanner/complete
Auth: Admin
Mark a scan as completed. Fires the dpo_kit_scan_complete action.
Request body:
{ "scan_id": 42 }GET /dpo-kit/v1/scanner/results/{scan_id}
Auth: Admin
Retrieve results and vendor summary for a completed scan.
Query params:
resource_type(optional) — Filter by type:script,iframe,pixel,cookie,api_call,gtm_tag,form.
Data Map
GET /dpo-kit/v1/data-map
Auth: Admin
List all data map entries.
Query params:
status— Filter by status (active,archived).legal_basis— Filter by legal basis.search— Text search.
POST /dpo-kit/v1/data-map
Auth: Admin
Create a data map entry.
Request body:
{
"vendor_id": 5,
"purpose": "Analytics",
"legal_basis": "consent",
"data_categories": ["IP address", "Page views"],
"retention_period": "26 months"
}PUT /dpo-kit/v1/data-map/{id}
Auth: Admin
Update a data map entry.
DELETE /dpo-kit/v1/data-map/{id}
Auth: Admin
Delete a data map entry.
POST /dpo-kit/v1/data-map/bulk-add
Auth: Admin
Bulk add data map entries from scan results.
Request body:
{
"scan_id": 42,
"vendors": [ "google-analytics", "meta-pixel" ]
}Vendor Library
GET /dpo-kit/v1/vendors
Auth: Admin
List all vendors (built-in + custom).
Query params:
search— Text search by name or slug.
POST /dpo-kit/v1/vendors
Auth: Admin
Create a custom vendor.
Request body:
{
"name": "My Analytics",
"slug": "my-analytics",
"purpose": "Website analytics",
"data_categories": ["IP address", "Browser info"],
"default_retention": "13 months"
}PUT /dpo-kit/v1/vendors/{id}
Auth: Admin
Update a vendor.
DELETE /dpo-kit/v1/vendors/{id}
Auth: Admin
Delete a custom vendor. Built-in vendors cannot be deleted (returns 403).
Consent
POST /dpo-kit/v1/consent
Auth: Public
Record a consent decision from the front-end banner. Called automatically by the consent banner JavaScript.
Request body:
{
"visitor_id": "abc123",
"categories_accepted": ["functional", "analytics"],
"categories_rejected": ["marketing"],
"consent_action": "manage"
}Response:
{ "id": 101, "message": "Consent recorded." }GET /dpo-kit/v1/consent/status
Auth: Public
Get the consent banner configuration and categories. Use this in headless/decoupled setups to render a custom consent UI.
Response:
{
"enabled": true,
"categories": {
"functional": { "id": "functional", "label": "Functional", "required": true },
"analytics": { "id": "analytics", "label": "Analytics", "required": false }
},
"settings": {
"position": "bottom",
"title": "We value your privacy",
"message": "...",
"accept_text": "Accept All",
"reject_text": "Reject All",
"expiry_days": 365,
"privacy_url": "https://example.com/privacy"
}
}GET /dpo-kit/v1/consent/records
Auth: Admin
List consent records.
Query params:
consent_action— Filter by action:accept_all,reject_all,manage.date_from— Start date (YYYY-MM-DD).date_to— End date (YYYY-MM-DD).search— Search by visitor ID.per_page— Records per page (default: 50).offset— Pagination offset.
GET /dpo-kit/v1/consent/stats
Auth: Admin
Get aggregate consent statistics (totals, breakdown by action, category acceptance rates).
Script Registry
GET /dpo-kit/v1/scripts
Auth: Admin
List registered consent-gated scripts.
Query params:
category— Filter by consent category.is_active— Filter by active status (1 or 0).search— Text search.
POST /dpo-kit/v1/scripts
Auth: Admin
Register a consent-gated script.
Request body:
{
"handle": "my-chat-widget",
"label": "Live Chat",
"category": "functional",
"script_url": "https://chat.example.com/widget.js",
"blocking_method": "type_attribute"
}PUT /dpo-kit/v1/scripts/{id}
Auth: Admin
Update a registered script.
DELETE /dpo-kit/v1/scripts/{id}
Auth: Admin
Delete a registered script.
DSAR (Headless / Decoupled)
POST /dpo-kit/v1/dsar/intake
Auth: Public
Submit a Data Subject Access Request from a headless front-end (e.g. a React or Next.js app).
Spam protection: Include a X-PV-HP-Check header with an empty value (omit entirely for real requests). Bots that send all headers will be rejected.
Request body:
{
"request_type": "access",
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@example.com",
"additional_info": "Please include all marketing communications."
}Valid request types: access, deletion, rectification, portability, objection
Response (201):
{
"id": 15,
"reference": "DSR-20260311-A1B2C3",
"deadline_date": "2026-04-10",
"message": "Your request has been received. You will receive an acknowledgement email shortly."
}GET /dpo-kit/v1/dsar/status
Auth: Public
Check the status of a DSAR case. Requires both the reference number and the requestor's email address to prevent enumeration.
Query params:
reference(required) — Case reference number (e.g.DSR-20260311-A1B2C3).email(required) — Email address used to submit the request.
Response:
{
"reference": "DSR-20260311-A1B2C3",
"request_type": "access",
"status": "in_progress",
"status_label": "In Progress",
"deadline": "2026-04-10",
"submitted": "2026-03-11 09:00:00",
"completed": null
}Retention
GET /dpo-kit/v1/retention/policies
Auth: Admin
List retention policies.
Query params:
active_only— Return only active policies (1).data_category— Filter by data category slug.
POST /dpo-kit/v1/retention/run
Auth: Admin
Trigger a retention enforcement run. Supports dry-run mode.
Request body:
{
"dry_run": true,
"policy_id": 3
}Omit policy_id to run all active policies.
Response:
{
"results": [
{ "policy_name": "Old Comments", "data_category": "comments", "action": "anonymise", "records_affected": 12 }
],
"policies_run": 1,
"total_affected": 12,
"dry_run": true
}Developer
GET /dpo-kit/v1/developer/hooks
Auth: Admin
List all documented action and filter hooks provided by DPOKit.
Query params:
type— Filter by type:actionorfilter.
GET /dpo-kit/v1/developer/extensions
Auth: Admin
List all third-party extensions currently registered via the dpo_kit_register_extensions action.
Response:
{
"extensions": {
"dsar_sources": [ { "id": "my-crm", "label": "CRM Records" } ],
"consent_scripts": [ { "handle": "my-tracker", "label": "My Tracker" } ],
"vendors": [ { "slug": "my-analytics", "name": "My Analytics" } ]
},
"totals": {
"dsar_sources": 1,
"consent_scripts": 1,
"vendors": 1
}
}Error Responses
All error responses follow the standard WordPress REST API error format:
{
"code": "not_found",
"message": "No request found matching that reference number and email address.",
"data": { "status": 404 }
}Common error codes:
| Code | HTTP Status | Meaning |
|---|---|---|
rest_forbidden | 401 / 403 | Not authenticated or insufficient permissions |
not_found | 404 | Resource not found |
invalid_email | 422 | Email address failed validation |
dsar_disabled | 503 | DSAR intake is currently disabled |
spam_detected | 400 | Honeypot triggered |
create_failed | 500 | Database write failed |
update_failed | 500 | Database update failed |
delete_failed | 403 | Cannot delete protected resource |