Reporting runs (ingest)

Ingesting is for non-hosted agents: your code runs the agent on your own infrastructure, and you report what happened so AgentOS can show it as runs and events. AgentOS is the observer; it never executes your agent.

If instead you want AgentOS to run the agent and hand you the output, that's the opposite flow. See Invoking hosted agents.


The run lifecycle

Every reported run follows the same shape:

startRun()  ──▶  run created (status: running)
   │
   ├─ emit(type, payload, level)   ×N      (progress, tool calls, custom events)
   │
   └─ complete()  |  fail()  |  cancel()   ──▶  status: completed | failed | cancelled

You open a run, emit events as work happens, and close it exactly once. The SDK wraps this; the raw endpoints are below.

import { AgentOS } from "@agentos-sdk/core";

const client = new AgentOS({ agentId: "<agentId>", apiKey: "aos_agent_…" });

const run = await client.startRun({ input: { invoiceId: "INV-001" } });
try {
  run.emit("invoice.fetched", { rows: 42 });
  const result = await processInvoice("INV-001");
  await run.complete({ output: { total: result.total } });
} catch (err) {
  await run.fail({ errorMessage: String(err) });
}

Or let withRun() close the run for you: it completes with your function's return value, or fails with the error it throws (and rethrows it).

const total = await client.withRun(async (run) => {
  run.emit("invoice.fetched", { rows: 42 });
  return { total: (await processInvoice("INV-001")).total };
}, { input: { invoiceId: "INV-001" } });

Authenticate ingest with an agent key (aos_agent_…, limited to its own agent) or a workspace key (aos_ws_…, any agent in the workspace). Personal keys are not accepted. See Authentication.


SDK ↔ HTTP map

Every SDK method is a thin wrapper over one ingest endpoint:

SDKHTTP endpointPurpose
client.startRun()POST /api/ingest/v1/run/startOpen a run, get a run_id
run.emit() and helpersPOST /api/ingest/v1/eventsAppend one or more events (and spans)
run.complete()POST /api/ingest/v1/run/completeClose as completed with output and token usage
run.fail()POST /api/ingest/v1/run/failClose as failed with an error
run.cancel()POST /api/ingest/v1/run/cancelClose as cancelled, with an optional reason
run.startTask()POST /api/ingest/v1/tasksCreate a task; the returned task has complete(), fail(), skip()
run.requestHumanInput()POST /api/ingest/v1/human-input/requestAsk a person in the dashboard and wait for the answer (long-poll)
(OpenTelemetry exporter)POST /api/ingest/v1/tracesSend OTLP/JSON traces. See Tracing & spans

Python uses the same methods in snake_case (start_run, start_task, request_human_input). Full request and response shapes are in the HTTP API reference.


Request fields and limits

EndpointFields
run/startagent_id (required), input (object), external_run_id (up to 128 chars), trigger_type (manual, scheduled, webhook, or sdk; default sdk), parent_run_id
eventsrun_id, events: 1 to 500 per request. Each has type (1 to 128 chars), level (default info), payload (object), optional occurred_at and span fields
run/completerun_id, output (object), optional input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens
run/failrun_id, error_message (up to 4000 chars), optional error_detail (object)
run/cancelrun_id, optional reason (up to 1000 chars)

In the SDK, pass token counts as run.complete({ output, usage: { inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens } }). They feed the cost and usage views.

Closing a run is idempotent: repeating the same close returns 200 again. Closing it with a different status returns 409 CONFLICT.


Events: ordering & batching

  • Events get a server-assigned sequence_number, so the run timeline renders in the order AgentOS received them. Emit in the order things happen.
  • The events endpoint takes an array, so batch several events into one call. The SDK batches and flushes for you. See Events.
  • Set occurred_at (ISO 8601) on an event to record when it happened if that differs from when you send it.

Correlating with your own IDs

Pass external_run_id on startRun to store your own identifier (a job id, a message id) on the run, so you can map an AgentOS run back to the work item that produced it.

{ "agent_id": "<agentId>", "input": { … }, "external_run_id": "job_8f2a" }

It also makes starting a run idempotent: starting again with the same external_run_id for the same agent returns the existing run instead of creating a second one. The SDK always sends one (a random UUID if you don't pass externalRunId), so retries after a network error are safe.


Errors

CodeHTTPMeaning
UNAUTHENTICATED401Missing, invalid, or revoked key
VALIDATION_ERROR400Body fails the limits above
FORBIDDEN403Key scoped to a different agent, or the agent is paused or archived
WORKSPACE_SUSPENDED403The workspace is suspended
NOT_FOUND404Unknown agent, run, or parent_run_id
CONFLICT409Run already closed with a different status
NO_PLAN402The workspace has no active plan. Choose one in Backoffice → Billing
RATE_LIMITED429Over your plan's per-minute limit. Wait for the Retry-After header

Per-plan rate limits are listed in the HTTP API reference.


Tips

  • Emit custom event types freely (invoice.fetched, retry.scheduled). They're just strings and render in the timeline. See Events.
  • Use Tasks for sub-steps you want tracked with their own status.
  • Want a notification when a reported run fails? Create an alert rule and send it to a webhook or your inbox.

See Hosted vs non-hosted for choosing between ingest and invoke, and the HTTP API reference for exact shapes.