List what needs attention
endpoint
Scopes:
contracts:read. Contracts-Acting-Person is required.starting_after and limit, ordered by urgency.
200 OK
Four kinds of row
Not every row is a letter. Somebody has to sign, somebody signed and then the words moved, somebody on your own side has to say yes before a position goes out. Every row carries akind, so a client can
branch on it.
Common on every row:
id, kind, contract, title, state, since, why_you.
string
required
A stable
atn_ id. It is what starting_after takes, and it is what you dedupe on across a poll and
a webhook-driven refresh.string
required
One of
letter, sign_wait, resign, approval. This is the field to branch on.string | null
required
Nullable. It is null on every kind except
letter.string
required
When this started waiting on you.
string
required
Why this row is yours, in one sentence. Not a role name. A reason, written for a person to read,
so never the thing your code branches on.
integer
On
letter rows. How many things this round asks of you.boolean
On
letter rows. Whether you have something staged on this contract and have not sent it. Private to
your seat.Ordering is urgency, not recency
- Groups are ordered by their most urgent row. A group is never split across a page.
- Within a group:
resignfirst, thenapproval, thensign_wait, thenletter. - Within one kind, the oldest
sincecomes first, because the thing that has been waiting longest is the most urgent.
Newest first would put a letter that arrived this morning above a re-sign row that has been blocking a
signature for a week. The week-old row is the one holding the deal up, so it sorts to the top.
Pagination
limit counts rows, never relationships. A group is never split, so a page can come back with
slightly more rows than you asked for in order to finish the last group. starting_after takes a row
id, the atn_ one. has_more and next_cursor are always present.
What is waiting on them
waiting_on_them is a separate tail of the response, outside data. It holds the contracts where the next
move is theirs, each one carrying relationship, counterparty, contract and since.
Keeping it out of data is the point: the count at the top of your inbox then stays honest about what is
actually yours to do.
The acting person is required here
This feed belongs to one person, and itsunsent field is private to that seat, so merging everybody’s
queue would push one person’s private drafts sideways across the org. See Errors for the
envelope.
It is read-only
A row leaves the list when the thing it was waiting on happens. You send a reply, you sign, the contract executes or is cancelled. Attention is a computed view of state, not a mutable inbox, so it can never disagree with the contracts it describes. Rows are cleared by acting on the contract, so the verb you want lives on the contract:
A forwarded mail thread also shows up in this feed when
ingress.detected fires, and that item has two
verbs of its own: POST /ingress/{id}/accept and POST /ingress/{id}/reject.
Approvals appear here, but you approve in the app
Anapproval row tells an approver what is waiting on them, and that is all the API does. Approval
routing is explicitly not a v1 API surface, so GET /attention shows an approver their rows and there is
no endpoint to approve through.
Building an inbox
PollGET /attention on login, then refresh it from webhooks rather than on a
timer. Seven events move this feed:
letter.received · reply.sent · contract.agreed · words.changed · contract.executed ·
contract.cancelled · ingress.detected
unsent is the field that makes a letter row honest. A contract with a reply staged and nothing sent is
waiting on you, not on them, and a Monday inbox that hides that is lying to its reader.