---
title: "Ingest a single CloudEvent"
method: POST
path: "/v1/auditmanager/events"
---

# Ingest a single CloudEvent

`POST /v1/auditmanager/events`

Accepts one CloudEvent, validates its schema, and enqueues it for durable publication to Kafka.

Returns **202 Accepted** the moment the event lands in the in-process queue — Kafka and Postgres latency are fully hidden from the caller. Delivery from there is at-least-once with idempotent inserts on (`id`, `occurred_at`), so replays after a crash never produce duplicate rows in the audit store.

## Request body

- CloudEvent — CloudEvents v1.0 envelope. Spec: https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md
  - `specversion` '1.0'
  - `id` string
  - `source` string, required
  - `type` string, required
  - `subject` string, nullable
  - `time` string, date-time
  - `datacontenttype` string
  - `traceparent` string, nullable
  - `data` AuditData, required — OpenG2P-specific `data` shape. Open — extra fields allowed per event type.
    - `actor` Actor, required — Who (or what system) triggered the event. `extra="allow"` lets emitters carry custom actor attributes (e.g. a Keycloak `username` distinct from `name`, an OAuth `session_state`) without requiring a schema change here. Whatever extras are sent land in the `details.actor.*` JSONB column alongside the named fields below.
      - `type` 'user' | 'system' | 'service' | 'anonymous'
      - `id` string, required
      - `name` string, nullable
      - `roles` string[]
      - `ip` string, nullable
      - `session_id` string, nullable
    - `action` string, required
    - `outcome` 'success' | 'failure' | 'denied', required
    - `resource` Resource — The primary object acted upon (optional for plain logins).
      - `type` string, required
      - `id` string, nullable
    - `reason` string, nullable

## Response `202`

Event accepted into the ingest queue. The `id` you submitted is echoed back.

- AcceptedResponse
  - `id` string — Constant — identifies the service that produced this envelope.
  - `version` string — Envelope schema version (not the service version).
  - `responsetime` string, required — RFC3339 timestamp at which this response was produced.
  - `response` AcceptedPayload, required
    - `accepted` string, required — The `id` of the CloudEvent that was accepted into the ingest queue.
  - `errors` ErrorDetail[]
    - `errorCode` string, required — OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition).
    - `message` string, required

## Other responses

- `422` — Malformed CloudEvent — failed schema validation (missing required field, invalid `outcome` enum, unparseable `time`, etc.).
- `503` — Service not ready or backpressure. - `AUD-004` — ingest queue full. Retry with exponential backoff + jitter. - `AUD-005` — service startup not complete. - `AUD-006` — database health check failed (surfaces via `/health`, but also blocks ingest readiness indirectly).

---

[API](https://skmtc.net/openg2p/apis/openg2p-registry-beneficiary-portal-api.md) · [All operations](https://skmtc.net/openg2p/apis/openg2p-registry-beneficiary-portal-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/openg2p/openg2p-registry-beneficiary-portal-api/revisions/557f1a1d4145/schema)
