Skip to main content
API reference

Audit Events

Generated HTTP reference for the 6 operations the published OpenAPI document describes under audit-events.

8 min read
View MarkdownEdit on GitHub

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

NameTypeRequiredDescription
before_sequenceintegernoReturns entries strictly below this sequence. Use the previous page's next_before_sequence.
event_typestringnoExact match, or a prefix match when it ends with a dot.
fromstring (date-time)noISO 8601. Defaults to seven days ago.
limitintegernoClamped 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.
tostring (date-time)noISO 8601. Defaults to now.

Responses

StatusBodyDescription
200objectOK
400objectBad 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

FieldTypeRequiredDescription
eventsarray of objectyes
next_before_sequenceinteger,nullyesThe 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

StatusBodyDescription
200objectOK

Response 200

FieldTypeRequiredDescription
cold_max_staleness_secondsintegeryes
created_atstring (date-time)yes
hot_max_staleness_secondsintegeryes
hot_window_daysintegeryes
initial_walk_completed_atstring,null (date-time)yesNull until a forward walk reaches chain tip.
org_idstringyes
updated_atstring (date-time)yes
verification_window_daysintegeryes
warm_max_staleness_secondsintegeryes
warm_window_daysintegeryes

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

FieldTypeRequiredDescription
cold_max_staleness_secondsintegerno
hot_max_staleness_secondsintegernoCapped at 259200 (72 hours). Above that the whole patch is refused.
hot_window_daysintegerno
verification_window_daysintegerno
warm_max_staleness_secondsintegerno
warm_window_daysintegerno

Responses

StatusBodyDescription
200objectOK
400object | objectBad 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.
401objectUnauthorized. The x-codespar-user header is missing or blank. The credential itself was accepted; what is missing is who is acting.
403objectForbidden. 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

FieldTypeRequiredDescription
cold_max_staleness_secondsintegeryes
created_atstring (date-time)yes
hot_max_staleness_secondsintegeryes
hot_window_daysintegeryes
initial_walk_completed_atstring,null (date-time)yesNull until a forward walk reaches chain tip.
org_idstringyes
updated_atstring (date-time)yes
verification_window_daysintegeryes
warm_max_staleness_secondsintegeryes
warm_window_daysintegeryes

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

StatusBodyDescription
200objectOK

Response 200

FieldTypeRequiredDescription
actionable_status"verifying" | "healthy" | "catching_up" | "link_unverifiable" | "degraded" | "broken"yesbroken 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.
detailstringyesOne 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.
incidentsobjectyes
initial_walkobjectyes
last_checked_atstring (date-time)yes
last_sequence_numberintegeryesChain tip. 0 for an empty chain.
status"healthy" | "degraded"yesThe 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".
verificationobjectyes

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

NameTypeRequiredDescription
statusstringnoComma-separated: open, acknowledged, all. Defaults to open.

Responses

StatusBodyDescription
200objectOK
400objectBad Request. invalid_status when none of the comma-separated values is one of open, acknowledged or all.

Response 200

FieldTypeRequiredDescription
incidentsarray of objectyes

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

NameTypeRequiredDescription
idstring (uuid)yesThe incident UUID.

Request body

FieldTypeRequiredDescription
affected_from_sequencenumber,nullnoSend with affected_to_sequence or with neither. One without the other is refused, and from above to is refused.
affected_to_sequencenumber,nullno
anpd_notification_status"not_required" | "notified"yesTerminal only. pending_assessment is refused with its own code: an incident cannot be closed while the notification question is still open.
incident_referencestring,nullnoYour ticket or case number. Control characters are stripped; null is the same as omitting it.
personal_data_affectedbooleanyesRequired. There is no default.
resolution_summarystringyesRequired. 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

StatusBodyDescription
200objectOK
400object | objectBad 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.
401objectUnauthorized. The x-codespar-user header is missing or blank. The credential itself was accepted; what is missing is who is acting.
403objectForbidden. 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.
404objectNot 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.
409objectConflict. 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.
503objectService Unavailable. lock_timeout: the per-org chain lock was not granted within the timeout, so nothing was written. Safe to retry.

Response 200

FieldTypeRequiredDescription
chain_sequence_numberintegeryesSequence of the chain entry this acknowledgement wrote.
incidentobjectyes

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
     }'
Audit Events | CodeSpar