Search the docs
Find a page, a section, or an endpoint.
Threads
A conversation, its owner, and the state it carries.
A thread is a row, not a grouping key. Messages are threaded when they arrive,
by Message-ID, In-Reply-To and References, and the thread carries the
two things software needs to coordinate with a person: a status and an
owner.
One call across every address
GET /threads is workspace-wide — identity_id narrows it to one mailbox
and is optional — and every row names the address it is in. It is the
listing to build a shared inbox on, because the folder
listing takes one address and one folder, and three
addresses over four folders is twelve requests for one screen.
A row carries enough of the newest message to draw it without a second
call: folder, from, from_name, preview and has_attachments,
beside identity, subject, status, assignee_id and message_count.
They are the newest message's facts, so folder is where that message is
filed and moves when you move it.
Fetch the conversation itself with GET /messages?thread_id=… when
somebody opens it. The row is for the list.
Status
| Status | Means |
|---|---|
open | Nobody has finished with it. |
waiting | Waiting on somebody outside. |
resolved | Done. |
human_review | A person needs to look. |
Owner
| Owner | Means |
|---|---|
human | A person is reading it. |
automation | Deterministic code owns it. |
agent | A model owns it. |
The pair is the coordination primitive. agent + open is software working
on something; human + human_review is the same conversation, in somebody's
inbox, waiting for them.
Hand a conversation to a person
When your software is out of its depth, give the conversation back. It appears
in the inbox, and thread.updated — carrying the new owner — tells whatever
was driving that it no longer is.
curl https://pidgeon.ai/api/v1/threads/feaa4f72-af24-4fa5-ad05-368a0a9217c6 -X PATCH -H "Authorization: Bearer $PIDGEON_API_KEY" -H "Content-Type: application/json" -d '{ "owner_type": "human", "status": "human_review" }'The SDK's handOff is the same two fields, named. It is one call because it
is one intention, and splitting it into two PATCHes is how a conversation
ends up owned by a person but not marked for review.
Who is answering it
owner_type says which kind of thing is answering. assignee_id says which
person — and the two compose rather than replacing one another. A thread
with status: "human_review" and no assignee is a handover nobody has taken,
which is the state a queue exists to make visible.
Somebody's id comes from GET /workspace/members. null puts the
conversation back in the unassigned queue, which is a decision like any other
and emits thread.unassigned.
curl https://pidgeon.ai/api/v1/threads/feaa4f72-af24-4fa5-ad05-368a0a9217c6 -X PATCH -H "Authorization: Bearer $PIDGEON_API_KEY" -H "Content-Type: application/json" -d '{ "assignee_id": "82438cb3-89a0-4c58-a79e-542e091f9ee8" }'List one person's queue with ?assignee_id=, and the work nobody has picked
up with ?assignee_id=unassigned. thread.assigned and thread.unassigned
are both subscribable, so a queue running through Pidgeon can post to Slack or
open a ticket when a conversation changes hands.
Assignment is a property of a shared address — one created as a team mailbox, or an agent's. A person's own inbox has no queue, however many colleagues they have.
Taking it back
There is no automatic hand-back. A person who has been given a conversation keeps it until somebody sets the owner again, which is deliberate: software deciding on its own that a human is finished is the failure mode this exists to prevent.
Pidgeon ships no agent runtime
The API and the webhooks are the substrate you build one on. There is no prompt, no model and no loop here, and saying otherwise would be the wrong kind of promise.