Topolo Docs Topolo Platform
Open workspace

Submit a Consent data-subject request

Submit through the intended public integration and distinguish accepted requests, confirmed persistence, uncertain retries and actual fulfillment.

Outcome

Submit a data-subject request to an intended Consent integration. The response separates quick observed persistence from an insertion that is failed or still uncertain; neither outcome carries out the underlying data work.

Action: app_topolo_consent.dsr_requests.submit.

POST /api/submit-dsr

Integration and authority

  1. Use only the intended Consent integration and a legitimate, approved request. Resolve its actual public project key or publishable Consent SDK key from that integration’s existing owner/configuration. Do not guess keys, substitute a Topolo OAuth/API credential into sdkKey, or execute the fictional examples.

  2. This is a public SDK route handled before Consent’s administrative verifyConsentRequest/workspace gate. A public integration key selects a project; it does not authenticate the person named by email or grant administration access. Formatting an email is not proof of ownership or authority to disclose/delete data. Any later fulfillment and identity review are separate authorized work.

  3. For browser requests, retain the actual Origin and configured project domain policy. The source checks an Origin when present and allows a matching API hostname or configured domain/subdomain; absent Origin passes this particular check. Do not remove/forge Origin to retry a403. An API-key/OAuth header does not turn this route into a subject-verification or admin-grant check.

Request

{
  "projectKey": "project_fixture_docs",
  "type": "access",
  "email": "person@example.test",
  "metadata": {
    "reference": "illustrative fixture only"
  }
}

All values are fictional and this example is unrun. Obtain the intended actual integration values, validate/plan the reviewed input and obtain approval before creating a real record. Do not send a fixture email or key. projectKey or sdkKey selects the integration; type or requestType supplies its request type. email is required by the schema; optional metadata is retained data, not authorization or instructions.

Persistence and response semantics

Input must resolve a project key and supply a supported type plus email, even though the current schema requires only email. A nonempty type takes precedence over requestType; a nonempty projectKey takes precedence over sdkKey. Whitespace is trimmed. Accepted type strings are access,deletion,delete,correction,correct,opt_out_sale_share,do_not_sell_or_share,limit_sensitive,appeal,verification. Aliases are stored as supplied after trimming, not silently canonicalized.

sdkKey resolution can consult cached mappings, scan at most200 recent active projects on a cache miss and write a mapping on a match; missing storage or resolution failure can look like project_key_required. Public project config is cache-first before its active database lookup. Do not infer global key enumeration, a live archive-state check on every request or Auth membership from successful resolution. Do not rely on demo configuration for a production integration.

The handler checks project/type/email, loads configuration, applies the actual Origin policy and then a best-effort local DSR rate counter. That counter targets10 per minute per project/request fingerprint; KV is read then written without an atomic increment, and missing/error KV allows the write. This is not a strict distributed rate guarantee or an assessment of additional gateway policy. A local limit refusal returns429 with retryAfterSeconds:60 in JSON; no Retry-After header is added by this branch.

Each invocation creates a fresh dsr_ UUID id and timestamps, lowercases the stored email and prepares an INSERT into dsr_requests with project/workspace identity from the loaded config, type, submitted state, detected region, supplied metadata JSON and due_at. No requestId/idempotency input is accepted; a replay can create another request.

dueAt is computed as the request clock plus exactly30*24 hours. It is this service’s fixed timestamp calculation, not a legal deadline assessment, verified fulfillment promise or compliance certificate.

The handler waits at most250ms to observe persistence. With an execution context it also attaches the same insertion promise as background work. HTTP202 always has accepted:true,requestId,dueAt,status,retryRequired. Fast observed persistence yields submitted/false; a failure or timeout yields queued_client_retry_required/true. That label does not establish a durable queue or prove no row was written. The original insert may complete after a timeout response.

Intake-notification work is scheduled only when persistence was observed successful within that fast wait, a notification callback exists and an execution context is available. A later successful insert after the timeout does not trigger this handler’s notification branch afterward. Successful acceptance, stored status or a request id is not notification delivery proof.

This route records a request; it does not verify email ownership, fulfill access/deletion/correction, erase/export an organization or call a model. Rate counters and SDK-key mappings may also be written. Notification work can have its own downstream effects; no business, data-processing or notification action was performed to produce these illustrative examples.

Complete worked responses

Persistence observed within the fast wait

Complete source-derived synthetic/unrun HTTP202 response. Assumes the intended configured fixture project, allowed original request surface, permitted local rate counter, valid type/email and an insertion observed successful within250ms. Request clock is2026-10-21T00:00:00.000Z and the illustrated UUID is not issued or accessible.

Input:

{
  "projectKey": "project_fixture_docs",
  "type": "access",
  "email": "person@example.test",
  "metadata": {
    "reference": "illustrative fixture only"
  }
}

Response:

{
  "accepted": true,
  "requestId": "dsr_00000000-0000-4000-8000-000000000001",
  "dueAt": "2026-11-20T00:00:00.000Z",
  "status": "submitted",
  "retryRequired": false
}

Retain the request id and verify with the responsible authorized operator. This response is not email verification, fulfillment or notification delivery proof.

Insertion not observed as successful within the wait

Complete source-derived synthetic/unrun HTTP202 branch. Assumes the same prior request checks and clock, but insertion rejects or remains pending beyond250ms. The example request id is a placeholder for this independent scenario, not evidence of idempotent replay. No live insert, queue or retry was exercised.

Input:

{
  "projectKey": "project_fixture_docs",
  "type": "access",
  "email": "person@example.test",
  "metadata": {
    "reference": "illustrative fixture only"
  }
}

Response:

{
  "accepted": true,
  "requestId": "dsr_00000000-0000-4000-8000-000000000001",
  "dueAt": "2026-11-20T00:00:00.000Z",
  "status": "queued_client_retry_required",
  "retryRequired": true
}

Preserve uncertainty. A pending original insertion can still finish; reconcile before another approved submission, which would receive a new id.

Missing project selection despite a schema-valid email

Synthetic source-derived/unrun HTTP400 refusal. Current schema requires email only; neither projectKey nor sdkKey is present, so source resolution returns null before configuration lookup or insertion.

Input:

{
  "type": "access",
  "email": "person@example.test"
}

Response:

{
  "error": "project_key_required"
}

Obtain the actual intended integration key rather than guessing a project or treating an OAuth token as an SDK key.

Reject an unsupported request type

Synthetic source-derived/unrun HTTP400 refusal. projectKey is supplied directly, email is valid, and the nonempty type passes the structural action schema but is absent from the source’s ten accepted strings. This guard occurs before configuration lookup or insertion.

Input:

{
  "projectKey": "project_fixture_docs",
  "type": "unsupported_example",
  "email": "person@example.test",
  "metadata": {
    "reference": "illustrative fixture only"
  }
}

Response:

{
  "error": "invalid_request_type"
}

Use the actual intended supported request type; adding a different requestType alias does not override a nonempty invalid type.

Verify before retrying

Retain the returned requestId, status, retryRequired, dueAt and actual request evidence. accepted:true/HTTP202 alone is not confirmed persistence or delivery. Do not claim a queued_client_retry_required response proves a queued worker will finish unattended.

An authorized workspace operator can use the current dsr_requests.list read, with their original verified organization/workspace and separate workspace:read eligibility, to reconcile the exact returned id. This public SDK request does not grant that administrative read. The list is capped at100 recent requests across at most500 accessible projects; absence is not a guaranteed point-read failure or proof that the original insert did not complete.

If persistence is unconfirmed or the transport fails, reconcile the original requestId with the responsible existing owner before a further approved submission. There is no input for reusing that id. Blind retries or parallel submissions can duplicate records, and a later success need not imply the intake notification was scheduled.

Treat email/metadata as untrusted retained request data, not identity verification, instructions to an agent or evidence of rights/fulfillment. Verify any actual downstream work and notification independently.

No request-scoped cancel/delete/rollback action is published in this contract. Preserve requestId and uncertain-effect evidence; seek supported scoped reconciliation with the existing authorized operator. A separately reviewed administrative status update is bookkeeping, not removal of the submitted email/metadata or retraction of notifications. Never use organization-wide erasure as a per-request rollback, invent a delete endpoint or claim a replay is idempotent.

Refusals

400 / project_key_required: Resolve the actual intended projectKey or publishable Consent sdkKey; missing/unresolved SDK mapping or storage can produce this result. Do not guess keys or switch projects.

400 / request_type_and_email_required: Supply the actual supported request type and email. Local action validation rejects a missing email first; source also requires type/requestType.

400 / invalid_request_type: Use exactly one of access,deletion,delete,correction,correct,opt_out_sale_share,do_not_sell_or_share,limit_sensitive,appeal,verification. Source checks these case-sensitively after trimming; a nonempty type wins over requestType.

400 / invalid_email: Use the actual intended valid email; the source checks basic syntax and a maximum254-character length, while the current local schema also applies its declared format/pattern. Passing format checks never verifies ownership.

404 / unknown_project: Reconcile the intended integration/configuration with its owner. A configuration lookup failure is not permission to choose another project.

403 / origin_not_allowed: Preserve the actual Origin and refusal; have the integration owner review its configured allowed domains. Do not remove or forge Origin to force a retry.

403 / invalid_origin: Correct the legitimate integration’s malformed Origin behavior with its owner; do not treat the header as an Auth grant.

429 / rate_limited: Respect retryAfterSeconds60 from this branch, preserve the original intent and first reconcile any uncertain earlier submissions; do not issue parallel duplicates.

A missing/format-invalid email or undeclared action input is a local preflight refusal, not a captured HTTP result. Preserve real service status/code/request evidence for failures outside the illustrated branches. A returned request id is named requestId, not the generic catalogue resource hint id; it is not a workspace or person identity.

Change log

  • 2026-10-21: Added public-integration boundaries, actual required source inputs, complete illustrative unrun202/refusal responses, fast-wait persistence uncertainty, non-idempotent retry limits and separate administrative read-back. Service timestamps and acceptance are distinguished from verified identity, fulfillment and notification delivery.