Overview
This topic provides the right to be forgotten (RTBF) scenarios to run and the results to expect. For the request/response contract (fields, validation rules, error codes) refer to the RTBF API reference.
What Redpoint provides at onboarding
You need the following before you can test. Redpoint supplies all of it; you supply your egress IP addresses if asked.
|
Item |
Value |
Notes |
|---|---|---|
|
Test-environment base URL |
|
All calls go to |
|
OAuth2 |
|
Your application's identity in Redpoint's Microsoft Entra tenant. Identifies who is calling. Not the same value as the id inside |
|
OAuth2 Client secret or certificate |
(provided separately) |
Delivered to you separately, through a secure channel. Treat as a production credential, even in test. Rotation is done in Entra and needs nothing from you on the API side. |
|
Entra tenant id ( |
|
Redpoint's Microsoft Entra directory id. Used only in the token endpoint URL, and the same for each Redpoint environment. Not your Data Readiness Hub tenant id (below). |
|
Scope / audience ( |
|
The RTBF API's identifier, provided as an Application ID URI of the form |
|
Your Data Readiness Hub test Client id(s) |
|
Sent as |
|
Egress IP allow-listing |
(you supply) |
If the environment's WAF uses an IP allow-list, give Redpoint the egress IPs your test traffic will come from; otherwise calls are refused with |
Test-data rules: read before your first POST
Erasure is irreversible, even in the test environment. A completed erasure request deletes all records matching the identifiers you submit. Only test against the designated test tenant, and only with synthetic subjects; never submit a real person's identifiers. Use obviously fake values such as rtbf-test-001@example.com.
-
Multiple identifiers in one request are OR'd (union): all records matching any of them is erased. Keep each test request to one synthetic subject.
-
Use a fresh, randomly generated lowercase UUID v4 as
subject_request_idfor each new test case (it is your idempotency key; reusing one deliberately is itself a test, see Scenario B).
Before you start: mint a token
Standard OAuth2 client-credentials authenticate against Microsoft Entra ID. Cache the token until shortly before expires_in and reuse it; do not mint one per request.
TOKEN=$(curl -sS -X POST "https://login.microsoftonline.com/<ENTRA_TENANT_ID>/oauth2/v2.0/token" \
-d "grant_type=client_credentials" \
-d "client_id=<OAUTH_CLIENT_ID>" \
-d "client_secret=<OAUTH_CLIENT_SECRET>" \
-d "scope=<AUDIENCE>/.default" | jq -r .access_token)
BASE="<TEST_BASE_URL>/api-op/v1/rtbf/requests"
AUTH="Authorization: Bearer $TOKEN"
DRH_CLIENT_ID="X-ClientId: <DRH_CLIENT_ID>"
JSON="Content-Type: application/json"
The <…> values above are provided by Redpoint during onboarding. If the token call fails, the problem is on the Entra side (wrong client_id/secret, wrong tenant, wrong scope); subsequent calls will not work until it succeeds.
First: Verify your access
Before submitting anything, confirm your credential and tenant authorization are wired up end to end. GET /self-check runs the identical authentication as a real call but creates no request and changes no data; so unlike a POST (which starts a real, irreversible erasure), it is safe to call first, and as often as you like. This is the recommended first call for any new integration.
# Uses $AUTH from the token step above. /self-check is a sibling of /requests.
curl -sS -i "<TEST_BASE_URL>/api-op/v1/rtbf/self-check" -H "$AUTH"
# add -H "$TENANT" only if you are authorized for more than one tenant.
#
# EXPECT: 200, echoing the context we resolved for your call:
# {
# "client_id": "<the Data Readiness Hub tenant we resolved you to>",
# "app_id": "<your application id>",
# "roles_satisfied": true,
# "origin_verified": true,
# "token_expires_at": "<UTC timestamp>"
# }
#
# It creates no request and no data change. When X-ClientId is omitted, client_id
# confirms which tenant we resolved you to. A 401 or 403 here means the same as it
# would on a real request (see Scenario C) and carries the same
# X-RedPoint-CorrelationId -- fix it here, before you submit anything.
Scenario A: Submit, poll, cancel (happy path)
ID=$(python3 -c "import uuid; print(uuid.uuid4())")
# A1. Submit an erasure request for one synthetic subject
curl -sS -i -X POST "$BASE" -H "$AUTH" -H "$DRH_CLIENT_ID" -H "$JSON" -d "{
\"subject_request_id\": \"$ID\",
\"subject_request_type\": \"erasure\",
\"subject_identities\": [
{ \"identity_type\": \"email\", \"identity_value\": \"rtbf-test-001@example.com\", \"identity_format\": \"raw\" }
],
\"submitted_time\": \"2026-09-08T12:00:00Z\"
}"
# EXPECT: 201 Created, a Location header ending in /rtbf/requests/$ID,
# body: request_status "pending", received_time set,
# expected_completion_time = submitted_time + 30 days.
# subject_identities is NOT echoed back (by design).
# A2. Poll its status
curl -sS "$BASE/$ID" -H "$AUTH" -H "$DRH_CLIENT_ID"
# EXPECT: 200, request_status "pending"
# A3. Cancel it while still pending
curl -sS -i -X DELETE "$BASE/$ID" -H "$AUTH" -H "$DRH_CLIENT_ID"
# EXPECT: 202 Accepted, body request_status "cancelled"
# A4. Confirm the cancel persisted
curl -sS "$BASE/$ID" -H "$AUTH" -H "$DRH_CLIENT_ID"
# EXPECT: 200, request_status "cancelled" (final — it never changes again)
# A5. Cancel again
curl -sS -i -X DELETE "$BASE/$ID" -H "$AUTH" -H "$DRH_CLIENT_ID"
# EXPECT: 409, error.message "request cannot be cancelled in status: cancelled"
Scenario B: Idempotency
ID=$(python3 -c "import uuid; print(uuid.uuid4())")
BODY="{ \"subject_request_id\": \"$ID\", \"subject_request_type\": \"erasure\",
\"subject_identities\": [ { \"identity_type\": \"email\", \"identity_value\": \"rtbf-test-002@example.com\", \"identity_format\": \"raw\" } ],
\"submitted_time\": \"2026-09-08T12:00:00Z\" }"
# B1. Submit
curl -sS -i -X POST "$BASE" -H "$AUTH" -H "$DRH_CLIENT_ID" -H "$JSON" -d "$BODY"
# EXPECT: 201
# B2. Re-send the IDENTICAL body (simulates a retry after a dropped response)
curl -sS -i -X POST "$BASE" -H "$AUTH" -H "$DRH_CLIENT_ID" -H "$JSON" -d "$BODY"
# EXPECT: 201 again, returning the SAME request — nothing new was created.
# Your connector must treat this as success, not as a duplicate error.
# B3. Re-use the same id for a DIFFERENT subject
curl -sS -i -X POST "$BASE" -H "$AUTH" -H "$DRH_CLIENT_ID" -H "$JSON" -d "{
\"subject_request_id\": \"$ID\", \"subject_request_type\": \"erasure\",
\"subject_identities\": [ { \"identity_type\": \"email\", \"identity_value\": \"rtbf-test-999@example.com\", \"identity_format\": \"raw\" } ],
\"submitted_time\": \"2026-09-08T12:00:00Z\" }"
# EXPECT: 400 — the id is already bound to a different request. Mint a fresh id and resubmit.
Scenario C: Authentication and validation errors
Each error carries the OpenDSR §7.6 object { "error": { "code": <http status>, "message": "…", "correlation_id": "…" } }. Confirm your connector reads error.code/error.message rather than assuming a plain-text body, and that it records error.correlation_id; see C7 in the code example below, which affects how supportable your integration is in production.
# C1. No token at all
curl -sS -i -X POST "$BASE" -H "$DRH_CLIENT_ID" -H "$JSON" -d "$BODY"
# EXPECT: 401, "Authorization: Bearer token is required"
# (the one credential error that tells you exactly what is wrong)
# C2. A bad or expired token
curl -sS -i -X POST "$BASE" -H "Authorization: Bearer not-a-real-token" -H "$DRH_CLIENT_ID" -H "$JSON" -d "$BODY"
# EXPECT: 403, "invalid token or tenant not granted"
# C3. A tenant you are NOT authorized for
curl -sS -i -X POST "$BASE" -H "$AUTH" -H "X-ClientId: some-other-tenant" -H "$JSON" -d "$BODY"
# EXPECT: 403, the SAME message as C2 — deliberately. The service never reveals
# which tenants or applications exist, or how close a guess was.
# C4. Malformed body (not valid JSON)
curl -sS -i -X POST "$BASE" -H "$AUTH" -H "$DRH_CLIENT_ID" -H "$JSON" -d '{ not json'
# EXPECT: 400 with the §7.6 error object
# C5. Bad subject_request_id (uppercase / not a v4 UUID)
curl -sS -i -X POST "$BASE" -H "$AUTH" -H "$DRH_CLIENT_ID" -H "$JSON" -d '{
"subject_request_id": "NOT-A-UUID", "subject_request_type": "erasure",
"subject_identities": [ { "identity_type": "email", "identity_value": "x@example.com" } ],
"submitted_time": "2026-09-08T12:00:00Z" }'
# EXPECT: 400
# C6. Unknown request id
curl -sS -i "$BASE/$(python3 -c 'import uuid; print(uuid.uuid4())')" -H "$AUTH" -H "$DRH_CLIENT_ID"
# EXPECT: 404
# C7. Capture the correlation id from a failure. Do this for EVERY non-2xx.
curl -sS -i -X POST "$BASE" -H "Authorization: Bearer not-a-real-token" -H "$DRH_CLIENT_ID" -H "$JSON" -d "$BODY" \
| grep -i '^X-RedPoint-CorrelationId:'
# EXPECT: exactly one header carrying a random UUID v4, identical to
# error.correlation_id in the body. A different value on every call.
#
# Record it in YOUR logs alongside the failed request. Because C2 and C3
# are indistinguishable to you, this id is the only way Redpoint can tell
# you which cause actually fired — and it cannot be recovered later if
# you did not keep it.
On the uniform 403. Scenarios C2 and C3 return an identical response by design. If you receive a 403 during real use: first check that your token is unexpired and was requested for the correct AUDIENCE; then confirm with Redpoint that your application's authorization for the tenant is active and that your egress IPs are permitted. The service will not tell you which of these it was, but the correlation_id on that response lets Redpoint tell you. Quote it, with the approximate time and the endpoint you called, and support can name the exact cause from the server-side log. Report it promptly: those log entries are retained for a limited window.
What to expect about status progression
-
A request does not complete instantly. Requests are batched: arrival schedules a future processing window, and processing runs only when the tenant's other workloads are idle. A request will sit at
pendingfor some time after submission; this is normal. -
The transitions
pending → in_progress → completedare driven by Redpoint's processing, not by anything you call. There is no consumer endpoint to advance a request, and the public API exposes status only, not the underlying processing history. -
In the test environment, Redpoint will run or simulate processing on request so you can observe a full lifecycle. To see your test request reach
completed, coordinate with your Redpoint contact and tell them thesubject_request_id. -
Once a request is
in_progress, it can no longer be cancelled (DELETE→409);completedandcancelledare final. There is nofailedstatus; a request that cannot be completed on one pass is retried on the next window rather than surfaced to you as a failure. -
Poll
GETat a sensible interval (minutes, not seconds).expected_completion_timeis your statutory reference point and is anchored on thesubmitted_timeyou sent, not on when Redpoint accepted the call.
Certification checklist
Your integration is ready for a production credential when each item below has been demonstrated against the test environment:
- Verify access:
GET /self-checkreturns200before any submission; verifies access without creating a request. - Scenario A, A1–A2: submit a request and read it back as
pending(201/200). - Scenario A, A3–A5: cancel while pending (
202), confirmcancelled, and handle the repeat-cancel409without alarming. - Scenario B, B2: treat an identical-replay
201as success, not as a duplicate error. - Scenario B, B3: on a reused-id
400, mint a freshsubject_request_idrather than retrying. - Scenario C, C1–C3: distinguish
401(fix the header) from403(check token audience, then contact Redpoint). - Scenario C, C4–C6: parse the §7.6
errorobject on each non-2xx. - Scenario C, C7: capture
error.correlation_id(or theX-RedPoint-CorrelationIdheader) on each non-2xx and write it to your own logs; it is what Redpoint needs to diagnose a403, and it cannot be recovered after the fact. - Token caching: reuse a token across calls until near
expires_in; do not mint per request. - Observed one request reach
completed(with Redpoint driving processing) and confirmed your connector reports it as fulfilled. - Back off and retry on
503+Retry-Afterrather than failing the request.
Related: RTBF API reference (the contract this procedure exercises). Questions about your credential, tenant authorization, or a test request's progress should be directed to your Redpoint onboarding contact.