HTTP API
AgentInbox exposes a local control-plane HTTP API over either:
- a Unix domain socket
- a loopback TCP port when started with
serve --port
The HTTP surface is now resource-oriented and is intended to remain stable going forward.
Meta
GET /healthzGET /statusGET /openapi.jsonPOST /gc
Hosts
GET /hostsPOST /hostsGET /hosts/{hostId}GET /hosts/{hostId}/schema
POST /hosts accepts the shared provider/runtime host configuration:
{
"hostType": "github",
"hostKey": "uxcAuth:github-default",
"config": {
"uxcAuth": "github-default"
}
}
Streams / Sources
GET /streams/{streamId} and GET /sources/{sourceId} preserve their existing
detail fields and add streamStats (null when no stream exists).
eventCount remains the total number of identity rows, including pruned events;
replayableEventCount and prunedEventCount distinguish payload availability.
prunedThroughOffset, retainedFromOffset and highWatermarkOffset expose
replay boundaries. The new compaction-specific fields may be absent for
third-party backends.
POST /streams/{streamId}/compaction-preview accepts
{"retentionMs": 2592000000, "scanLimit": 1000} and performs a bounded,
read-only payload-compaction preview. The path uses the public source/stream ID.
It does not initialize a missing event stream or invoke a provider.
See CLI preview semantics for consumer
protection, sample counts, byte estimates and truncation fields.
Invalid durations/limits return 400, missing streams return 404, and a backend
without preview support returns 501. This endpoint cannot apply compaction.
POST /streams/{streamId}/compaction-apply is a separate, manual opt-in endpoint:
{"apply": true, "retentionMs": 2592000000, "batchSize": 100, "maxEvents": 1000, "maxPayloadBytes": 8388608}
apply: true and a positive retentionMs are required. The optional limits above
are both defaults and hard upper bounds. Unknown options (including preview-only
scanLimit) and missing/false apply return 400; missing streams return 404 and
unsupported backends return 501. No-consumer streams are skipped. Eligibility is
rechecked per short batch; retries skip committed pruned payloads. A single event
over the byte budget blocks progress with stopReason: oversized.
Responses report committed batches, pruned logical bytes, replay bounds,
nextOffset, hasMore and the stop reason. Identity stubs and historical offsets
remain, but payload pruning cannot be undone without a backup. There is no
automatic GC, VACUUM, or promise of immediate database-file shrinkage.
See manual compaction and replay boundaries
for latest, earliest, at_offset, at_time, reset and commit behavior.
History-unavailable failures return 409 with error, code: history_unavailable,
streamId, requested (offset/time/startPolicy), retainedFromOffset, and
highWatermarkOffset; these fields also survive CLI remote errors.
GET /sourcesPOST /sourcesGET /sources/{sourceId}GET /sources/{sourceId}/schemaPATCH /sources/{sourceId}DELETE /sources/{sourceId}POST /sources/{sourceId}/pausePOST /sources/{sourceId}/resumePOST /sources/{sourceId}/pollPOST /sources/{sourceId}/events
The canonical registration shape is host+stream:
{
"hostId": "hst_...",
"streamKind": "repo_events",
"streamKey": "holon-run/agentinbox",
"config": {
"owner": "holon-run",
"repo": "agentinbox"
}
}
POST /sources is the stream-oriented creation endpoint. GET /sources/{sourceId}
and related routes continue to operate on stream/source instances after
registration.
Agents
GET /agentsPOST /agentsGET /agents/{agentId}DELETE /agents/{agentId}POST /agents/{agentId}/pausePOST /agents/{agentId}/resume
pause persists agent.deliveryPaused independently of aggregation policy and
target offline state. It gates all proactive webhook/terminal notifications,
including digest, retry and timer notifications. Collection, matching, inbox
materialization, reads and explicit outbound replies continue. A request already
started before the pause may finish.
Resume with {"deliveryOnly":true} clears this flag and catches up once per effective active
target using persisted unacknowledged entries, without creating inbox entries.
Existing target offline state and retry/defer backoff are preserved. Repeated
resume calls retry due durable pending work, but never replay successfully
notified backlog. Pending catch-up is persisted before clearing the pause, so
request failure or process interruption can be retried by resume or the daemon.
Pause survives daemon restarts.
deliveryOnly defaults to false. With an omitted body or {}, resume retains
legacy recovery of all offline targets and also clears the delivery pause,
regardless of pause history. Concurrent calls with different scopes are
serialized so target recovery is not lost. Target-level resume only recovers
that target and never clears the agent delivery pause.
Activation Targets
GET /agents/{agentId}/targetsPOST /agents/{agentId}/targetsDELETE /agents/{agentId}/targets/{targetId}POST /agents/{agentId}/targets/{targetId}/resume
Timers
GET /timersPOST /timersPOST /timers/{scheduleId}/pausePOST /timers/{scheduleId}/resumeDELETE /timers/{scheduleId}
POST /timers accepts a narrow reminder body:
{
"agentId": "agt_...",
"at": "2026-04-15T08:00:00+08:00",
"message": "Analyze the agentinbox project and prepare today's task plan.",
"sender": "timer"
}
Use exactly one of at, every, or cron. every is an interval in milliseconds. timezone is optional and defaults to the daemon host timezone.
Subscriptions
GET /subscriptionsPOST /subscriptionsGET /subscriptions/{subscriptionId}DELETE /subscriptions/{subscriptionId}POST /subscriptions/{subscriptionId}/pollGET /subscriptions/{subscriptionId}/lagPOST /subscriptions/{subscriptionId}/reset
POST /subscriptions accepts:
{
"agentId": "agt_...",
"sourceId": "src_...",
"filter": {},
"trackedResourceRef": "pr:373",
"cleanupPolicy": {
"mode": "on_terminal_or_at",
"at": "2026-05-01T00:00:00.000Z",
"gracePeriodSecs": 300
}
}
cleanupPolicy defaults to {"mode":"manual"} when omitted.
POST /subscriptions always returns an envelope:
{
"subscriptions": [
{
"subscriptionId": "sub_...",
"agentId": "agt_...",
"sourceId": "src_..."
}
]
}
Non-shortcut creation returns one element. A shortcut may expand into multiple
subscriptions, so callers should always read subscriptions[].
Inbox
GET /agents/{agentId}/inboxGET /agents/{agentId}/inbox/itemsPOST /agents/{agentId}/inbox/itemsGET /agents/{agentId}/inbox/watchPOST /agents/{agentId}/inbox/ackPOST /agents/{agentId}/inbox/compact
POST /agents/{agentId}/inbox/items accepts a narrow direct-text ingress body:
{
"message": "Review PR #51 CI failure and push a fix.",
"sender": "local-script"
}
message is required. sender is optional.
POST /agents/{agentId}/inbox/ack accepts exactly one of:
{ "throughEntryId": "ent_..." }
{ "entryIds": ["ent_..."] }
{ "all": true }
Notes
-
The OpenAPI contract is generated from the same Fastify route schemas used for runtime validation and is served at
GET /openapi.json. -
local_eventis the only source type that supports direct manual append throughPOST /sources/{sourceId}/events. -
remote_sourceis supported and requiresconfig.modulePath(and optionalconfig.moduleConfig) on the stream/source config when registering. -
PATCH /sources/{sourceId}updates persistedconfigand/orconfigRefwithout changing thesourceIdor existing subscriptions. Active/error sources are re-ensured after update; paused sources keep their paused lifecycle state until explicitly resumed. -
POST /sources/{sourceId}/pauseandPOST /sources/{sourceId}/resumeapply only to managed remote sources. Pause stops the managed runtime without deleting its binding or stream state. Resume re-enters the normal ensure path.POST /sources/{sourceId}/polldoes not implicitly resume a paused source. -
GET /sources/{sourceId}may includeidleStatefor managed remote sources that are waiting for auto-pause or were auto-paused after their last subscription was removed.