#Compliance Caller Requests
Compliance caller-request endpoints provide masked access/deletion workflow rows for Compliance > Caller requests.
This is a metadata workflow contract. It does not expose call analytics rows, transcripts, recordings, raw caller identifiers, raw metadata, internal key or credential material, ciphertext, internal storage details, or stored identifier digests.
#API Routes
| Route | Method | Access |
|---|---|---|
/v1/compliance/caller-requests | GET | client_admin for own tenant/data environment; aidial_admin or aidial_operator with explicit client_id |
/v1/compliance/caller-requests/open-count | GET | Same scope rules as list |
/v1/compliance/caller-requests | POST | Internal aidial_admin or aidial_operator with explicit client_id, or API-key support_ingest/support-ingest |
/v1/compliance/caller-requests/{request_id}/status-transitions | POST | Internal aidial_admin or aidial_operator with explicit client_id |
All routes use the shared dual-mode authentication dependency. X-API-Key is evaluated before Authorization: Bearer <jwt>. Bearer authentication requires ZITADEL_ISSUER, ZITADEL_AUDIENCE, and ZITADEL_JWKS_URL; these settings are required and have no fallback defaults.
Partner roles (partner_admin and partner_user) and customer non-admin roles (client_manager and client_staff) are denied on every route in this contract and receive 404 Not Found; the denial is enforced before the request reaches the caller-request service. Out-of-scope tenants, inactive assignments, and invalid project scopes also fail closed with 404 Not Found.
#List Requests
GET /v1/compliance/caller-requests
Query parameters:
| Name | Type | Required | Description |
|---|---|---|---|
status | string | No | One of needs_review, ready, in_progress, completed, cancelled, unable_to_verify. |
due_state | string | No | One of normal, urgent, overdue, closed. The value is derived from status and the due date evaluated in the row's timezone: closed for terminal statuses, overdue once the due date has passed, urgent within seven days of it, otherwise normal. |
type | string | No | One of access or deletion. |
project_id | string | No | Active project filter. Must match [A-Za-z0-9._:-]{1,128}, resolve to an active project for the target tenant, and be in the caller's allowed project scope where applicable. |
client_id | string | No | Required for internal roles (aidial_admin, aidial_operator). Customer client_admin reads must omit it or match their own tenant. |
page_size | integer | No | Minimum 1, maximum 50, default 20. |
cursor | string | No | Opaque pagination cursor returned by a previous list response. |
Response:
{
"items": [
{
"id": "4d9f0d7a-8a60-4f8f-b64e-2a96a29d6d6f",
"tracking_id": "REQ-0001",
"revision_number": 1,
"request_type": "access",
"received_at_utc": "2026-05-01T00:00:00Z",
"due_at_utc": "2026-05-31T00:00:00Z",
"due_date_local": "2026-05-31",
"timezone": "Australia/Brisbane",
"due_state": "normal",
"days_remaining": 28,
"caller_display": "+61***123",
"contact_kind": "phone",
"identifier_kind": "phone",
"data_found_state": "manual_review_required",
"data_found_label": "Manual review required",
"project_labels": ["Reception"],
"status": "needs_review",
"status_label": "Needs review",
"linked_support_ticket_reference": "SUP-123",
"access_mode": "read"
}
]
}Optional fields without a value are omitted rather than serialized as null. This applies to days_remaining, data_found_count, closed_at_utc, linked_support_ticket_reference, and next_cursor on list, create, and transition responses. Treat an absent field as unset.
Results are ordered by received_at_utc descending, then by id descending. next_cursor is present only when more rows are available.
access_mode is read or transition. List responses return read for customer client_admin reads and transition for internal reads. Create and transition responses always return transition, including for support-ingest API keys, which cannot call the status-transition route.
#Open Count
GET /v1/compliance/caller-requests/open-count
Query parameters:
| Name | Type | Required | Description |
|---|---|---|---|
client_id | string | No | Same scoping rules as list. Required for internal roles. |
project_id | string | No | Optional active project filter. |
Response:
{
"open_count": 3
}Open rows are requests whose status is not completed, cancelled, or unable_to_verify.
#Create Request
POST /v1/compliance/caller-requests
Query parameters:
| Name | Type | Required | Description |
|---|---|---|---|
client_id | string | Conditional | Required for internal roles (aidial_admin, aidial_operator). API-key support-ingest callers may omit it or provide their own client id. |
Request body:
| Name | Type | Required | Description |
|---|---|---|---|
request_type | string | Yes | access or deletion. |
identifier_kind | string | Yes | phone or manual. |
caller_identifier | string | Conditional | Required when identifier_kind is phone. Treated as transient input only. |
contact_kind | string | Yes | One of phone, email, manual, letter, support_ticket, or other. |
contact_display_source | string | No | Source text used only to derive a masked display value for manual identifiers. |
received_at_utc | timestamp | Yes | Time the request was received. |
source | string | Yes | One of partner, internal, support, support_ingest, or api. |
source_project_id | string | No | Optional active project id for the target client. |
support_ticket_reference | string | No | Optional reference matching [A-Za-z0-9._:-]{1,128}. |
Successful creation returns 201 Created with a caller-request item. New rows start in needs_review with revision 1, data_found_state manual_review_required, and a sequential tracking_id in the form REQ-0001 that is unique per tenant and data environment.
due_at_utc is set to 30 days after received_at_utc, evaluated in the timezone of the referenced project when source_project_id is supplied, otherwise the client timezone, and UTC when neither is configured.
Phone identifiers are normalized to E.164, converted to masked display text, and hashed with a keyed server-side HMAC-SHA-256 step. The normalized plaintext is discarded before persistence; only the masked display and the resulting internal HMAC digest are stored. If server-side identifier hashing is unavailable, creation fails with 503 Service Unavailable and no workflow row is persisted. Manual identifiers do not produce a deterministic digest.
#Status Transitions
POST /v1/compliance/caller-requests/{request_id}/status-transitions
Query parameters:
| Name | Type | Required | Description |
|---|---|---|---|
client_id | string | Conditional | Required. Only internal roles (aidial_admin, aidial_operator) may transition; the value must identify the target client. Support-ingest API keys cannot transition requests. |
Request body:
| Name | Type | Required | Description |
|---|---|---|---|
expected_revision | integer | Yes | Current revision expected by the caller. Must be at least 1. |
status | string | Yes | One of needs_review, ready, in_progress, completed, cancelled, or unable_to_verify. |
status_note | string | Yes | Transition note, 1 to 500 characters. Stored in workflow history, not returned by list responses. |
processing_result | object | No | Optional processing update. |
processing_result fields:
| Name | Type | Required | Description |
|---|---|---|---|
data_found_state | string | No | One of unknown, searching, manual_review_required, found, not_found, or failed. |
data_found_count | integer | No | Non-negative count, when known. |
project_labels | array of strings | No | Display labels for matched projects. |
closed_at_utc | timestamp | No | Closure timestamp. If omitted for terminal statuses, the API sets it to the current UTC time. |
Successful transitions return 200 OK with the updated caller-request item. If expected_revision is stale, the route returns 409 Conflict. If the request id is not in scope, the route returns 404 Not Found.
#Privacy Contract
Caller identifiers are transient input only. identifier_kind is limited to phone or manual. Phone values are normalized to E.164, masked for display, and passed through a keyed server-side HMAC-SHA-256 step before the plaintext value is discarded. Manual, email, letter, support-ticket, and other contact records expose only masked display text and remain manual-review workflows without a deterministic digest.
Response models expose only the safe fields shown above. They never include raw identifiers, HMAC digests, ciphertext, internal key references, transcript text, recording URLs, raw metadata, client_id, source_project_id, API keys, or bearer tokens.
These endpoints do not perform phone or transcript decryption and do not unlock raw caller identifiers based on client PII flags. PII-sensitive call data remains outside this contract.
#Persistence And Audit
Caller-request workflow rows are stored in the local API metadata database on the serving API host, scoped by client_id and data_env. The implementation uses the API metadata store path and does not resolve routed tenant data store analytics routes for these endpoints.
Create and transition mutations write the workflow change and caller_request.* audit rows in one database transaction against portal_audit_events; audit failure rolls back the caller-request mutation.
Audit actions emitted by this contract are:
caller_request.createdcaller_request.status_changedcaller_request.support_linkedcaller_request.processing_recordedcaller_request.processing_failed
Audit metadata is limited to safe request identifiers, status/type state, due state, data-found count, and support-ticket reference where present.
#Error Semantics
| Condition | Status | Code |
|---|---|---|
| Missing authentication, invalid API key, expired API key, expired bearer token, invalid bearer token, or invalid bearer scheme | 401 Unauthorized | Platform auth error |
Unsupported list filter, invalid pagination cursor, phone identifier that is not valid E.164, missing caller_identifier for identifier_kind phone, or unresolvable tenant/project timezone | 400 Bad Request | validation_failed |
| Source address blocked by the tenant IP allowlist | 403 Forbidden | ip_allowlist_restricted |
| Valid authentication but unsupported role, inactive assignment, out-of-scope tenant, out-of-scope project, or missing request id | 404 Not Found | not_found |
| Stale transition revision | 409 Conflict | conflict |
| Request body or query parsing failure caught by FastAPI validation | 422 Unprocessable Entity | Platform validation error |
| Missing required bearer authentication configuration | 500 Internal Server Error | Platform auth error |
| Caller identifier HMAC unavailable | 503 Service Unavailable | caller_identifier_hmac_unavailable |
| Authentication lookup dependency unavailable | 503 Service Unavailable | Platform auth error |
| Caller-request storage unavailable | 503 Service Unavailable | caller_requests_unavailable |
Caller-request errors use a flat error envelope with a correlation id:
{
"code": "not_found",
"message": "Requested resource not found.",
"request_id": "9b603a24-4d95-4d0b-a865-20c0f8a2c5de"
}Platform authentication and FastAPI validation failures are raised before the caller-request handlers run and use the shared platform error envelope described in Errors.