Data Authority API Guide
How to call Data Authority REST endpoints for bitemporal entities. This guide covers the consumer side: what requests to send, what responses to expect, and how to query historical state.
Companion documents:
- Data Authority Endpoint Guide — how to implement a new endpoint (producer side)
- API Design — conventions for all REST APIs in this system
Common Headers
Section titled “Common Headers”Every request to a Data Authority endpoint requires the following headers.
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer token (JWT) issued by the identity provider |
Content-Type | Yes (POST/PUT) | application/json — required on requests with a body |
Accept | Yes | application/json |
X-Tenant-ID | Yes | UUID of the tenant whose data is being accessed |
The X-Tenant-ID header is the primary scoping mechanism. Every query, mutation, and navigation call is filtered to the tenant specified in this header. Requests that omit it are rejected with 400 Bad Request.
CRUD Operations
Section titled “CRUD Operations”Create
Section titled “Create”Send a POST to create a new entity. The server assigns a new eId (entity ID) and records the first version.
Request
POST /v1/{app}/{resource}Authorization: Bearer <token>X-Tenant-ID: <tenantUUID>Content-Type: application/jsonAccept: application/json
{ "name": "example value"}Response — 201 Created
{ "eId": "a1b2c3d4-...", "rId": "e5f6g7h8-...", "asOf": { "effectiveFrom": 1700000000000, "recordedFrom": 1700000000000 }, "payload": { "eId": "a1b2c3d4-...", "name": "example value" }, "metadata": { "tenantId": "<tenantUUID>" }}Send a GET to retrieve the current state of an entity. Append bitemporal query parameters to retrieve historical state — see Bitemporal Query Patterns.
Request
GET /v1/{app}/{resource}/{eId}Authorization: Bearer <token>X-Tenant-ID: <tenantUUID>Accept: application/jsonOptional query parameters:
| Parameter | Type | Description |
|---|---|---|
effectiveAsOf | epoch milliseconds | Retrieve the version effective at this point in business time |
recordedAsOf | epoch milliseconds | Retrieve the version as it was recorded at this point in system time |
Response — 200 OK or 404 Not Found
{ "eId": "a1b2c3d4-...", "rId": "e5f6g7h8-...", "asOf": { "effectiveFrom": 1700000000000, "recordedFrom": 1700000000000 }, "payload": { "eId": "a1b2c3d4-...", "name": "example value" }, "metadata": { "tenantId": "<tenantUUID>" }}Update
Section titled “Update”Send a PUT to update an existing entity. The server closes the previous version and creates a new one with an incremented version number and a new rId.
Request
PUT /v1/{app}/{resource}/{eId}Authorization: Bearer <token>X-Tenant-ID: <tenantUUID>Content-Type: application/jsonAccept: application/json
{ "name": "updated value"}Response — 200 OK
The response is an EntityRecord identical in structure to the Create response. The rId will differ from the previous version; the eId remains the same.
Delete
Section titled “Delete”Send a DELETE to retire an entity. Data Authority performs a logical soft delete: no rows are removed from the database. The entity is marked retired: true and will not appear in subsequent reads or queries.
Request
DELETE /v1/{app}/{resource}/{eId}Authorization: Bearer <token>X-Tenant-ID: <tenantUUID>Accept: application/jsonResponse — 200 OK
{ "eId": "a1b2c3d4-...", "rId": "z9y8x7w6-...", "retired": true}Query Operations
Section titled “Query Operations”Data Authority uses a two-step query protocol. The first call initiates a query and returns the first page of results together with an opaque page token. Subsequent calls use the page token to navigate forward through the result set.
Initiate Query
Section titled “Initiate Query”Request
POST /v1/{app}/{resource}/queryAuthorization: Bearer <token>X-Tenant-ID: <tenantUUID>Content-Type: application/jsonAccept: application/json
{ "filter": { ... }, "sort": [{ "field": "name", "direction": "ASC" }], "pagination": { "pageSize": 25 }}Response — 200 OK
{ "items": [ { "eId": "...", "payload": { ... }, "metadata": { ... } } ], "nextPageToken": "eyJwYWdlIjoxfQ==", "totalCount": 143}When nextPageToken is absent from the response, the current page is the last page.
Navigate Pages
Section titled “Navigate Pages”Request
GET /v1/{app}/{resource}/query/{pageToken}Authorization: Bearer <token>X-Tenant-ID: <tenantUUID>Accept: application/jsonThe pageToken value is opaque — treat it as a string and do not attempt to parse or construct it. Pass the exact value received in the previous response.
Response — 200 OK
Same structure as the initiate response. When nextPageToken is absent, this is the last page.
Filter Reference
Section titled “Filter Reference”The filter field in the query body accepts a recursive expression tree. Each node is either a leaf condition or a logical combinator.
Comparison Operators
Section titled “Comparison Operators”| Operator | Description |
|---|---|
EQ | Equal to |
NEQ | Not equal to |
GT | Greater than |
GTE | Greater than or equal to |
LT | Less than |
LTE | Less than or equal to |
Set Operators
Section titled “Set Operators”| Operator | Description |
|---|---|
IN | Value is one of a supplied list |
Null Operators
Section titled “Null Operators”| Operator | Description |
|---|---|
IsNull | Field is null |
String Operators
Section titled “String Operators”| Operator | Description |
|---|---|
Like | SQL-style pattern match (% wildcard) |
Contains | Substring match |
StartsWith | Prefix match |
EndsWith | Suffix match |
Logical Combinators
Section titled “Logical Combinators”| Operator | Description |
|---|---|
AND | All child conditions must be true |
OR | At least one child condition must be true |
NOT | Negates a single child condition |
For full JSON examples and the complete filter schema, see the Query DSL reference.
Error Responses
Section titled “Error Responses”All error responses use a consistent body structure:
{ "error": "<code>", "message": "<human-readable detail>", "requestId": "<correlation id>"}Use requestId when reporting issues — it correlates the client error to server-side logs.
HTTP Status Codes
Section titled “HTTP Status Codes”| Code | Meaning | When |
|---|---|---|
400 | Bad Request | Validation failure, malformed JSON, missing required header |
401 | Unauthorized | Missing or invalid bearer token |
403 | Forbidden | Token is valid but the caller lacks sufficient scope for this operation |
404 | Not Found | Entity does not exist, belongs to a different tenant, or has been retired |
409 | Conflict | Concurrent modification detected, or a uniqueness constraint was violated |
500 | Internal Server Error | Unexpected server-side failure — contact the service owner with the requestId |
Bitemporal Query Patterns
Section titled “Bitemporal Query Patterns”Every entity version carries two independent time axes:
- Effective time — when the fact was true in the business domain (e.g., when a price change took effect).
- Recorded time — when the fact was written into the system (system clock at write time).
The GET endpoint accepts effectiveAsOf and recordedAsOf query parameters as epoch milliseconds. Omitting a parameter defaults it to “now” on the server, which returns the currently effective, currently recorded version.
Common patterns
| Goal | Parameters |
|---|---|
| Current state | Omit both parameters |
| State as it was at a past business date | effectiveAsOf=<past epoch ms> |
| What the system believed at a past system time | recordedAsOf=<past epoch ms> |
| Full bitemoral point-in-time query | effectiveAsOf=<epoch ms>&recordedAsOf=<epoch ms> |
Bitemporal queries are read-only. Mutations (POST, PUT, DELETE) always write at the current server time for the recorded axis; the effective time may be set by including an effectiveAsOf parameter on mutation requests if the endpoint supports backdating.
For the conceptual model, time coordinate types, and the closed-open interval convention used by the persistence layer, see Bitemporal Persistence.
Copyright: © Arda Systems 2025-2026, All rights reserved