Manage personal data
Submit, track, download, and cancel data subject requests through the Intempt API: endpoints, request types, identity formats, and error responses.
Overview
Everything the Privacy Center does for data subject requests is also available over the API. Use it when requests arrive at volume, or when you want your own support tooling to file them rather than a person filling in a form.
Intempt implements OpenDSR, so the request shape is portable across processors that support the same framework.
📘 Good to know
The API and the Privacy Center act on the same requests. A request filed over the API appears on the Data subject requests tab, and one filed in the UI is visible to the API. See Managing DSR for the UI walkthrough.
Authentication
Every DSR endpoint takes the same Bearer token as the rest of the Intempt API. There's no separate privacy credential to generate or rotate.
Authorization: Bearer <JWT>All endpoints are nested under /v1/{orgName}/projects/{projectName}, so a request is always scoped to one project. See API Keys.
Create a request
POST /v1/{orgName}/projects/{projectName}/requests
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
regulation | string | Yes | gdpr or ccpa |
subject_request_type | string | Yes | access, erasure, or portability |
subject_identities | array | Yes | At least one identity for the data subject |
subject_identities[].identity_type | string | Yes | email, mobile_number, or master_id |
subject_identities[].identity_value | string | Yes | Must match the format implied by the type |
subject_identities[].identity_format | string | Yes | Always raw |
api_version | string | Yes | 2.0 |
Examples
Erasure request by email
{
"regulation": "gdpr",
"subject_request_type": "erasure",
"subject_identities": [
{
"identity_type": "email",
"identity_value": "dana.whitfield@northbay.io",
"identity_format": "raw"
}
],
"api_version": "2.0"
}Access request by Master ID
{
"regulation": "ccpa",
"subject_request_type": "access",
"subject_identities": [
{
"identity_type": "master_id",
"identity_value": "614829301174820993",
"identity_format": "raw"
}
],
"api_version": "2.0"
}Responses
200 OK
Responses are HAL+JSON, so they also carry a _links object alongside the fields below.
{
"subject_request_id": "792318291839823872",
"subject_request_type": "erasure",
"regulation": "gdpr",
"submitted_time": "2026-08-27T10:30:00Z",
"subject_identities": [
{
"identity_type": "email",
"identity_value": "dana.whitfield@northbay.io",
"identity_format": "raw"
}
],
"api_version": "2.0",
"status": "pending",
"results_url": null
}📘 Good to know
subject_request_id is a 64-bit Snowflake ID and is serialized as a string, because it exceeds what a JavaScript number can hold precisely. Don't parse it as a number.
Filing for many users at once
Send up to 2,000 identities in a single request. Every identity in the batch gets the same regulation and request type.
{
"regulation": "gdpr",
"subject_request_type": "erasure",
"subject_identities": [
{ "identity_type": "email", "identity_value": "dana.whitfield@northbay.io", "identity_format": "raw" },
{ "identity_type": "email", "identity_value": "marcus.oduya@northbay.io", "identity_format": "raw" },
{ "identity_type": "master_id", "identity_value": "614829301174820993", "identity_format": "raw" }
],
"api_version": "2.0"
}The Privacy Center accepts the same volume as a CSV upload, one identity per row, for teams who'd rather not script it.
CCPA disclosure types
CCPA distinguishes three kinds of disclosure. Set disclosure_type on a CCPA request to answer one of them specifically:
| Value | Returns |
|---|---|
data | Everything held about the person. The default |
categories | The categories of data held, without the values |
sources | Where the data was collected from |
{
"regulation": "ccpa",
"subject_request_type": "access",
"disclosure_type": "categories",
"subject_identities": [
{ "identity_type": "email", "identity_value": "dana.whitfield@northbay.io", "identity_format": "raw" }
],
"api_version": "2.0"
}GDPR requests ignore this field, because GDPR doesn't make the distinction.
📘 Good to know
A CCPA export returns everything Intempt holds, not just the previous twelve months. The twelve-month lookback in the statute is a minimum, and returning more of the person's own data to them is always compliant.
List requests
GET /v1/{orgName}/projects/{projectName}/requests
With no query parameters, returns every non-deleted request for the project.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
requestId | long | No | Return a single request by ID |
regulation | string | No | Filter by gdpr or ccpa |
type | string | No | Filter by access, erasure, or portability |
startTime | date | No | Submitted on or after this date, yyyy-MM-dd |
endTime | date | No | Submitted on or before the end of this date, yyyy-MM-dd |
Examples
Every erasure request filed in August
GET /v1/acme/projects/production/requests?type=erasure&startTime=2026-08-01&endTime=2026-08-31Check status
GET /v1/{orgName}/projects/{projectName}/requests/{requestId}
Responses
200 OK
{
"subject_request_id": "792318291839823872",
"controller_id": "1",
"expected_completion_time": "2026-08-27T10:30:00Z",
"request_status": "completed",
"results_url": "https://api.intempt.com/v1/acme/projects/production/requests/792318291839823872/results",
"api_version": "2.0"
}Response Fields
| Field | Type | Description |
|---|---|---|
subject_request_id | string | The request's ID |
controller_id | string | Identifier of the data controller that filed it |
expected_completion_time | string | The submission time. Processing begins immediately, so this is not a future deadline; it holds the moment the request was received |
request_status | string | Current status, for example pending or completed |
results_url | string | Download URL for access and portability results. null until results exist |
api_version | string | 2.0 |
Download results
GET /v1/{orgName}/projects/{projectName}/requests/{requestId}/results
Streams the results file through the service, with a Content-Disposition: attachment header. The download takes the same Bearer token as every other endpoint, and the URL doesn't expire. Object storage itself is internal and is never exposed to callers.
Returns 404 when the request doesn't exist for your org and project, when results aren't ready yet, or when the stored file is missing.
Cancel a request
DELETE /v1/{orgName}/projects/{projectName}/requests/{requestId}
Marks the request record deleted in the console. It does not stop an erasure. Erasure begins on receipt and the deletion work runs downstream independently, so this removes the request from your list without recalling anything — the service's own documentation states that once a deletion request is created it cannot be undone. Use it to tidy a record, never as a way out.
Responses
200 OK
{
"subject_request_id": "792318291839823872",
"controller_id": "1",
"expected_completion_time": null,
"received_time": "2026-08-27T11:00:00Z"
}Discovery
GET /v1/discovery
Returns what this OpenDSR implementation supports. This is the only DSR endpoint that takes no authentication, so a partner can read your processor's capabilities without credentials.
Responses
200 OK
{
"api_version": "2.0",
"supported_identities": {
"identity_type": "email, mobile_number",
"identity_format": "raw"
},
"supported_subject_request_types": ["ACCESS", "ERASURE", "PORTABILITY"],
"processor_certificate": "https://static.intempt.com/dsr/opendsr_cert.pem"
}Errors
| Status | Cause |
|---|---|
400 | Validation failure. The body carries a structured errors array |
401 | Missing or invalid JWT |
403 | The request belongs to a different organization |
404 | Request not found for this org and project |
500 | Unexpected error. No detail is returned |
400 example
{
"errors": [
{ "message": "The regulation and the type of the request must be declared." }
]
}Use cases
- Your support desk files erasure requests automatically when a ticket is tagged, instead of a person opening the Privacy Center.
- A monthly compliance report counts requests by type and regulation using
startTimeandendTime. - Your own admin tool polls status and emails the requester when
request_statusreachescompleted. - A request was filed against the wrong identity, and support clears the record from your tooling — the erasure itself has already run and cannot be recalled.
- A partner verifies Intempt's OpenDSR capabilities and processor certificate before signing off on a data flow.
- An access request completes and your tool fetches the results file to hand to the requester directly.
- You reconcile requests filed in the UI against your own ticket system by listing everything for a date range.
