Audit Events
Generated HTTP reference for the 6 operations the published OpenAPI document describes under audit-events.
Audit Events
This page is generated from the published OpenAPI document. It is complete with respect to that document and says nothing about surfaces the document does not describe yet. See what is generated here for what that means.
Base URL: https://api.codespar.dev
Every operation below requires a Bearer token. See Authentication.
GET /v1/audit-events
Browse the audit chain
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
before_sequence | integer | no | Returns entries strictly below this sequence. Use the previous page's next_before_sequence. |
event_type | string | no | Exact match, or a prefix match when it ends with a dot. |
from | string (date-time) | no | ISO 8601. Defaults to seven days ago. |
limit | integer | no | Clamped to 1..200. Defaults to 50, and falls back to 50 when the value is 0 or is not a number; a negative value becomes 1. |
to | string (date-time) | no | ISO 8601. Defaults to now. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | Bad Request. invalid_before_sequence when the cursor is not a number, invalid_iso_8601 when from or to is not a date this server can parse, from_after_to when the window is inverted. An out-of-range limit is NOT an error here: it is clamped. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
events | array of object | yes | — |
next_before_sequence | integer,null | yes | The lowest sequence on this page when it was full; null when it was not. |
Example response
{
"events": [
{
"sequence_number": 0,
"event_type": "string",
"happened_at": "2026-01-15T12:00:00.000Z",
"payload": {},
"prev_hash": "string",
"entry_hash": "string"
}
]
}Example request
curl -X GET https://api.codespar.dev/v1/audit-events \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/audit-events/config
Read the chain verification thresholds
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
cold_max_staleness_seconds | integer | yes | — |
created_at | string (date-time) | yes | — |
hot_max_staleness_seconds | integer | yes | — |
hot_window_days | integer | yes | — |
initial_walk_completed_at | string,null (date-time) | yes | Null until a forward walk reaches chain tip. |
org_id | string | yes | — |
updated_at | string (date-time) | yes | — |
verification_window_days | integer | yes | — |
warm_max_staleness_seconds | integer | yes | — |
warm_window_days | integer | yes | — |
Example response
{
"org_id": "org_0000000000000000",
"verification_window_days": 0,
"hot_window_days": 0,
"hot_max_staleness_seconds": 1,
"warm_window_days": 0,
"warm_max_staleness_seconds": 1,
"cold_max_staleness_seconds": 1,
"created_at": "2026-01-15T12:00:00.000Z",
"updated_at": "2026-01-15T12:00:00.000Z"
}Example request
curl -X GET https://api.codespar.dev/v1/audit-events/config \
-H "Authorization: Bearer $CODESPAR_API_KEY"PATCH /v1/audit-events/config
Change the chain verification thresholds
Request body
| Field | Type | Required | Description |
|---|---|---|---|
cold_max_staleness_seconds | integer | no | — |
hot_max_staleness_seconds | integer | no | Capped at 259200 (72 hours). Above that the whole patch is refused. |
hot_window_days | integer | no | — |
verification_window_days | integer | no | — |
warm_max_staleness_seconds | integer | no | — |
warm_window_days | integer | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | object | Bad Request. regulatory_minimum_violation when the merged hot_max_staleness_seconds exceeds the ceiling, with limit carrying it as a number. window_ordering_invalid when the merged windows are out of order, with limit carrying the three values as an OBJECT so the caller can see which pair collided — the two bodies give limit different types, which is why they are a union here and not one schema. Nothing is written in either case. |
401 | object | Unauthorized. The x-codespar-user header is missing or blank. The credential itself was accepted; what is missing is who is acting. |
403 | object | Forbidden. human_session_required when the credential is an API key: this operation is attributed to a person, so a machine key cannot perform it. insufficient_role when the x-codespar-user member is not an admin or owner of the org. The API-key check runs FIRST, so a key with no x-codespar-user header gets this 403 rather than the 401 below. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
cold_max_staleness_seconds | integer | yes | — |
created_at | string (date-time) | yes | — |
hot_max_staleness_seconds | integer | yes | — |
hot_window_days | integer | yes | — |
initial_walk_completed_at | string,null (date-time) | yes | Null until a forward walk reaches chain tip. |
org_id | string | yes | — |
updated_at | string (date-time) | yes | — |
verification_window_days | integer | yes | — |
warm_max_staleness_seconds | integer | yes | — |
warm_window_days | integer | yes | — |
Example response
{
"org_id": "org_0000000000000000",
"verification_window_days": 0,
"hot_window_days": 0,
"hot_max_staleness_seconds": 1,
"warm_window_days": 0,
"warm_max_staleness_seconds": 1,
"cold_max_staleness_seconds": 1,
"created_at": "2026-01-15T12:00:00.000Z",
"updated_at": "2026-01-15T12:00:00.000Z"
}Example request
curl -X PATCH https://api.codespar.dev/v1/audit-events/config \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"verification_window_days": 0,
"hot_window_days": 0,
"hot_max_staleness_seconds": 1,
"warm_window_days": 0,
"warm_max_staleness_seconds": 1,
"cold_max_staleness_seconds": 1
}'GET /v1/audit-events/health
What the verifier says about this chain
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
actionable_status | "verifying" | "healthy" | "catching_up" | "link_unverifiable" | "degraded" | "broken" | yes | broken means a row is not what the chain says it is. degraded means an incident is open. link_unverifiable is the absence of proof rather than evidence of tampering: a stretch of chain has no verified link, either because the rows that would close it are gone or because a known break pins the verifier below it. catching_up is transient and self-healing. verifying means no walk has reached chain tip yet — which is not the same as no walk having finished. It is returned when there is no watermark row at all, and equally when initial_walk_completed_at is unset on the org's config row, which covers a walk that ran to completion without reaching the tip AND an org that has no config row at all. A chain can therefore sit at verifying indefinitely; use detail and verification.watermark_sequence to tell which case you are in. |
detail | string | yes | One human sentence explaining the status. Under verifying it is also the only field that separates the two ways to get there: it reads no verification run has ever completed when no watermark row exists, and initial verification walk in progress when one does. |
incidents | object | yes | — |
initial_walk | object | yes | — |
last_checked_at | string (date-time) | yes | — |
last_sequence_number | integer | yes | Chain tip. 0 for an empty chain. |
status | "healthy" | "degraded" | yes | The two-valued summary, kept for compatibility. degraded covers exactly four situations: a detected break (actionable_status broken), an open incident (degraded), an uncertified stretch of chain (link_unverifiable), and a chain still under verifying whose last_sequence_number is above verification.watermark_sequence. Read actionable_status for which one. Note what it does NOT cover: catching_up reads healthy here, on purpose. That state means the first walk DID complete and the verifier is merely behind on newer events, which is transient and self-healing. So healthy in this field is not the same claim as actionable_status: "healthy". |
verification | object | yes | — |
Example response
{
"status": "healthy",
"last_sequence_number": 0,
"last_checked_at": "2026-01-15T12:00:00.000Z",
"detail": "string",
"actionable_status": "verifying",
"verification": {
"window_days": 0,
"watermark_sequence": 0,
"coverage_ratio": 0,
"hot_bucket_max_staleness_seconds": 1,
"writer_tip_check_at": "2026-01-15T12:00:00.000Z",
"chain_link_check": {
"unverifiable_segments": 0,
"oldest_unverifiable_segment": {
"from_sequence": 0,
"to_sequence": 0,
"reason": "sequence_gap",
"first_detected_at": "2026-01-15T12:00:00.000Z"
},
"writer_tip_link_unverifiable": true
}
},
"incidents": {
"open_count": 1,
"acknowledged_count_30d": 1,
"open_truncated": true,
"open": [
{
"id": "obj_0000000000000000",
"detected_at": "2026-01-15T12:00:00.000Z",
"detected_at_sequence": 0
}
]
},
"initial_walk": {
"in_progress": true,
"events_processed": 0
}
}Example request
curl -X GET https://api.codespar.dev/v1/audit-events/health \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/audit-events/incidents
List chain-integrity incidents
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
status | string | no | Comma-separated: open, acknowledged, all. Defaults to open. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | Bad Request. invalid_status when none of the comma-separated values is one of open, acknowledged or all. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
incidents | array of object | yes | — |
Example response
{
"incidents": [
{
"id": "obj_0000000000000000",
"org_id": "org_0000000000000000",
"detected_at": "2026-01-15T12:00:00.000Z",
"detected_at_sequence": 0,
"status": "open",
"anpd_notification_status": "not_required",
"created_at": "2026-01-15T12:00:00.000Z"
}
]
}Example request
curl -X GET https://api.codespar.dev/v1/audit-events/incidents \
-H "Authorization: Bearer $CODESPAR_API_KEY"POST /v1/audit-events/incidents/{id}/acknowledge
Acknowledge a chain-integrity incident
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | yes | The incident UUID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
affected_from_sequence | number,null | no | Send with affected_to_sequence or with neither. One without the other is refused, and from above to is refused. |
affected_to_sequence | number,null | no | — |
anpd_notification_status | "not_required" | "notified" | yes | Terminal only. pending_assessment is refused with its own code: an incident cannot be closed while the notification question is still open. |
incident_reference | string,null | no | Your ticket or case number. Control characters are stripped; null is the same as omitting it. |
personal_data_affected | boolean | yes | Required. There is no default. |
resolution_summary | string | yes | Required. C0 control characters and DEL are stripped before both the emptiness check and the length check, so the 8000 limit is measured on the stripped text and a summary that is only whitespace counts as empty. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | object | Bad Request. pending_assessment_not_terminal when anpd_notification_status is pending_assessment, which is a working state and never a closing one; it is checked before everything else. Otherwise field_invalid, naming the field and why: empty and too_long for resolution_summary (with limit: 8000), invalid_type and too_long for incident_reference (with limit: 256), invalid_enum for anpd_notification_status, invalid_type for personal_data_affected, and paired_required, invalid_type or range_inconsistent for the affected-sequence pair. One field is reported per response, the first that fails. Note that a resolution_summary that is not a string reports empty, not invalid_type. |
401 | object | Unauthorized. The x-codespar-user header is missing or blank. The credential itself was accepted; what is missing is who is acting. |
403 | object | Forbidden. human_session_required when the credential is an API key: this operation is attributed to a person, so a machine key cannot perform it. insufficient_role when the x-codespar-user member is not an admin or owner of the org. The API-key check runs FIRST, so a key with no x-codespar-user header gets this 403 rather than the 401 below. |
404 | object | Not Found. No open or acknowledged incident with that id belongs to the caller's org. An incident that exists under another tenant is indistinguishable from one that does not exist. |
409 | object | Conflict. The incident is already acknowledged, either from an earlier call or from one that won a concurrent race. incident carries the current row, and is null only if the row disappeared between losing the race and being re-read. |
503 | object | Service Unavailable. lock_timeout: the per-org chain lock was not granted within the timeout, so nothing was written. Safe to retry. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
chain_sequence_number | integer | yes | Sequence of the chain entry this acknowledgement wrote. |
incident | object | yes | — |
Example response
{
"incident": {
"id": "obj_0000000000000000",
"org_id": "org_0000000000000000",
"detected_at": "2026-01-15T12:00:00.000Z",
"detected_at_sequence": 0,
"status": "open",
"anpd_notification_status": "not_required",
"created_at": "2026-01-15T12:00:00.000Z"
},
"chain_sequence_number": 0
}Example request
curl -X POST https://api.codespar.dev/v1/audit-events/incidents/{id}/acknowledge \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"resolution_summary": "string",
"anpd_notification_status": "not_required",
"personal_data_affected": true
}'