{"openapi":"3.1.0","info":{"title":"Privacy Requests client API","version":"1.0.0","description":"Limited workspace service API for cases and explicitly granted tasks. A Business workspace Admin creates tokens in Settings → API. Tokens are workspace service credentials, not personal logins: they do not create a staff session or inherit the creator's full permissions. Only the selected scopes apply. Up to 10 active tokens per workspace, expiry 1–365 days, up to 90 task grants per token. Access stops on expiry, revocation or loss of Business access; existing unexpired, unrevoked tokens resume if Business access returns. REST tokens are separate from OAuth MCP connections. Approval, response delivery, evidence uploads and case closure require staff action in the app. Deadlines are suggestions for staff review, not legal advice."},"servers":[{"url":"https://app.privacyrequests.co/api/v1","description":"Production"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Cases"},{"name":"Tasks"}],"paths":{"/cases":{"get":{"operationId":"listCases","tags":["Cases"],"summary":"List cases","x-required-scopes":["cases:read"],"description":"Requires cases:read. Returns real cases in ascending ID order, 50 per page. Pass nextCursor as after until nextCursor is null. No other filters are supported.","parameters":[{"name":"after","in":"query","schema":{"type":"string"},"description":"The previous response's nextCursor; omit on the first request."}],"responses":{"200":{"description":"Case page.","content":{"application/json":{"schema":{"type":"object","properties":{"cases":{"type":"array","maxItems":50,"items":{"$ref":"#/components/schemas/Case"}},"nextCursor":{"type":["string","null"]}},"required":["cases","nextCursor"]}}}},"401":{"description":"Missing, malformed, expired or revoked bearer token. Browser sessions do not authenticate this API.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing the operation's required scope, or workspace no longer has Business access (code: plan_upgrade_required).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"60 requests per minute per token, in fixed minute windows. Wait for Retry-After before retrying. Rate-limit headers are returned on 429 responses only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until the next window.","schema":{"type":"integer","minimum":1}},"X-RateLimit-Limit":{"schema":{"type":"integer","const":60}},"X-RateLimit-Remaining":{"schema":{"type":"integer","const":0}},"X-RateLimit-Reset":{"description":"Window reset timestamp.","schema":{"type":"string","format":"date-time"}}}},"500":{"description":"Internal server error. Retry reads; retry case creation with the same idempotency key and payload.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createCase","tags":["Cases"],"summary":"Create a case","x-required-scopes":["cases:create"],"description":"Requires cases:create. Creates a real case in the workspace without sending requester emails. receivedAt must not be in the future. Use a new Idempotency-Key for each new request. Reusing the same key and normalized payload with the same token returns the original case; different details return 409. Keys are scoped to the token, so retain the original token for retries during rotation. This does not verify identity or approve fulfillment.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","minLength":8,"maxLength":128,"pattern":"^[A-Za-z0-9_.-]{8,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCase"}}}},"responses":{"200":{"description":"Idempotent replay.","content":{"application/json":{"schema":{"type":"object","properties":{"caseId":{"type":"string"},"reference":{"type":"string"},"replayed":{"const":true,"type":"boolean"}},"required":["caseId","reference","replayed"]}}}},"201":{"description":"Case created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedCase"}}}},"400":{"description":"Invalid JSON, more than 8192 bytes, invalid fields, or workflow requirements not met. See error for details. JSON bodies must use Content-Type: application/json; unknown fields are rejected.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, expired or revoked bearer token. Browser sessions do not authenticate this API.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing the operation's required scope, or workspace no longer has Business access (code: plan_upgrade_required).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency-Key already used with different details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"60 requests per minute per token, in fixed minute windows. Wait for Retry-After before retrying. Rate-limit headers are returned on 429 responses only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until the next window.","schema":{"type":"integer","minimum":1}},"X-RateLimit-Limit":{"schema":{"type":"integer","const":60}},"X-RateLimit-Remaining":{"schema":{"type":"integer","const":0}},"X-RateLimit-Reset":{"description":"Window reset timestamp.","schema":{"type":"string","format":"date-time"}}}},"500":{"description":"Internal server error. Retry reads; retry case creation with the same idempotency key and payload.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/cases/{caseId}":{"get":{"operationId":"getCase","tags":["Cases"],"summary":"Read a case","x-required-scopes":["cases:read"],"description":"Requires cases:read. Returns the same summary fields as the case list, including requester contact details. It does not return files, notes or the full staff case view.","parameters":[{"name":"caseId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Case summary.","content":{"application/json":{"schema":{"type":"object","properties":{"case":{"$ref":"#/components/schemas/Case"}},"required":["case"]}}}},"401":{"description":"Missing, malformed, expired or revoked bearer token. Browser sessions do not authenticate this API.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing the operation's required scope, or workspace no longer has Business access (code: plan_upgrade_required).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource is absent or outside the token's workspace/task grants; practice cases are excluded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"60 requests per minute per token, in fixed minute windows. Wait for Retry-After before retrying. Rate-limit headers are returned on 429 responses only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until the next window.","schema":{"type":"integer","minimum":1}},"X-RateLimit-Limit":{"schema":{"type":"integer","const":60}},"X-RateLimit-Remaining":{"schema":{"type":"integer","const":0}},"X-RateLimit-Reset":{"description":"Window reset timestamp.","schema":{"type":"string","format":"date-time"}}}},"500":{"description":"Internal server error. Retry reads; retry case creation with the same idempotency key and payload.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/tasks":{"get":{"operationId":"listTasks","tags":["Tasks"],"summary":"List granted tasks","x-required-scopes":["tasks:read"],"description":"Requires tasks:read and explicit task grants configured by an Admin in Settings → API → Manage tasks. Empty until grants are added. Ascending task ID order, maximum 100 returned; no pagination. The supported grant limit is 90 tasks per token.","responses":{"200":{"description":"Granted tasks.","content":{"application/json":{"schema":{"type":"object","properties":{"tasks":{"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/Task"}}},"required":["tasks"]}}}},"401":{"description":"Missing, malformed, expired or revoked bearer token. Browser sessions do not authenticate this API.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing the operation's required scope, or workspace no longer has Business access (code: plan_upgrade_required).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"60 requests per minute per token, in fixed minute windows. Wait for Retry-After before retrying. Rate-limit headers are returned on 429 responses only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until the next window.","schema":{"type":"integer","minimum":1}},"X-RateLimit-Limit":{"schema":{"type":"integer","const":60}},"X-RateLimit-Remaining":{"schema":{"type":"integer","const":0}},"X-RateLimit-Reset":{"description":"Window reset timestamp.","schema":{"type":"string","format":"date-time"}}}},"500":{"description":"Internal server error. Retry reads; retry case creation with the same idempotency key and payload.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/tasks/{taskId}":{"patch":{"operationId":"updateTask","tags":["Tasks"],"summary":"Update a granted task","x-required-scopes":["tasks:update"],"description":"Requires tasks:update and an explicit grant. Changes the real task's status and notes. All three fields are required: copy updated_at from a fresh GET /tasks response into expectedUpdatedAt. On 409, read again and reconcile before retrying. Data Found, Deleted, Corrected and Needs Review require existing evidence uploaded through the app. Tasks on Closed, Completed or Rejected cases cannot be changed. Staff remain responsible for reviewing outcomes.","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTask"}}}},"responses":{"200":{"description":"Task updated.","content":{"application/json":{"schema":{"type":"object","properties":{"updated":{"type":"boolean","const":true},"updatedAt":{"type":"string","format":"date-time"}},"required":["updated","updatedAt"]}}}},"400":{"description":"Invalid JSON, more than 8192 bytes, invalid fields, or workflow requirements not met. See error for details. JSON bodies must use Content-Type: application/json; unknown fields are rejected.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, expired or revoked bearer token. Browser sessions do not authenticate this API.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing the operation's required scope, or workspace no longer has Business access (code: plan_upgrade_required).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource is absent or outside the token's workspace/task grants; practice cases are excluded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Task changed since expectedUpdatedAt. Read again before updating.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"60 requests per minute per token, in fixed minute windows. Wait for Retry-After before retrying. Rate-limit headers are returned on 429 responses only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"description":"Seconds until the next window.","schema":{"type":"integer","minimum":1}},"X-RateLimit-Limit":{"schema":{"type":"integer","const":60}},"X-RateLimit-Remaining":{"schema":{"type":"integer","const":0}},"X-RateLimit-Reset":{"description":"Window reset timestamp.","schema":{"type":"string","format":"date-time"}}}},"500":{"description":"Internal server error. Retry reads; retry case creation with the same idempotency key and payload.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Paste the token only (pr_live_…). Sent in Authorization: Bearer TOKEN. Never put it in a URL."}},"schemas":{"CreateCase":{"type":"object","properties":{"requesterName":{"type":"string","minLength":1,"maxLength":240},"requesterEmail":{"type":"string","maxLength":254,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"requestType":{"type":"string","enum":["access","deletion","correction","portability","opt-out","restriction-objection","appeal","other"]},"jurisdiction":{"anyOf":[{"type":"string","enum":["GDPR","UK GDPR","California CCPA/CPRA","Colorado CPA","Connecticut CTDPA"]},{"type":"string","const":"Staff review required"}]},"originalWording":{"type":"string","minLength":1,"maxLength":4000},"receivedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},"required":["requesterName","requesterEmail","requestType","jurisdiction","originalWording","receivedAt"],"additionalProperties":false,"examples":[{"requesterName":"Example Requester","requesterEmail":"requester@example.com","requestType":"access","jurisdiction":"Staff review required","originalWording":"Please provide my personal data.","receivedAt":"2026-09-01T12:00:00.000Z"}]},"UpdateTask":{"type":"object","properties":{"status":{"type":"string","enum":["Not Started","In Progress","No Data Found","Data Found","Deleted","Corrected","Needs Review","Complete"]},"notes":{"type":"string","minLength":1,"maxLength":4000},"expectedUpdatedAt":{"type":"string","minLength":1}},"required":["status","notes","expectedUpdatedAt"],"additionalProperties":false,"examples":[{"status":"In Progress","notes":"Started checking the assigned system.","expectedUpdatedAt":"PASTE_UPDATED_AT_FROM_LATEST_TASK_RESPONSE"}]},"CreatedCase":{"type":"object","properties":{"caseId":{"type":"string"},"reference":{"type":"string"}},"required":["caseId","reference"]},"Case":{"type":"object","properties":{"id":{"type":"string"},"reference":{"type":"string"},"request_type":{"type":"string"},"status":{"type":"string"},"requester_name":{"type":"string"},"requester_email":{"type":"string"},"due_date":{"type":["string","null"]},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","reference","request_type","status","requester_name","requester_email","due_date","created_at","updated_at"]},"Task":{"type":"object","properties":{"id":{"type":"string"},"case_id":{"type":"string"},"title":{"type":"string"},"status":{"type":"string"},"notes":{"type":["string","null"]},"updated_at":{"type":"string"}},"required":["id","case_id","title","status","notes","updated_at"]},"Error":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string"},"feature":{"type":"string"},"requiredPlan":{"type":"string"},"currentPlan":{"type":"string"}},"required":["error"]}}}}