---
title: Ingest a batch of gateway events
description: "Internal gateway -> Wiblo hop, NOT a browser/cookie route."
api_method: POST
api_path: "/v1/agent-runs/{runId}/events/ingest"
canonical_url: https://wiblo.app/docs/developers/api/agent-runs/ingest-agent-run-events
last_updated: 2026-07-28T17:31:44+02:00
md_url: https://wiblo.app/docs/developers/api/agent-runs/ingest-agent-run-events.md
---

# Ingest a batch of gateway events

`POST /v1/agent-runs/{runId}/events/ingest`

Internal gateway -> Wiblo hop, NOT a browser/cookie route. Authenticated by a PER-RUN bearer token (`Authorization: Bearer <ingest token>`) constant-time compared against the run's sandbox-lease `ingest_token_hash` — a token minted for run A is rejected on run B. Service-role batch-appends each event to the durable `agent_events` log, idempotent on `event_id` (`source: "gateway"`); a retried batch dedups rather than duplicating. Returns `{ appended, deduped }` where `appended + deduped` equals the batch size. Ingested events then replay through the browser SSE by durable `seq`. Not rate-limited (high-frequency internal path).

## Path parameters

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `runId` | `string` | Yes |  |

## Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `events` | `array<IngestAgentEvent>` | Yes |  |

### The IngestAgentEvent object

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `eventId` | `string` | Yes |  |
| `type` | `"run.started" \| "turn.started" \| "message.user" \| "message.delta" \| "message.completed" \| "reasoning.delta" \| ...` | Yes |  |
| `threadId` | `string \| null` | No | Defaults to `null`. |
| `turnId` | `string \| null` | No | Defaults to `null`. |
| `payload` | `object` | Yes |  |
| `createdAt` | `string` | No |  |

## Request

**curl**

```bash
curl https://api.wiblo.app/v1/agent-runs/{runId}/events/ingest \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
  "events": []
}'
```

**TypeScript**

```ts
import { createSdk, ingestAgentRunEvents } from "@workspace/sdk"

const sdk = createSdk({ baseUrl: "https://api.wiblo.app" })

const { data, error } = await ingestAgentRunEvents({
  client: sdk,
  path: { runId: "..." },
  body: {
    "events": []
  },
})
```

## Responses

**`200`** — The batch was appended; counts reflect dedup on replay. Returns `IngestAgentEventsResponse`.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `appended` | `integer` | Yes |  |
| `deduped` | `integer` | Yes |  |

**`400`** — Body failed Zod validation (`VALIDATION_FAILED`) — e.g. an empty batch or an out-of-vocabulary event type. Returns `ApiErrorEnvelope`.

**`401`** — Missing, malformed, or wrong per-run ingest token (`INVALID_TOKEN`). A token for another run is rejected here. Returns `ApiErrorEnvelope`.

**`422`** — Path param failed UUID validation (`INVALID_PARAMS`). Returns `ApiErrorEnvelope`.
