Skip to main content

Overview

The Inbox API lets your agent ask for something and wait — instead of hoping the user is in the conversation right now. User.Inbox.push() puts a card on the user’s desk: an approval to click, a notice to read, or a broken integration to reconnect. You get a receipt back immediately; the user deals with the card whenever they next open their inbox.
This is the counterpart to Channels. Channels.* sends a message into a conversation and expects the user to read it there. Inbox.push files a task that survives being ignored — it stays on the desk until it is acted on or expires.

Approval

Options the user resolves in one click

Notice

A finished-work or heads-up card

Connection fix

An integration the agent needs reconnected

Where you can call it

User.Inbox.push() takes no recipient — the card always goes to the user of the current execution context. That makes it available exactly where your code already has an ambient user.
The recipient is derived from the execution context or your own credentials — never from the payload, and it cannot be overridden. This is the same context split that makes User.get() work in a tool but require an explicit User.get(userId) in a webhook: surfaces with no ambient user have nothing for Inbox.push to target, and a push from one fails with no_user_context.To reach a specific user from a webhook or a pre-defined job, message them directly with user.send() or Channels instead.

push(input)

Parameters

string
required
The card’s first line. Trimmed and capped at 140 characters.
string
required
The human explanation — the card’s context line, and the prose in the detail pane. Capped at 1000 characters.
string
Longer text shown only when the user opens the card. Capped at 4000 characters.
A source link for the card — an issue URL, a document permalink. Must be http:// or https://. Rendered as a link the user can follow; never opened automatically.
'urgent' | 'high' | 'normal' | 'low'
How loudly the card announces itself. urgent is rate-limited — see Limits.
('approve' | 'redirect' | 'fix')[]
What the card offers. 'approve' gives the user a decision to make; 'fix' requires connection. See Card kinds for how this maps to what the user sees.
{ label: string; description?: string }[]
Two to four one-click answers. Labels are capped at 60 characters, descriptions at 200. Anything past the fourth option is dropped.
{ type: string; name: string }
Required with actions: ['fix']. type is the integration’s catalog slug ('google-calendar'), name is what the user should recognise ('Google Calendar').
string
Your idempotency and revision handle. 1–120 characters of A–Z, a–z, 0–9, ., _, :, -. Push the same key again and you land on the existing card instead of knocking a second time. Omit it for one-shot cards.
string
The conversation the card should hand off into when the user acts on it. Defaults to the current conversation.

Returns

'deposited' | 'updated' | 'exists' | 'capped'
deposited — a new card is on the desk. updated — an existing card with this key was revised in place. exists — a card with this identity was already there, unchanged. capped — the daily limit was reached and nothing was filed.
'input_request' | 'connection_fix' | 'agent_notice'
Which card kind the push routed to.
string
The key the card is filed under — your key if you supplied one, otherwise the generated one. Reuse it to revise the card later.
string
Only present on capped. A short explanation you can surface to the agent author.

Card kinds

You don’t pick the kind directly — it follows from what you ask for, in this order:
1

Two or more options → a question card

Supplying options (2–4) files an input_request. So does actions: ['approve'] without options — the Approve/Decline pair is synthesized for you, so the card still resolves in one click.
2

A fix action → a connection card

actions: ['fix'] with connection: { type, name } files a connection_fix — the card that walks the user through reconnecting the integration your agent is blocked on.
3

Everything else → a notice

Anything with no options and no fix — including actions: ['redirect'] — files an agent_notice: work you finished, something the user should know about.
If you supply both options and actions: ['fix'], the options win — the question card is checked first.

Limits

Inbox pushes are budgeted so an agent cannot train its user to ignore the inbox. Budgets are shared with the agent’s other inbox activity of the same class, so a question pushed from code spends the same allowance as a question the agent asks on its own.
Treat capped as an expected outcome, not an error. Branch on it and fold the information into your run summary instead of retrying:

Revising a card

Give a card a key and later pushes with that same key revise it in place rather than filing a second one. The user is not notified again for an unchanged card.

Errors

A full inbox resolves to capped. Everything else throws, with a code you can branch on:

Examples

Ask for an approval before acting

Report finished work from a recurring job

Dynamic jobs carry the user who created them, so a push inside one lands on that user’s desk:
Because the key is stable, tomorrow’s run revises today’s card instead of stacking a second one.

Ask the user to reconnect an integration

Best Practices

A digest, a review, a status card — anything your agent produces on a schedule should carry the same key every run. Without one, each run files a new card and the inbox becomes a log.
Only two urgent cards per user per day survive at that priority; the rest are demoted. Spending the allowance on routine cards means the genuinely urgent one arrives looking ordinary.
A notice asking “let me know if this is okay” needs the user to open a conversation and type. Two to four options turn the same ask into one click, and the answer comes back into the thread.
capped means the budget is spent for the day, so an immediate retry fails identically. Put the information in your return value instead — the user still gets it, through the conversation.
Users scan the first line. Approve the Q3 renewal quote is actionable at a glance; Action required is not.

TypeScript Support

The input and receipt are fully typed at the call site, so receipt.outcome narrows correctly without any annotation. If you need the shapes as named types, derive them from the method:

Next Steps

User API

Read and write the user data behind the card

Channels API

Send a message into the conversation instead

Jobs

Schedule the work that files the card

Spaces

Multi-agent delegation