atteq API
  • Guides
  • Recipes
  • Cookbook
  • API Reference
Information
agents
    List AgentsgetCreate AgentpostDocument AttachmentsgetGet VersiongetEdit DraftpatchReplace Draft GraphputPublish VersionpostGet AgentgetDelete AgentdeleteCopy AgentpostGet DraftgetGet Agent MapgetPending ChangesgetList PlacementsgetRestore VersionpostList VersionsgetGet Mapget
voice_agent
    Open Browser CallpostEnd Browser CalldeleteGet Agent Test LinkgetGet Bridge CeilinggetSet Bridge Ceilingpatch
ai_profiles
    Get Ai Profile CapabilitiesgetList Live SettingsgetCreate Live SettingspostDelete Live SettingsdeleteUpdate Live SettingspatchList Llm SettingsgetCreate Llm SettingspostDelete Llm SettingsdeleteUpdate Llm SettingspatchList ProfilesgetCreate ProfilepostGet ProfilegetDelete ProfiledeleteUpdate ProfilepatchCopy ProfilepostSet Text SectionputSet Voice SectionputList Stt SettingsgetCreate Stt SettingspostDelete Stt SettingsdeleteUpdate Stt SettingspatchList Tts SettingsgetCreate Tts SettingspostDelete Tts SettingsdeleteUpdate Tts SettingspatchList Vad SettingsgetCreate Vad SettingspostDelete Vad SettingsdeleteUpdate Vad Settingspatch
analyst
    List Agent FindingsgetCall Back FindingpostDismiss FindingpostUndo Finding DismissaldeleteTeach FindingpostWatch FindingpostList Question ChartsgetList Tracked QuestionsgetCreate Tracked QuestionpostGet Question Search CostgetSearch For Tracked QuestionpostPropose Tracked QuestionpostDelete Tracked QuestiondeleteUpdate Tracked QuestionpatchGet Reading SwitchgetSet Reading SwitchputGet Agent Summaryget
analytics
    List ChartsgetCreate ChartpostPreview ChartpostGet ChartgetDelete ChartdeleteUpdate ChartpatchRun ChartpostList DatasetsgetList BoardsgetCreate BoardpostGet TraversalgetGet BoardgetDelete BoarddeleteUpdate Boardpatch
authentication
    List InvitationsgetInvite MemberpostRevoke InvitationdeleteResend InvitationpostList MembersgetRemove MemberdeleteGet Member Conversation AccessgetSet Member Conversation AccesspatchSet Member FlagspatchSet Member RolepatchGet OrganizationgetUpdate OrganizationpatchGet Role FlagsgetSet Role Flagspatch
automations
    List AutomationsgetCreate AutomationpostGet AutomationgetDelete AutomationdeleteUpdate AutomationpatchSet Automation EnabledputGet Automation MapgetRun AutomationpostList Automation RunsgetGet Automation Runget
billable-metrics
    List MetricsgetUsage SummarygetGet Metricget
outbound_calls
    Place CallpostList CampaignsgetCreate CampaignpostGet CampaigngetDelete CampaigndeleteUpdate CampaignpatchActivate CampaignpostLink CollectionpostAdd ContactspostAdd Contacts From CollectionpostRemove Contacts From CollectionpostSet Campaign HoursputPreview Campaign HourspostPause CampaignpostGet Campaign SummarygetExclude ContactpostInclude Contactpost
channels
    List ChannelsgetCreate ChannelpostList Thread SubjectsgetClose Channel ThreadpostList Thread DecisionsgetGet Thread Contact FilegetHand Thread To AgentpostList Thread MailgetGet Prepared ReplygetAsk Again For Prepared ReplypostDiscard Prepared ReplypostSend Prepared ReplypostReply On Channel ThreadpostGet ChannelgetDelete ChanneldeleteUpdate ChannelpatchSend On ChannelpostList Channel ThreadsgetList RostergetCreate Roster EntrypostGet Own Roster EntrygetGet Roster EntrygetDelete Roster EntrydeleteUpdate Roster EntrypatchAdd Roster AddresspostChange Roster AddressputRemove Roster Addressdelete
custom-data
    List CollectionsgetCreate CollectionpostList Collection NamesgetGet CollectiongetDelete CollectiondeleteUpdate CollectionpatchAdd FieldpostDelete FielddeleteUpdate FieldpatchImport RecordspostPlan ImportpostPropose ImportpostGet ImportgetList RecordsgetCreate RecordpostBulk Delete RecordspostBulk Set FieldpostCount RecordsgetList Records Of ContactgetDelete Records Of ContactdeleteGet RecordgetDelete RecorddeleteUpdate Recordpatch
connections
    List ConnectionsgetCreate ConnectionpostCreate Forwarded AddresspostCreate Mail ServerpostDiscover Mail ServerpostAuthorize Microsoft MailboxpostList ProvidersgetGet ConnectiongetDelete ConnectiondeleteUpdate ConnectionpatchGet Forwarded AddressgetCheck Forwarded AddresspostList Person GrantsgetSet Person GrantputRemove Person GrantdeleteGet Mail ServergetUpdate Mail ServerpatchList Mail Server FoldersgetEnter Mail Server PasswordputChange Mail Server ServersputTest Mail Server AgainpostGet Microsoft MailboxgetUpdate Microsoft MailboxpatchList Microsoft Mailbox FoldersgetConsent To Microsoft Mailbox WritespostWithdraw Microsoft Mailbox Writesdelete
custom-tools
    List Custom ToolsgetCreate Custom ToolpostGet Custom ToolgetDelete Custom TooldeleteGet Custom Tool DraftgetEdit Custom Tool DraftpatchPublish Custom ToolpostRestore Custom Tool VersionpostTry Custom ToolpostList Custom Tool Versionsget
rag
    List DocumentsgetUpload DocumentpostSearch DocumentspostGet DocumentgetDelete DocumentdeleteDownload DocumentgetReingest Documentpost
flows
    List FlowsgetCreate FlowpostGet FlowgetDelete FlowdeleteRename FlowpatchAdd Flow EdgepostDelete Flow EdgedeleteSet Entry SteppatchAdd SteppostDelete StepdeleteUpdate SteppatchTest FlowpostList Flow Testsget
dynamic_context
    Create GraphpostGet GraphgetDelete GraphdeleteAdd EdgepostDelete EdgedeleteAdd NodepostDelete NodedeleteUpdate NodepatchSet Rootput
inbox
    Get Inbox Open CountgetSet Inbox PreferencespatchGet Inbox RailgetCreate Inbox ViewpostDelete Inbox ViewdeleteUpdate Inbox Viewpatch
handoff
    Assign Threadpost
interactions
    List InteractionsgetCount InteractionsgetGet InteractiongetSet Conversation LabelspostGet TracegetGet TranscriptgetList LabelsgetCreate LabelpostDelete LabeldeleteUpdate Labelpatch
legal
    Agree To Processing AgreementpostAgree To TermspostLegal BoxesgetList Legal DocumentsgetRead Legal DocumentgetList Legal VersionsgetRead Legal VersiongetDownload Legal VersiongetOrganization Legal HistorygetOwn Legal HistorygetObject To SubprocessorspostWithdraw ObjectionpostLegal Pendingget
not-a-tool
    Agree To Processing AgreementpostAgree To TermspostObject To SubprocessorspostWithdraw Objectionpost
memory
    List MemoriesgetList Memory LinesgetCreate Memory LinepostDelete Memory LinedeleteUpdate Memory LinepatchList Memory PeoplegetGet Memory PersongetForget PersondeleteGet Memory SwitchgetSet Memory SwitchputWipe ContactdeleteGet Conversation MemorygetRecall MemoriespostGet MemorygetDelete MemorydeletePatch Memorypatch
notifications
    List InboxgetGet Unread CountgetMark ReadpostMark Unreaddelete
recipes
    List RecipesgetList Installed RecipesgetGet Installed RecipegetUninstall RecipedeleteSet Recipe EnabledputUpdate Recipe ParameterspatchUpdate RecipepostInstall Recipepost
data-retention
    Get RetentiongetSet Retentionpatch
text
    Open ConversationpostClose ConversationpostAnswer ConfirmationpostGet Sent FilegetSend MessagepostSet Modepatch
tools
    List ToolsgetCreate ToolpostList BuiltinsgetImport Mcp ServerpostImport OpenapipostPreview Openapi ImportpostSuggest Tool ExamplespostGet ToolgetDelete TooldeleteUpdate ToolpatchTest ToolpostList Tool Testsget
trunks
    List Transfer DirectorygetCreate Transfer Directory EntrypostGet Transfer Directory EntrygetDelete Transfer Directory EntrydeleteUpdate Transfer Directory EntrypatchList TrunksgetCreate TrunkpostGet TrunkgetDelete TrunkdeleteUpdate TrunkpatchList Forward Recordsget
Schemas
powered by Zudoku
attek API
attek API

Schemas


AIProfileCapabilities

What the backend will accept — the one response a model picker, a voice picker, a slider's range, and the write path's own refusal all read, so a client and the server it talks to cannot end up disagreeing about what a value may be. A component's form is `models[kind]` filtered to `enabled`, then `classes[<the chosen row's configuration_class>].fields`.
​object · required
​ModelsByKind · required

Every registry row, per kind, disabled rows included with enabled=false: a picker offers the enabled ones by display name, and a component already on a disabled one can still say what it is on and that it is no longer offered. The client filters; the server does not guess which rows a caller already has written.

​ProvidersByKind · required
​object · required
​ReaderCeilings

What the reader reads at most, every value filled in: the platform's own (PLATFORM_READER_CEILINGS) or a profile's lower one, as ReaderLimits.ceilings resolves them. What the text channel holds an attachment to.

Default: {"document_bytes":10485760,"document_chars":50000,"image_bytes":5242880,"image_side_px":800,"images_per_burst":5,"questions_per_turn":3,"scan_pages":20,"sheet_rows":300}

AbsoluteWindow

`[since, until)` — half-open, matching every dataset owner's own convention (`list_interactions`, `summarize_usage`). A bound with no offset is interpreted as UTC rather than refused, so a window saved before offsets were required still opens. Send one anyway: `since` and `until` mean the instant they name, and the offset is what fixes it.
since
​string · date-time · required
until
​string · date-time · required

AccessLevel

string · enum
Enum values:
none
all
people
own

How much of one channel a member reads (authentication.md rule 32): none; every conversation; those a chosen set of people have or had; or only their own. Every level but none also reads what no person has or had and what the member has or had themselves (rule 33, owner ruling 2026-10-06).

ActRefusalOut

The body of a refused act: the sentence, and which refusal it is.
detail
​string · required
refusal
​ActionRefusal · enum · required

Why a finding's action is not offered to this person, or was refused (rule 28): it is offered only to someone who could do the act themselves.

Enum values:
outbound_calls_flag
no_outbound_trunk
agent_takes_no_calls
no_number_kept
text_agents_flag
not_this_finding_s_action
question_bound

ActionRefusal

string · enum
Enum values:
outbound_calls_flag
no_outbound_trunk
agent_takes_no_calls
no_number_kept
text_agents_flag
not_this_finding_s_action
question_bound

Why a finding's action is not offered to this person, or was refused (rule 28): it is offered only to someone who could do the act themselves.

AgentCopy

A copy needs its own name, "or a list of agents becomes a list of guesses". Everything else about the copy comes from what it was copied from.
name
​string · minLength: 1 · required

AgentCreate

name
​string · minLength: 1 · required
record_audio
​boolean
Default: false

AgentMapOut

One agent's frame, as this reader may see it (rule 40): the agent's own card, the outer cards its frame names (what brings it conversations, what it reads and writes), the lines between those, and the frame.
agent_id
​string · uuid · required
​MapPartOut · required

One card. key is the map's own handle for it (<kind>:<id>), what every line and lane names; id is the part's own id, for opening its screen (rule 23) -- for a documents or capability card, the agent's.

state is None for a card that carries no dot (rule 35): a collection, documents, a capability, a number. failure -- why it failed -- is filled only for a reader who may read where the failure was recorded (rule 30). missing marks a part something points at that no longer exists, or a part an incomplete recipe lacks; name is then the last name known, or None (rule 37).

​MapPartOut[] · required
​MapFrameOut · required

One opened card's inside (rule 11). parts are the cards inside it, each with frame set to key; lines every line drawn inside it, including those from a part inside to an outer card it reads or writes. reads and writes name the outer cards drawn above and below it; one both read and written is in writes alone (rule 17).

​MapGroupOut[] · required
hidden
​integer · required
​MapLineOut[] · required
name
​string · required
​required
published
​boolean · required
read_at
​string · date-time · required
version_id
​string · uuid · required

AgentOut

One row of the list of agents. `language` and `updated_at` are **not** columns of the agent — they are read off its versions, which is why they are built by `service` and not by a `model_validate` — and both are here so the list screen can show them without asking a second question per row.
​required
id
​string · uuid · required
language
​Language · enum · required

A language the platform can speak — which is to say, one it has default phrases written in. The agent states one of these and every phrase on it is written in that one; a language with no phrases behind it would be a label.

Enum values:
en
es
name
​string · required
owner_id
​string · uuid · required
platform
​boolean · required
record_audio
​boolean · required
​required
​required
updated_at
​string · date-time · required
​

AgentVersionOut

agent_id
​string · uuid · required
​required
answering_machine_enabled
​boolean · required
answering_machine_instruction
​string · required
​required
​required
​required
courtesy_window_seconds
​integer · required
created_at
​string · date-time · required
duration_ceiling_seconds
​integer · required
failure_threshold
​integer · required
first_line_delay_ms
​integer · required
graph_id
​string · uuid · required
greeting_interruptible
​boolean · required
id
​string · uuid · required
language
​Language · enum · required

A language the platform can speak — which is to say, one it has default phrases written in. The agent states one of these and every phrase on it is written in that one; a language with no phrases behind it would be a label.

Enum values:
en
es
search_fragment_limit
​integer · required
silence_timeout_seconds
​integer · required
status
​Status · enum · required

A version's place in the draft → published → archived lifecycle.

Enum values:
draft
published
archived
takes_documents
​boolean · required
takes_images
​boolean · required
takes_videos
​boolean · required
takes_voice_notes
​boolean · required
​object · required
​required
​VersionChange[]
document_ids
​string[]
global_tool_ids
​string[]
notices
​string[]
post_hook_tool_ids
​string[]
pre_hook_tool_ids
​string[]

Aggregation

string · enum
Enum values:
count
sum
avg
min
max

How a measure reduces the matching rows to one number per group. Owned here because analytics is the one caller that has to speak it identically over three different slices' data (interactions, billable_metrics, custom_data) — none of which may import this module to borrow it, so each one's own aggregate surface takes it as a plain string instead.

AgreedVersionOut

One version an entry names: the document, its name and the fingerprint of the text in the language shown (rule 26).
document
​LegalDocument · enum · required

The six legal documents the platform keeps (rule 1). Each value is also the last segment of the document's address, so it is spelled as a URL is.

Enum values:
terms
privacy
cookies
acceptable-use
processing-agreement
subprocessors
fingerprint
​string · required
language
​LegalLanguage · enum · required

The languages a version may be written in (values table): Spanish required, English optional, and the Spanish prevails (rule 4).

Enum values:
es
en
name
​string · required

AllTimeWindow

Everything stored — the third arm of `analytics.md`'s period, and the one a client reaches for when the question is "ever", not "lately". A one-field object rather than `window: None`, because `None` on an optional field already means "the caller said nothing" everywhere else in this slice (`ChartRunIn.window`, `BoardChartPlacement.window`) and a second meaning for it would make "no override" and "no bounds" indistinguishable on the wire.
all_time
​boolean · const · required
Const value:

AnalystCostOut

What the Analyst's work for this agent cost since `since` (rule 15). `amount` is `None` for a person who may not see consumption: they are told the Analyst is charged by what its model reads and writes, and no amount -- not even zero, which would be one. There is deliberately no price per conversation here: none is fixed.
​required
since
​string · date-time · required

AnswerField

One column a finished call fills in, and the question that asks for it. The question is **required and is the client's own words** — *"the machine number they gave"*, *"whether they agreed to a visit"*. A column asked for with nothing said about it is a column that fills up with noise, and the words are what the reading is actually done against, so they are not a label that can be left blank. What kind of answer it is, and which values it may take, are **not** here: those are the collection's, read from the field this names at the moment the questions are saved. Restating them would be a second declaration of the column's type, in a place that cannot be told when the first one changes.
key
​string · minLength: 1 · maxLength: 100 · required
question
​string · minLength: 1 · maxLength: 500 · required

AnsweredBy

string · enum
Enum values:
person
voicemail
menu
unknown

Who picked up an outbound call.

VOICEMAIL and MENU are separate members on purpose: "a number behind a menu is screened, not unattended, and a client deciding what to do with a list needs to tell them apart".

AnsweredFolderOut

The folder the mail a thread answered is filed in (email-threads.md rule 36): `id` as `MailFolderOut` gives it and its `name`, both `None` while nothing is filed.
​required
​required

AnsweringRecipe

The recipe whose flow a connection runs as its thread flow (recipes.md rule 38; ADR 0086), and its install: the flow is edited from that recipe and refused by hand, and the recipe's page is where the connection's answer is changed (UI: "answered by Triage").
install_id
​string · uuid · required
recipe_key
​string · required

ApiDescriptionIn

A published API description, by its address or as the file's text (api-import.md rule 1). Give exactly one. `url` is fetched by the platform, to the internet only. `document` is the text of a JSON OpenAPI 3.x or Google Discovery description.
​
​
​

ApiImportIn

What to create from a description: the server for the set, the operations the client chose (by the `key` the preview gave each, at most 50), and one connection or one group of connections for all of them, or neither. Importing again with the same server refreshes the tools already made from it and creates the ones not made yet.
operations
​string[] · minItems: 1 · required
server
​string · required
​
​
​
​
​

ApiImportOut

The tools created or refreshed, and the ones whose operation is gone.
​ApiMissingOut[] · required
​ToolView[] · required

ApiMissingOut

A tool whose operation the description no longer holds; left unchanged.
name
​string · required
operation
​string · required
tool_id
​string · uuid · required

ApiOperationOut

An operation that can become a tool. `key` is what to send in `operations`.
description
​string · required
​object · required
key
​string · required
​object · required
method
​string · required
name
​string · required
path
​string · required
summary
​string · required

ApiPreviewOut

What the client chooses from. Nothing was written.
kind
​string · required
max_operations_per_import
​integer · required
​ApiOperationOut[] · required
​ApiServerOut[] · required
title
​string · required
​ApiUnimportableOut[] · required

ApiServerOut

A server the description names, variables filled with its defaults. `url` is `None` for one given relative to a file: the client types it.
​ApiSuggestionOut · required

The connection a description suggests (api-import.md rule 9). provider http_key comes with the placement (header, query or basic) and the header / parameter / prefix to put in that connection; google suggests the client's Google account; None suggests nothing, and note says why.

​required
description
​string
Default:

ApiSuggestionOut

The connection a description suggests (api-import.md rule 9). `provider` `http_key` comes with the `placement` (`header`, `query` or `basic`) and the `header` / `parameter` / `prefix` to put in that connection; `google` suggests the client's Google account; `None` suggests nothing, and `note` says why.
​
note
​string
Default:
​
​
​
​

ApiUnimportableOut

An operation that cannot become a tool, and why (api-import.md rule 17).
key
​string · required
method
​string · required
path
​string · required
reason
​string · required
summary
​string · required

ApplyTo

string · enum
Enum values:
all
copy

Where an edit of a component used by more than one profile lands (ai-profiles.md rule 3: "change it for every profile on it, or make a copy for this profile only. It never guesses between them.").

ALL edits the component itself, so every profile on it changes. COPY edits a copy of it and repoints only the one profile named beside it.

ArrangementAttempt

One arrangement of the voice section that was tried at the start of a call and did not hold it, and why (interactions.md rule 10: it failed to open, or was passed over because it could not hold the agent's graph). `kind` is `ai_profiles`' arrangement kind as text -- this slice does not import that vocabulary, the same posture as the provider columns. `passed_over` is the second case: never tried, because it could not hold the graph (voice-agent.md rule 89). A row from before it reads `False`, which is what every such row was.
kind
​string · required
position
​integer · required
why
​string · required
passed_over
​boolean
Default: false

ArrangementIn

One complete way of holding a call. A `pipeline` names a recognizer, a language model, a voice and a detector; a `speech_to_speech` arrangement names one Live component and nothing beside it, because each of the others would be a second judge of something the live model already does -- the contradiction the spec says is never offered rather than refused. It is refused here all the same, since a request is not a screen.
kind
​string · enum · required
Enum values:
pipeline
speech_to_speech
​
​
​
​
​

ArrangementOut

One arrangement of a voice section, with its components resolved inline. The components of the other kind are `None`.
kind
​string · enum · required
Enum values:
pipeline
speech_to_speech
position
​integer · required
​
​
​
​
​

AssignIn

Who the thread is given to (handoff.md rule 23): a member of the organization, by person id — the id the member list and the record's `held_by_person_id` use.
person_id
​string · uuid · required

Attachment

One file sent with a message: its name, its media type and its bytes, base64-encoded. At most `MAX_ATTACHMENTS_PER_MESSAGE` per message, together at most `MAX_ATTACHMENT_BYTES` decoded. An image or a document the agent takes is read by its profile's reader and reaches the agent as a description or as the document's contents, marked as such; a video, or a kind the agent does not take, is announced. The file is kept for the organization's retention window and purged with the conversation.
data
​string · minLength: 1 · maxLength: 11184812 · required
filename
​string · minLength: 1 · maxLength: 255 · required
mime
​string · minLength: 1 · maxLength: 255 · required

AttachmentIn

One file sent by its public https link, which a person wrote (rule 76). The platform keeps no copy and never opens the link, so `size_bytes` and `mime` are what the sender declares, and a road's bounds are judged against them and against the link's own file name (rule 78). `filename` is the name the file is shown under where the road shows one; a road that tells a file's kind by its extension reads it from here, else from the link.
kind
​MediaKind · enum · required

What one outbound attachment is (channels.md rule 76): an image, a video, an audio file or a document. Not channels' AttachmentKind, which names what arrived -- a sticker, a contact card, a location among them -- and which no road is asked to send.

Enum values:
image
video
audio
document
url
​string · minLength: 1 · maxLength: 2048 · required
​
​
​

Audience-Input

string · enum
Enum values:
members
contacts

Who writes on a connection (channels.md rule 50): the organization's own members, or its contacts -- people outside it. A road declares which it can carry (rule 51); a connection names one of those. Not notifications' Audience, which is who a notice is for: same word, another question.

AuthorKind

string · enum
Enum values:
person
agent_builder

Who a published version came from.

AutomationCreate

flow_id
​string · uuid · required
name
​string · minLength: 1 · maxLength: 200 · required
trigger_kind
​TriggerKind · enum · required

What sets an automation off. Closed, and each member names exactly one place in this deployment where it is detected — see tasks.py and the webhook route.

Four, and four is all the spec asks for. automations.md used to list a fifth, "when a message or call arrives — including on channels we do not answer conversationally", and withdrew it on 2026-09-13 when channels.md was specified: "a channel nobody answers conversationally is simply a flow with no branch that hands to an agent", so an arriving message runs the channel's own flow and there is nothing left for a trigger to do. What an automation does with a channel is send on one — channels' send_on_channel built-in inside a scheduled flow, which is SCHEDULE. #395 closed.

Enum values:
interaction_ended
schedule
record_written
webhook
​
min_interval_seconds
​integer
Default: 300
​TriggerConfig

The small typed shape a trigger is configured by. Every field is optional here and required by service for the kind that needs it, so one column stores four vocabularies without four columns of nulls.

AutomationCreated

What `create_automation` answers with: the automation, plus the webhook secret **in clear, once**, for a `WEBHOOK` automation. The same posture `api_keys` takes with a key. Nothing stores the clear value, so a client that loses it rotates rather than recovers it.
consecutive_failures
​integer · required
enabled
​boolean · required
​required
flow_id
​string · uuid · required
has_webhook_secret
​boolean · required
id
​string · uuid · required
​required
​required
​required
min_interval_seconds
​integer · required
name
​string · required
org_id
​string · uuid · required
​required
​required
​TriggerConfig · required

The small typed shape a trigger is configured by. Every field is optional here and required by service for the kind that needs it, so one column stores four vocabularies without four columns of nulls.

trigger_kind
​TriggerKind · enum · required

What sets an automation off. Closed, and each member names exactly one place in this deployment where it is detected — see tasks.py and the webhook route.

Four, and four is all the spec asks for. automations.md used to list a fifth, "when a message or call arrives — including on channels we do not answer conversationally", and withdrew it on 2026-09-13 when channels.md was specified: "a channel nobody answers conversationally is simply a flow with no branch that hands to an agent", so an arriving message runs the channel's own flow and there is nothing left for a trigger to do. What an automation does with a channel is send on one — channels' send_on_channel built-in inside a scheduled flow, which is SCHEDULE. #395 closed.

Enum values:
interaction_ended
schedule
record_written
webhook
​
​

AutomationMapOut

One automation's frame, as this reader may see it (rule 40): the automation's own card, the outer cards its frame names (the collection whose record sets it off, what its flow reads and writes), the lines between those, and the frame.
automation_id
​string · uuid · required
​MapPartOut · required

One card. key is the map's own handle for it (<kind>:<id>), what every line and lane names; id is the part's own id, for opening its screen (rule 23) -- for a documents or capability card, the agent's.

state is None for a card that carries no dot (rule 35): a collection, documents, a capability, a number. failure -- why it failed -- is filled only for a reader who may read where the failure was recorded (rule 30). missing marks a part something points at that no longer exists, or a part an incomplete recipe lacks; name is then the last name known, or None (rule 37).

​MapPartOut[] · required
​MapFrameOut · required

One opened card's inside (rule 11). parts are the cards inside it, each with frame set to key; lines every line drawn inside it, including those from a part inside to an outer card it reads or writes. reads and writes name the outer cards drawn above and below it; one both read and written is in writes alone (rule 17).

​MapGroupOut[] · required
hidden
​integer · required
​MapLineOut[] · required
name
​string · required
​required
read_at
​string · date-time · required

AutomationOut

consecutive_failures
​integer · required
enabled
​boolean · required
​required
flow_id
​string · uuid · required
has_webhook_secret
​boolean · required
id
​string · uuid · required
​required
​required
​required
min_interval_seconds
​integer · required
name
​string · required
org_id
​string · uuid · required
​required
​required
​TriggerConfig · required

The small typed shape a trigger is configured by. Every field is optional here and required by service for the kind that needs it, so one column stores four vocabularies without four columns of nulls.

trigger_kind
​TriggerKind · enum · required

What sets an automation off. Closed, and each member names exactly one place in this deployment where it is detected — see tasks.py and the webhook route.

Four, and four is all the spec asks for. automations.md used to list a fifth, "when a message or call arrives — including on channels we do not answer conversationally", and withdrew it on 2026-09-13 when channels.md was specified: "a channel nobody answers conversationally is simply a flow with no branch that hands to an agent", so an arriving message runs the channel's own flow and there is nothing left for a trigger to do. What an automation does with a channel is send on one — channels' send_on_channel built-in inside a scheduled flow, which is SCHEDULE. #395 closed.

Enum values:
interaction_ended
schedule
record_written
webhook
​

AutomationRunOut

attempts
​integer · required
automation_id
​string · uuid · required
​required
​required
id
​string · uuid · required
​object · required
org_id
​string · uuid · required
outcome
​RunOutcome · enum · required

Where one run got to.

Enum values:
pending
running
succeeded
failed
did_not_terminate
​required
​required
​object[] · required
​required
trigger_key
​string · required

AutomationScope

string · enum
Enum values:
every_conversation
this_agent
schedule
record_written
outside_call
conversation_ends

For an automation: what sets it off (rules 7, 15).

AutomationUpdate

A partial edit. `enabled` is deliberately **not** here: enabling an automation is a consequential operation with its own route, and folding it into a general patch would let it happen inside an edit that was about something else.
​
​
​
​
​

BlockedReason

string · enum
Enum values:
hop_limit
immediate_return

Which bound stopped a hop — dynamic-context.md's two.

BoardChartPlacement

Where one widget sits on a board's grid — a saved chart, or the tree graph (`dashboard.md`'s "signature tree graph", `GET /boards/traversal`'s shape). `kind` defaults to `"chart"`, the only shape this slice shipped before this field existed, so a board saved by an older client — no `kind` key at all in its stored JSON — loads exactly as it did: a chart placement, `chart_id` present. A `"tree_graph"` placement carries no `chart_id` — there is no saved chart behind it — and instead names the one agent whose graph it shows, `agent_id`, plus an optional `window` (the same shape `traversal` itself accepts; omitted means that call's own default). Exactly one of `chart_id`/`agent_id` is required, by `kind`.
h
​integer · required
w
​integer · required
x
​integer · required
y
​integer · required
​
​
kind
​string · enum
Enum values:
chart
tree_graph
Default: chart
​

BoardIn

name
​string · minLength: 1 · maxLength: 200 · required
​BoardLayout

A board's arrangement, and its own global filters (docs/features/dashboard.md:7) — merged into every chart's spec at run time by run_chart/get_board and never written back into the chart itself (§3.5 of the design study).

BoardLayout

A board's arrangement, and its own global filters (`docs/features/dashboard.md:7`) — merged into every chart's spec at run time by `run_chart`/`get_board` and never written back into the chart itself (§3.5 of the design study).
​BoardChartPlacement[]
​Filter[]

BoardOut

created_at
​string · date-time · required
id
​string · uuid · required
​BoardLayout · required

A board's arrangement, and its own global filters (docs/features/dashboard.md:7) — merged into every chart's spec at run time by run_chart/get_board and never written back into the chart itself (§3.5 of the design study).

name
​string · required
org_id
​string · uuid · required
​required
updated_at
​string · date-time · required

BoardUpdate

Partial edit — only the provided fields change. No board is anyone's default (#525): which board a campaign shows is the campaign's own field.
​
​

Body_import_records

file
​string · required
​

Body_plan_import

file
​string · required
​

Body_propose_import

file
​string · required

Body_set_automation_enabled

enabled
​boolean · required

Body_upload_document

file
​string · required

BoxesOut

`GET /legal/boxes`: the exact words of each box, in each language, as the proof keeps them (rule 26). A screen shows these words and no others.
​object · required
​object · required
​object · required
​object · required

BridgeCeilingIn

A new bridge ceiling, in whole minutes, within the platform's range (5 minutes to 4 hours). Applies to bridges begun after it.
minutes
​integer · min: 5 · max: 240 · required

BridgeCeilingOut

How long a bridge of this organization's may last, from the moment its agent leaves (voice-agent.md rule 109). `minutes` is the value in force -- the default where the organization never set one, which `is_default` says -- and the range is the platform's.
is_default
​boolean · required
minutes
​integer · required
max_minutes
​integer
Default: 240
min_minutes
​integer
Default: 5

BrowserCall

Everything the browser needs to join the room it was just admitted into. What `open_browser_call` hands back over HTTP, and what `BrowserRoom` answers with — the same shape twice, because the route adds nothing to it: `interaction_id` is already on the engine's answer by the time it is asked (this slice opened the record before it asked), so there is one type rather than a route-facing one wrapping an engine-facing one. `token` is a LiveKit participant `AccessToken`, already signed — this slice holds no key to sign one itself and never sees the raw JWT beyond passing it through. `url` is the LiveKit server the browser dials, copied rather than assumed so a client never has to know this deployment's address out of band.
expires_at
​string · date-time · required
interaction_id
​string · uuid · required
room
​string · required
token
​string · required
url
​string · required

BrowserCallRefusalOut

The body of a refused browser call (409). `detail` says it in a sentence, as every error on this API does. When the refusal is `BrowserCallInProgress`, `interaction_id` and `agent_id` name the person's own call already open and the agent it was opened under (voice-agent.md rule 74), so a screen can end it through that agent's `DELETE` without parsing the sentence. **The route's other 409s** -- an unpublished agent, a provider that cannot run -- carry neither, and a call refused behind one nothing live could name carries neither either.
detail
​string · required
​
​

Bucket

string · enum
Enum values:
hour
day
week
month
year

A time-bucket width. Same authorship reason as Aggregation.

BuiltinOut

One first-party built-in this deployment runs, described so a person can make an instance of it from the panel (#558) -- `GET /tools/builtins`. `name` is what a row carries in `config["builtin"]`. `input_schema` is the schema that row is created with, fixed keys included; `fixed_parameters` names the keys a human usually fills on the instance and the model never sees; `provider` is the connection provider the instance needs (`google` for the calendar pair), or `providers` the several that can carry it. A built-in no client creates by hand -- one an organization gets made for it, like a collection's three -- is still listed, because the deployment binds it, with `input_schema` `None` and nothing else to offer.
name
​string · required
config_keys
​string[]
fixed_parameters
​string[]
​
​
providers
​string[]
​

BulkDeleteIn

What to delete. Nothing but the selection: which records, and that is the whole of the decision.
​required

BulkResult

What one bulk action did. `matched` is the number of records it actually touched, resolved the same way the count was — so a client that saw "3,412" and then reads "3,412" here is looking at one number asked twice, not two numbers that happen to agree.
matched
​integer · required

BulkSetFieldIn

One field, one value, over a selection. `value` is text and is parsed against the field's declared type, exactly as a filter's value is.
key
​string · minLength: 1 · maxLength: 64 · required
​required
value
​string · required

CallBackIn

The yes to calling the people back, with the retry policy a person must write (outbound-calls.md rule 7).
max_attempts
​integer · min: 1 · required
retry_delay_minutes
​integer · min: 1 · required
​

CallBackOut

campaign_id
​string · uuid · required
people
​integer · required

CallIn

The primitive: place one outbound call. `docs/features/outbound-calls.md` — *"the atomic unit is place one outbound call = { agent, outbound trunk, destination number, context payload }"*.
agent_id
​string · uuid · required
destination
​string · required
trunk_id
​string · uuid · required
​

When to place the call. Absent means it takes its turn in the organization's rotation, on the next free slot; a value is an instant somebody named, which jumps the rotation and is never brought forward into a free slot. A call that belongs to no campaign has no calling hours, so any instant is kept. Must carry a UTC offset (2026-09-10T11:00:00Z or 2026-09-10T13:00:00+02:00); without one the request is refused with 422 naming the field, so the call is never placed at a guessed-at moment.

​
​
​
told_fields
​string[]

CampaignIn

What a client sends to open a campaign. Every field that decides how it dials is here and pre-filled, except the ones that cannot be guessed -- and the retry policy, which is the client's to write (rule 7).
agent_id
​string · uuid · required
name
​string · minLength: 1 · maxLength: 200 · required
timezone
​string · required
trunk_id
​string · uuid · required
​AnswerField[]
​
​
​
collection_told_fields
​string[]
contact_source
​ContactSourceKind · enum

Where a campaign's contacts come from.

COLLECTION names docs/features/custom-data.md's stored collection. This slice holds the id and nothing else: the read is an injected ContactSource, bound to nothing today.

Enum values:
inline
collection
Default: inline
​
​
​
​

CampaignOut

agent_id
​string · uuid · required
​required
​required
​AnswerField[] · required
​required
​required
​required
collection_told_fields
​string[] · required
contact_source
​ContactSourceKind · enum · required

Where a campaign's contacts come from.

COLLECTION names docs/features/custom-data.md's stored collection. This slice holds the id and nothing else: the read is an injected ContactSource, bound to nothing today.

Enum values:
inline
collection
​object · required
created_at
​string · date-time · required
​required
​HoursSlot[] · required
id
​string · uuid · required
​required
name
​string · required
org_id
​string · uuid · required
​required
​required
​required
provider_failure_streak
​integer · required
​required
status
​CampaignStatus · enum · required

Where a campaign is in its life.

PAUSED is one state whoever caused it — see PauseOrigin. There is no separate "stopped by the provider" status, because resuming is the same operation either way and a second status would double every read of "is this campaign dialling".

Enum values:
draft
active
paused
completed
timezone
​string · required
trunk_id
​string · uuid · required
updated_at
​string · date-time · required
warnings
​string[] · required

CampaignStatus

string · enum
Enum values:
draft
active
paused
completed

Where a campaign is in its life.

PAUSED is one state whoever caused it — see PauseOrigin. There is no separate "stopped by the provider" status, because resuming is the same operation either way and a second status would double every read of "is this campaign dialling".

CampaignSummaryOut

What a campaign's screen reads, and all of it comes from this slice's own tables. There is no money and no minutes here, and that is deliberate: a per-campaign euro figure is `analytics`' shape and needs an aggregate on `interactions` that does not exist. A join from here would be two features reading one table.
as_of
​string · date-time · required
​object · required
attempts_spent
​integer · required
campaign_id
​string · uuid · required
​required
​object · required
contacts_without_number
​integer · required
due_later
​integer · required
due_now
​integer · required
status
​CampaignStatus · enum · required

Where a campaign is in its life.

PAUSED is one state whoever caused it — see PauseOrigin. There is no separate "stopped by the provider" status, because resuming is the same operation either way and a second status would double every read of "is this campaign dialling".

Enum values:
draft
active
paused
completed
unclassified_pickups
​integer · required
​

CampaignUpdate

A partial edit. `contact_source` and `collection_id` are absent on purpose: where a campaign's list comes from is decided when it is created, and changing it under a half-dialled list would leave the attempt ledger describing rows that no longer exist.
​
​
​
​
​
​
​
​
​
​

CapabilityField

One settings field's shape, in one configuration class. `min`/`max` are `None` when the field carries no numeric bound (a string or boolean field). `step` is `1` on a field the schema types as `int` (a fractional value is not merely out of range, the write rejects the *type*) and `None` on a continuous one. `default` is what a fresh component starts with, and `None` where the person has to write one.
kind
​string · enum · required
Enum values:
number
string
boolean
enum
strings
​
​
​
​
​
​
​
​

CatalogueVoice

One voice a provider offers: what to write on the row, what to show a person, and the language it speaks where the provider says so.
id
​string · required
name
​string · required
​

ChangeKind

string · enum
Enum values:
node_added
node_removed
node_instructions_changed
node_tools_changed
node_moved
edge_added
edge_removed
edge_condition_changed

What changed since the version before, in the vocabulary of the product.

Not a diff of text: each member is a sentence a client would recognise about their own agent. Rendering one into their language is the dashboard's job, which is why these are named states and not English strings.

Channel

string · enum
Enum values:
voice
text

Voice and text are two channels over one graph, not two kinds of record.

ChannelAccessBody

One level on a channel (authentication.md rule 32), and the people it names when it is `people` -- empty for every other level.
level
​AccessLevel · enum · required

How much of one channel a member reads (authentication.md rule 32): none; every conversation; those a chosen set of people have or had; or only their own. Every level but none also reads what no person has or had and what the member has or had themselves (rule 33, owner ruling 2026-10-06).

Enum values:
none
all
people
own
people
​string[] · maxItems: 500

ChannelIn

Create a channel: a connection, a name, and exactly one of an agent or a flow. `agent_id` is the short spelling of `flow_id` — the platform writes the one-step flow behind it — and passing both, or neither, is refused. `audience` is who writes on it (rule 50): one the connection's road declares (rule 51). Left out, it is the road's only one, or contacts on a road that carries both.
connection_id
​string · uuid · required
name
​string · minLength: 1 · maxLength: 200 · required
​
​
​
​
​
​
​

ChannelKind

string · enum
Enum values:
phone
whatsapp
platform_app
web_chat
tests_api

What an inbox channel is, as the person granting access would name it (inbox.md rules 6-7).

ChannelOut

​required
audience
​backend__features__messaging__schemas__Audience · enum · required

Who writes on a connection (channels.md rule 50): the organization's own members, or its contacts -- people outside it. A road declares which it can carry (rule 51); a connection names one of those. Not notifications' Audience, which is who a notice is for: same word, another question.

Enum values:
members
contacts
burst_window_seconds
​integer · required
connection_id
​string · uuid · required
created_at
​string · date-time · required
enabled
​boolean · required
flow_id
​string · uuid · required
hands_to_agent
​boolean · required
id
​string · uuid · required
inbox_off
​boolean · required
name
​string · required
org_id
​string · uuid · required
owns_flow
​boolean · required
updated_at
​string · date-time · required
agent_cannot_write
​boolean
Default: false
​
​
​
​

ChannelScope

One channel the inbox is entered by, and how many open conversations it holds. `key` is `InteractionOut.channel_key`'s value and the `channel_key` filter's. `label` is the number or the account's own name; empty where only the interface can name it -- web chat, tests and API, and a channel whose number or account was removed (`removed`, inbox.md rule 8).
key
​string · required
kind
​ChannelKind · enum · required

What an inbox channel is, as the person granting access would name it (inbox.md rules 6-7).

Enum values:
phone
whatsapp
platform_app
web_chat
tests_api
label
​string · required
open
​integer · required
removed
​boolean · required

ChannelUpdate

Partial edit — only the fields set change. The connection and the flow are deliberately absent: either is a different channel, not an edit to this one.
​
​
​
​
​
​
​

ChartIn

name
​string · minLength: 1 · maxLength: 200 · required
​ChartSpec · required

A chart's whole definition — what analytics_charts.spec stores, and what POST /analytics/charts/preview runs unsaved. dataset is one of the catalogue's keys (GET /analytics/datasets lists them): "interactions", "tool_executions", "node_traversal", "usage", the Analyst's "analyst_readings" and "analyst_answers", or "collection:<uuid>" for one of the organization's own custom_data collections.

There is no tz here, and its absence is the design. docs/features/analytics.md: "a chart's days belong to whoever is looking at it — the zone is resolved every time a chart is read, and is never saved with it" (owner, 2026-09-15). This is the same rule window already obeys one field up: what is stored is the question that was asked, not what that question happened to mean at the moment of saving. The zone arrives instead as a per-request argument to service.run_spec/run_chart, resolved at the edge by backend.api.deps.get_person_timezone from the person doing the reading.

A spec read back out of JSONB may still carry a tz key, and it is ignored. Pydantic's default extra="ignore" does that with no branch to maintain and nothing to remove later; DECISIONS.md records why this shape is tolerated where the pre-series shape was rewritten by a migration instead.

description
​string
Default:

ChartOption

One answer of a closed set and how many conversations gave it.
count
​integer · required
option
​string · required

ChartOut

created_at
​string · date-time · required
​required
description
​string · required
id
​string · uuid · required
name
​string · required
org_id
​string · uuid · required
​ChartSpec · required

A chart's whole definition — what analytics_charts.spec stores, and what POST /analytics/charts/preview runs unsaved. dataset is one of the catalogue's keys (GET /analytics/datasets lists them): "interactions", "tool_executions", "node_traversal", "usage", the Analyst's "analyst_readings" and "analyst_answers", or "collection:<uuid>" for one of the organization's own custom_data collections.

There is no tz here, and its absence is the design. docs/features/analytics.md: "a chart's days belong to whoever is looking at it — the zone is resolved every time a chart is read, and is never saved with it" (owner, 2026-09-15). This is the same rule window already obeys one field up: what is stored is the question that was asked, not what that question happened to mean at the moment of saving. The zone arrives instead as a per-request argument to service.run_spec/run_chart, resolved at the edge by backend.api.deps.get_person_timezone from the person doing the reading.

A spec read back out of JSONB may still carry a tz key, and it is ignored. Pydantic's default extra="ignore" does that with no branch to maintain and nothing to remove later; DECISIONS.md records why this shape is tolerated where the pre-series shape was rewritten by a migration instead.

updated_at
​string · date-time · required

ChartResult

A bounded chart run: per series, the largest `cap` groups by value, and whether more existed. Never silent (`docs/legacy-delta.md` REG-08's disposition, read as "keep the caps, lose the silence"). `truncated` is the **chart's**, not one series' — true when any series hit the cap. A reader is being told "this picture is incomplete", and a picture with one clipped line in it is incomplete whichever line it was.
cap
​integer · required
​ChartSeriesResult[] · required
truncated
​boolean · required

ChartResultRow

One group's number. `group` holds one entry per underlying column the spec's `dimension` resolved to — `{}` for a stat tile — keyed by that column's own name, exactly like `interactions.AggregateRow.group`; this slice reshapes each dataset owner's own result into this one shape rather than inventing a different one per dataset.
​required
​object · required
value
​string · pattern: ^(?!^[-+.]*$)[+-]?0*… · required

ChartRunIn

`POST /analytics/charts/{chart_id}/run`'s body: an optional window override, and additional filters, for this run only — neither is ever written back to the saved chart. `filters` is how a board's own global filters (`BoardLayout.filters`) reach a chart run from the client: it reads a board, then runs one of its charts with the board's filters attached here (`docs/features/dashboard.md:7`).
​Filter[]
​

ChartSeries

One question asked of the dataset — `analytics.md`'s "each a named measure over its own filters". What a series does **not** carry is as load-bearing as what it does: the dataset, the dimension, the window and the timezone are the chart's, not the series', because two numbers plotted against different axes or different stretches of time are not a comparison and this schema is where that stops being possible to express.
​Measure · required

agg(column), scaled if scale is given. column is None only for Aggregation.COUNT; every other aggregation names a column the dataset's catalogue declares as measurable. Validated against that catalogue by service.run_spec/service.create_chart — never trusted on its own.

name
​string · minLength: 1 · maxLength: 200 · required
​Filter[]

ChartSeriesResult

One series' own numbers, under the name the spec gave it. The name is echoed back rather than left for the caller to line up by index: a legend that has to trust two lists stayed in the same order is a legend one refactor away from labelling the wrong line.
name
​string · required
​ChartResultRow[] · required

ChartSpec

A chart's whole definition — what `analytics_charts.spec` stores, and what `POST /analytics/charts/preview` runs unsaved. `dataset` is one of the catalogue's keys (`GET /analytics/datasets` lists them): `"interactions"`, `"tool_executions"`, `"node_traversal"`, `"usage"`, the Analyst's `"analyst_readings"` and `"analyst_answers"`, or `"collection:<uuid>"` for one of the organization's own `custom_data` collections. **There is no `tz` here, and its absence is the design.** `docs/features/analytics.md`: "a chart's days belong to whoever is looking at it — the zone is resolved every time a chart is read, and is never saved with it" (owner, 2026-09-15). This is the same rule `window` already obeys one field up: what is stored is the question that was asked, not what that question happened to mean at the moment of saving. The zone arrives instead as a per-request argument to `service.run_spec`/`run_chart`, resolved at the edge by `backend.api.deps.get_person_timezone` from the person doing the reading. **A spec read back out of JSONB may still carry a `tz` key, and it is ignored.** Pydantic's default `extra="ignore"` does that with no branch to maintain and nothing to remove later; `DECISIONS.md` records why this shape is tolerated where the pre-series shape was rewritten by a migration instead.
chart_type
​ChartType · enum · required
Enum values:
bar
line
area
pie
donut
funnel
stat
dataset
​string · required
​ChartSeries[] · minItems: 1 · maxItems: 8 · required
​required
​

ChartType

string · enum
Enum values:
bar
line
area
pie
donut
funnel
stat

ChartUpdate

Partial edit — only the provided fields change.
​
​
​

ChartWeek

One week of a yes-or-no or a number question's chart, the week the reader's calendar cuts (analytics.md rule 8).
answered
​integer · required
​required
week
​string · date-time · required
​required

ChoiceIn

One choice to pick from: what it shows, and what comes back when it is picked -- its label when no payload is given.
label
​string · minLength: 1 · maxLength: 100 · required
​

ClassCapabilities

One configuration class as a picker reads it: the fields a component of that class shows (and nothing else), each with the bound the write path holds it to, and the engine attributes that say what the class does.
carries_language
​boolean · required
carries_speed
​boolean · required
ends_own_turn
​boolean · required
​object · required
kind
​string · enum · required
Enum values:
llm
stt
tts
live
provider
​string · required
reads_images
​boolean · required
reads_temperature
​boolean · required
sends_language_hint
​boolean · required

ClosedConversation

A conversation that has said goodbye. The farewell is the agent's own, from its published set — nothing here invents a parting sentence.
farewell
​string · required
interaction_id
​string · uuid · required

CollectionCreate

name
​string · minLength: 1 · maxLength: 200 · required
description
​string
Default:
​FieldIn[]
​

CollectionLink

Which list a campaign is being given, and which of its columns is dialled. **The two arrive together and neither is editable afterwards.** Which column holds the number is chosen once for the life of the campaign — every contact that enters freezes its number from it — so it is asked at the one moment there is a list in front of the person to choose it from. `records` is the same question the records screen asks everywhere else: the filters it was showing and the rows unticked afterwards, never a list of rows. Absent means the list is attached and nobody is taken from it yet.
collection_id
​string · uuid · required
number_field
​string · minLength: 1 · required
​
​

CollectionNameOut

The lean row `GET /collections/names` answers — id and name only, for a picker that stays open without the `databases` flag (rule 21).
id
​string · uuid · required
name
​string · required

CollectionOut

created_at
​string · date-time · required
description
​string · required
​FieldOut[] · required
​required
id
​string · uuid · required
​required
name
​string · required
org_id
​string · uuid · required
​required
slug
​string · required
updated_at
​string · date-time · required
​required
​
​
records_deleted_whole
​boolean
Default: false

CollectionQuestion

Which of a collection's records a campaign is being asked to take, or to let go of — **the question, never a list of rows**. `custom-data.md`'s ruling of 2026-09-09 in this slice's terms: a client ticking "select all" over a filtered collection of five hundred means the five hundred, not the two hundred their browser had drawn. So what crosses the wire is the filters the screen was showing, plus the rows they unticked afterwards; this side re-runs it against the store. An empty question is the whole collection, which is what a campaign created from a collection already takes.
exclude_ids
​string[] · maxItems: 10000
​SourceFilter[] · maxItems: 32
ids
​string[] · maxItems: 10000

CollectionUpdate

​
​
​

Condition

`field` is a dotted path into accumulated step outputs: `"<step_id>.<path>"`, where `<step_id>` is the id of the step whose output is being read.
field
​string · required
op
​ConditionOp · enum · required

The DSL's comparison operators, evaluated against a step output field.

Enum values:
===
!==
>
<
includes
exists
​

ConditionOp

string · enum
Enum values:
===
!==
>
<
includes
exists

The DSL's comparison operators, evaluated against a step output field.

ConfirmationAnswer

Yes or no to one question the agent asked. One field, and it is the whole of what a person may say here. The tool, its arguments and the request are all fixed by the question — a body that could adjust any of them would let the answer authorise something other than what was shown, which is precisely what the question exists to prevent.
decision
​ConfirmationDecision · enum · required

How a person answered the question a consequential tool was put behind.

Two members and no third. There is no "expired" or "timed out": a conversation that ends with a question outstanding answers it DECLINED, because the thing the record has to be able to say is whether the action happened, and it did not. Inventing a member for why nobody said yes would put two answers in the one field a client reads to find out.

Enum values:
confirmed
declined

ConfirmationDecision

string · enum
Enum values:
confirmed
declined

How a person answered the question a consequential tool was put behind.

Two members and no third. There is no "expired" or "timed out": a conversation that ends with a question outstanding answers it DECLINED, because the thing the record has to be able to say is whether the action happened, and it did not. Inventing a member for why nobody said yes would put two answers in the one field a client reads to find out.

ConfirmationMode

string · enum
Enum values:
ask
auto

Whether one conversation is still asking before consequential actions.

architect.md rule 20: "a person may tell one conversation to stop asking… It is a property of that conversation only — not of the person or the organization — and asking is always where a conversation starts."

It is a column vocabulary here and an input to the run-loop there, which is the same two-names split Channel has and for the same reason: this one is what the record says a conversation was set to, and that one is what decides whether the loop stops. Member for member, so a writer maps with interactions.ConfirmationMode(mode) and needs no table.

ConnectionIn

A connection to add: the provider, the name the organization will know it by, what it may do, and the secret the provider gave you. `group`, `subject_number` and `subject_ref` say on whose behalf it acts, when it is one person's. The platform's own app (`provider: "app"`) is the one provider with no outside account: send no `grant_secret` and no `scopes`, only a name that is not blank and not already used by another app connection in the organization. Every other provider needs its secret.
account_label
​string · required
provider
​string · required
​
granted_via
​GrantedVia · enum

Which door the grant arrived by.

The spec's "a grant can arrive by more than one door, and the connection is the same whichever it came through": this is the record of the door, and nothing in this slice or above it branches on it.

Enum values:
pasted
embedded_signup
Default: pasted
​
per_person
​boolean
Default: false
scopes
​string[]
Default: []
​
​

ConnectionOut

account_label
​string · required
capabilities
​GoogleCapability[] · readOnly · required

What a google connection may do, in the words the person picked from, and empty for every other provider.

scopes is on this shape too and stays: it is what the grant literally holds, including anything Google volunteered that this platform never asked about. But a scope URL is this module's vocabulary and not a person's, and connections.md is explicit that what a connection may do is offered "never as the provider's own vocabulary for the same thing" -- so the reading in the client's words travels with the row rather than leaving a dashboard to spell https://www.googleapis.com/auth/... back at them.

Enum values:
see_busy
see_appointments
manage_events
see_spreadsheets
manage_spreadsheets
see_presentations
manage_presentations
see_documents
created_at
​string · date-time · required
​required
granted_via
​GrantedVia · enum · required

Which door the grant arrived by.

The spec's "a grant can arrive by more than one door, and the connection is the same whichever it came through": this is the record of the door, and nothing in this slice or above it branches on it.

Enum values:
pasted
embedded_signup
id
​string · uuid · required
products
​GoogleProduct[] · readOnly · required

Which Google products this connection is authorized for, and empty for every other provider -- which has no such division, rather than one this would have to invent a word for.

A reading of scopes and never a column, for the reason GoogleProduct gives: what a connection is for is what its grant reaches, so a stored copy would be a second place for one fact and the first of the two to go stale. Computed here rather than filled in by the service because every path that hands a connection out goes through this class, and one of them would eventually forget.

Enum values:
calendars
spreadsheets
presentations
documents
contacts
files
mail
provider
​string · required
scopes
​string[] · required
status
​HealthStatus · enum · required

Whether the grant is currently usable.

Enum values:
healthy
broken
​required
updated_at
​string · date-time · required
​
​
per_person
​boolean
Default: false
​
​

ConnectionUpdate

Partial edit — only provided fields change (`model_dump(exclude_unset=True)`). Rotating `grant_secret` also clears any prior broken status — a fresh grant is presumed good until proven otherwise, mirroring `mark_healthy`. There is deliberately no `granted_via` here: how an account was authorized is a fact about the authorization, and a fresh secret through the same door is still that door. Re-authorizing by a *different* door is a different grant.
​
​
​
​
​
​

ContactFileOut

A file a contact sent, while retention keeps it (inbox.md, Conversation): a signed link good for `CONTACT_FILE_URL_TTL`, and what the road said of the file. `message_id` is the `ref_id` on the record's placeholder utterance, which is how an inbox line finds its file.
message_id
​string · uuid · required
size_bytes
​integer · required
url
​string · required
​
​
​

ContactIn

number
​string · required
​
​

When to call this contact. An instant somebody named: it jumps the rotation, outranks the retry delay and spends nobody's turn. An instant outside the campaign's calling hours is kept: the row is added and also returned in warnings, naming the hours. Omit it to let the contact take its turn in the rotation. Must carry a UTC offset (2026-09-10T11:00:00Z or 2026-09-10T13:00:00+02:00); without one the request is refused with 422 naming the field, so the call is never placed at a guessed-at moment.

​

ContactOut

attempts_spent
​integer · required
​required
​required
created_at
​string · date-time · required
due_now
​boolean · required
​required
​required
id
​string · uuid · required
​required
​required
​required
status
​ContactStatus · enum · required

One contact's place in the state machine outbound-calls.md calls single-writer and idempotent.

EXHAUSTED is we are finished with this number — the attempt budget is gone, the provider confirmed the destination will never work, or the organization's own retention window destroyed the number while the contact was still waiting for a turn and there is nothing left to dial. EXCLUDED is the client said not to call this person, and only a client ever writes it. Keeping them apart is what lets "excluding a contact cancels its pending calls" be re-openable while a confirmed-invalid number is not.

There is no pending. A contact is dialable the moment it exists — "activating a campaign does not queue its contacts" — so it is written SCHEDULED and there is no earlier state for it to sit in. A member nothing writes is a state a reader has to ask about and nobody can reach.

Enum values:
scheduled
calling
done
exhausted
excluded
updated_at
​string · date-time · required

ContactSourceKind

string · enum
Enum values:
inline
collection

Where a campaign's contacts come from.

COLLECTION names docs/features/custom-data.md's stored collection. This slice holds the id and nothing else: the read is an injected ContactSource, bound to nothing today.

ContactStatus

string · enum
Enum values:
scheduled
calling
done
exhausted
excluded

One contact's place in the state machine outbound-calls.md calls single-writer and idempotent.

EXHAUSTED is we are finished with this number — the attempt budget is gone, the provider confirmed the destination will never work, or the organization's own retention window destroyed the number while the contact was still waiting for a turn and there is nothing left to dial. EXCLUDED is the client said not to call this person, and only a client ever writes it. Keeping them apart is what lets "excluding a contact cancels its pending calls" be re-openable while a confirmed-invalid number is not.

There is no pending. A contact is dialable the moment it exists — "activating a campaign does not queue its contacts" — so it is written SCHEDULED and there is no earlier state for it to sit in. A member nothing writes is a state a reader has to ask about and nobody can reach.

ContactsIn

A paste or a CSV, already split into rows. Rejected rows are reported and never silently dropped. **The list is bounded by the cap a campaign holds**, so a paste no campaign could ever accept is refused at the edge, before ten thousand savepoints are opened to find that out one row at a time.
​ContactIn[] · maxItems: 10000 · required

ConversationAccessOut

What a member reads. `everything` is an owner or an admin, who read every conversation and whose access is never set (rule 35); `default` and `channels` are then every conversation, and nothing set on its own.
​object · required
​ChannelAccessBody · required

One level on a channel (authentication.md rule 32), and the people it names when it is people -- empty for every other level.

everything
​boolean · required

ConversationAccessUpdate

A member's whole conversation access, replaced: the level on every channel not set on its own (rule 34), and each channel that is, by the key `GET /interactions/scopes` names it with.
​ChannelAccessBody · required

One level on a channel (authentication.md rule 32), and the people it names when it is people -- empty for every other level.

​object · maxProps: 500

ConversationLabels

The labels a conversation carries after a change, by id, in the order they were put on.
interaction_id
​string · uuid · required
label_ids
​string[] · required

ConversationMemoryOut

What an agent's memory knew when a conversation started and what it noted from it (rule 33), to whoever may read the conversation and reaches memory. A person's note appears among them only as rule 31 says. On a conversation still under way, `noted` is what it has noted so far.
interaction_id
​string · uuid · required
kept
​boolean · required
​KnewNoteOut[] · required
​MemoryOut[] · required
noted_gone
​integer · required

ConversationMode

What a conversation is set to now. The mode is not readable anywhere else: it belongs to the live conversation and there is no row behind it. This is what the switch answers with, and it is the current state rather than a receipt for the change.
interaction_id
​string · uuid · required
mode
​ConfirmationMode · enum · required

Whether one conversation is still asking before consequential actions.

architect.md rule 20: "a person may tell one conversation to stop asking… It is a property of that conversation only — not of the person or the organization — and asking is always where a conversation starts."

It is a column vocabulary here and an input to the run-loop there, which is the same two-names split Channel has and for the same reason: this one is what the record says a conversation was set to, and that one is what decides whether the loop stops. Member for member, so a writer maps with interactions.ConfirmationMode(mode) and needs no table.

Enum values:
ask
auto

ConversationState

string · enum
Enum values:
open
ended

The inbox's state filter (inbox.md rule 11): still going, or over. Not stored -- ended_at is the one place that fact lives.

CustomToolAuthor

Who is publishing. Omitted, a publish is attributed to the acting person.
kind
​CustomToolAuthorKind · enum

Who a published version came from.

The same distinction agents records for a published agent version, spelled in this slice's own words because a slice does not import another's enum for a fact of its own.

Enum values:
person
builder
Default: person

CustomToolAuthorKind

string · enum
Enum values:
person
builder

Who a published version came from.

The same distinction agents records for a published agent version, spelled in this slice's own words because a slice does not import another's enum for a fact of its own.

CustomToolCreate

A new custom tool, as a draft, with the purpose a person will review.
name
​string · minLength: 1 · maxLength: 120 · required
purpose
​string · minLength: 1 · required
declared_reach
​string[]
description
​string
Default:
​object
​object
source_code
​string
Default:

CustomToolDraftUpdate

A partial edit of the draft. Only the fields that are set change.
​
​
​
​
​
​
​
​

CustomToolOut

One custom tool, as everything but its code.
created_at
​string · date-time · required
​required
declared_reach
​string[] · required
description
​string · required
id
​string · uuid · required
name
​string · required
org_id
​string · uuid · required
purpose
​string · required
status
​CustomToolStatus · enum · required

Whether this custom tool has ever gone live.

DRAFT is not "being edited" — there is always a draft. It is "no version of this has ever been published", which is what decides whether the registered row has anything to run.

Enum values:
draft
published
​required
updated_at
​string · date-time · required

CustomToolStatus

string · enum
Enum values:
draft
published

Whether this custom tool has ever gone live.

DRAFT is not "being edited" — there is always a draft. It is "no version of this has ever been published", which is what decides whether the registered row has anything to run.

CustomToolVersionOut

One version of a custom tool: the draft, or one that went live.
author_kind
​CustomToolAuthorKind · enum · required

Who a published version came from.

The same distinction agents records for a published agent version, spelled in this slice's own words because a slice does not import another's enum for a fact of its own.

Enum values:
person
builder
​required
change_summary
​string · required
created_at
​string · date-time · required
custom_tool_id
​string · uuid · required
id
​string · uuid · required
​object · required
​object · required
​required
source_code
​string · required
updated_at
​string · date-time · required
​required

CutCounts

How often the caller cut in on a call, and how each cut ended. interactions.md, "the settings it ran with": *"on a call, how often the caller cut in: proposed, confirmed and how, resumed -- by a breath ending or by the budget running out -- cut before a word was heard, and how many words were phantoms"*. Written once by the voice channel at the end of the call, through `settle`, and the reason it is on the record and not only on a dashboard is the same as the settings': how a call felt is on no version, and the thresholds that decide it are tuned on what this record says. `proposed` is every onset against a line that could be cut; the two `confirmed_*` are the ones the recognizer's final landed as a cut, by what first backed the proposal -- words on their way, or half a second of speech. Since a caller's sound lowers the line rather than stopping it (voice-agent.md rule 45, 2026-10-29) only words back a proposal, so `confirmed_by_duration` is 0 on every call the platform's switch lowered and counts only at its stop position. `withdrawn` the ones nothing confirmed, of which `withdrawn_by_budget` are the backed lowerings or pauses the dead-air budget ran out on (voice-agent.md: *"a cut that nothing follows is not a cut"*) -- the rest are a backing window or a stop's resume window running out, a segment closing short, a phantom, a backchannel, our own line's echo, or words from before the line -- and `resumed` how many of the withdrawn resumed a line in flight: returned it to full volume where it had got to, or resumed a paused one; `cuts` is lines actually ended by a cut, and `empty` the ones ended before a word of them was heard. `phantom` is a final of one word with no speech behind it: withdrawn, not a turn, and counted so the rule that drops it can be judged call by call. `before_line` is a final for speech the caller had finished before the line began: their turn and never a cut of it, whatever its word count, and counted for the same reason as `phantom` -- the exception has a cost, and only the count can say how often it is paid. `backchannel` is a final of one or two words that were all agreement -- *sí sí*, *vale*, *okay* -- which returns the line to full volume instead of cutting it and is recorded as heard rather than handed over as a turn; counted for `phantom`'s reason, since the closed list that decides it drops a real two-word interruption made of the same words. `None` on the record means no channel reported -- a text conversation, a call that ended before its counters were settled, a row from before the column. A row settled before `withdrawn_by_budget` and `phantom` existed, or before `before_line` and `backchannel` joined them on 2026-09-08, carries no key for them and reads `0`, which is what happened on every such row (DECISIONS.md, 2026-09-05).
backchannel
​integer · min: 0
Default: 0
before_line
​integer · min: 0
Default: 0
confirmed_by_duration
​integer · min: 0
Default: 0
confirmed_by_words
​integer · min: 0
Default: 0
cuts
​integer · min: 0
Default: 0
empty
​integer · min: 0
Default: 0
overtaken
​integer · min: 0
Default: 0
phantom
​integer · min: 0
Default: 0
proposed
​integer · min: 0
Default: 0
resumed
​integer · min: 0
Default: 0
withdrawn
​integer · min: 0
Default: 0
withdrawn_by_budget
​integer · min: 0
Default: 0

DatasetDimensionOut

bucketable
​boolean · required
column
​string · required
label
​string · required

DatasetFilterOut

One filterable column, and everything an editor needs to build a control for it: what it is called, what it holds, which questions it answers, and — for a column with a closed vocabulary — what is in that vocabulary. **`kind` and `options` are #521's fix.** Without them the only honest control is a plain text input whatever the column, so a date is typed in whatever format the person guesses and a wrong guess comes back as a refusal after the fact, and an enum is typed from memory with `voice` and `Voice` one typo apart. Recovering either in the browser would mean deciding that `received_at` is a date because of how it is spelled — a domain rule in the SPA, which `apps/web/CLAUDE.md` refuses outright.
column
​string · required
kind
​FilterKind · enum · required

What a filterable column holds, in the only terms a filter cares about.

Coarser than any one owner's own type vocabulary on purpose: it exists so a control can be chosen for a column, and a control is chosen by "is this picked from a list, typed as text, compared as a number, or picked from a calendar". custom_data.FieldType maps onto it one-to-one today; an analytics dataset column maps onto it from whatever it actually is — an enum column, a uuid a client picks by name, a timestamp.

Enum values:
text
number
date
boolean
option
label
​string · required
ops
​FilterOp[] · required
Enum values:
eq
ne
contains
gt
gte
lt
lte
in
options
​string[]

DatasetMeasureOut

agg
​Aggregation · enum · required

How a measure reduces the matching rows to one number per group. Owned here because analytics is the one caller that has to speak it identically over three different slices' data (interactions, billable_metrics, custom_data) — none of which may import this module to borrow it, so each one's own aggregate surface takes it as a plain string instead.

Enum values:
count
sum
avg
min
max
​required
label
​string · required
scales
​Scale[] · required
Enum values:
seconds_to_minutes

DatasetOut

One dataset's catalogue entry — `GET /analytics/datasets`' whole answer, one of these per dataset. The chart editor's entire left-hand side is data-driven from this: which datasets exist, their dimensions, their measures, their filterable columns and the operators each supports.
dataset
​string · required
​DatasetDimensionOut[] · required
​DatasetFilterOut[] · required
label
​string · required
​DatasetMeasureOut[] · required

DecisionAct

One act of a run (mail-handling.md rule 7): a step that did something beyond reading, by its built-in's name or the tool's own. `kept` is a reply kept as the prepared reply, `held` one the connection's reply mode kept (rule 4), `delivered` one that went; `labels` the labels it put on.
tool
​string · required
delivered
​boolean
Default: false
held
​boolean
Default: false
kept
​boolean
Default: false
labels
​string[]
ok
​boolean
Default: true

DecisionOut

A decision record (mail-handling.md rules 7-9), on the counterpart's message `message_id` names -- the `ref_id` of its line on the conversation. `outcome`, `acts`, `flow_id`, `models`, `failed` and `rule` are facts kept through the retention purge; `reason` is content and is `None` once purged, as `failure` is. `rule` is set, and nothing else is, for a burst that ran no flow (rule 8). `models` is the model each reading step read with, in order, `None` for the platform's own reading model.
​DecisionAct[] · required
decided_at
​string · date-time · required
failed
​boolean · required
​required
​required
id
​string · uuid · required
interaction_id
​string · uuid · required
message_id
​string · uuid · required
​array · required
​required
​required
​required

DecisionOutcome

string · enum
Enum values:
answer
draft
internal_only
escalate
ignore

The five outcomes a thread flow's run may declare (mail-handling.md rule 1): the counterpart was answered, a reply was kept for a person to send, something was done and nothing sent, the thread went to a person, or nothing was done beyond reading. A run declares it; the platform never infers it.

DecisionReading

One answer of one of the run's reading steps, with the question it answered (mail-handling.md rule 7) -- content, emptied by retention.
question
​string · required
answer
​

DeletedKey

One API key that died with the membership `DELETE /auth/members/{id}` just ended — api-keys.md, "it dies with its creator". Named here rather than reused from `api_keys.schemas`: this type crosses the boundary *upward*, through `service.OnMemberRemoved`, and `authentication` (tier 0) may not import a tier-1 module's shape to describe its own response (A4).
id
​string · uuid · required
name
​string · required

DeletionRefusalOut

The body of a refused delete (409). `routes` names what still sends conversations to the agent, so the screen can offer to go and unassign them instead of asking the client to find them. Same reading as `PublishRefusalOut`: `detail` says it in a sentence, and the field beside it says it in a shape.
detail
​string · required
​Route[] · required

Dimension

One `GROUP BY` axis, by the catalogue's own name for it — which may resolve to more than one underlying column (an answer-rate chart's `rang` × `answered_by`, in one dimension key) or to no `GROUP BY` column at all (a dataset's canonical time column, when `bucket` alone carries the grouping). `bucket` may accompany *any* dimension, not only the time one — "usage per metric per day" groups by `metric_code` *and* buckets by day at once.
column
​string · required
​

DirectoryEntryIn

A new entry of the transfer directory (rules 55-57). `target` is read as `kind` says: a number is normalised against the organization's default country when it carries none, an extension or an address is kept exactly as written. `peer_id` is the trunk -- `TrunkPeerOut.id`, the phone system's -- an extension or an address is reached through, and a number entry names none.
kind
​DirectoryTargetKind · enum · required

What a directory entry reaches (rule 55): a phone number, normalised to E.164 like every number; or an extension, or an address the client's phone system gives a person, a queue or a group -- both held as text exactly as the system names them (rule 56) and reached only through the entry's trunk.

Enum values:
number
extension
address
name
​string · minLength: 1 · maxLength: 80 · required
target
​string · required
​
​
​
ring_even_if_diverted
​boolean
Default: false

DirectoryEntryOut

An entry of the transfer directory, as whoever may set up a transfer tool reads it (rule 60). `reaches_nothing` is an extension or address entry whose trunk is gone (rule 61), or no longer a phone system with an address to send calls to: it is kept, and never shown to an agent. `closed` is the entry outside its hours now, read in the organization's timezone (rule 58); a closed entry is still transferable.
closed
​boolean · readOnly · required
created_at
​string · date-time · required
​required
id
​string · uuid · required
kind
​DirectoryTargetKind · enum · required

What a directory entry reaches (rule 55): a phone number, normalised to E.164 like every number; or an extension, or an address the client's phone system gives a person, a queue or a group -- both held as text exactly as the system names them (rule 56) and reached only through the entry's trunk.

Enum values:
number
extension
address
name
​string · required
​required
​required
reaches_nothing
​boolean · readOnly · required
ring_even_if_diverted
​boolean · required
target
​string · required
updated_at
​string · date-time · required

DirectoryEntryUpdate

A partial edit of an entry: only the fields sent change. `null` clears `hours`, `note` and `peer_id`; `name`, `kind` and `target` cannot be cleared. Every check `DirectoryEntryIn` meets is made against the entry the edit would produce.
​
​
​
​
​
​
​

DirectoryHours

One opening of a directory entry, on one weekday (0 is Monday), in the organization's timezone. The shape of a campaign's calling-hours slot, written again here rather than imported (this slice is tier 0): no slot crosses midnight.
ends_at
​string · time · required
starts_at
​string · time · required
weekday
​integer · min: 0 · max: 6 · required

DirectoryTargetKind

string · enum
Enum values:
number
extension
address

What a directory entry reaches (rule 55): a phone number, normalised to E.164 like every number; or an extension, or an address the client's phone system gives a person, a queue or a group -- both held as text exactly as the system names them (rule 56) and reached only through the entry's trunk.

DnsRecordOut

One record the client adds in the address's domain, as the platform's mail service lists it: its type, name and value, and whether the service found it the last time it looked (`None` before it ever did).
name
​string · required
record_type
​string · required
value
​string · required
​

DocumentAttachmentOut

One agent grounding on one document — the reverse of `document_ids`. **Two booleans and not one**, because the two states a person has to be able to tell apart are both "attached". `in_draft` is what the editor holds and `in_live` is what a caller reaches, and `docs/features/rag.md` makes the difference visible rather than discoverable: a document assigned and not yet published shows as pending, and one taken off and not yet published is still being read on calls. A single `attached` flag would have to pick one of those truths and silently lose the other. `name` is the agent's, because this answers a question asked from the library, where an agent id is not something anybody can read. It is built rather than validated from a row: an attachment is not a table, it is one pair drawn out of a version's array.
agent_id
​string · uuid · required
document_id
​string · uuid · required
in_draft
​boolean · required
in_live
​boolean · required
name
​string · required

DocumentOut

One document, as everything outside this slice sees it. **`content` and `text` are absent by construction**, the way `ToolView` omits `config`. A listing must never carry a 10 MiB blob, and building the shape without the field means there is no flag to forget. The bytes come back from one route, streamed.
byte_size
​integer · required
chunk_count
​integer · required
created_at
​string · date-time · required
error
​string · required
filename
​string · required
id
​string · uuid · required
media_type
​string · required
org_id
​string · uuid · required
status
​IngestionStatus · enum · required

Where a document is between "uploaded" and "searchable".

failed is a state, not an exception (DECISIONS.md D7): a PDF that will not parse stays in the library with a reason the client can read, because they uploaded a file and a file that vanishes is worse than one that says what went wrong.

Enum values:
pending
extracting
embedding
ready
failed
updated_at
​string · date-time · required

DomainState

string · enum
Enum values:
unchecked
not_registered
unverified
verified

What the platform's mail service last said of the address's domain.

unchecked until anybody asked; not_registered while the mail service holds no sending domain for it -- every client domain today, with no records to show, until per-client sending domains land (#1555); unverified while the records the client must add are not all found; verified once they are. Shown, and holding nothing back: no state lets or stops a send, since nothing leaves as the address -- answers leave from the address the platform gave the connection (rules 53-54, email-threads.md rule 28).

DraftUpdate

Partial edit of the draft's own fields (graph edits go through `dynamic_context` using the draft's `graph_id`). Only provided fields change. `utterances` is itself partial: the named phrases are rewritten and the rest are left alone. A key outside `Utterance` fails validation — that is the closed set being closed — and so does a blank phrase. A blank `name` is refused for the same reason. Omitting `name` leaves the draft's name as it was, and sending it as `null` does the same: `null` reads as "not given" here, so it is accepted and the name is left alone rather than emptied. The twelve values below are partial in the same sense and refuse in the same place: each number carries its range, so one outside it is refused at the boundary rather than stored, and omitting one keeps what the draft already has. `greeting_interruptible`, `answering_machine_enabled` and the three `takes_*` switches are `bool`s and take `False` as a value, which is why every one of these is read as "given or not" rather than as truthy. `record_audio` is `name`'s neighbour rather than theirs: both are columns of the **agent**, so neither is versioned and neither waits for a publish. It is here rather than on a route of its own because this is already the one call that edits an agent, and a second endpoint for one boolean would be a second place a client has to know about.
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​

EdgeIn

source_node_id
​string · uuid · required
target_node_id
​string · uuid · required
condition
​string
Default:

EdgeOut

condition
​string · required
id
​string · uuid · required
source_node_id
​string · uuid · required
target_node_id
​string · uuid · required

EnabledIn

enabled
​boolean · required

EnabledModelOut

configuration_class
​string · required
display_name
​string · required
enabled
​boolean · required
enabled_at
​string · date-time · required
id
​string · uuid · required
kind
​string · enum · required
Enum values:
llm
stt
tts
live
​required
​required
model
​string · required
provider
​string · required
​
​

EndReason

string · enum
Enum values:
closed
idle
duration_limit
error
abandoned
session_limit

Why the conversation ended.

Deliberately extendable: what ends a text conversation is an open product question, so this is stored as text rather than as a database enum. Adding a way to end is one line here and no migration.

EventType

string · enum
Enum values:
node_entered
navigation_blocked
tool_call
utterance
turn_failed
tool_refused
confirmation_requested
confirmation_resolved

Everything a conversation can record about the path it walked.

ExampleSuggestionIn

What the model is shown of a tool, read off the form -- which may not be saved yet.
name
​string · minLength: 1 · required
description
​string
Default:
example_requests
​string[]
​object

ExampleSuggestionOut

The requests the model wrote, to be kept, changed or dropped one by one. Never stored by this answer.
example_requests
​string[] · required

FallbackCondition

string · enum
Enum values:
normal
fallback

Whether the trunk is currently forwarding calls to its fallback number.

Binary on purpose: "A trunk can sit in fallback indefinitely... entering fallback raises a notification... and again when the condition clears" — the spec only ever names two states, not a severity scale.

FanOut

"Run this flow once per row of this query." The design study's Appendix names fan-out as the single largest gap between this platform and the integrations it has to replace, and recommends putting it **here** rather than growing a `for_each` step in `flows`: it is smaller, and it puts the row bound where `automations.md` already asks for a bound.
collection_id
​string · uuid · required
​object
limit
​integer
Default: 200

FeatureFlag

string · enum
Enum values:
voice_agents
text_agents
ai_profiles
outbound_calls
rag
memory
tools
custom_tools

The capability vocabulary. Grows with a one-line edit, never a migration: flags are stored as string arrays and a JSONB map, never as an enum column.

FieldDefault

What a new record gets in this field when whoever creates it gives none (`custom-data.md`, "A field's default"). One shape rather than a union, so a client reads `kind` and then the one member that kind uses. Whether `value` fits the field is the service's to judge, because only it knows the field's type and vocabulary.
kind
​FieldDefaultKind · enum · required
Enum values:
value
creation_day
offset_days
​integer · min: -3650 · max: 3650
Default: 0
value
​

FieldDefaultKind

string · enum
Enum values:
value
creation_day

FieldIn

One declared field. `key` is what a record's values are keyed by and what the model sees as a parameter name; `label` is what a person reads.
key
​string · minLength: 1 · maxLength: 64 · pattern: ^[a-z][a-z0-9_]*$ · required
label
​string · minLength: 1 · maxLength: 200 · required
type
​FieldType · enum · required

What one declared field holds.

Five, and each is a parse rule rather than a column type. The values live in JSONB and the database enforces nothing about them, so what a type means is service's to keep — which is where it had to live anyway: "a 0 is not a no" is a product rule, not a column.

Still deliberately no email and no phone, even now that DATOS-02's default country is ruled (the organization's own — docs/features/ authentication.md). A PHONE type would impose a parse obligation on the whole column — one bad number and the record stops being writable — where what #378 needed was a normaliser on the identity, which changes only how two rows compare. So an identity value is canonicalised by shape, not by a declared type: service._canonical_identity reads the value itself, and if it parses as a phone number under the organization's default country it is stored E.164 (shared.phone.to_e164, ADR 0023); otherwise strip() + casefold(), as before. A TEXT field holding a phone number is still the ordinary case this platform's collections use. A field holding an email address is a TEXT field the client marked personal, which is what the retention rule actually keys on.

Enum values:
text
number
boolean
date
option
​
options
​string[]
personal
​boolean
Default: false
required
​boolean
Default: false
​

FieldOut

collection_id
​string · uuid · required
filter_kind
​FilterKind · enum · readOnly · required

What this field holds, in the terms a control is chosen by.

Enum values:
text
number
date
boolean
option
filter_ops
​FilterOp[] · readOnly · required

The questions this field admits — "the type travels with the question" (custom-data.md). Computed rather than stored: it is a function of type, and a column holding a copy of it is a column that can disagree with it. Answered on the field itself so a control is chosen from what the platform says the field is, never from what its name looks like — which is the rule apps/web/CLAUDE.md refuses to let the browser hold and the same gap #521 names for a chart's own filter editor.

Enum values:
eq
ne
contains
gt
gte
lt
lte
in
id
​string · uuid · required
key
​string · required
label
​string · required
options
​string[] · required
personal
​boolean · required
position
​integer · required
required
​boolean · required
type
​FieldType · enum · required

What one declared field holds.

Five, and each is a parse rule rather than a column type. The values live in JSONB and the database enforces nothing about them, so what a type means is service's to keep — which is where it had to live anyway: "a 0 is not a no" is a product rule, not a column.

Still deliberately no email and no phone, even now that DATOS-02's default country is ruled (the organization's own — docs/features/ authentication.md). A PHONE type would impose a parse obligation on the whole column — one bad number and the record stops being writable — where what #378 needed was a normaliser on the identity, which changes only how two rows compare. So an identity value is canonicalised by shape, not by a declared type: service._canonical_identity reads the value itself, and if it parses as a phone number under the organization's default country it is stored E.164 (shared.phone.to_e164, ADR 0023); otherwise strip() + casefold(), as before. A TEXT field holding a phone number is still the ordinary case this platform's collections use. A field holding an email address is a TEXT field the client marked personal, which is what the retention rule actually keys on.

Enum values:
text
number
boolean
date
option
​
​
​

FieldType

string · enum
Enum values:
text
number
boolean
date
option

What one declared field holds.

Five, and each is a parse rule rather than a column type. The values live in JSONB and the database enforces nothing about them, so what a type means is service's to keep — which is where it had to live anyway: "a 0 is not a no" is a product rule, not a column.

Still deliberately no email and no phone, even now that DATOS-02's default country is ruled (the organization's own — docs/features/ authentication.md). A PHONE type would impose a parse obligation on the whole column — one bad number and the record stops being writable — where what #378 needed was a normaliser on the identity, which changes only how two rows compare. So an identity value is canonicalised by shape, not by a declared type: service._canonical_identity reads the value itself, and if it parses as a phone number under the organization's default country it is stored E.164 (shared.phone.to_e164, ADR 0023); otherwise strip() + casefold(), as before. A TEXT field holding a phone number is still the ordinary case this platform's collections use. A field holding an email address is a TEXT field the client marked personal, which is what the retention rule actually keys on.

FieldUpdate

Partial edit — only the provided fields change. `default: null` removes the default; leaving `default` out leaves it as it is.
​
​
​
​
​
​
​

Filter

One `WHERE` clause. `column` and `op` are checked against the dataset's catalogue the same way `Dimension.column` is. `generated` marks a filter a board's global filters merged in at run time rather than one a person saved on the chart itself — legacy's own reason for the flag: it keeps a board-injected filter out of the chart editor's "remove a filter" list, and out of what gets written back (`docs/features/dashboard.md:7`, `analytics`' design study §3.5).
column
​string · required
op
​FilterOp · enum · required

Every question that may be asked of a column. A closed set: a word that is not here cannot be asked anywhere, and a word that is here is asked with the same meaning wherever the column's owner admits it.

IS_EMPTY / IS_NOT_EMPTY are the pair with no value, and they are the reason this set is not merely the comparison operators. A state that exists as an absence — a cell never filled in, one emptied by the retention window, a column the last file said nothing about — is unaskable as an equality, and a client who cannot ask "which of these has no phone number" cannot find the work they have to do. Legacy asked the same question as isnull on its own engine (src/lib/dashboard/widgetConfig.ts), and for the same reason.

CONTAINS is LIKE renamed, not a new word. It was spelled like while nothing accepted it; contains is what it means to the person choosing it, and the SQL it becomes is the owner's business rather than the name's.

Enum values:
eq
ne
contains
gt
gte
lt
lte
in
value
​required
generated
​boolean
Default: false

FilterKind

string · enum
Enum values:
text
number
date
boolean
option

What a filterable column holds, in the only terms a filter cares about.

Coarser than any one owner's own type vocabulary on purpose: it exists so a control can be chosen for a column, and a control is chosen by "is this picked from a list, typed as text, compared as a number, or picked from a calendar". custom_data.FieldType maps onto it one-to-one today; an analytics dataset column maps onto it from whatever it actually is — an enum column, a uuid a client picks by name, a timestamp.

FilterOp

string · enum
Enum values:
eq
ne
contains
gt
gte
lt
lte
in

Every question that may be asked of a column. A closed set: a word that is not here cannot be asked anywhere, and a word that is here is asked with the same meaning wherever the column's owner admits it.

IS_EMPTY / IS_NOT_EMPTY are the pair with no value, and they are the reason this set is not merely the comparison operators. A state that exists as an absence — a cell never filled in, one emptied by the retention window, a column the last file said nothing about — is unaskable as an equality, and a client who cannot ask "which of these has no phone number" cannot find the work they have to do. Legacy asked the same question as isnull on its own engine (src/lib/dashboard/widgetConfig.ts), and for the same reason.

CONTAINS is LIKE renamed, not a new word. It was spelled like while nothing accepted it; contains is what it means to the person choosing it, and the SQL it becomes is the owner's business rather than the name's.

FindingAction

string · enum
Enum values:
call_back
teach
watch

The one act a finding offers (rule 28).

FindingActionOut

A finding's one action, shown first as what will happen (rule 28).
kind
​FindingAction · enum · required

The one act a finding offers (rule 28).

Enum values:
call_back
teach
watch
offered
​boolean · required
​required
​
​
​
​

FindingKind

string · enum
Enum values:
new
changed
unusual
behind_a_number
left_without

The kinds of finding rule 25 names; their labels are the interface's.

FindingMomentOut

One proof moment, shown only to someone who may read its conversation (rule 18). `lines` is `None` once the retention window took its words (rule 27).
interaction_id
​string · uuid · required
​required

FindingOut

​FindingActionOut · required

A finding's one action, shown first as what will happen (rule 28).

agent_id
​string · uuid · required
conversations
​integer · required
expires_at
​string · date-time · required
id
​string · uuid · required
kind
​FindingKind · enum · required

The kinds of finding rule 25 names; their labels are the interface's.

Enum values:
new
changed
unusual
behind_a_number
left_without
listed
​string[] · required
​FindingMomentOut[] · required
raised_at
​string · date-time · required
sentence
​string · required

FindingsOut

An agent's open findings (rules 29-32): nothing closed, expired or dismissed is listed anywhere.
agent_id
​string · uuid · required
free
​integer · required
looking
​boolean · required
​FindingOut[] · required
ready
​boolean · required
​required

FlagsUpdate

flags
​FeatureFlag[] · required
Enum values:
voice_agents
text_agents
ai_profiles
outbound_calls
rag
memory
tools
custom_tools

FlowCreate

name
​string · required

FlowEdgeIn

source_step_id
​string · uuid · required
target_step_id
​string · uuid · required
​

FlowEdgeOut

​required
id
​string · uuid · required
source_step_id
​string · uuid · required
target_step_id
​string · uuid · required

FlowEntryStepIn

Names the step `run_flow` starts from. `step_id` must already be a step of this flow.
step_id
​string · uuid · required

FlowGraphOut

A whole flow: its steps, its edges, and which step is the entry point.
​FlowEdgeOut[] · required
​required
id
​string · uuid · required
name
​string · required
org_id
​string · uuid · required
​FlowStepOut[] · required
​

FlowOut

​required
id
​string · uuid · required
name
​string · required
org_id
​string · uuid · required
​

FlowStepIn

tool_id
​string · uuid · required
​object

FlowStepOut

id
​string · uuid · required
​object · required
tool_id
​string · uuid · required

FlowStepStatus

string · enum
Enum values:
succeeded
failed
reused

What became of one step a test reached. REUSED is an output the flow's latest test recorded, handed on without running the step (rule 22) -- never reported as run.

FlowStepTrace

One step a test reached, in the order it reached it (rule 20). `parameters` is what the step ran with: its `param_overrides` with every `$ref:` resolved against what the run held at that point -- what the step was handed. For a reused step it is what the step ran with in the test its output was taken from, and `tool_id`/`tool_name` are the ones recorded there. `edge_id` is the edge taken next, `None` where the run ended or stopped there. `fingerprint` names what produced `output`: the step's tool, its saved parameters and the fingerprints of the outputs and inputs those read. `stale` is set on a reused step whose fingerprint today differs from the recorded one -- the step, or something it reads, changed since that output was recorded (rule 22). `truncated` says `parameters`, `output` or `detail` was longer than `TRACE_VALUE_LIMIT` bytes and is kept as the start of its JSON text.
order
​integer · required
status
​FlowStepStatus · enum · required

What became of one step a test reached. REUSED is an output the flow's latest test recorded, handed on without running the step (rule 22) -- never reported as run.

Enum values:
succeeded
failed
reused
step_id
​string · uuid · required
tool_id
​string · uuid · required
tool_name
​string · required
detail
​string
Default:
duration_ms
​integer
Default: 0
​
fingerprint
​string
Default:
​
​object
stale
​boolean
Default: false
truncated
​boolean
Default: false

FlowStepUpdate

Partial edit — only the provided fields change.
​
​

FlowTestIn

What a test is started with. `inputs` is what the entry step is handed, under `trigger` as any run's (rule 19), at most `TRACE_VALUE_LIMIT` bytes as JSON. Left out (`None`), a test with `run_step_id` is handed what the flow's latest test was, and any other test nothing; `{}` is really empty. `stop_after_step_id` ends the test right after that step (rule 21). `run_step_id` runs that one step: the steps before it reuse what the flow's latest test recorded them returning, and only those with nothing recorded run for real (rule 22). At most one of the two is given.
​
​
​

FlowTestOut

One test of a flow, recorded against the flow and not as a conversation.
created_at
​string · date-time · required
duration_ms
​integer · required
flow_id
​string · uuid · required
id
​string · uuid · required
​object · required
outcome
​FlowTestOutcome · enum · required

How a test ended (rule 20). STOPPED is the person's choice, not a failure (rule 21); DID_NOT_TERMINATE is the graph passing the step bound, not a step failing (rule 10).

Enum values:
succeeded
failed
stopped
did_not_terminate
​FlowStepTrace[] · required
inputs_inherited
​boolean
Default: false

FlowTestOutcome

string · enum
Enum values:
succeeded
failed
stopped
did_not_terminate

How a test ended (rule 20). STOPPED is the person's choice, not a failure (rule 21); DID_NOT_TERMINATE is the graph passing the step bound, not a step failing (rule 10).

ForwardHealth

string · enum
Enum values:
waiting_for_forward
working
broken

A forwarded address's health is its forward (rule 54): waiting for the forward, then working once it is proved, whatever its domain's state, which is DomainState and shown beside it -- or broken, when the connection is marked so.

ForwardOrigin

string · enum
Enum values:
our_routing
carrier_edge

Which of the two places forwarded a given call — "Two guarantees, and they are not the same size" (docs/features/trunks.md). Kept apart because they carry different evidentiary weight: one is first-hand, the other is hearsay we may never receive.

ForwardOutcome

string · enum
Enum values:
forwarded
unserved

What became of a call the trunk could not put through to its agent. A trunk with a fallback number FORWARDED it there; one with none left it UNSERVED -- it reached nobody (trunks.md rule 34, #361). Its own column and not an inference from forwarded_to IS NULL, which a carrier-edge record may also carry.

ForwardRecordOut

One forwarded call, as its line remembers it — "when, why, and where it went." Deliberately not an interaction (docs/features/interactions.md: "A call that never reached an agent is not an interaction"). `outcome` is `UNSERVED` for a call that went nowhere. `trunk_id` is the **line's** id, the id a line has had since it was a trunk of its own.
​required
created_at
​string · date-time · required
​required
id
​string · uuid · required
origin
​ForwardOrigin · enum · required

Which of the two places forwarded a given call — "Two guarantees, and they are not the same size" (docs/features/trunks.md). Kept apart because they carry different evidentiary weight: one is first-hand, the other is hearsay we may never receive.

Enum values:
our_routing
carrier_edge
reason
​string · required
trunk_id
​string · uuid · required
outcome
​ForwardOutcome · enum

What became of a call the trunk could not put through to its agent. A trunk with a fallback number FORWARDED it there; one with none left it UNSERVED -- it reached nobody (trunks.md rule 34, #361). Its own column and not an inference from forwarded_to IS NULL, which a carrier-edge record may also carry.

Enum values:
forwarded
unserved
Default: forwarded

ForwardedAddressIn

A dedicated address to connect (rule 50): the address its customers write to, the name the organization knows the connection by (the address itself when left out), and the two settings a recipient sees (email-threads.md rule 21): the sender's name -- the organization's name when left out (email-threads.md rule 28) -- and the subject of a conversation the organization starts.
address
​string · required
​
​
​

ForwardedAddressOut

Everything the connection's page shows (rules 51-57), and nothing secret: there is none. `forwarded_to` is the address the platform gave the connection -- what the client forwards to and what answers leave from, shown only inside the organization and to those it answers (rule 52). `confirmation_code` is the code the client's own mail provider sent to confirm a new forward (Gmail's), while the forward waits (rule 53); `confirmation_link` is the link that confirms it, where the provider's mail carries one instead or beside the code (rule 67), for the member to open. `dns_records` are what the client adds in the domain, and `domain_state` what the mail service last said of them -- shown, and holding nothing back: `health` never waits for it (rule 53). `on_disconnect` is what disconnecting leaves for the client to remove (rule 57), in English; the dashboard words it in its reader's language from `forwarded_to` and `domain` (#1722).
account_label
​string · required
address
​string · required
​required
​required
​required
connection_id
​string · uuid · required
​DnsRecordOut[] · required
domain
​string · required
​required
domain_state
​DomainState · enum · required

What the platform's mail service last said of the address's domain.

unchecked until anybody asked; not_registered while the mail service holds no sending domain for it -- every client domain today, with no records to show, until per-client sending domains land (#1555); unverified while the records the client must add are not all found; verified once they are. Shown, and holding nothing back: no state lets or stops a send, since nothing leaves as the address -- answers leave from the address the platform gave the connection (rules 53-54, email-threads.md rule 28).

Enum values:
unchecked
not_registered
unverified
verified
​required
forwarded_to
​string · required
health
​ForwardHealth · enum · required

A forwarded address's health is its forward (rule 54): waiting for the forward, then working once it is proved, whatever its domain's state, which is DomainState and shown beside it -- or broken, when the connection is marked so.

Enum values:
waiting_for_forward
working
broken
​required
on_disconnect
​string · required
​required
​required
​required
​

GoogleCapability

string · enum
Enum values:
see_busy
see_appointments
manage_events
see_spreadsheets
manage_spreadsheets
see_presentations
manage_presentations
see_documents

What a person can let this platform do with their Google account, named the way they would say it rather than the way Google spells it.

The gallery offers these and the person picks; what is then asked of Google is exactly the scopes behind what was picked (connections.md, "Choosing what it may do"). The provider's own vocabulary stays inside this module: a dashboard that had to name a scope URL would be a dashboard deciding what a permission means.

They come in seeing/changing pairs, one pair per Google product, because that is the line a person actually draws: a client who wants a sheet read is not thereby a client who wants it written into (spreadsheets.md, "What the client allowed"). A product whose provider offers no narrower read is a pair whose halves map to the same scope, and that is a fact to state on the entry rather than a pair to collapse -- the words are what the person consents to.

A product with nothing that writes is a single entry, not a pair with an empty half (contacts.md, "Nothing here writes"). see_contacts has no manage_contacts beside it because this platform builds no tool that changes an address book: a capability a client can tick that backs no tool is a permission asked for nothing, on the gallery and at Google's review alike. The day such a tool exists, the capability is added with it.

And a product may be three entries rather than a pair, when the provider draws its line somewhere other than between seeing and changing (files.md, "What the client allowed"). Drive's are manage_own_files — the files this platform itself created, both read and written, and the only non-sensitive one Google publishes — plus see_all_files and manage_all_files over the whole drive. manage_own_files is deliberately not half of a pair: within the app's own files it is both halves at once, and a see_own_files beside it would back nothing a client could not already do. Which of these the connection holds decides not only which file tools work but how much of the drive they can see, which is the first time a capability has done that and is why the spec says so out loud.

And a product's three may not be a ladder. Mail's are send_mail, see_mail and manage_mail (mail.md, "What the client allowed"), and the first is beside the other two rather than under them: it puts a message into the world and reads, searches and lists nothing, so a client can grant a sending agent without opening their correspondence at all. That is Google's own line -- gmail.send is the only Gmail scope it classifies as sensitive rather than restricted -- and it is the one shape in this enum where the narrowest entry is also the one that acts most consequentially.

GoogleProduct

string · enum
Enum values:
calendars
spreadsheets
presentations
documents
contacts
files
mail

Which of Google's products an account is authorized for.

A connection is an account authorized for a product, not an account (connections.md, "One account and two products are two connections", 2026-09-23). This is that product, and it is named by the domain rather than by Google's brand for it -- the same words the slices that consume it carry, mail and not Gmail -- because the brand is a word on a screen and this is what the grant is about. Which brand name a person reads is the dashboard's to say, the way the capabilities' own wording already is.

There is no google_product column behind this and there must not be: a connection is a provider, an account, a health state and one opaque secret, and a field shaped by one provider is what connections.md refuses by name. So a row's product is read from its grant -- google_products_for -- which also makes it impossible for the two to disagree.

GrantedVia

string · enum
Enum values:
pasted
embedded_signup

Which door the grant arrived by.

The spec's "a grant can arrive by more than one door, and the connection is the same whichever it came through": this is the record of the door, and nothing in this slice or above it branches on it.

GraphEdgeSpec

One edge of a graph written whole, by the keys of the nodes it joins.
source
​string · required
target
​string · required
condition
​string
Default:

GraphNodeSpec

One node of a graph written whole. `key` is the caller's own handle for it inside this payload — any non-blank string, unique within `nodes` — and is never stored.
key
​string · minLength: 1 · required
instructions
​string
Default:
​
​Position

Where a node sits in the visual editor.

tool_ids
​string[]

GraphOut

A whole graph: its nodes, its edges, and which node is the root.
​EdgeOut[] · required
id
​string · uuid · required
​NodeOut[] · required
​required

GraphReplace

Everything a graph is, declared at once. `root` is the key of the node a conversation starts at, or `None` for a graph that has no root yet — allowed, because a draft may be unfinished.
​GraphEdgeSpec[]
​GraphNodeSpec[]
​

GraphReplaced

The graph as stored, and each payload key resolved to the node id it got.
​GraphOut · required

A whole graph: its nodes, its edges, and which node is the root.

​object · required

HTTPValidationError

​ValidationError[]

HandToAgentIn

Hand a thread to an agent from outside a flow — an operator's door onto the same built-in, for testing and for ops.
agent_id
​string · uuid · required
text
​string · minLength: 1 · required
​
deliver
​boolean
Default: true

HandedOverOut

What a hand-over answers with: what the agent said, and whether it left -- or was `kept` as the thread's prepared reply, `held` when the connection's reply mode kept what the step's own mode would have sent (mail-handling.md rules 2 and 4).
agent_id
​string · uuid · required
delivered
​boolean · required
interaction_id
​string · uuid · required
said
​string[] · required
held
​boolean
Default: false
kept
​boolean
Default: false

HandoffAct

string · enum
Enum values:
handed_over
taken
taken_over
released
assigned
prepared
prepared_sent
prepared_discarded

What a PersonHandoff event records -- the five acts interactions.md rule 45 names, and the four of a reply prepared for review (handoff.md rules 32, 37 and 41). A close from the inbox is not one: it is an ending, and the record already has one of those (finish).

A native PostgreSQL enum (interaction_handoff_act).

HandoffReasonCode

string · enum
Enum values:
kept_for_people
recipe_off
sent_from_mailbox

Why the platform itself left a thread waiting, in a word the reader's language can say (handoff.md rules 2, 4): beside a hand-over's reason, which is English, names the conversation's facts and is purged with its content. The code names nobody and survives the purge.

A native PostgreSQL enum (interaction_handoff_reason_code); None on a hand-over the built-in or a person made, whose reason is their own words.

HeaderCollision

Two or more headers of one file that mean the same name. Reported rather than deduplicated: which of two identically-named columns a value belonged to is not ours to guess, and picking one would write the wrong data with no signal that it happened.
headers
​string[] · required
key
​string · required

HealthStatus

string · enum
Enum values:
healthy
broken

Whether the grant is currently usable.

HoursChange

What saving a schedule would do to the calls already set for an exact time (`outbound-calls.md` rule 41), read before it is saved. Both counts are over calls still to come; one already due is the dial's (rule 22).
moves
​integer · required

How many waiting calls that keep to the calling hours -- a timed retry, or a callback the client asked to keep to them -- these hours would not hold. Saving the hours moves each to the first later day whose hours hold the same local time, or, when no day's hours hold it, to the next opening.

stays_outside
​integer · required

How many waiting calls set for a time a person, an integration or a caller named these hours would not hold. Saving the hours leaves each at its time: this is a warning, not a move.

HoursIn

A whole schedule, replaced at once. Slots are validated together — no two on one weekday may overlap — so a schedule is never half-saved.
​HoursSlot[] · required

HoursSlot

One opening, on one weekday, in the campaign's own timezone. No slot crosses midnight — that is a `CHECK` on the table as well as this validator, because "the hours are a stored, validated value" and a value only one writer validates is not one.
ends_at
​string · time · required
starts_at
​string · time · required
weekday
​integer · min: 0 · max: 6 · required

HungUpBy

string · enum
Enum values:
contact
platform
line
machine

Which side ended a call's line (interactions.md rule 16, #690).

The side that left first (rule 54). CONTACT is the other party leaving on their own -- a remote BYE, the room's client_initiated -- and also when the agent's own hang-up ran after they had already gone (#1818). PLATFORM is this side putting the line down: an ending the channel decided, the agent's own hang-up while the contact was still there, or the room removing the participant. LINE is neither: the telephony bridge closed the leg because its audio stopped, a drop the room reports as it reports a hang-up (#1817). MACHINE is CONTACT's case where what answered was a machine -- a voicemail or a menu (AnsweredBy) that left first, during the machine ending: a machine is not a contact, so a count of contacts who hung up must not include it (owner ruling 2026-10-26, #1974). Text on the row, like end_reason, so the room growing a reason -- or this growing a value -- never needs a migration.

ImportPlanOut

What the import would do, computed without doing any of it. An import is the one moment a client changes thousands of records at once and there is no undoing it afterwards, so this is the step where they find out.
creates
​integer · required
rejected
​integer · required
total
​integer · required
updates
​integer · required
ambiguous_dates
​string[]
declares
​string[]
​Rejection[]
untouched
​string[]

ImportProposalOut

What a file would declare, before the client has said anything. Writes nothing, decides nothing, and is not required before an import: it is what makes the mapping screen possible.
​ProposedColumn[] · required
total
​integer · required
ambiguous_dates
​string[]
​HeaderCollision[]
​
unnamed
​string[]

ImportResult

added
​integer · required
​RejectedRow[] · required
​WarnedRow[]

ImportRunOut

accepted
​integer · required
collection_id
​string · uuid · required
created_at
​string · date-time · required
​required
id
​string · uuid · required
rejected
​integer · required
​Rejection[] · required
status
​ImportStatus · enum · required
Enum values:
succeeded
failed
total
​integer · required

ImportStatus

string · enum
Enum values:
succeeded
failed

InboxItemOut

A `NotificationOut` plus the read state of the person asking — per-person, per notifications.md:26, never shared and never part of the organization's own record. `None` means this person has not marked it read.
audience
​backend__features__notifications__schemas__Audience · enum · required
Enum values:
client
platform
​required
​required
​required
event_code
​string · required
id
​string · uuid · required
occurred_at
​string · date-time · required
​required
​object · required
​required
​required
​required
​required
​required

InboxViewUpdate

What changes on a view; anything left out stays. Saving over a view is `filters` (rule 9); renaming is `name`.
​
​
​
​

IngestionStatus

string · enum
Enum values:
pending
extracting
embedding
ready
failed

Where a document is between "uploaded" and "searchable".

failed is a state, not an exception (DECISIONS.md D7): a PDF that will not parse stays in the library with a reason the client can read, because they uploaded a file and a file that vanishes is worse than one that says what went wrong.

InstallIn

The values given for the recipe's parameters, by name, and -- for a recipe that proposes labels, on its first install -- the labels the client keeps (rules 50-53).
​
​object

InstallState

string · enum
Enum values:
installed
incomplete
needs_value

Where an installed recipe stands (rules 8, 16, 18).

InstalledRecipeOut

An installed recipe as one thing with its parts beneath (rule 10).
​required
enabled
​boolean · required
​required
handed_over
​string[] · required
id
​string · uuid · required
installed_at
​string · date-time · required
key
​string · required
missing
​string[] · required
​required
name
​string · required
​object · required
​PartOut[] · required
setup
​string · required
state
​InstallState · enum · required

Where an installed recipe stands (rules 8, 16, 18).

Enum values:
installed
incomplete
needs_value
​required
version
​integer · required
​
​
​object
​
unseen
​integer
Default: 0
​
worst_state
​string · enum
Enum values:
failing
unknown
paused
working
Default: working

InteractionCount

How many conversations match a set of filters (inbox.md rule 3).
count
​integer · required

InteractionEventOut

One row of the trace. Flat on purpose: a trace is read as a table, and the fields a type does not use are absent rather than shaped differently.
id
​string · uuid · required
occurred_at
​string · date-time · required
seq
​integer · required
type
​EventType · enum · required

Everything a conversation can record about the path it walked.

Enum values:
node_entered
navigation_blocked
tool_call
utterance
turn_failed
tool_refused
confirmation_requested
confirmation_resolved
​
​
​
​
​
​
​
​
​
colleague
​boolean
Default: false
​
​
​
​
​
flow_act
​boolean
Default: false
​
​
held
​boolean
Default: false
​
​
​
​
overlapped
​boolean
Default: false
​
prepared_changed
​boolean
Default: false
​
returned_to_flow
​boolean
Default: false
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
unvoiced
​boolean
Default: false
​
​
​
​
​

InteractionOut

One conversation, as the log shows it.
channel
​Channel · enum · required

Voice and text are two channels over one graph, not two kinds of record.

Enum values:
voice
text
direction
​backend__features__interactions__schemas__Direction · enum · required
Enum values:
inbound
outbound
id
​string · uuid · required
is_open
​boolean · required
org_id
​string · uuid · required
​RunSettings · required

The settings a conversation actually ran with, copied in as values.

An AI profile is not frozen into an agent's published version, so the version alone cannot say how a conversation sounded. The profile id is kept for provenance; the provider, model and voice are kept because they are the answer. used_standby records that a standby served the conversation instead of some component's first choice — the same fields still describe what was actually served — and standby_component names which one switched.

​backend__features__interactions__schemas__Source · required

Source attribution, as recorded. kind says which of the rest is meaningful; the others stay unset.

connection_id and address are CHANNEL's pair, and they are two fields for the reason the thread itself is identified by two: "Threads are per connection; contacts are per organization" (channels.md). The connection says which of the organization's accounts the message arrived on — "reply on this thread has to know which account it leaves by" — and the address says who wrote, in whatever shape the provider's own namespace has: a phone number in E.164 on WhatsApp, an opaque user id elsewhere, "decided when each is built".

address is personal data and connection_id is not. The address is the contact — "on a channel, the contact is their address" — so the retention purge clears it exactly as it clears number, which is the same datum arriving by a different road; the connection names one of the organization's own accounts and survives, like trunk_id beside it.

provider is CHANNEL's third, and it is stored as a value rather than read off the connection. interactions.md: "a conversation that arrived on a channel names the provider it arrived on, beside the contact's address", because an address alone does not say whether the person was reached on WhatsApp, by mail or somewhere else. It is written by the channel at open, from the provider it is already speaking to, for the reason the run settings beside it are values too: a connection is what it is now, and this answers what the conversation arrived on then. It also keeps connection_id the opaque reference it is — nothing here dereferences a connection to render a row. Not personal data: it names a kind of account of the organization's own and nobody outside it, so it survives the purge exactly as connection_id does.

started_at
​string · date-time · required
​
​
​
​
​
channel_key
​string
Default:
contact_spoke
​boolean
Default: false
​
​
​
draft_test
​boolean
Default: false
​
​
​
​
​
​
​
label_ids
​string[]
​
​
​
​
​
​
​
rang
​boolean
Default: false
​
​
​
​
unreplied
​boolean
Default: false
​object

InteractionTraceOut

*"Per-interaction trace — see the exact path through the node graph for one conversation."* The record and its events, in the order they happened.
​InteractionEventOut[] · required
​InteractionOut · required

One conversation, as the log shows it.

InteractionTranscriptOut

The conversation, without its technical trace (live-view.md rule 34, 2026-09-28 amendment): the record and only the events that are the conversation itself — what was said, the door's later correction, the confirmation gate, an agent taking a thread over, a thread handed to a person and every take and release, what a branch of the flow sent (a `TOOL_CALL` marked `flow_act`), a turn that produced no reply, and a call's missing recording. **Never** a `NODE_ENTERED` or `NAVIGATION_BLOCKED` row (node traversal), any other `TOOL_CALL` (tool payloads), a `PROVIDER_USAGE` row (provider metadata) or a `TURN_TIMED` row (latencies) — `service.describe_transcript`'s filter is what keeps those off this shape, so a person with `interactions` but not `interaction_traces` never receives them. `service._is_transcript_event` names the exact set.
​InteractionEventOut[] · required
​InteractionOut · required

One conversation, as the log shows it.

InvitationCreate

`POST /auth/invitations`: invite an address into the acting organization (accounts.md rules 35-37). Nobody's password is set here and nobody joins until the invitation is accepted.
email
​string · email · required
​
role
​Role · enum

A person's role inside one organization. Per-membership, not per-person.

Enum values:
owner
admin
member
Default: member

InvitationOut

One invitation still waiting, as its organization (or staff, for a first owner) sees it (rule 37). It reads the same whether or not the address has an account (rule 41): no name, nothing about the person behind the address.
email
​string · email · required
expired
​boolean · required
expires_at
​string · date-time · required
flags
​FeatureFlag[] · required
Enum values:
voice_agents
text_agents
ai_profiles
outbound_calls
rag
memory
tools
custom_tools
id
​string · uuid · required
​required
org_id
​string · uuid · required
role
​Role · enum · required

A person's role inside one organization. Per-membership, not per-person.

Enum values:
owner
admin
member
sent_at
​string · date-time · required
​

Kind

string · enum
Enum values:
byo_sip
pbx
platform

Which peer a trunk connects to (trunks.md rule 4).

KnewNoteOut

A note the agent's memory reread into a conversation when it started, as it was told then: a later correction never rewrites it.
content
​string · required
​required
scope
​Scope · enum · required

Which layer a memory lives in. The retrieval side pulls the relevant layer(s) into context; the write side must respect the PII boundary.

Enum values:
agent_global
per_contact
per_session

LLMProvider

string · enum
Enum values:
openai
anthropic
google
azure_openai
groq

LLMSettingsIn

model
​string · minLength: 1 · required
name
​string · minLength: 1 · required
provider
​LLMProvider · enum · required
Enum values:
openai
anthropic
google
azure_openai
groq
​
​
Default: 10
​
temperature
​number · min: 0 · max: 2
Default: 0.7
​

LLMSettingsOut

id
​string · uuid · required
​required
​required
model
​string · required
name
​string · required
​required
provider
​LLMProvider · enum · required
Enum values:
openai
anthropic
google
azure_openai
groq
​required
temperature
​number · required
​
​
​
​
​

LLMSettingsUpdate

​
​
​
​
​
​
​
​

LabelChoice

One label the client keeps at install (rules 51, 52): an organization's own label by `label_id`, or a new one by `name` and `description`, created in the vocabulary as the organization's. `proposal` says which proposal it answers, none for a label the client added; `joins` whether it fills the recipe's labels parameter.
description
​string
Default:
​
​
​
​

LabelIn

A label to create: a name, and one line saying what belongs under it (interactions.md rule 58). No colour -- the spec names none.
name
​string · minLength: 1 · maxLength: 60 · required
description
​string · maxLength: 300
Default:

LabelOut

One label of the organization's vocabulary. `conversation_count` is how many conversations carry it -- what deleting it takes it off (rule 62), said before it is confirmed.
description
​string · required
id
​string · uuid · required
name
​string · required
conversation_count
​integer
Default: 0

LabelUpdate

What changes on a label; anything left out stays. Renaming keeps the label on every conversation it is on (rule 62).
​
​

LabelsChange

Labels to put on a conversation and to take off it, by id. Either may be empty; a label already on is left on and one already off is left off, saying nothing on the record.
add
​string[] · maxItems: 50
remove
​string[] · maxItems: 50

Language

string · enum
Enum values:
en
es

A language the platform can speak — which is to say, one it has default phrases written in. The agent states one of these and every phrase on it is written in that one; a language with no phrases behind it would be a label.

LastLine

A conversation's latest line, as the inbox shows it (inbox.md rule 4): who said it, the start of what they said, and when. The latest `UTTERANCE` of the trace. A flow's send (`ToolCall.flow_act`) is a line of the transcript but has no speaker, so it is never this. `text` is the line with its whitespace folded and, past `LAST_LINE_MAX_CHARS`, cut on a word and ended with `…`; a voice note whose words have since been learned shows those words (rule 7), at the time the note arrived. `actor_person_id` is the `Utterance`'s own: the person on an `OPERATOR` line.
at
​string · date-time · required
speaker
​Speaker · enum · required

Who said something. Three, and the third only happens on a channel -- and, on an email thread, a fourth: a reply sent from the mailbox itself (MAILBOX).

OPERATOR is "a person answering through the platform, recorded as a third speaker" (interactions.md) — the hand-over to a human that channels.md describes, "reply on this thread, the same built-in, from outside a flow". It is a speaker and not a flag on AGENT because the transcript has to say who the words belong to: an agent shown a person's reply as its own would read its history as things it had said and had not.

What a branch of the client's flow sent is not a fourth member. A template a branch fired is "recorded on the thread as the flow's act, as a tool call is, not as something an agent said" — and a tool call is a ToolCall, which this vocabulary already has. A speaker for it would claim somebody spoke.

A native PostgreSQL enum (interaction_speaker), so a member added here ships an ALTER TYPE ... ADD VALUE migration — b7e2c4d9a1f3's precedent.

Enum values:
agent
contact
operator
mailbox
text
​string · required
​

Layout

string · enum
Enum values:
split
single

How an entry lays out the list and a conversation (inbox.md rule 37): side by side, or one at a time across the whole width.

LegalDocument

string · enum
Enum values:
terms
privacy
cookies
acceptable-use
processing-agreement
subprocessors

The six legal documents the platform keeps (rule 1). Each value is also the last segment of the document's address, so it is spelled as a URL is.

LegalDocumentOut

One document: its address that always shows the version in force (rule 6), that version, and one published to take effect later (rule 5).
address
​string · required
​required
document
​LegalDocument · enum · required

The six legal documents the platform keeps (rule 1). Each value is also the last segment of the document's address, so it is spelled as a URL is.

Enum values:
terms
privacy
cookies
acceptable-use
processing-agreement
subprocessors
​required

LegalLanguage

string · enum
Enum values:
es
en

The languages a version may be written in (values table): Spanish required, English optional, and the Spanish prevails (rule 4).

LegalRecordOut

One entry of the proof, as its organization's owners and its person read it (rules 26, 28). Never changed once written.
​required
id
​string · uuid · required
kind
​RecordKind · enum · required

What one entry of the proof records.

Enum values:
person_agreement
organization_agreement
objection
objection_withdrawn
own_agreement_marked
own_agreement_unmarked
​required
​required
​required
​required
​required
origin
​Origin · enum · required

Where an entry came from (rule 26): the dashboard or a key -- and, for a mark, staff from the directory (rule 23). before_legal is an agreement kept before this proof existed, carried over with what was kept then.

Enum values:
dashboard
key
staff
before_legal
​required
​required
​required
person_name
​string · required
​required
recorded_at
​string · date-time · required
​required
​required
​required
​AgreedVersionOut[] · required
​required

LineAbout

string · enum
Enum values:
person
business

Whom a line asks the agent to notice something about (rule 19): the person in each conversation, or the business. It decides where the note is filed under rule 2 -- a line about a person produces notes about that person, one about the business produces business notes.

LineKind

string · enum
Enum values:
brings_conversations
runs_before
may_call
runs_after
switched_on
set_off_when_conversation_ends
set_off_when_record_written
runs_flow

Why two cards are connected, in the client's words (rule 18).

LineType

string · enum
Enum values:
number
extension

What a line's number is (trunks.md rule 7): a phone number, normalised to E.164, or an extension of the client's phone system, held as text exactly as the system names it and never read as a number.

LiveProvider

string · enum
Enum values:
google
openai

Who runs a live (speech-to-speech) model. ai-profiles.md, "The providers are an enumerated list, and the list grows by adding to it": Google first, OpenAI next (R11) -- a further provider is one more member here and the classes its models were measured into, never a new kind of component.

LiveSettingsIn

language
​string · minLength: 1 · required
model
​string · minLength: 1 · required
name
​string · minLength: 1 · required
provider
​LiveProvider · enum · required

Who runs a live (speech-to-speech) model. ai-profiles.md, "The providers are an enumerated list, and the list grows by adding to it": Google first, OpenAI next (R11) -- a further provider is one more member here and the classes its models were measured into, never a new kind of component.

Enum values:
google
openai
voice
​string · minLength: 1 · required
​
Default: 5
​

LiveSettingsOut

id
​string · uuid · required
language
​string · required
model
​string · required
name
​string · required
​required
provider
​LiveProvider · enum · required

Who runs a live (speech-to-speech) model. ai-profiles.md, "The providers are an enumerated list, and the list grows by adding to it": Google first, OpenAI next (R11) -- a further provider is one more member here and the classes its models were measured into, never a new kind of component.

Enum values:
google
openai
voice
​string · required
​
​
Default: 5
​

LiveSettingsUpdate

​
​
​
​
​
​
​

MailFolderOut

One folder of the mailbox a client may read instead of the inbox (email-threads.md rule 35): `id` is what `read_folder` takes -- Graph's id on a signed-in mailbox, the folder's name on its server on a mailbox on its own server -- `name` its own name, and `path` where it sits (`Inbox / Support`).
id
​string · required
name
​string · required
path
​string · required

MailFoldersOut

The mailbox's own folders a client may choose from (email-threads.md rule 35), and when the list was read: the inbox is read while none is chosen and is not listed; junk, sent mail, drafts, deleted mail, the outbox and the folders inside them are never offered. At most `MAIL_FOLDERS_OFFERED_MOST`. `listed_at` is `None` on a mailbox on its own server not yet listed.
​MailFolderOut[] · required
​required

MailLineOut

What an email thread keeps beside one message's words (inbox.md rule 35, email-threads.md rules 7-8, channels.md rule 88): its subject and whom it was copied to, kept apart from its text, and the road's mark. `message_id` is the `ref_id` of the contact's line it belongs to on the conversation.
copied_to
​string[] · required
​required
message_id
​string · uuid · required
received_at
​string · date-time · required
​required

MailServerDiscoverIn

The address whose servers to look for (rule 6).
address
​string · required

MailServerDiscoveryOut

The servers proposed for an address, for the member to see and confirm before giving the password (rule 6). `reading`/`sending` are `None` where nothing was found; `username` is what the source says the servers know the mailbox by, the address itself when it says nothing; `found_at` names the host the proposal was read from ("Found from <source>"), `None` with `source` `none`.
address
​string · required
​required
​required
​required
source
​MailServerSource · enum · required

Where the proposed servers were found (rule 6): the domain's own autoconfig, Thunderbird's database of providers (by the domain, or by the provider its mail goes to), the domain's SRV records, or nowhere -- the member enters them.

Enum values:
autoconfig
provider_database
srv
none
username
​string · required

MailServerEndpoint

One server: its name, its port and how the connection to it is encrypted (rule 2). Checked by the service, not here (`MailServerIn`'s reason): the name is a host name or an IP literal (`mail_host`), the port one of the road's (`READING_PORTS`, `SENDING_PORTS`).
host
​string · required
port
​integer · required
security
​MailServerSecurity · enum · required

How the connection to a server is encrypted (rule 2): tls from the first byte (993, 465), or starttls on a plain port that must upgrade before the password is sent (143, 587, 2525). There is no plain value: a server that offers no encryption is refused (rule 7).

Enum values:
tls
starttls

MailServerFault

string · enum
Enum values:
reading_refused
sending_refused
mailbox_gone
unreachable
untrusted
no_encryption
forbidden_host
did_not_arrive

Why a test refused it or a use broke it (rules 7, 10, 13), for the screen to say in the member's language; fault_host names the server where one is meant. platform_cannot_open is the platform's own fault and never the server's (rule 13): the password is asked for again.

MailServerHealth

string · enum
Enum values:
testing
working
refused
broken

What the connection shows (rules 10, 13, 17): being tested, working, refused by its last test (the values kept for the member to correct), or broken in use. Only working reads or sends.

MailServerIn

A dedicated address to connect with its mailbox's password (rules 1-7): the address, the two servers the member confirmed, the user name those servers know the mailbox by (the address when left out), and the password -- sealed at once, never shown back. `account_label` is the name the organization knows the connection by (the address when left out); `sender_name` and `new_subject` are the two settings a recipient sees (email-threads.md rule 21). **Nothing here refuses** (rule 4): FastAPI's 422 carries each error's `input`, and for a missing field or a check over the whole body that input is the body, password and all. So every field has a default, no validator raises, and `mail_server` checks the body and refuses in words that name no value (`InvalidMailServerRequest`, 422). The schema still says which are required.
​required
​password · writeOnly · required
​required
​required
​
​
​
​

MailServerOut

Everything the connection's page shows (rules 2, 10, 12-15), and never the password. `testing_since` and `probe_sent_at` while it is tested; `proved_at` the first proof, from which its mail is the platform's (email-threads.md rule 29); `working_since` the latest; `unread_before`, after a break longer than the read-back bound, the moment before which mail was not read; `last_read_at` when its mailbox was last read (rule 13). `sent_folder` is where a copy of what it sends is filed, `None` where the server names none -- answers are then kept only on the platform (rule 12) -- and `last_copy_failed_at` when the latest copy could not be filed. `on_disconnect` is what disconnecting leaves to the client at the provider (rule 15).
account_label
​string · required
​required
​required
acts_in_mailbox
​boolean · required
address
​string · required
​AnsweredFolderOut · required

The folder the mail a thread answered is filed in (email-threads.md rule 36): id as MailFolderOut gives it and its name, both None while nothing is filed.

​required
​required
connection_id
​string · uuid · required
​required
​required
​required
​MailFolderOut[] · required
health
​MailServerHealth · enum · required

What the connection shows (rules 10, 13, 17): being tested, working, refused by its last test (the values kept for the member to correct), or broken in use. Only working reads or sends.

Enum values:
testing
working
refused
broken
​required
​required
​required
​required
mark_answered_read
​boolean · required
​required
on_disconnect
​string · required
​required
​required
​ReadFolderOut · required

The folder a mailbox is read in (email-threads.md rule 35): id and name of the one chosen instead of the inbox, both None while the inbox is read; since when the choice last changed -- what reached the folder before it is never read -- None while it never did.

​required
​MailServerEndpoint · required

One server: its name, its port and how the connection to it is encrypted (rule 2). Checked by the service, not here (MailServerIn's reason): the name is a host name or an IP literal (mail_host), the port one of the road's (READING_PORTS, SENDING_PORTS).

​required
​MailServerEndpoint · required

One server: its name, its port and how the connection to it is encrypted (rule 2). Checked by the service, not here (MailServerIn's reason): the name is a host name or an IP literal (mail_host), the port one of the road's (READING_PORTS, SENDING_PORTS).

​required
​required
​required
​required
username
​string · required
​required

MailServerPasswordIn

The mailbox's password, entered again (rules 14, 17): sealed at once and tested before anything is read or sent with it. Refuses nothing itself (`MailServerIn`'s reason).
​password · writeOnly · required

MailServerSecurity

string · enum
Enum values:
tls
starttls

How the connection to a server is encrypted (rule 2): tls from the first byte (993, 465), or starttls on a plain port that must upgrade before the password is sent (143, 587, 2525). There is no plain value: a server that offers no encryption is refused (rule 7).

MailServerServersIn

The two servers and the user name, changed (rule 17). **The password comes with them**: a password is sent only to a server the member confirmed for it (rule 6), so changing a server is giving the password again. Refuses nothing itself (`MailServerIn`'s reason).
​password · writeOnly · required
​required
​required
​

MailServerSource

string · enum
Enum values:
autoconfig
provider_database
srv
none

Where the proposed servers were found (rule 6): the domain's own autoconfig, Thunderbird's database of providers (by the domain, or by the provider its mail goes to), the domain's SRV records, or nowhere -- the member enters them.

MailServerUpdate

The two settings a recipient sees (email-threads.md rule 21), changed with no new test, `null` clearing one; and the folder read (rule 35), the `id` of one of the mailbox's own folders as the platform last listed them (`MailFoldersOut`), `null` for the inbox -- read from that moment on, never what it held before. An omission keeps each. Acting in the mailbox (email-threads.md rule 36; mail-server.md rule 12), as on a signed-in mailbox but with no consent to ask -- the password already reaches it: `acts_in_mailbox`, `answered_folder` (the `id` of one folder of the list other than the folder read, `null` files nothing) and `mark_answered_read`. Neither is charged. `filing_folders`, as on a signed-in mailbox (`MailboxUpdate`), out of the same list (mail-handling.md rule 10).
acts_in_mailbox
​boolean
Default: false
​
​
mark_answered_read
​boolean
Default: false
​
​
​

MailboxAuthorizeIn

Begin signing in a dedicated address's mailbox (rule 59): the address the client names -- the sign-in must come back as exactly that mailbox's primary address (rule 61) -- the name the organization knows it by (the address when left out), and the subject of a conversation the organization starts (email-threads.md rule 21).
address
​string · required
​
​

MailboxAuthorizeOut

Where the browser goes next -- Microsoft's consent screen, carrying a sealed `state` that expires in ten minutes (rule 63) -- and the approval page to pass on to an administrator should Microsoft ask for one (rule 62).
admin_approval_url
​string · required
admin_note
​string · required
url
​string · required

MailboxDraftState

string · enum
Enum values:
writing
mailbox
removing
taken_over

Where a thread's prepared reply stands in the mailbox's drafts, on a signed-in mailbox whose client opted in (mail-handling.md rules 12-18): mailbox, it waits there too, as the platform wrote it; taken_over, the draft was changed or removed in the mail app, so it is the person's and the platform no longer follows it (rule 13). A prepared reply with neither waits in the platform alone. writing and removing are the draft's own row while the platform asks the mailbox to write or remove it -- recorded before it asks, so an answer that never came is known on the next run -- and never shown on a prepared reply.

A native PostgreSQL enum (channel_mailbox_draft_state).

MailboxHealth

string · enum
Enum values:
working
broken

A signed-in mailbox works from the moment of consent and breaks only on the provider's word (rule 64).

MailboxOut

What a signed-in mailbox's page shows (rules 59-65), and nothing secret. `consented_at` is the moment its mail is taken from (email-threads.md rule 23); `unread_before`, set after a break longer than the read-back bound, is the moment before which mail was not read (rule 27); `on_disconnect` is where the client or their administrator removes the platform at Microsoft (rule 65), in English -- the dashboard words it in its reader's language (#1722).
account_label
​string · required
​required
​required
acts_in_mailbox
​boolean · required
address
​string · required
​AnsweredFolderOut · required

The folder the mail a thread answered is filed in (email-threads.md rule 36): id as MailFolderOut gives it and its name, both None while nothing is filed.

​required
connection_id
​string · uuid · required
consented_at
​string · date-time · required
​MailFolderOut[] · required
health
​MailboxHealth · enum · required

A signed-in mailbox works from the moment of consent and breaks only on the provider's word (rule 64).

Enum values:
working
broken
​required
​required
mark_answered_read
​boolean · required
​required
on_disconnect
​string · required
on_opt_out
​string · required
​ReadFolderOut · required

The folder a mailbox is read in (email-threads.md rule 35): id and name of the one chosen instead of the inbox, both None while the inbox is read; since when the choice last changed -- what reached the folder before it is never read -- None while it never did.

​required
scopes
​string[] · required
​required
​required
working_since
​string · date-time · required
write_consent
​WriteConsent · enum · required

Microsoft's second consent, to change mail (connections.md rule 60): never asked, given, or refused by Microsoft since -- which stops the acting and nothing else (email-threads.md rule 37).

Enum values:
none
granted
lapsed
​required
​required
​
​
​
​
mirrors_drafts
​boolean
Default: false

MailboxUpdate

The settings a signed-in mailbox has beside the burst window: the subject of a conversation the organization starts (email-threads.md rule 21; the sender's name is the one Microsoft holds), `null` clearing it; and the folder read (rule 35), the `id` of one of the mailbox's own folders (`MailFoldersOut`), `null` for the inbox. Changing the folder reads it from that moment on, never what it held before. Acting in the mailbox (rules 36-37): `acts_in_mailbox` true needs the second consent given first (409 otherwise), and false is opting out, which forgets that consent; `answered_folder`, the `id` of one of the mailbox's own folders other than the folder read, files the mail a thread answered there, `null` files nothing; `mark_answered_read` marks it read. Neither is charged. `filing_folders` (mail-handling.md rule 10): the `id`s of the mailbox's own folders a thread flow may file into, at most `FILING_FOLDERS_MOST`, none the folder read; the list given replaces the one held, `null` or `[]` lists none. The prepared reply in the mailbox (mail-handling.md rules 12-19): `mirrors_drafts` true needs the same second consent (409 otherwise) and keeps a thread's prepared reply in the mailbox's drafts too; false stops writing, replacing and removing them, leaving the drafts already there. The second consent is forgotten only once neither `acts_in_mailbox` nor `mirrors_drafts` is on. Not charged (rule 20).
acts_in_mailbox
​boolean
Default: false
​
​
mark_answered_read
​boolean
Default: false
mirrors_drafts
​boolean
Default: false
​
​

MapFrameOut

One opened card's inside (rule 11). `parts` are the cards inside it, each with `frame` set to `key`; `lines` every line drawn inside it, including those from a part inside to an outer card it reads or writes. `reads` and `writes` name the outer cards drawn above and below it; one both read and written is in `writes` alone (rule 17).
alone
​boolean · required
hidden
​integer · required
key
​string · required
​MapLanesOut · required

A frame left to right (rules 12, 13), by key. inbound names outer cards; during starts with the frame's own card -- the agent, or the automation.

​MapLineOut[] · required
​MapPartOut[] · required
reads
​string[] · required
writes
​string[] · required
draft
​boolean
Default: false
​
​

MapGroupOut

The parts of one owner, grouped (rule 25), with the worst of their states (rule 31) -- `None` when none of them carries a state.
​MapOwner · required

A part's owner: an installed recipe (id the install's, key and name the recipe's), the organization's own, or the platform's (a capability).

parts
​string[] · required
​required
​

MapLanesOut

A frame left to right (rules 12, 13), by key. `inbound` names outer cards; `during` starts with the frame's own card -- the agent, or the automation.
after
​string[]
Default: []
before
​string[]
Default: []
during
​string[]
Default: []
inbound
​string[]
Default: []

MapLineOut

One road between two cards, by their `key`s. A card reached by several roads has several lines and is drawn once (rule 19).
kind
​LineKind · enum · required

Why two cards are connected, in the client's words (rule 18).

Enum values:
brings_conversations
runs_before
may_call
runs_after
switched_on
set_off_when_conversation_ends
set_off_when_record_written
runs_flow
source
​string · required
target
​string · required

MapOwner

A part's owner: an installed recipe (`id` the install's, `key` and `name` the recipe's), the organization's own, or the platform's (a capability).
kind
​OwnerKind · enum · required

Who a part belongs to, as the organization's inventory marks it (rule 25).

Enum values:
recipe
organization
platform
​
​
​

MapPartOut

One card. `key` is the map's own handle for it (`<kind>:<id>`), what every line and lane names; `id` is the part's own id, for opening its screen (rule 23) -- for a documents or capability card, the agent's. `state` is `None` for a card that carries no dot (rule 35): a collection, documents, a capability, a number. `failure` -- why it failed -- is filled only for a reader who may read where the failure was recorded (rule 30). `missing` marks a part something points at that no longer exists, or a part an incomplete recipe lacks; `name` is then the last name known, or `None` (rule 37).
​required
key
​string · required
kind
​PartKind · enum · required

What a card is. The first six sit on the outer level (rule 4: agents, automations no conversation sets off, what brings conversations, collections, an agent's documents); the rest only inside an open frame. Each opens its own ordinary screen (rule 23).

Enum values:
agent
automation
trunk
campaign
channel
collection
document
tool
​required
​MapOwner · required

A part's owner: an installed recipe (id the install's, key and name the recipe's), the organization's own, or the platform's (a capability).

​
​
​
​
​
missing
​boolean
Default: false
never_run
​boolean
Default: false
nodes
​integer
Default: 0
on_every_node
​boolean
Default: false
openable
​boolean
Default: false
reaches_no_agent
​boolean
Default: false
​
​MapOwner[]
Default: []
runs_flow_as_step
​boolean
Default: false
​
seen_by
​string[]
Default: []
​
​
​MapStepToolOut[]
Default: []

MapStepToolOut

A tool one of a flow's steps runs (rule 14), by id; `name` is `None` and `missing` true for a step naming a tool that no longer exists.
id
​string · uuid · required
​required
missing
​boolean
Default: false

McpImportIn

Which MCP server to import, how to reach it, and what authorizes it. `connection_id` points every imported instance at a connection, which is how an import authorizes: the grant stays with the connection and the instance carries no key of its own. `auth` is the **legacy** way to say the same thing — an opaque bearer for a third party's server, stored on each instance's `config` and therefore write-only, like every other credential here: it goes in and never comes back out. It is kept because it is a published parameter with existing callers and because no stored bearer can be turned into a connection; prefer `connection_id` for anything new. **Give one or the other, never both.** Leave both unset for our own server — a tool that calls this platform runs as whoever is talking to the agent, and no credential is stored for it at all. `name_prefix` is put in front of every imported tool's name. Two servers may each publish a `search`, and a person attaching both to one agent has to be able to tell them apart.
server_url
​string · required
​
​
​

Measure

`agg(column)`, scaled if `scale` is given. `column` is `None` only for `Aggregation.COUNT`; every other aggregation names a column the dataset's catalogue declares as measurable. Validated against that catalogue by `service.run_spec`/`service.create_chart` — never trusted on its own.
agg
​Aggregation · enum · required

How a measure reduces the matching rows to one number per group. Owned here because analytics is the one caller that has to speak it identically over three different slices' data (interactions, billable_metrics, custom_data) — none of which may import this module to borrow it, so each one's own aggregate surface takes it as a plain string instead.

Enum values:
count
sum
avg
min
max
​
​

MediaKind

string · enum
Enum values:
image
video
audio
document

What one outbound attachment is (channels.md rule 76): an image, a video, an audio file or a document. Not channels' AttachmentKind, which names what arrived -- a sticker, a contact card, a location among them -- and which no road is asked to send.

MemberRemoved

`DELETE /auth/members/{id}`'s response. `deleted_keys` is empty for the common case — most members never minted one.
​DeletedKey[] · required

MembershipOut

​ConversationAccessOut · required

What a member reads. everything is an owner or an admin, who read every conversation and whose access is never set (rule 35); default and channels are then every conversation, and nothing set on its own.

effective_flags
​FeatureFlag[] · required
Enum values:
voice_agents
text_agents
ai_profiles
outbound_calls
rag
memory
tools
custom_tools
email
​string · email · required
flags
​FeatureFlag[] · required
Enum values:
voice_agents
text_agents
ai_profiles
outbound_calls
rag
memory
tools
custom_tools
grantable_flags
​FeatureFlag[] · required
Enum values:
voice_agents
text_agents
ai_profiles
outbound_calls
rag
memory
tools
custom_tools
id
​string · uuid · required
is_active
​boolean · required
name
​string · required
org_id
​string · uuid · required
person_id
​string · uuid · required
role
​Role · enum · required

A person's role inside one organization. Per-membership, not per-person.

Enum values:
owner
admin
member
​

MemoryBoundsOut

The notebook's bounds as the interface prints them (rule 27): lines, a line's length, the notes reread from each layer and the business notes that may be marked "remember always". The same numbers recall and the writes enforce.
max_line_length
​integer · required
max_lines
​integer · required
remember_always_max
​integer · required
reread_per_scope
​integer · required

MemoryLineIn

A new line: what to notice, in the owner's own words, and whom it is about. It applies to what is noted from then on; conversations already noted are never gone back over (rule 22).
about
​LineAbout · enum · required

Whom a line asks the agent to notice something about (rule 19): the person in each conversation, or the business. It decides where the note is filed under rule 2 -- a line about a person produces notes about that person, one about the business produces business notes.

Enum values:
person
business
words
​string · minLength: 1 · maxLength: 120 · required

MemoryLineOut

One line of an agent's memory, with how many notes it has produced (rule 23). A line belongs to the agent, never to a version (rule 22).
about
​LineAbout · enum · required

Whom a line asks the agent to notice something about (rule 19): the person in each conversation, or the business. It decides where the note is filed under rule 2 -- a line about a person produces notes about that person, one about the business produces business notes.

Enum values:
person
business
agent_id
​string · uuid · required
created_at
​string · date-time · required
id
​string · uuid · required
notes
​integer · required
updated_at
​string · date-time · required
words
​string · required

MemoryLinePatch

What may change on a line: its words and whom it is about. Both are optional and independent. Refused while the Analyst counts the line (rule 24).
​
​

MemoryLinesOut

An agent's lines, oldest first, beside the bounds they are held to.
​MemoryBoundsOut · required

The notebook's bounds as the interface prints them (rule 27): lines, a line's length, the notes reread from each layer and the business notes that may be marked "remember always". The same numbers recall and the writes enforce.

​MemoryLineOut[] · required

MemoryOut

A stored memory as the management surface sees it. `org_id` is the tenant that owns it — never the person who happened to create it (principle 6). `interaction_id` names the conversation this fact was **first** learned in (`None` for a memory no interaction produced — a seed, a test, an explicit write with none to name); `revise` never moves it, because a correction does not change where a fact was first learned. `support_count` is how many conversations support it — see `service.consolidate`. `pinned` is editable only where the fact is: agent-global, from `PATCH /memory/{id}`. `looks_personal` is `schemas.looks_personal(content)`, computed fresh on every read — a mark, never a gate; see `DECISIONS.md`.
agent_id
​string · uuid · required
​required
content
​string · required
created_at
​string · date-time · required
id
​string · uuid · required
​required
​required
​required
looks_personal
​boolean · required
org_id
​string · uuid · required
pinned
​boolean · required
redacted
​integer · required
scope
​Scope · enum · required

Which layer a memory lives in. The retrieval side pulls the relevant layer(s) into context; the write side must respect the PII boundary.

Enum values:
agent_global
per_contact
per_session
​required
support_count
​integer · required
updated_at
​string · date-time · required

MemoryPatch

A partial edit to one note -- only the fields provided change. `content` is the correction (rule 10): it applies to a business note and to a person's note alike, to a person's only for whoever may read the conversation it was learned in (rule 31). `pinned` is "remember always" (rule 26) and is a business note's alone: a person's note is not marked, and a sixth business note marked is refused naming the bound. Both are optional and independent, so marking needs no reworded content and a correction needs no pin decision. A per-session note is not edited here. Scope is not a field here and cannot be: the note's own scope decides which of the two fields it takes.
​
​

MemoryPersonNotesOut

A person's sheet: every note the reader may read, whole, each with its line, when it was learned and the conversation it was learned in (rule 13).
contact_key
​string · required
​MemoryOut[] · required
redacted
​integer · required
​

MemoryPersonOut

One person the agent has notes about, as the list shows them: by the number or address their notes are keyed to, and the label of a record of the client's Databases tied to that number when there is one -- memory itself keeps no name. A person is listed only to a reader who may read at least one of their notes, and counts only the notes that reader may read (rule 31).
contact_key
​string · required
​required
last_learned_at
​string · date-time · required
notes
​integer · required
redacted
​integer · required
​

MemoryRefusal

string · enum
Enum values:
lines_full
line_repeated
line_counted
remember_always_full

Which refusal a 409 from the lines or from marking a note is, as a word the screen can name rather than a sentence to parse.

MemoryRefusalOut

The body of a refused line or mark: the sentence, which refusal it is and, when a bound refused it, the bound.
detail
​string · required
refusal
​MemoryRefusal · enum · required

Which refusal a 409 from the lines or from marking a note is, as a word the screen can name rather than a sentence to parse.

Enum values:
lines_full
line_repeated
line_counted
remember_always_full
​

MemorySwitchIn

Turn an agent's memory on or off. It takes effect on the agent's next conversation and deletes nothing it has already learned.
on
​boolean · required

MemorySwitchOut

Whether one agent remembers (memory.md rule 15): the agent's own switch. `on` is the switch alone. Whether the agent *runs* memory also needs the organization's entitlement (rule 16), which this slice cannot ask and a person who may see the switch holds by the same flag that reaches this route.
agent_id
​string · uuid · required
on
​boolean · required

Message

One thing the other side said: words, files, or both.
​
​Attachment[] · maxItems: 5
text
​string · maxLength: 64000
Default:

MessageMark

string · enum
Enum values:
machine
spam
auth_failed

The mark a road gives an arriving message (channels.md rule 88): written by a machine, unwanted by the provider's own verdict, or failing the proof of who sent it that the provider checked (owner ruling 2026-10-16, #1550). A burst holding only marked messages runs no flow and no agent turn.

MetricCategory

string · enum
Enum values:
calls
messages
ai
tools
storage

The five families every metric belongs to, and the whole of the list.

docs/features/billable-metrics.md fixes them there and says why they are not staff-editable: "a category is a sentence about what the platform sells, and the list of those sentences is this document's to hold". They are the top level of every consumption picture and of every price list, so a sixth is a decision written into the spec before it is a row anywhere. Ruled by the owner (2026-09-15).

STORAGE was added 2026-09-18 (ADR 0040) for what it costs to keep something, as distinct from something that happened: a gigabyte held for a month costs the same whether it came from a call recording, a RAG document or an upload.

MetricOut

active
​boolean · required
category
​MetricCategory · enum · required

The five families every metric belongs to, and the whole of the list.

docs/features/billable-metrics.md fixes them there and says why they are not staff-editable: "a category is a sentence about what the platform sells, and the list of those sentences is this document's to hold". They are the top level of every consumption picture and of every price list, so a sixth is a decision written into the spec before it is a row anywhere. Ruled by the owner (2026-09-15).

STORAGE was added 2026-09-18 (ADR 0040) for what it costs to keep something, as distinct from something that happened: a gigabyte held for a month costs the same whether it came from a call recording, a RAG document or an upload.

Enum values:
calls
messages
ai
tools
storage
code
​string · required
created_at
​string · date-time · required
id
​string · uuid · required
name
​string · required
​required
updated_at
​string · date-time · required

ModeUpdate

Stop asking, or start again.
mode
​ConfirmationMode · enum · required

Whether one conversation is still asking before consequential actions.

architect.md rule 20: "a person may tell one conversation to stop asking… It is a property of that conversation only — not of the person or the organization — and asking is always where a conversation starts."

It is a column vocabulary here and an input to the run-loop there, which is the same two-names split Channel has and for the same reason: this one is what the record says a conversation was set to, and that one is what decides whether the loop stops. Member for member, so a writer maps with interactions.ConfirmationMode(mode) and needs no table.

Enum values:
ask
auto

ModelsByKind

Every registry row, per kind, **disabled rows included** with `enabled=false`: a picker offers the enabled ones by display name, and a component already on a disabled one can still say what it is on and that it is no longer offered. The client filters; the server does not guess which rows a caller already has written.
​EnabledModelOut[] · required
​EnabledModelOut[] · required
​EnabledModelOut[] · required
​EnabledModelOut[]
Default: []

Moment

​MomentLine[] · required

MomentLine

One line of a moment, verbatim from the record (rule 5).
at
​string · date-time · required
seq
​integer · required
speaker
​string · required
text
​string · required

NoRunRule

string · enum
Enum values:
marked
kept_for_people
with_a_person
recipe_off
too_late
gated
not_working
not_a_member

Why a burst ran no flow, by the platform's own rules (mail-handling.md rule 8): the reason its decision record carries. not_working is a connection not proved or broken (connections.md rule 55) and not_a_member a sender nobody vouches for on a members connection (rule 52), beside rule 8's six.

NodeIn

instructions
​string
Default:
​
​Position

Where a node sits in the visual editor.

tool_ids
​string[]

NodeOut

id
​string · uuid · required
instructions
​string · required
label
​string · required
​required
origin_id
​string · uuid · required
​Position · required

Where a node sits in the visual editor.

tool_ids
​string[] · required

NodeUpdate

Partial edit — only the provided fields change. `name` is the one field whose `None` is a value: sending `"name": null` (or a blank string) clears the name, while leaving the key out keeps it. The service reads `model_fields_set` to tell the two apart.
​
​
​
​

NotAnswered

string · enum
Enum values:
paused
reading_off

Why a question is not being answered now (rules 39, 41). Each question says so on its own, so a list read while reading is off says it of every one.

ObjectionIn

`POST /legal/objections`: an owner objects to a version of the list of subprocessors during its period, saying which and why (rule 24).
language
​LegalLanguage · enum · required

The languages a version may be written in (values table): Spanish required, English optional, and the Spanish prevails (rule 4).

Enum values:
es
en
reason
​string · minLength: 1 · maxLength: 5000 · required
subprocessors
​string[] · minItems: 1 · required
version
​string · required

ObjectionPeriodOut

A version of the list of subprocessors still open to objection (rule 24).
ends_at
​string · date-time · required
version
​string · required

OpenCountOut

How many open conversations the reader may read -- the number the rail's Everything entry carries (rule 14), asked alone for a badge that needs no rail (#2069).
count
​integer · required

OpenRequest

Which agent to talk to, and whether to ask before consequential actions. The version, the graph, the tools and the settings are the agent's published answer and are not choosable here — picking any of them would be a caller choosing what it was never offered. `mode` is not the same kind of thing: it is a fact about *this conversation* rather than about the agent, and it is the client saying how they want to work. It **defaults to `ask`**, which is the spec's "asking is always the default" in the shape of a field: a client that says nothing gets every question. `draft_version_id` is the one exception to the first paragraph, stated precisely (text-agent.md, "A conversation with a draft, for a test"): a caller that already edits an agent's draft may name **that draft** and talk to it before it is published. It is the draft's own version id, read off `get_draft` -- an id rather than a flag, because the spec's word is a *named* draft and the record then carries what was named; and it must be the draft, not a published or archived version, which would be picking a version the way the first paragraph forbids (409). Absent, the conversation runs against the published version exactly as before.
agent_id
​string · uuid · required
​
​
mode
​ConfirmationMode · enum

Whether one conversation is still asking before consequential actions.

architect.md rule 20: "a person may tell one conversation to stop asking… It is a property of that conversation only — not of the person or the organization — and asking is always where a conversation starts."

It is a column vocabulary here and an input to the run-loop there, which is the same two-names split Channel has and for the same reason: this one is what the record says a conversation was set to, and that one is what decides whether the loop stops. Member for member, so a writer maps with interactions.ConfirmationMode(mode) and needs no table.

Enum values:
ask
auto
Default: ask

OpenedConversation

A conversation that has started, and the first thing the agent said. `interaction_id` is both halves at once: it is the handle every later request in this conversation carries, and it is the id of the record in the [log](../interactions/CONTRACT.md). One id rather than two, because a conversation and the record of it are the same conversation.
agent_id
​string · uuid · required
greeting
​string · required
interaction_id
​string · uuid · required
mode
​ConfirmationMode · enum · required

Whether one conversation is still asking before consequential actions.

architect.md rule 20: "a person may tell one conversation to stop asking… It is a property of that conversation only — not of the person or the organization — and asking is always where a conversation starts."

It is a column vocabulary here and an input to the run-loop there, which is the same two-names split Channel has and for the same reason: this one is what the record says a conversation was set to, and that one is what decides whether the loop stops. Member for member, so a writer maps with interactions.ConfirmationMode(mode) and needs no table.

Enum values:
ask
auto

OrgMapOut

The organization's map, as this reader may see it (rule 40).
alone
​boolean · required
​MapPartOut[] · required
​MapFrameOut[] · required
​MapGroupOut[] · required
hidden
​integer · required
​MapLineOut[] · required
​required
read_at
​string · date-time · required

OrganizationAgreementIn

`POST /legal/agreements/organization`: an owner agrees to the data processing agreement for the organization, stating they may bind it, in one box (rules 17-18, 21-22). `version` is the one the page showed: the version in force, or one published to take effect later.
language
​LegalLanguage · enum · required

The languages a version may be written in (values table): Spanish required, English optional, and the Spanish prevails (rule 4).

Enum values:
es
en
ticked
​boolean · required
version
​string · required

OrganizationAskOut

The data processing agreement asked of the organization (rules 20, 22): which version, by when, whether the organization is held now, and whether this person may agree -- an owner, not impersonating, not through a key minted in a session.
​required
held
​boolean · required
may_agree
​boolean · required
version
​string · required

OrganizationOut

default_country
​string · required
entitled_flags
​FeatureFlag[] · required
Enum values:
voice_agents
text_agents
ai_profiles
outbound_calls
rag
memory
tools
custom_tools
grantable_flags
​FeatureFlag[] · required
Enum values:
voice_agents
text_agents
ai_profiles
outbound_calls
rag
memory
tools
custom_tools
id
​string · uuid · required
is_active
​boolean · required
language
​string · required
name
​string · required
timezone
​string · required

OrganizationUpdate

Partial edit of the organization's own settings — only provided fields change (`model_dump(exclude_unset=True)`). Four fields and deliberately only these. What an organization is *entitled* to is the platform's decision about a customer and stays with staff (`EntitlementUpdate`); `is_active` is the same. A client editing either would be a client granting themselves a feature or reinstating their own suspended account.
​
​
​
​

Origin

string · enum
Enum values:
dashboard
key
staff
before_legal

Where an entry came from (rule 26): the dashboard or a key -- and, for a mark, staff from the directory (rule 23). before_legal is an agreement kept before this proof existed, carried over with what was kept then.

OurSide

What a peer must be told of us (trunks.md rule 51): where to send calls, the addresses our calls come from and the transports we accept -- read from this deployment's configuration, in one place (`service.our_side`). An empty value is one the deployment has not configured, shown as such rather than guessed.
​
source_addresses
​string[]
Default: []
transports
​string[]
Default: []

OutboundIn

What one outgoing message is: free text, an attachment, or both -- the text then its caption -- **or** an approved template. Exactly one of a template and the rest, and the refusal for both or neither is the service's — a template with a line of text beside it is two messages asked for as one, and the provider would send whichever the builder happened to read first. `choices` go under free text: as reply buttons where the road shows them, as numbered text where it does not (rule 62) or where an attachment carries the text as its caption (rule 79), and a reply naming one by its number or label then arrives as its payload (rule 63).
​
​ChoiceIn[] · maxItems: 10
​
​

Outcome

string · enum
Enum values:
succeeded
failed
refused

How one execution of a tool ended.

Owner

What another feature says when it owns a row: what kind of thing it is, and which one. **A value and not two loose columns**, because the pair is meaningless apart: an id with no kind names nothing, and a kind with no id names everything. Both halves are opaque to this slice on purpose — `tools` is tier 0 and the things that own rows sit above it, so resolving *"campaign 3f2a"* into a name a person reads is the screen's job and never this one's. What is asked of it here is only ever equality: is the caller the owner.
id
​string · uuid · required
kind
​string · minLength: 1 · maxLength: 50 · required

OwnerKind

string · enum
Enum values:
recipe
organization
platform

Who a part belongs to, as the organization's inventory marks it (rule 25).

Parameter

One thing a recipe asks the organization for (rule 2): required, or with a default. Never a secret -- a credential lives in a connection.
kind
​ParameterKind · enum · required

recipes.md rule 2: what a parameter's value is.

Enum values:
text
number
boolean
date
email
collection
weekly_moment
agent
label
​string · required
name
​string · pattern: ^[a-z][a-z0-9_]*$ · required
default
​
description
​string
Default:
​
​
required
​boolean
Default: false

ParameterKind

string · enum
Enum values:
text
number
boolean
date
email
collection
weekly_moment
agent

recipes.md rule 2: what a parameter's value is.

ParametersIn

New values for some of an installed recipe's parameters; the rest keep theirs.
​object · required

PartKind

string · enum
Enum values:
agent
automation
trunk
campaign
channel
collection
document
tool

What a card is. The first six sit on the outer level (rule 4: agents, automations no conversation sets off, what brings conversations, collections, an agent's documents); the rest only inside an open frame. Each opens its own ordinary screen (rule 23).

PartOut

One resource an installed recipe made, and where it lives (rule 10).
id
​string · uuid · required
key
​string · required
kind
​string · required
where
​string · required
​
editable
​boolean
Default: false
​
​
position
​integer
Default: 0
​
​
​
​

PartState

string · enum
Enum values:
failing
unknown
paused
working

Rule 29's four states. Declared worst first: the worst of several is the first of these (rule 31).

PauseOrigin

string · enum
Enum values:
operator
provider
our_failure
configuration

Who stopped a campaign: a person, the provider's breaker, or the cap on failures of our own.

OUR_FAILURE is docs/features/outbound-calls.md rule 31: a contact returned for failures of ours OUR_FAILURE_RETURN_CAP times, or this deployment unable to reach the carrier run after run. It is kept apart from PROVIDER because the two are fixed by different people -- the provider's account is the client's to settle at the carrier, and ours is ours.

PendingOut

`GET /legal/pending`: what the platform asks of this person and their organization now.
​ObjectionPeriodOut[] · required
​required
​PersonAskOut[] · required

PersonAgreementIn

`POST /legal/agreements/person`: a person agrees again to the Terms and the Privacy policy in force, together (rules 15-16). The versions are the ones the page showed, so what is kept is what was read; `ticked` is the box.
language
​LegalLanguage · enum · required

The languages a version may be written in (values table): Spanish required, English optional, and the Spanish prevails (rule 4).

Enum values:
es
en
privacy
​string · required
terms
​string · required
ticked
​boolean · required

PersonAskOut

A version of the Terms or the Privacy policy the person is asked to agree to (rule 16): until `deadline_at` they may put it off; `blocking` once it passed.
blocking
​boolean · required
​required
document
​LegalDocument · enum · required

The six legal documents the platform keeps (rule 1). Each value is also the last segment of the document's address, so it is spelled as a URL is.

Enum values:
terms
privacy
cookies
acceptable-use
processing-agreement
subprocessors
version
​string · required

PersonGrantIn

One roster entry's grant on a per-person connection (connections.md rule 35): the secret the provider gave that person, in the provider's own shape -- for `http_key`, the JSON object `header`, `prefix`, `value` with **their** key. Setting it again replaces it and clears a broken state; it is never shown back.
grant_secret
​string · minLength: 1 · required

PersonGrantOut

Which roster entry holds a grant on a per-person connection, and whether it still works -- never the secret. `display_name` is the entry's, so a broken grant says whose it is (rule 36).
created_at
​string · date-time · required
display_name
​string · required
granted_via
​GrantedVia · enum · required

Which door the grant arrived by.

The spec's "a grant can arrive by more than one door, and the connection is the same whichever it came through": this is the record of the door, and nothing in this slice or above it branches on it.

Enum values:
pasted
embedded_signup
roster_entry_id
​string · uuid · required
status
​HealthStatus · enum · required

Whether the grant is currently usable.

Enum values:
healthy
broken
​required
updated_at
​string · date-time · required

PersonState

string · enum
Enum values:
waiting
held

Where a thread is when it is not with its flow (handoff.md rule 1).

Two members and no third: "with a person, it is either waiting -- nobody has taken it -- or held by exactly one person". With its flow is the absence of either -- None on the record -- because every thread starts there and is there whenever it is not with a person; a member for it would be a value every row but a few carries to say nothing happened. One enum rather than two columns, so "waiting and held at once" cannot be written.

A native PostgreSQL enum (interaction_person_state).

PickupDecider

string · enum
Enum values:
classifier
platform

Who decided who picked up: the classifier's verdict, or the platform without one (interactions.md rule 4). The platform decides when nobody spoke (a person), when the ceiling produced no verdict (a voicemail), and when the classifier was asked and reached nothing (a person).

PickupDecision

How an outbound call's pickup was decided, as the record keeps it. interactions.md rule 4 and voice-agent.md rule 61: *"the interaction records who answered, whether the classifier or the platform decided, by which path ... which model classified, and how long it took."* Who answered is `answered_by`, beside it; this is the rest. Written once by the voice channel through `settle`, and only on a call that asked. `provider`/`model` name the model the classifier ran on, `None` where no model was asked (nobody spoke) or where the ask reported nothing it ran on. `duration_ms` runs from the answer to the decision -- the whole of the deciding, waiting included, since that is what the caller heard as silence.
decided_by
​PickupDecider · enum · required

Who decided who picked up: the classifier's verdict, or the platform without one (interactions.md rule 4). The platform decides when nobody spoke (a person), when the ceiling produced no verdict (a voicemail), and when the classifier was asked and reached nothing (a person).

Enum values:
classifier
platform
duration_ms
​integer · min: 0 · required
path
​PickupPath · enum · required

By which path who picked up an outbound call was decided (voice-agent.md, "Who picked up", rule 61): "the other side spoke, nobody spoke, the ceiling". The values are the voice channel's own labels for the three, so the record, the metric and the log line say the same word.

Enum values:
spoke
silent
ceiling
​
​

PickupPath

string · enum
Enum values:
spoke
silent
ceiling

By which path who picked up an outbound call was decided (voice-agent.md, "Who picked up", rule 61): "the other side spoke, nobody spoke, the ceiling". The values are the voice channel's own labels for the three, so the record, the metric and the log line say the same word.

PlacementOut

One tool placed in an agent's `pre` or `post` slot **by something that owns the placement**, outside every version (recipes.md rule 33, agents.md rule 41). It belongs to the agent: it runs from the agent's next conversation without a publish, stays through every publish and restore, and is not carried by a copy (rule 47). `owner` is `tools`' own shape -- what kind of thing placed it and which one -- compared for equality and never resolved here. `enabled` is the owner's switch: a placement switched off is kept and does not run.
agent_id
​string · uuid · required
enabled
​boolean · required
id
​string · uuid · required
​Owner · required

What another feature says when it owns a row: what kind of thing it is, and which one.

A value and not two loose columns, because the pair is meaningless apart: an id with no kind names nothing, and a kind with no id names everything. Both halves are opaque to this slice on purpose — tools is tier 0 and the things that own rows sit above it, so resolving "campaign 3f2a" into a name a person reads is the screen's job and never this one's. What is asked of it here is only ever equality: is the caller the owner.

slot
​Slot · enum · required

The lifecycle slot a tool is placed in. Which tool is attached to which slot belongs to agents; whether a tool can be in a slot belongs here, because it is a fact about the tool (service.available_in_slot).

Enum values:
pre
in
post
tool_id
​string · uuid · required

Position

Where a node sits in the visual editor.
x
​number
Default: 0
y
​number
Default: 0

PreferencesOut

A person's inbox preferences (rules 15 and 39), kept with their membership. `collapsed` is whether the rail opens collapsed; `hidden_entries` the keys of the predefined views they hid.
collapsed
​boolean · required
hidden_entries
​string[] · required

PreferencesUpdate

What changes; anything left out stays. `hidden` is the whole set of shared views hidden from the reader's rail, `hidden_entries` the whole set of predefined views hidden (rule 36), `order` the reader's views in the order they want them — each replaces what was kept. `layouts` is merged into what was kept, entry by entry (rule 37).
​
​
​
​
​

PreparedOutdatedBy

string · enum
Enum values:
contact
mailbox

What turned a prepared reply outdated (handoff.md rule 33): the counterpart writing again unmarked, or a reply sent from the mailbox itself (email-threads.md rule 33). Set only while the reply is outdated; None otherwise.

A native PostgreSQL enum (interaction_prepared_outdated_by).

PreparedReplyOut

A thread's prepared reply (handoff.md rules 32-37): what an agent's turn wrote and nobody has sent, bound to the counterpart's message it answers. Exactly one of `text` and `template` -- the agent's reply with a template (rule 67) is kept as the template -- until retention empties both; the rest names nobody and stays. `handed_over` is whether the turn that prepared it also handed the thread to a person (rule 40): sending it then leaves the thread as that hand-over left it.
​required
​required
​required
handed_over
​boolean · required
id
​string · uuid · required
interaction_id
​string · uuid · required
​required
prepared_at
​string · date-time · required
state
​PreparedState · enum · required

Where a conversation's prepared reply stands (handoff.md rule 33): ready to send as it is, or outdated because the counterpart wrote again unmarked after the message it answers, or a reply was sent from the mailbox itself (PreparedOutdatedBy says which) -- sent then only with the text a person gives (rule 34). It never expires. None on the record when it holds none.

A native PostgreSQL enum (interaction_prepared_state).

Enum values:
ready
outdated
​required
​required
burst
​string[]
​
​

PreparedSendIn

Send a thread's prepared reply (handoff.md rule 34): as it is, or with `text` in its place -- the person's changed words, sent as theirs and marked as changed (rule 37). An outdated reply is sent only with `text`.
​

PreparedSentOut

What sending a prepared reply answers (handoff.md rule 34): the send, and whether the thread went back to its flow -- false only where the turn that prepared it also handed it to a person (rule 40).
changed
​boolean · required
interaction_id
​string · uuid · required
returned_to_flow
​boolean · required
held
​boolean
Default: false
kept
​boolean
Default: false
​

PreparedState

string · enum
Enum values:
ready
outdated

Where a conversation's prepared reply stands (handoff.md rule 33): ready to send as it is, or outdated because the counterpart wrote again unmarked after the message it answers, or a reply was sent from the mailbox itself (PreparedOutdatedBy says which) -- sent then only with the text a person gives (rule 34). It never expires. None on the record when it holds none.

A native PostgreSQL enum (interaction_prepared_state).

ProfileCopy

Taking a copy of a profile. The name is required and carries no default: the spec's whole complaint about the legacy fork is that the copy kept the template's name and the client ended up with two profiles called the same thing.
name
​string · minLength: 1 · required

ProfileIn

A new profile, written as its two sections: the text section, required because any agent can be reached by text, and the voice section's arrangements in the order a call tries them -- none for a profile that takes no calls. Unknown fields are refused, so a caller still sending the flat slot ids the sections replaced is told so (422) rather than answered 200 with them ignored.
name
​string · minLength: 1 · required
description
​string
Default:
​
​
​ArrangementIn[]

ProfileOut

The bundle: a profile with its two sections resolved inline.
​required
description
​string · required
id
​string · uuid · required
​required
kind
​string · enum · required
Enum values:
text
voice
Default: text
name
​string · required
​required
​required
​ArrangementOut[] · required

ProfileUpdate

Partial edit of what a profile is called. The sections are written by `set_voice_section` and `set_text_section`, each whole. Unknown fields -- the retired slot ids among them -- are refused rather than ignored.
​
​

ProposedColumn

One column of a file, as the proposal reads it. Every value here is a **proposal**: the screen preselects it and the client changes it before anything is written. Nothing on this shape decides.
header
​string · required
key
​string · required
label
​string · required
type
​FieldType · enum · required

What one declared field holds.

Five, and each is a parse rule rather than a column type. The values live in JSONB and the database enforces nothing about them, so what a type means is service's to keep — which is where it had to live anyway: "a 0 is not a no" is a product rule, not a column.

Still deliberately no email and no phone, even now that DATOS-02's default country is ruled (the organization's own — docs/features/ authentication.md). A PHONE type would impose a parse obligation on the whole column — one bad number and the record stops being writable — where what #378 needed was a normaliser on the identity, which changes only how two rows compare. So an identity value is canonicalised by shape, not by a declared type: service._canonical_identity reads the value itself, and if it parses as a phone number under the organization's default country it is stored E.164 (shared.phone.to_e164, ADR 0023); otherwise strip() + casefold(), as before. A TEXT field holding a phone number is still the ordinary case this platform's collections use. A field holding an email address is a TEXT field the client marked personal, which is what the retention rule actually keys on.

Enum values:
text
number
boolean
date
option
matches_existing
​boolean
Default: false
options
​string[]
personal
​boolean
Default: false
sample
​string[]

ProposedLabel

One label a recipe proposes for the organization's vocabulary (rule 50): a name and the one line saying what belongs under it, and whether it joins the labels parameter the proposals are for. Created at install as the organization's own, never the recipe's (rule 54).
key
​string · pattern: ^[a-z][a-z0-9_]*$ · required
name
​string · minLength: 1 · required
description
​string
Default:
joins
​boolean
Default: false

ProposedLabels

The labels a recipe proposes (rules 50-54), and the parameter choosing labels that those the client keeps, and lets join, fill at install.
​ProposedLabel[] · minItems: 1 · required
parameter
​string · required

ProviderOut

What one provider this slice names declares for every connection of it (rule 34) -- read by the create dialog so it offers "one key for each person" where the declaration says so, rather than from a list of its own (#1137). `provider` is free text, so a slug absent from the answer declares nothing and may not be per person, which `ConnectionIn` refuses alike.
per_person
​boolean · required
provider
​string · required

ProviderPresence

One provider, for one kind: whether this deployment runs it, and its voices where it has any. `available` is `None` when nobody asked -- `get_capabilities` was called without `runs`, which only a caller outside the composition root does -- and is not `False`: a picker with no answer shows the provider as it always did, and only a definite `False` greys it (ai-profiles.md, "A provider is offered only where this platform can run it"; #535). `voices` is the provider's whole list with the language each speaks, `None` where it has none or none was read; `read_at` is when it was last read and `stale` says the last attempt to refresh it failed, so a picker offers yesterday's list with its age rather than nothing. The provider's *models* are not here: a client is offered the registry's rows (`models` below), never the list.
​
​
stale
​boolean
Default: false
​

ProvidersByKind

​object · required
​object · required
​object · required
​object · required
​object
Default: {}

PublishRefusalOut

The body of a refused publish (422). `detail` is the whole refusal as one sentence, which is what every other error on this API answers with and what a client that knows nothing about this route can still show. `reasons` is the same content **unjoined** — one string per thing that has to change — because a list is what the screen actually renders, and reconstructing it by splitting `detail` on `"; "` makes a semicolon inside a tool's name into two problems. **This is not the only 422 the route can answer with.** FastAPI validates the request body first and answers its own `{"detail": [...]}` when the optional `Author` is malformed — a shape this declaration replaces in the OpenAPI document rather than joins. So a caller reading `reasons` should treat it as possibly absent, exactly as it would for any 422 it did not send a well-formed body for. Every refusal *this platform* raises carries it.
detail
​string · required
reasons
​string[] · required

QuestionChartOut

A tracked question drawn as the one chart its kind gives (rule 42): never configured, and made from the same values `analytics` charts (rule 43). `weeks` is the series of a yes-or-no or a number question, oldest first and the last being this week; `ranking` is a closed set's, most given first. Each holds nothing while no conversation answered the question in the period -- which is every question of an agent never read -- and `conversations` says so. Every conversation counts, whoever may open it (interactions.md rule 51).
agent_id
​string · uuid · required
conversations
​integer · required
kind
​QuestionKind · enum · required

The kind of answer a question takes (rule 35), which is also what its chart is (rule 42): nobody configures one.

Enum values:
yes_no
closed_set
number
​required
question_id
​string · uuid · required
​ChartOption[] · required
short_name
​string · required
since
​string · date-time · required
state
​QuestionState · enum · required
Enum values:
active
paused
timezone
​string · required
until
​string · date-time · required
​ChartWeek[] · required

QuestionKind

string · enum
Enum values:
yes_no
closed_set
number

The kind of answer a question takes (rule 35), which is also what its chart is (rule 42): nobody configures one.

QuestionMatch

A conversation the search offers for a verdict: one the person may read (rule 36), shown by what its reading kept -- never its transcript (rule 37).
​required
interaction_id
​string · uuid · required
​Moment[] · required
​required

QuestionProposalOut

from_verdicts
​boolean · required
kind
​QuestionKind · enum · required

The kind of answer a question takes (rule 35), which is also what its chart is (rule 42): nobody configures one.

Enum values:
yes_no
closed_set
number
meaning
​string · required
search_id
​string · uuid · required
short_name
​string · required
options
​string[]

QuestionRefusal

string · enum
Enum values:
reading_off
cannot_turn_on
question_bound

Why a question cannot be added now, or its count cannot be confirmed (rules 44, 59): a word the screen can name, to invite or to explain.

QuestionRefusalOut

The body of a refused question: the sentence, which refusal it is, and -- for the bound -- the number it names (rule 44).
detail
​string · required
refusal
​QuestionRefusal · enum · required

Why a question cannot be added now, or its count cannot be confirmed (rules 44, 59): a word the screen can name, to invite or to explain.

Enum values:
reading_off
cannot_turn_on
question_bound
​

QuestionSearchIn

What to count (rules 35-36): the owner's question, in their own words, written in the agent's results section. Trimmed; empty or past `MAX_QUESTION_LENGTH` characters is refused.
sentence
​string · minLength: 1 · maxLength: 120 · required

QuestionSearchOut

What the search of the recent past found (rules 36, 37).
found
​integer · required
id
​string · uuid · required
​QuestionMatch[] · required
sentence
​string · required
verdicts_asked
​integer · required
​

QuestionState

string · enum
Enum values:
active
paused

RailEntry

One entry of a person's rail, in order. `key` names it for the preferences — `to_review`, `everything`, `unassigned`, `channel:<channel key>`, `person:<id>` or `view:<id>` — and `layout` is how its reader lays it out (rule 37). `filters` is what applying it narrows the list by. `open` is how many open conversations match it, or absent for a view saved without a count (rule 14). `view` is set for a saved view, `channel` for a channel's predefined view, `person_id` for a person's.
​ViewFilters · required

The inbox's filters (inbox.md rule 17), by the names its address gives them. A name the inbox does not recognise is refused, never dropped (extra="forbid"); every one is optional, and an unset one is absent.

The values are the address's: holder, channelKey, agentId, endReason, counterparty, answeredBy, label and preparedReply hold several values joined by commas; since/until are calendar days in the reader's own zone; the either-way filters are yes or no; unreplied is on or absent. An answer sends an unset filter as null, which the address reads as absent; what is stored leaves it out.

key
​string · required
kind
​RailEntryKind · enum · required

What a rail entry is (rules 12-13).

Enum values:
to_review
everything
view
channel
person
unassigned
​required
​
layout
​Layout · enum

How an entry lays out the list and a conversation (inbox.md rule 37): side by side, or one at a time across the whole width.

Enum values:
split
single
Default: split
​
​

RailEntryKind

string · enum
Enum values:
to_review
everything
view
channel
person
unassigned

What a rail entry is (rules 12-13).

RailOut

A person's rail (rules 12-16): its entries in order — To review, while it holds a thread the reader may read, then everything, then shared views, then the reader's own, then the channels, then the people with unassigned last — and what the editor needs besides. `shown` is whether the rail holds more than one distinct entry (rule 15); a rail that is not shown is not drawn, collapsed or not. `hidden` are the shared views the reader hid, and `archived` the predefined views they hid (rule 36), to show again. `max_views` is the bound.
​RailEntry[] · required
can_share
​boolean · required
​RailEntry[] · required
​ViewOut[] · required
max_views
​integer · required
​PreferencesOut · required

A person's inbox preferences (rules 15 and 39), kept with their membership. collapsed is whether the rail opens collapsed; hidden_entries the keys of the predefined views they hid.

shown
​boolean · required

ReachMode

string · enum
Enum values:
everyone
only
except

Who reaches agents on a connection (rule 90), on a road that declares it -- the three email roads (email-threads.md rules 21, 34): everyone, the default; only the senders its entries list; or everyone except them. A sender it keeps for people runs no flow and waits for a person.

Reachability

string · enum
Enum values:
reachable
unreachable
unknown
incomplete

Whether the phone network can reach a trunk, as far as the reconciler knows -- four readings, not two (docs/features/trunks.md: "a trunk nobody has checked is not shown as reachable either"; #513). UNREACHABLE is a fact a run found; REACHABLE is a fact a recent run found; UNKNOWN is the absence of a recent run, which is not health; INCOMPLETE is the one reading that is not about a run at all -- the trunk is missing what its carrier authenticates by, and no check could make it work (#110). A StrEnum and not a bare Literal for the reason FallbackCondition is one: a client switches on it, and the contract renders an enum's members where an alias renders a name.

ReadFolderOut

The folder a mailbox is read in (email-threads.md rule 35): `id` and `name` of the one chosen instead of the inbox, both `None` while the inbox is read; `since` when the choice last changed -- what reached the folder before it is never read -- `None` while it never did.
​required
​required
​required

ReaderCeilings

What the reader reads at most, every value filled in: the platform's own (`PLATFORM_READER_CEILINGS`) or a profile's lower one, as `ReaderLimits.ceilings` resolves them. What the text channel holds an attachment to.
document_bytes
​integer · required
document_chars
​integer · required
image_bytes
​integer · required
image_side_px
​integer · required
images_per_burst
​integer · required
questions_per_turn
​integer · required
scan_pages
​integer · required
sheet_rows
​integer · required

ReaderLimits

The reader's ceilings as a profile writes them (ai-profiles.md rule 77): each one the platform's, or lower -- `None` is "the platform's", which is what every profile starts with. The smallest image runs the other way: a profile may ask for a larger one, never a smaller. Unknown fields are refused, so a ceiling misspelled is a 422 rather than the platform's silently.
​
​
​
​
​
​
​
​

ReadingRefusal

string · enum
Enum values:
retention_zero

Why reading cannot be turned on (analyst.md rule 12). One today: an organization whose window is zero keeps nothing of a conversation to quote.

ReadingRefusalOut

The body of a refused switch: the sentence, and which refusal it is.
detail
​string · required
refusal
​ReadingRefusal · enum · required

Why reading cannot be turned on (analyst.md rule 12). One today: an organization whose window is zero keeps nothing of a conversation to quote.

Enum values:
retention_zero

ReadingSwitchIn

on
​boolean · required

ReadingSwitchOut

An agent's reading switch, where it is moved (rules 10-15).
agent_id
​string · uuid · required
​required
​AnalystCostOut · required

What the Analyst's work for this agent cost since since (rule 15).

amount is None for a person who may not see consumption: they are told the Analyst is charged by what its model reads and writes, and no amount -- not even zero, which would be one. There is deliberately no price per conversation here: none is fixed.

on
​boolean · required
reading
​boolean · required

RecallIn

What to recall: one memory layer, of one agent, about one question. `scope` chooses the layer and decides which key you must also send: `agent_global` takes neither key, `per_contact` requires `contact_key` (the person the memories are about), `per_session` requires `session_key`. Sending a key the scope does not take, or omitting one it needs, is refused. `query` is what the layer is searched with — say what you want to know, in words. Leave it empty to get the most recently updated memories instead.
agent_id
​string · uuid · required
scope
​Scope · enum · required

Which layer a memory lives in. The retrieval side pulls the relevant layer(s) into context; the write side must respect the PII boundary.

Enum values:
agent_global
per_contact
per_session
​
limit
​integer · min: 1 · max: 50
Default: 10
query
​string
Default:
​

RecipeAccount

An installed recipe's own account of itself: switched on or off, and why; whether its install or update completed, and what failed (rule 37).
enabled
​boolean · required
​required
key
​string · required
state
​InstallState · enum · required

Where an installed recipe stands (rules 8, 16, 18).

Enum values:
installed
incomplete
needs_value
​required

RecipeMark

string · enum
Enum values:
brain
calendar
agent
database
mail
package

The glyph a recipe is drawn with beside its name -- a closed set, so a new recipe picks one in its YAML and no screen needs a change to draw it (#1849). Each names what it depicts, never an icon library's name.

RecipeOut

A catalogue entry: what the recipe does, what it creates, what it asks for and which features it needs (rules 4-5).
adapt
​string · required
​ResourceSummary[] · required
description
​string · required
intent
​string · required
key
​string · required
name
​string · required
needs
​FeatureFlag[] · required
Enum values:
voice_agents
text_agents
ai_profiles
outbound_calls
rag
memory
tools
custom_tools
​Parameter[] · required
setup
​string · required
version
​integer · required
headline
​string
Default:
mark
​RecipeMark · enum

The glyph a recipe is drawn with beside its name -- a closed set, so a new recipe picks one in its YAML and no screen needs a change to draw it (#1849). Each names what it depicts, never an icon library's name.

Enum values:
brain
calendar
agent
database
mail
package
Default: package
​
​object
​

RecordCount

How many records a question finds. Its own shape rather than a bare integer because it is what a client shows *before* acting, and a number with no name beside it is the sort of thing that ends up meaning "loaded".
count
​integer · required

RecordFilter

One question about one declared field. Three value shapes, and which one applies is decided by the op rather than by the caller: `IS_EMPTY`/`IS_NOT_EMPTY` take none, `IN`/`NOT_IN` take `values`, everything else takes `value`. Checked here so the same mistake is refused the same way whether it arrives over HTTP or from another call in this process — and refused rather than ignored, because a filter whose value went unread is a narrower list than the client asked for, silently. Values are **text**, always, including for a number and a date: they are parsed against the field's declared type by `service`, using the very function that parsed the stored cell (`_parse`), so a filter and a write can never disagree about what `08001` or `3/2/26` means.
field
​string · minLength: 1 · maxLength: 64 · required
op
​FilterOp · enum · required

Every question that may be asked of a column. A closed set: a word that is not here cannot be asked anywhere, and a word that is here is asked with the same meaning wherever the column's owner admits it.

IS_EMPTY / IS_NOT_EMPTY are the pair with no value, and they are the reason this set is not merely the comparison operators. A state that exists as an absence — a cell never filled in, one emptied by the retention window, a column the last file said nothing about — is unaskable as an equality, and a client who cannot ask "which of these has no phone number" cannot find the work they have to do. Legacy asked the same question as isnull on its own engine (src/lib/dashboard/widgetConfig.ts), and for the same reason.

CONTAINS is LIKE renamed, not a new word. It was spelled like while nothing accepted it; contains is what it means to the person choosing it, and the SQL it becomes is the owner's business rather than the name's.

Enum values:
eq
ne
contains
gt
gte
lt
lte
in
​
values
​string[] · maxItems: 96

RecordIn

The values to write, keyed by field `key`. A key the collection does not declare is rejected rather than stored: shapeless data pushes the guessing onto every consumer, permanently.
​object
without_defaults
​string[] · maxItems: 256

RecordKind

string · enum
Enum values:
person_agreement
organization_agreement
objection
objection_withdrawn
own_agreement_marked
own_agreement_unmarked

What one entry of the proof records.

RecordOut

collection_id
​string · uuid · required
created_at
​string · date-time · required
​object · required
id
​string · uuid · required
​object · required
updated_at
​string · date-time · required
​required

RejectedRow

number
​string · required
reason
​string · required

Rejection

One cell or row the import would not take, in the words a person can act on: which row of their file, which column, what was in it, and why.
​required
reason
​string · required
row
​integer · required
value
​string · required

RelativeWindow

**The rule, not the two dates it meant when it was saved** — `analytics.md`'s "the period is remembered as what was asked for". A chart saved with `last=30, unit=day` covers the thirty days before whenever somebody opens it, so a board arranged a month ago is current without anyone editing it. Generic rather than a closed set of preset names on purpose. "Last 7 days", "last 90 days" and "last 6 weeks" are the *client's* vocabulary over one rule; naming each of them here would make every new preset a schema change, a migration and a client release for a number this shape already carries. `MONTH` and `YEAR` count calendar months and years, not thirty-day blocks — "the last six months" from the 31st lands on the last day of the month six back, never on a day that month does not have.
last
​integer · min: 1 · max: 1000 · required
unit
​WindowUnit · enum · required

What a relative window counts back in. The same five widths Bucket offers, and deliberately the same words: a chart bucketed by week over the last six weeks should not need two vocabularies to say "week".

Enum values:
hour
day
week
month
year

RemovedFromCampaign

How many of a campaign's contacts a question took back out. Contacts, not records: a record the campaign never held is not an error and is not counted — the client asked for a state, not for a transaction.
removed
​integer · required

ReplyMode

string · enum
Enum values:
send
prepare

How a connection replies (rule 92), on a road that declares it -- the three email roads (email-threads.md rule 21): send delivers the reply of "hand to the agent" (rule 14), prepare keeps it on the thread as its prepared reply, for a person to send (handoff.md rules 32-41). A road that declares none sends, and its channel shows no mode.

ReplyOut

What a person's reply answers with: what any send does (`channels.SentOut`), and whether this answer took the thread (handoff.md rule 7). `took_thread` is true when nobody held the thread and this reply made its sender the holder: from then on the agent stays silent until they release it. False when the sender already held it. It is the server's word for what the inbox tells the sender with that answer, so a caller through the API is told the same thing.
interaction_id
​string · uuid · required
took_thread
​boolean · required
held
​boolean
Default: false
kept
​boolean
Default: false
​

ResourceSummary

One thing a recipe will create, as the catalogue says so before anything is installed (rule 5).
key
​string · required
kind
​string · required
name
​string · required
says
​string · required

RetentionIn

Set the window, over the API: a number of days from `0` to `MAX_RETENTION_DAYS`, required. `null` is not a window (`data-retention.md` rules 1-2, #769); a client wanting the maximum writes it.
retention_days
​integer · min: 0 · max: 3650 · required

RetentionOut

One organization's window, as the API reports it.
retention_days
​integer · min: 0 · max: 3650 · required
​required

Role

string · enum
Enum values:
owner
admin
member

A person's role inside one organization. Per-membership, not per-person.

RoleFlagsOut

grantable_flags
​FeatureFlag[] · required
Enum values:
voice_agents
text_agents
ai_profiles
outbound_calls
rag
memory
tools
custom_tools
​object · required

RoleFlagsUpdate

flags
​FeatureFlag[] · required
Enum values:
voice_agents
text_agents
ai_profiles
outbound_calls
rag
memory
tools
custom_tools
role
​Role · enum · required

A person's role inside one organization. Per-membership, not per-person.

Enum values:
owner
admin
member

RoleUpdate

role
​Role · enum · required

A person's role inside one organization. Per-membership, not per-person.

Enum values:
owner
admin
member

RosterAddressIn

kind
​RosterAddressKind · enum · required

A kind of address a road knows a sender by (rule 70). The three WhatsApp roads share one. The app's address is the membership itself and is never written by hand (rule 72), so it is not a kind anyone writes.

Enum values:
phone
value
​string · minLength: 1 · maxLength: 64 · required

RosterAddressKind

string · enum
Enum values:
phone

A kind of address a road knows a sender by (rule 70). The three WhatsApp roads share one. The app's address is the membership itself and is never written by hand (rule 72), so it is not a kind anyone writes.

RosterAddressOut

id
​string · uuid · required
kind
​RosterAddressKind · enum · required

A kind of address a road knows a sender by (rule 70). The three WhatsApp roads share one. The app's address is the membership itself and is never written by hand (rule 72), so it is not a kind anyone writes.

Enum values:
phone
value
​string · required

RosterEntryIn

A person the organization vouches for who holds no account (rule 74): a name and at least one address.
​RosterAddressIn[] · minItems: 1 · maxItems: 20 · required
display_name
​string · minLength: 1 · maxLength: 200 · required

RosterEntryOut

One person on the roster. `membership_id` is set for an entry linked to a membership -- its address on the app -- and such an entry comes and goes with the membership (rule 72). `resolves` is false while its person is deactivated: its addresses resolve to no one.
​RosterAddressOut[] · required
display_name
​string · required
id
​string · uuid · required
​required
resolves
​boolean · required

RosterEntryUpdate

display_name
​string · minLength: 1 · maxLength: 200 · required

Route

One thing that routes conversations to an agent, in a shape this slice can name in a refusal without importing whoever owns it.
id
​string · uuid · required
kind
​RouteKind · enum · required

What kinds of thing send conversations to an agent. A trunk and a campaign "are the same case" per the spec, so they are one enum: the refusal reads the same either way. A channel -- WhatsApp, web chat -- is the third: it hands every message it receives to its agent (#774).

Enum values:
trunk
campaign
channel
label
​string · required

RouteKind

string · enum
Enum values:
trunk
campaign
channel

What kinds of thing send conversations to an agent. A trunk and a campaign "are the same case" per the spec, so they are one enum: the refusal reads the same either way. A channel -- WhatsApp, web chat -- is the third: it hands every message it receives to its agent (#774).

RunOutcome

string · enum
Enum values:
pending
running
succeeded
failed
did_not_terminate

Where one run got to.

RunSettings

The settings a conversation actually ran with, copied in as **values**. An AI profile is not frozen into an agent's published version, so the version alone cannot say how a conversation sounded. The profile id is kept for provenance; the provider, model and voice are kept because they are the answer. `used_standby` records that a standby served the conversation instead of some component's first choice — the same fields still describe what was actually served — and `standby_component` names which one switched.
​
​ArrangementAttempt[]
Default: []
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
used_standby
​boolean
Default: false

STTProvider

string · enum
Enum values:
deepgram
deepgram_flux
openai
assemblyai
google

STTSettingsIn

model
​string · minLength: 1 · required
name
​string · minLength: 1 · required
provider
​STTProvider · enum · required
Enum values:
deepgram
deepgram_flux
openai
assemblyai
google
​
eot_threshold
​number · min: 0 · max: 1
Default: 0.7
eot_timeout_ms
​integer · min: 100 · max: 60000
Default: 3000
filler_words
​boolean
Default: true
keyterms
​string[] · maxItems: 100
Default: []
​
numerals
​boolean
Default: false
profanity_filter
​boolean
Default: false
punctuate
​boolean
Default: true
redact
​string[]
Enum values:
pci
pii
numbers
Default: []
​

STTSettingsOut

eot_threshold
​number · required
eot_timeout_ms
​integer · required
filler_words
​boolean · required
id
​string · uuid · required
keyterms
​string[] · required
language
​string · required
model
​string · required
name
​string · required
numerals
​boolean · required
​required
profanity_filter
​boolean · required
provider
​STTProvider · enum · required
Enum values:
deepgram
deepgram_flux
openai
assemblyai
google
punctuate
​boolean · required
redact
​string[] · required
​required
​
​
​

STTSettingsUpdate

​
​
​
​
​
​
​
​
​
​
​
​
​
​

Scale

string · enum
Enum values:
seconds_to_minutes

A closed set of named transforms applied to a measure's value after it comes back — never a caller-supplied divisor (v1's own stated rule, carried forward: every duration this platform stores is seconds, every question about one is asked in minutes).

Scope

string · enum
Enum values:
agent_global
per_contact
per_session

Which layer a memory lives in. The retrieval side pulls the relevant layer(s) into context; the write side must respect the PII boundary.

SearchCostOut

What searching the recent past for a new question will cost, said before it starts (rules 15, 37): how many readings it will read, and an estimate of the cost. `amount` is `None` for a person who may not see consumption, who is told that the Analyst is charged by what its model reads and writes, and no amount.
​required
days
​integer · required
readings
​integer · required

SearchHit

One chunk that matched, and enough to quote it and say where from.
content
​string · required
document_id
​string · uuid · required
filename
​string · required
​required
seq
​integer · required

SearchIn

A human's search, over one agent's documents. `agent_id` is here because **this is the builder's and the panel's read**, and a person asking has to say which agent's library they mean. The conversation tool takes no such field: a model may not choose the scope it is searched under, which is the whole of ADR 0014.
agent_id
​string · uuid · required
query
​string · required
limit
​integer · min: 1 · max: 20
Default: 5

SelectionOfIds

The records somebody picked one at a time. Known, named, counted exactly. Still resolved against the store rather than trusted: the ids come from the client, and a record of another collection — even one they own — must not be actionable through this one.
ids
​string[] · minItems: 1 · maxItems: 10000 · required
mode
​string · const
Const value: ids
Default: ids

SelectionOfMatching

Everything the question finds, minus what was unpicked afterwards. **This travels as the question and never as a list of rows.** The screen holds the pages somebody scrolled past; a collection of five hundred with two hundred drawn would report "200 selected", which is true and useless, and then act on two hundred while the client believed they had chosen everything (`custom-data.md`, "Selecting is selecting the question, not the page"). `exclude_ids` is how a select-all box is really used.
exclude_ids
​string[] · maxItems: 10000
​RecordFilter[] · maxItems: 32
mode
​string · const
Const value: matching
Default: matching

SendOnChannelIn

`OutboundIn` plus who it goes to, for the route that opens a conversation with somebody who has not written.
address
​string · minLength: 1 · maxLength: 200 · required
​
​ChoiceIn[] · maxItems: 10
​
​

SentFileOut

A file sent on a text conversation, while retention keeps it (inbox.md, Conversation): a link signed for `SENT_FILE_URL_TTL`, and what the sender said of the file. `file_id` is the `ref_id` on the person's line it was sent with, which is how an inbox line finds its file -- a thread's `ContactFileOut.message_id` in this channel's words.
file_id
​string · uuid · required
mime
​string · required
size_bytes
​integer · required
url
​string · required
​
​

SentOut

What a send answers with: the provider's own id for the message, when the road gives one, and the thread it landed on. `kept` when a flow's send was kept as the thread's prepared reply instead, and `held` when it was the connection's reply mode that kept it (mail-handling.md rule 4).
interaction_id
​string · uuid · required
held
​boolean
Default: false
kept
​boolean
Default: false
​

Slot

string · enum
Enum values:
pre
in
post

The lifecycle slot a tool is placed in. Which tool is attached to which slot belongs to agents; whether a tool can be in a slot belongs here, because it is a fact about the tool (service.available_in_slot).

Source-Input

string · enum
Enum values:
builtin
custom
mcp

Where a tool comes from. Provenance matters to us (config/auth/billing), never to the LLM — which sees every source identically.

CUSTOM is retired and unreachable, and it is still here. It used to mean "code written for one organization", and runtime.execution._route refused it by name because nothing could run one. Code written for one organization now runs the way every other first-party tool does — BUILTIN, with config naming which built-in and the composition root binding it (ADR 0014, widened by ADR 0017) — so there is one dispatch path and this member names none of them. It is kept rather than removed because tool_source is a native Postgres enum: dropping a value means recreating the type, rewriting every dependent column and taking a lock on tools to delete a name no row has any reason to carry. backend/features/custom_tools/DECISIONS.md D10 records the trade. A row that somehow carries it is dispatched like any other non-MCP row and answers execute_http's "this tool has no destination configured", which is one answer rather than two.

SourceAddress

One address a trunk declares its peer calls from, and whether the platform has opened the way to it yet (trunks.md rules 49-50). `open` is read from the deployment's configuration -- the addresses its SIP port admits -- and a newly declared address waits until the deployment says it is open.
address
​string · required
open
​boolean · required

SourceFilter

One question about one field of the collection a campaign draws from. **The same four fields `custom_data.RecordFilter` has, and deliberately not that class.** This slice does not import `custom_data` and must not: a campaign reaches its collection through an opaque id and the `ContactSource` protocol, which is the whole of ADR 0002's inversion here. What the two shapes share is the *vocabulary* — `shared.FilterOp` (ADR 0031) — so the words cannot drift even though the shapes are two. The composition root, the one place permitted to see both, converts one into the other. Values are text and are parsed by whoever owns the field, exactly as they are on the collection's own screen.
field
​string · minLength: 1 · maxLength: 64 · required
op
​FilterOp · enum · required

Every question that may be asked of a column. A closed set: a word that is not here cannot be asked anywhere, and a word that is here is asked with the same meaning wherever the column's owner admits it.

IS_EMPTY / IS_NOT_EMPTY are the pair with no value, and they are the reason this set is not merely the comparison operators. A state that exists as an absence — a cell never filled in, one emptied by the retention window, a column the last file said nothing about — is unaskable as an equality, and a client who cannot ask "which of these has no phone number" cannot find the work they have to do. Legacy asked the same question as isnull on its own engine (src/lib/dashboard/widgetConfig.ts), and for the same reason.

CONTAINS is LIKE renamed, not a new word. It was spelled like while nothing accepted it; contains is what it means to the person choosing it, and the SQL it becomes is the owner's business rather than the name's.

Enum values:
eq
ne
contains
gt
gte
lt
lte
in
​
values
​string[] · maxItems: 100

SourceKind

string · enum
Enum values:
inbound_number
api_key
connected_app
browser
widget
campaign
impersonation
channel

Where the conversation came from.

BROWSER is the platform's own interface — a person trying their agent by voice or by text from the dashboard, or the agent builder doing it for them. WIDGET is the embeddable widget on the client's own site. They are two origins and not one because the one thing source attribution exists to answer is who was on the other end, and "the client trying their own agent" and "a visitor on the client's site" are different answers (interactions.md).

IMPERSONATION is not a sixth entry surface — it is staff reaching a customer's own dashboard, which arrives on the same routes as BROWSER and is admitted, recorded and counted identically. It is recorded apart for the same reason: "the client testing their own agent" and "we were looking at it for them" are different answers too. impersonation.md records the session and not what was done inside it (#527); a conversation held under one is the single exception that sentence names, and this is where it is written down.

CHANNEL is a messaging channel — WhatsApp and the rest — where the origin is "which connection, and the contact's address on it", as an inbound call's origin is the number that rang (channels.md, "The record"). It is a source and not a fourth Channel: a thread is recorded as Channel.TEXT in the log beside every other written conversation, and what makes it a thread is where it came from.

CONNECTED_APP is an outside AI app a member connected by signing in (connected-apps.md), held to every rule a key is and still its own origin (interactions.md rule 2): "which integration placed this" has two answers once an organization has both, and a cell that read "API key" for an app would name the wrong one. The record keeps the app connection's identifier beside it as it keeps a key's (rule 3), never the token.

A native PostgreSQL enum (interaction_source_kind): a member added here ships an ALTER TYPE ... ADD VALUE migration, b7e2c4d9a1f3's precedent.

Speaker

string · enum
Enum values:
agent
contact
operator
mailbox

Who said something. Three, and the third only happens on a channel -- and, on an email thread, a fourth: a reply sent from the mailbox itself (MAILBOX).

OPERATOR is "a person answering through the platform, recorded as a third speaker" (interactions.md) — the hand-over to a human that channels.md describes, "reply on this thread, the same built-in, from outside a flow". It is a speaker and not a flag on AGENT because the transcript has to say who the words belong to: an agent shown a person's reply as its own would read its history as things it had said and had not.

What a branch of the client's flow sent is not a fourth member. A template a branch fired is "recorded on the thread as the flow's act, as a tool call is, not as something an agent said" — and a tool call is a ToolCall, which this vocabulary already has. A speaker for it would claim somebody spoke.

A native PostgreSQL enum (interaction_speaker), so a member added here ships an ALTER TYPE ... ADD VALUE migration — b7e2c4d9a1f3's precedent.

StandbyComponent

string · enum
Enum values:
llm
stt
tts
vad

Which component of a profile switched to its standby.

ai-profiles.md: a standby is named per component, "because an outage belongs to one provider and not to the bundle" — so recording that one switched is only half the fact; recording which one is the other half.

StateReason

string · enum
Enum values:
last_run_failed
connection_broken
switched_off
paused_after_failing
campaign_paused
recipe_switched_off
recipe_needs_value
agent_never_published

Why a part is failing, paused or unknown. The dot carries none (rule 30); the reason is here for a reader of the API, who has no screen to open.

Status

string · enum
Enum values:
draft
published
archived

A version's place in the draft → published → archived lifecycle.

SummaryOut

The agent's summary (rules 20-23), beside how many findings are open.
agent_id
​string · uuid · required
​required
​required
open_findings
​integer · required
ready
​boolean · required
​required
​required
​required
​required

SummaryScope

string · enum
Enum values:
day
week

What a summary speaks of: the organization's day, or on a quiet day its week so far (rules 20, 21).

TTSProvider

string · enum
Enum values:
openai
elevenlabs
cartesia
google

TTSSettingsIn

model
​string · minLength: 1 · required
name
​string · minLength: 1 · required
provider
​TTSProvider · enum · required
Enum values:
openai
elevenlabs
cartesia
google
voice
​string · minLength: 1 · required
emotion
​string · enum
Enum values:
neutral
anger
curiosity
positivity
sadness
surprise
Default: neutral
emotion_intensity
​string · enum
Enum values:
lowest
low
normal
high
Default: normal
​
​
speed
​number · min: 0.25 · max: 4
Default: 1
​
volume
​number · min: 0.25 · max: 4
Default: 1

TTSSettingsOut

emotion
​string · required
emotion_intensity
​string · required
id
​string · uuid · required
language
​string · required
model
​string · required
name
​string · required
​required
​required
provider
​TTSProvider · enum · required
Enum values:
openai
elevenlabs
cartesia
google
speed
​number · required
​required
voice
​string · required
volume
​number · required
​
​

TTSSettingsUpdate

​
​
​
​
​
​
​
​
​
​
​

TeachIn

The yes to teaching the agent, with the answer as the person left it.
​

TeachOut

interaction_id
​string · uuid · required

TemplateIn

An approved template, as a flow step or an operator asks for one. The provider's shape is `messaging.TemplateMessage`'s and this is the HTTP / tool-argument spelling of it: a name, the language it was approved in, the body's positional variables and the quick-reply buttons' payloads. Positional because Meta addresses both by index — the template declares the order, and a caller that guesses it wrong gets the provider's own refusal rather than ours.
language
​string · minLength: 2 · maxLength: 16 · required
name
​string · minLength: 1 · maxLength: 512 · required
button_payloads
​string[]
variables
​string[]

TestLink

Where a person tests an agent by voice, for whoever cannot open the call. What `test_link` hands an API key or a connected app instead of a `BrowserCall` (connected-apps.md rule 16): `url` is this deployment's own test page for the agent, which opens nothing — the person signs in there and clicks Call, and that click is the dashboard's ordinary browser call. Nothing here joins a room, so nothing here is a credential. `how` is the sentence a client can show the person beside the link.
how
​string · required
url
​string · required

TextSectionIn

The text section: the model every written conversation is held by -- required, because any agent can be reached by text -- and, optionally, the recognizer that reads a voice note and the language model that reads an image or a scanned page. With no transcriber a voice note is announced, not transcribed (ai-profiles.md, 2026-09-21); with no reader an image or a document is announced, not read (rule 75). On a rewrite of a profile's section, `reader_id` and `reader_limits` left out mean *unchanged* (rule 75, #1385): a writer that predates the reader keeps it. `reader_id: null` removes the reader, and `reader_limits` sent is written whole -- a ceiling left out of it is the platform's. On a new profile, left out is no reader and the platform's ceilings.
llm_settings_id
​string · uuid · required
​
​ReaderLimits

The reader's ceilings as a profile writes them (ai-profiles.md rule 77): each one the platform's, or lower -- None is "the platform's", which is what every profile starts with. The smallest image runs the other way: a profile may ask for a larger one, never a smaller. Unknown fields are refused, so a ceiling misspelled is a 422 rather than the platform's silently.

​

TextSectionOut

​LLMSettingsOut · required
​
​ReaderLimits

The reader's ceilings as a profile writes them (ai-profiles.md rule 77): each one the platform's, or lower -- None is "the platform's", which is what every profile starts with. The smallest image runs the other way: a profile may ask for a larger one, never a smaller. Unknown fields are refused, so a ceiling misspelled is a 422 rather than the platform's silently.

Default: {}
​

TextStreamEnd

The turn is over, and every event of it the stream offers was framed. SSE has no other way to say "that was the whole turn", and without it a client cannot tell a finished answer from a connection still thinking. It is the **only** frame that means the stream was complete: a stream that stopped short ends in `TextStreamError` instead, so "did I see all of it" is a question with one answer rather than an inference from a closed socket. `seq` is the position of the turn's last recorded event -- framed or, like `provider_usage`, only recorded -- or `None` for a turn that produced none. It is what a client resumes from — the trace's own `seq`, so reading `interactions` from there is reading the same numbering.
interaction_id
​string · uuid · required
​

TextStreamError

The stream stopped short. **The record did not.** Two things end a stream early and neither of them ends the turn: the turn raised out of the run-loop (which should be unreachable — a provider that raises is already a failed turn and the agent's own error phrase), or the client fell too far behind and its reader was dropped. In both cases the turn ran to completion on its own task and the interaction holds all of it. So this frame is an instruction rather than an apology: `seq` is the last event the client actually saw, and the rest of the turn is in the trace from there. `detail` says which of the two happened, in a fixed sentence — an unexpected failure's own text is logged, not broadcast.
detail
​string · required
interaction_id
​string · uuid · required
​

TextStreamFrame

anyOf
At least one variant must match. Multiple variants may match simultaneously.

Decision Table

VariantMatching Criteria
type = object · requires: id, seq, type +1 more
type = object · requires: interaction_id
type = object · requires: interaction_id, detail
Properties for InteractionEventOut:
One row of the trace. Flat on purpose: a trace is read as a table, and the fields a type does not use are absent rather than shaped differently.
InteractionEventOut
id
​string · uuid · required
occurred_at
​string · date-time · required
seq
​integer · required
type
​EventType · enum · required

Everything a conversation can record about the path it walked.

Enum values:
node_entered
navigation_blocked
tool_call
utterance
turn_failed
tool_refused
confirmation_requested
confirmation_resolved
​
​
​
​
​
​
​
​
​
colleague
​boolean
Default: false
​
​
​
​
​
flow_act
​boolean
Default: false
​
​
held
​boolean
Default: false
​
​
​
​
overlapped
​boolean
Default: false
​
prepared_changed
​boolean
Default: false
​
returned_to_flow
​boolean
Default: false
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
unvoiced
​boolean
Default: false
​
​
​
​
​

ThreadOut

One open thread of a channel. Everything that was *said* on it is read through `interactions` — this is the channel's own half: whose address, which record, and when they last wrote.
address
​string · required
channel_id
​string · uuid · required
created_at
​string · date-time · required
id
​string · uuid · required
interaction_id
​string · uuid · required
​required
​
may_speak_freely
​boolean
Default: true

ThreadSubjectOut

An email thread's latest subject (inbox.md rule 35): the subject of its newest message that carries one, received or sent, and when that message was. What the inbox row shows beside its latest line, because one counterpart may have several email threads open at once.
at
​string · date-time · required
interaction_id
​string · uuid · required
subject
​string · required

ToolIn

name
​string · required
source
​Source-Input · enum · required

Where a tool comes from. Provenance matters to us (config/auth/billing), never to the LLM — which sees every source identically.

CUSTOM is retired and unreachable, and it is still here. It used to mean "code written for one organization", and runtime.execution._route refused it by name because nothing could run one. Code written for one organization now runs the way every other first-party tool does — BUILTIN, with config naming which built-in and the composition root binding it (ADR 0014, widened by ADR 0017) — so there is one dispatch path and this member names none of them. It is kept rather than removed because tool_source is a native Postgres enum: dropping a value means recreating the type, rewriting every dependent column and taking a lock on tools to delete a name no row has any reason to carry. backend/features/custom_tools/DECISIONS.md D10 records the trade. A row that somehow carries it is dispatched like any other non-MCP row and answers execute_http's "this tool has no destination configured", which is one answer rather than two.

Enum values:
builtin
custom
mcp
​object
​
​
conversation_only
​boolean
Default: false
deadline_seconds
​integer · min: 1 · max: 300
Default: 30
description
​string
Default:
example_requests
​string[]
​object
​object
requires_confirmation
​boolean
Default: false
tags
​string[]

ToolOutcome

string · enum
Enum values:
ok
failed
retried
abandoned

How one attempt at a tool call ended. One event is one attempt: RETRIED means this attempt failed and another was made, FAILED that it failed and was not, ABANDONED that it was given up without completing.

ToolPhase

string · enum
Enum values:
pre
in
post

tools.md's pre/in/post lifecycle. A PRE or POST failure happens where nobody in the conversation can hear it, which is exactly why it is recorded.

ToolTestIn

What a test supplies, and the only thing it supplies: parameters. The destination, the credentials and the fixed values are the instance's own — a test that could replace them would be testing something else.
​object

ToolTestOut

What one test of an instance did. Recorded against the instance, not as a conversation, because there was none. `parameters` is what the tool **actually ran with**, fixed values included — the client is shown what the model is not. It never contains `config`: those are the credentials, and they are not parameters.
attempts
​integer · required
created_at
​string · date-time · required
detail
​string · required
duration_ms
​integer · required
id
​string · uuid · required
outcome
​Outcome · enum · required

How one execution of a tool ended.

Enum values:
succeeded
failed
refused
​object · required
response_body
​string · required
​required
tool_id
​string · uuid · required

ToolUpdate

Partial edit — only the provided fields change (`model_dump(exclude_unset)`).
​
​
​
​
​
​
​
​
​
​
​
​
​
​

ToolView

A tool as the API returns it. A tool's `config` is write-only: it is set through `ToolIn`/`ToolUpdate` and never returned, because it holds the credentials the tool runs with.
​required
conversation_only
​boolean · required
deadline_seconds
​integer · required
description
​string · required
​object · required
id
​string · uuid · required
​object · required
name
​string · required
​required
requires_confirmation
​boolean · required
source
​backend__features__tools__schemas__Source · enum · required

Where a tool comes from. Provenance matters to us (config/auth/billing), never to the LLM — which sees every source identically.

CUSTOM is retired and unreachable, and it is still here. It used to mean "code written for one organization", and runtime.execution._route refused it by name because nothing could run one. Code written for one organization now runs the way every other first-party tool does — BUILTIN, with config naming which built-in and the composition root binding it (ADR 0014, widened by ADR 0017) — so there is one dispatch path and this member names none of them. It is kept rather than removed because tool_source is a native Postgres enum: dropping a value means recreating the type, rewriting every dependent column and taking a lock on tools to delete a name no row has any reason to carry. backend/features/custom_tools/DECISIONS.md D10 records the trade. A row that somehow carries it is dispatched like any other non-MCP row and answers execute_http's "this tool has no destination configured", which is one answer rather than two.

Enum values:
builtin
custom
mcp
​
​
example_requests
​string[]
holds_credential
​boolean
Default: false
​
namespace
​string
Default:
​
​
tags
​string[]
visibility
​Visibility · enum

Whether the model is shown a tool whenever it is in reach, or only once the conversation has found it (tools.md rules 87-89). A fact about the tool, the same wherever it is attached; never what an agent may do, which attachment decides.

Enum values:
pinned
discoverable
Default: pinned

TrackedQuestionIn

The owner's confirmation of a proposal: the kind and short name, changed or not (rule 36). The meaning is the proposal's and is not sent.
kind
​QuestionKind · enum · required

The kind of answer a question takes (rule 35), which is also what its chart is (rule 42): nobody configures one.

Enum values:
yes_no
closed_set
number
search_id
​string · uuid · required
short_name
​string · minLength: 1 · maxLength: 40 · required
options
​string[]

TrackedQuestionOut

agent_id
​string · uuid · required
answering_since
​string · date-time · required
created_at
​string · date-time · required
id
​string · uuid · required
kind
​QuestionKind · enum · required

The kind of answer a question takes (rule 35), which is also what its chart is (rule 42): nobody configures one.

Enum values:
yes_no
closed_set
number
meaning
​string · required
​required
options
​string[] · required
question
​string · required
short_name
​string · required
state
​QuestionState · enum · required
Enum values:
active
paused

TrackedQuestionUpdate

What may change once a question is confirmed: its short name, and whether it is paused (rules 39, 40). Meaning, kind and set are refused as unknown.
​
​

TransferOutcome

string · enum
Enum values:
handed_over
put_through
not_answered
busy
rejected
declined
caller_left
refused

How a call's transfer ended (interactions.md rule 57; voice-agent.md rules 103-107): the last attempt's while the conversation is open, then -- on a bridge -- the bridge's own last word, learned after the agent left.

HANDED_OVER is a hand-over the peer took; PUT_THROUGH a bridge whose colleague was put through to the caller and stayed until one of them left; NOT_ANSWERED, BUSY and REJECTED a second leg that never answered within the no-answer wait, found the line busy, or was refused with any other final word; DECLINED a colleague who declined or left during the consult; CALLER_LEFT a caller who hung up before being put through; REFUSED a transfer refused before anything was dialled (voice rule 86); AGENT_CEILING the agent's ceiling reached while the second leg rang or during the consult (voice rule 107); BRIDGE_CEILING a bridge the platform ended at the organization's bridge ceiling. Text on the row, like HungUpBy, so a value added here needs no migration. It names nobody, so it survives the purge.

TraversalEdgeOut

One transition's count: `from_origin` to `to_origin`, both origins (never a version's own node id, for the same reason `TraversalNodeOut` is keyed by origin).
count
​integer · required
from_origin
​string · uuid · required
to_origin
​string · uuid · required

TraversalNodeOut

One node's totals over the requested window — `dashboard.md`'s "how many conversations entered each node, where they flowed, and where they dropped", one row per node. Keyed by `origin_id` (`dynamic_context.NodeOut.origin_id`), never a version's own `Node.id`, so a publish that changes no node's content still reports one bar, not two: `origin_id` is what a node keeps across snapshots. `entries` and `exits` are independently counted, not derived from each other: `entries` is how many times a conversation moved into this node (`NODE_ENTERED`, this window); `exits` is the sum of every outgoing edge's count (`TraversalOut.edges`, this node as `from_origin`). `drops = entries - exits`, computed here and never stored — an interaction whose *last* node-traversal event in the window was this node contributes to its drop count, because it entered without a later exit for this run to have counted.
drops
​integer · required
entries
​integer · required
exits
​integer · required
label
​string · required
origin_id
​string · uuid · required
untitled
​boolean · required

TraversalOut

`GET /boards/traversal`'s whole answer — the dashboard tree graph. Totals over the requested window, no time dimension (`dashboard.md`'s "aggregate node-traversal visualization", not a time series). An empty window, or an agent with no traversal in it, answers with two empty lists, never a 404. `truncated` and `cap` are `ChartResult`'s, for the same reason (#462): entries and transitions are each bounded at `cap` groups, and `truncated` is true when either bound bit -- the graph is then the busiest part of the agent's, not all of it, and a reader has to be able to say so.
​TraversalEdgeOut[] · required
​TraversalNodeOut[] · required
cap
​integer
Default: 200
truncated
​boolean
Default: false

TriggerConfig

The small typed shape a trigger is configured by. Every field is optional here and required by `service` for the kind that needs it, so one column stores four vocabularies without four columns of nulls.
​
​
​
​
leave_out_tests
​boolean
Default: false

TriggerKind

string · enum
Enum values:
interaction_ended
schedule
record_written
webhook

What sets an automation off. Closed, and each member names exactly one place in this deployment where it is detected — see tasks.py and the webhook route.

Four, and four is all the spec asks for. automations.md used to list a fifth, "when a message or call arrives — including on channels we do not answer conversationally", and withdrew it on 2026-09-13 when channels.md was specified: "a channel nobody answers conversationally is simply a flow with no branch that hands to an agent", so an arriving message runs the channel's own flow and there is nothing left for a trigger to do. What an automation does with a channel is send on one — channels' send_on_channel built-in inside a scheduled flow, which is SCHEDULE. #395 closed.

TrunkIn

A new line -- on a new trunk, or on one of the organization's trunks already there (`peer_id`, trunks.md rule 1). **On a new trunk**, `kind` is required and the trunk's settings come from this body: the side its peer calls from (`allowed_ips`), the side we call it on (`sip_address`, and `transport` for a phone system only -- a carrier's is the carrier's, rule 8), and a credential. **On an existing trunk**, `kind` may be omitted and must match when given; any trunk setting the body carries edits the trunk, exactly as `TrunkUpdate` would. The credentials are per side (rule 5, #2214): `inbound_auth_*` is what the peer authenticates with on the calls it sends us, `outbound_auth_*` what we authenticate as on the calls we send it -- whichever way the line runs, so a phone system whose lines only receive can still be given the credential our calls back to it carry. `auth_username`/`auth_password` are the older spelling, the side this line's direction uses, kept one release for the screen that still sends them (`DECISIONS.md` 2026-10-28). **Passwords are write-only**: never on `TrunkOut`, sealed at rest (ADR 0050).
direction
​backend__features__trunks__schemas__Direction · enum · required

Which way calls flow over the trunk.

Enum values:
inbound
outbound
name
​string · required
number
​string · required
​
allowed_ips
​string[]
Default: []
​
​
​
​
line_type
​LineType · enum

What a line's number is (trunks.md rule 7): a phone number, normalised to E.164, or an extension of the client's phone system, held as text exactly as the system names it and never read as a number.

Enum values:
number
extension
Default: number
​
​
​
​
​
​
​
​deprecated
​deprecated

TrunkOut

One line, with its trunk's settings beside it -- the shape a "trunk" was read in before trunks had lines, and the one every caller of `get_trunk`/`list_trunks` still reads (`TrunkPeerOut` docstring). `id` is the line's; `peer` is the trunk it is on, and is what a reader who knows about lines reads the trunk from.
​required
allowed_ips
​string[] · required
carrier_failover_possible
​boolean · readOnly · required

The per-kind guarantee, stated rather than left for the client to infer from kind (rules 21-22): true only for a number the platform provides; a line on a trunk of the client's own gets our routing's forward only.

created_at
​string · date-time · required
direction
​backend__features__trunks__schemas__Direction · enum · required

Which way calls flow over the trunk.

Enum values:
inbound
outbound
fallback_condition
​FallbackCondition · enum · required

Whether the trunk is currently forwarding calls to its fallback number.

Binary on purpose: "A trunk can sit in fallback indefinitely... entering fallback raises a notification... and again when the condition clears" — the spec only ever names two states, not a severity scale.

Enum values:
normal
fallback
​required
​required
​required
id
​string · uuid · required
kind
​Kind · enum · required

Which peer a trunk connects to (trunks.md rule 4).

Enum values:
byo_sip
pbx
platform
name
​string · required
number
​string · required
​required
​required
​required
​required
updated_at
​string · date-time · required
answer_bound_seconds
​integer · readOnly
Default: 10
​
carrier
​string · readOnly

Which carrier this line's calls cross: for a placing line, the carrier its trunk's SIP host is; for a receiving line, the host or else the addresses it accepts calls from; for a platform trunk, the deployment's own connection. telnyx, netelip, or unknown. Read-only; there is no setting for it.

Default: unknown
fallback_answered_by_platform
​boolean · readOnly
Default: false
line_type
​LineType · enum

What a line's number is (trunks.md rule 7): a phone number, normalised to E.164, or an extension of the client's phone system, held as text exactly as the system names it and never read as a number.

Enum values:
number
extension
Default: number
​
reachability
​Reachability · enum

Whether the phone network can reach this line's trunk: incomplete (it lacks what its peer authenticates by), unreachable (the last run found no carrier-side object), reachable (a run within the last five minutes found it), or unknown (never reconciled, or not recently -- which is not health).

Enum values:
reachable
unreachable
unknown
incomplete
Default: unknown
​
​
​deprecated

Deprecated: always null, kept for one release.

fallback_wait_seconds
​integer · deprecated

Deprecated: answer_bound_seconds, kept for one release.

reachable
​boolean · deprecated

Deprecated: reachability == "reachable", kept for one release. False for an unknown trunk as well as an unreachable one; read reachability.

Default: false

TrunkPeerOut

A trunk: the organization's connection to one peer, above its lines (trunks.md rule 1). Every derived field is set by the validators below from the row's own fields, so one built from a row and one re-validated from its own dump read the same; `source_addresses` and `our_side` are the deployment's and are handed in by the service.
id
​string · uuid · required
kind
​Kind · enum · required

Which peer a trunk connects to (trunks.md rule 4).

Enum values:
byo_sip
pbx
platform
name
​string · required
allowed_ips
​string[]
Default: []
​
​
​
carries_directory
​boolean · readOnly
Default: false
hands_over_inbound
​boolean · readOnly
Default: false
hands_over_outbound
​boolean · readOnly
Default: false
​
​
places
​boolean
Default: false
reachability
​Reachability · enum

Whether the phone network can reach this trunk (rule 14): incomplete (it lacks what its peer authenticates by -- a receiving trunk with neither the addresses its peer calls from nor, on a carrier we already take calls from, a username), unreachable, reachable (a check in the last five minutes), or unknown.

Enum values:
reachable
unreachable
unknown
incomplete
Default: unknown
receives
​boolean
Default: false
recognised_as
​string · readOnly
Default: unknown
​
​
​SourceAddress[]
Default: []
​
​

TrunkUpdate

Partial edit of a line and its trunk -- only provided fields change (`model_dump(exclude_unset=True)`). The line's own fields are `name`, `number`, `line_type`, `direction`, `agent_id` and `fallback_number`; `peer_name`, `kind`, `sip_address`, `transport`, `allowed_ips`, the credentials and `provider_trunk_id` are the trunk's, so an edit to them is seen by every line on it. The credentials are per side, as on `TrunkIn`; the older `auth_*` pair and `provider_trunk_id` are the side this line's direction uses. `fallback_number` may be set to `null` deliberately: that clears it, and a cleared fallback is a legal, visible state (rule 24), not an omission. Whether the result still makes sense against `direction` — old or new — is a service-layer check: the two fields can arrive in different requests, so no single schema ever sees both values at once. The same holds for `number` against `line_type`, which `service.update_trunk` reads against the line it would produce.
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​deprecated
​deprecated

TryIn

Sample input for a draft, run without registering or publishing anything.
​object

TryOut

What the draft did with that input. `ok` is the whole verdict: a transform that raised, ran out of budget or answered something that is not JSON is `ok=False` with `detail` saying which, and never an error status — asking "what does this code do" is a question that was answered whichever way it came out.
duration_ms
​integer · required
ok
​boolean · required
detail
​string
Default:
result
​

TurnStages

Where one reply's time went, in milliseconds, as the process that ran the turn measured it (interactions.md rule 14, #524). `None` where the stage did not happen or its anchor was never taken -- a greeting answers nothing, so it has no end-of-turn wait -- and never a zero that would pull a percentile down. `think` is the whole of what the model, the tools, the record's writes and the loop spent between the caller's words and the reply being asked for; `queue`, `model`, `tools` and `record` are parts of it, and what is left of `think` after them is the hooks, the navigation and the scheduling. Measured, never reconstructed from the trace's timestamps. A row written before a stage was stored reads `None` for it (#1813: `queue`, `held`, `dead_air`).
​
​
​
​
​
​
​
​
​
​

UninstalledOut

What uninstalling deleted, records included (rule 19). Nothing is handed over.
​PartOut[] · required
key
​string · required
records_deleted
​integer · required

UnreadCountOut

How many notifications in the person's inbox they have not marked read, across the whole inbox. Per-person, like `InboxItemOut.read_at`.
count
​integer · required

UsageSummaryOut

One metric's consumption over the queried window: `SUM(value)` measured, and `SUM(amount)` — what those values were priced at when they were recorded, which is the money that actually moved. Never a rate times a total: the rate is per record. One row per `(metric, dimension)`, because that is the pair a value is recorded and priced against: the input tokens of two models over one window are two rows, and the metric's own total is the sum over them. `dimension` is `None` for a metric that measures no model.
amount
​string · pattern: ^(?!^[-+.]*$)[+-]?0*… · required
​required
metric_code
​string · required
total
​string · pattern: ^(?!^[-+.]*$)[+-]?0*… · required
​required

Utterance

string · enum
Enum values:
greeting
farewell
out_of_time
holding
tool_gave_up
internal_error
no_response
first_line

The closed set of situations the platform speaks for the agent in.

"A situation the platform can produce is a situation the platform names." The client rewrites every phrase and can add no entry, so this enum — not a free-form dictionary — is what the set is. Adding a member is a change to this platform (a default phrase per language, and a migration), which is the cost that keeps the set a set.

VADCapabilities

The detector has no model and no class, so its fields are its own.
​object · required
​

VADProvider

string · enum
Enum values:
silero
livekit

VADSettingsIn

name
​string · minLength: 1 · required
allow_interruptions
​boolean
Default: true
deactivation_gap
​number · min: 0 · max: 0.5
Default: 0.15
min_silence_ms
​integer · min: 200 · max: 5000
Default: 500
min_speech_ms
​integer · min: 0 · max: 500
Default: 50
prefix_padding_ms
​integer · min: 0 · max: 2000
Default: 300
provider
​VADProvider · enum
Enum values:
silero
livekit
Default: silero
​
threshold
​number · min: 0 · max: 1
Default: 0.5

VADSettingsOut

allow_interruptions
​boolean · required
deactivation_gap
​number · required
id
​string · uuid · required
min_silence_ms
​integer · required
min_speech_ms
​integer · required
name
​string · required
​required
prefix_padding_ms
​integer · required
provider
​VADProvider · enum · required
Enum values:
silero
livekit
​required
threshold
​number · required
​
​

VADSettingsUpdate

​
​
​
​
​
​
​
​
​

ValidationError

​array · required
msg
​string · required
type
​string · required
ctx
​object
input
​

Verdict

Whether one offered conversation is about the question (rule 36). It says only that, so it answers no question but a yes-or-no one.
about
​boolean · required
interaction_id
​string · uuid · required

VerdictsIn

​Verdict[] · maxItems: 50

VersionChange

One entry of a version's what-changed summary. `subject` names the thing it happened to — the utterance, the version number restored from, or the lineage id of the node or edge involved — and is `None` where the kind is about the agent as a whole. `label` is what a person calls the subject, where the subject is a node: `dynamic_context`'s one resolved display string (the node's name, else a recap of its instructions) as it read in the version this entry describes — the version's own row for a node it holds, the previous version's for a node it removed. `None` for every other kind. A screen shows it in front of the lineage id, never instead of it: two nodes may share a name, and the id is what keeps them apart.
kind
​ChangeKind · enum · required

What changed since the version before, in the vocabulary of the product.

Not a diff of text: each member is a sentence a client would recognise about their own agent. Rendering one into their language is the dashboard's job, which is why these are named states and not English strings.

Enum values:
node_added
node_removed
node_instructions_changed
node_tools_changed
node_moved
edge_added
edge_removed
edge_condition_changed
​
​

VersionOut

One published version of a document, without its text (rules 2, 7, 10).
address
​string · required
coming
​boolean · required
​required
document
​LegalDocument · enum · required

The six legal documents the platform keeps (rule 1). Each value is also the last segment of the document's address, so it is spelled as a URL is.

Enum values:
terms
privacy
cookies
acceptable-use
processing-agreement
subprocessors
effective_at
​string · date-time · required
​object · required
in_force
​boolean · required
languages
​LegalLanguage[] · required
Enum values:
es
en
material
​boolean · required
name
​string · required
​required
published_at
​string · date-time · required

VersionTextOut

A version's text in the language asked, or in Spanish where it has none, and which one it is (rule 8).
address
​string · required
coming
​boolean · required
​required
document
​LegalDocument · enum · required

The six legal documents the platform keeps (rule 1). Each value is also the last segment of the document's address, so it is spelled as a URL is.

Enum values:
terms
privacy
cookies
acceptable-use
processing-agreement
subprocessors
effective_at
​string · date-time · required
fell_back
​boolean · required
fingerprint
​string · required
​object · required
in_force
​boolean · required
language
​LegalLanguage · enum · required

The languages a version may be written in (values table): Spanish required, English optional, and the Spanish prevails (rule 4).

Enum values:
es
en
languages
​LegalLanguage[] · required
Enum values:
es
en
material
​boolean · required
name
​string · required
​required
published_at
​string · date-time · required
text
​string · required

ViewFilters

The inbox's filters (inbox.md rule 17), by the names its address gives them. A name the inbox does not recognise is refused, never dropped (`extra="forbid"`); every one is optional, and an unset one is absent. The values are the address's: `holder`, `channelKey`, `agentId`, `endReason`, `counterparty`, `answeredBy`, `label` and `preparedReply` hold several values joined by commas; `since`/`until` are calendar days in the reader's own zone; the either-way filters are `yes` or `no`; `unreplied` is on or absent. An answer sends an unset filter as `null`, which the address reads as absent; what is stored leaves it out.
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​

ViewIn

A view to save: a name and the filters in force (rule 9). `shared` is an owner's or admin's to set (rule 10); `show_count` off saves it without a count (rule 14).
​ViewFilters · required

The inbox's filters (inbox.md rule 17), by the names its address gives them. A name the inbox does not recognise is refused, never dropped (extra="forbid"); every one is optional, and an unset one is absent.

The values are the address's: holder, channelKey, agentId, endReason, counterparty, answeredBy, label and preparedReply hold several values joined by commas; since/until are calendar days in the reader's own zone; the either-way filters are yes or no; unreplied is on or absent. An answer sends an unset filter as null, which the address reads as absent; what is stored leaves it out.

name
​string · minLength: 1 · maxLength: 80 · required
shared
​boolean
Default: false
show_count
​boolean
Default: true

ViewOut

One saved view as its reader sees it. `mine` is whether the reader made it; `editable` whether the reader may rename it, change its filters or delete it — the maker for one not shared, an owner or admin for a shared one (rule 10).
editable
​boolean · required
​ViewFilters · required

The inbox's filters (inbox.md rule 17), by the names its address gives them. A name the inbox does not recognise is refused, never dropped (extra="forbid"); every one is optional, and an unset one is absent.

The values are the address's: holder, channelKey, agentId, endReason, counterparty, answeredBy, label and preparedReply hold several values joined by commas; since/until are calendar days in the reader's own zone; the either-way filters are yes or no; unreplied is on or absent. An answer sends an unset filter as null, which the address reads as absent; what is stored leaves it out.

id
​string · uuid · required
mine
​boolean · required
name
​string · required
shared
​boolean · required
show_count
​boolean · required

Visibility

string · enum
Enum values:
pinned
discoverable

Whether the model is shown a tool whenever it is in reach, or only once the conversation has found it (tools.md rules 87-89). A fact about the tool, the same wherever it is attached; never what an agent may do, which attachment decides.

VoiceSectionIn

The whole voice section, in the order the arrangements are tried at the start of a call. Written whole: the order *is* the fallback, so a partial edit that could leave two arrangements at one position is not offered. An empty list is a profile that takes no calls.
​ArrangementIn[] · required

WarnedRow

A row that was added, with what its sender should know about it.
number
​string · required
warning
​string · required

WindowUnit

string · enum
Enum values:
hour
day
week
month
year

What a relative window counts back in. The same five widths Bucket offers, and deliberately the same words: a chart bucketed by week over the last six weeks should not need two vocabularies to say "week".

WithdrawalIn

`POST /legal/objections/{id}/withdraw` (rule 25).
language
​LegalLanguage · enum · required

The languages a version may be written in (values table): Spanish required, English optional, and the Spanish prevails (rule 4).

Enum values:
es
en

WriteConsent

string · enum
Enum values:
none
granted
lapsed

Microsoft's second consent, to change mail (connections.md rule 60): never asked, given, or refused by Microsoft since -- which stops the acting and nothing else (email-threads.md rule 37).

WriteResult

What one write did, including what it declined to do. `dropped_fields` is the retention-`0` answer and is the reason this is a shape rather than a bare `RecordOut`: a personal field is **refused before it is written**, the rest of the record is written, and the caller is told which ones went nowhere. Nothing is written and then deleted.
created
​boolean · required
​RecordOut · required
dropped_fields
​string[]

backend__features__interactions__schemas__Direction

string · enum
Enum values:
inbound
outbound

backend__features__interactions__schemas__Source

Source attribution, as recorded. `kind` says which of the rest is meaningful; the others stay unset. `connection_id` and `address` are `CHANNEL`'s pair, and they are two fields for the reason the thread itself is identified by two: *"Threads are per connection; contacts are per organization"* (`channels.md`). The connection says which of the organization's accounts the message arrived on — *"reply on this thread has to know which account it leaves by"* — and the address says who wrote, in whatever shape the provider's own namespace has: a phone number in E.164 on WhatsApp, an opaque user id elsewhere, *"decided when each is built"*. **`address` is personal data and `connection_id` is not.** The address is the contact — *"on a channel, the contact is their address"* — so the retention purge clears it exactly as it clears `number`, which is the same datum arriving by a different road; the connection names one of the organization's own accounts and survives, like `trunk_id` beside it. **`provider` is `CHANNEL`'s third, and it is stored as a value rather than read off the connection.** interactions.md: *"a conversation that arrived on a channel names the provider it arrived on, beside the contact's address"*, because an address alone does not say whether the person was reached on WhatsApp, by mail or somewhere else. It is written by the channel at `open`, from the provider it is already speaking to, for the reason the run settings beside it are values too: a connection is what it is *now*, and this answers what the conversation arrived on *then*. It also keeps `connection_id` the opaque reference it is — nothing here dereferences a connection to render a row. Not personal data: it names a kind of account of the organization's own and nobody outside it, so it survives the purge exactly as `connection_id` does.
kind
​SourceKind · enum · required

Where the conversation came from.

BROWSER is the platform's own interface — a person trying their agent by voice or by text from the dashboard, or the agent builder doing it for them. WIDGET is the embeddable widget on the client's own site. They are two origins and not one because the one thing source attribution exists to answer is who was on the other end, and "the client trying their own agent" and "a visitor on the client's site" are different answers (interactions.md).

IMPERSONATION is not a sixth entry surface — it is staff reaching a customer's own dashboard, which arrives on the same routes as BROWSER and is admitted, recorded and counted identically. It is recorded apart for the same reason: "the client testing their own agent" and "we were looking at it for them" are different answers too. impersonation.md records the session and not what was done inside it (#527); a conversation held under one is the single exception that sentence names, and this is where it is written down.

CHANNEL is a messaging channel — WhatsApp and the rest — where the origin is "which connection, and the contact's address on it", as an inbound call's origin is the number that rang (channels.md, "The record"). It is a source and not a fourth Channel: a thread is recorded as Channel.TEXT in the log beside every other written conversation, and what makes it a thread is where it came from.

CONNECTED_APP is an outside AI app a member connected by signing in (connected-apps.md), held to every rule a key is and still its own origin (interactions.md rule 2): "which integration placed this" has two answers once an organization has both, and a cell that read "API key" for an app would name the wrong one. The record keeps the app connection's identifier beside it as it keeps a key's (rule 3), never the token.

A native PostgreSQL enum (interaction_source_kind): a member added here ships an ALTER TYPE ... ADD VALUE migration, b7e2c4d9a1f3's precedent.

Enum values:
inbound_number
api_key
connected_app
browser
widget
campaign
impersonation
channel
​
​
​
​
​
​
​
​
​
​
​

backend__features__messaging__schemas__Audience

string · enum
Enum values:
members
contacts

Who writes on a connection (channels.md rule 50): the organization's own members, or its contacts -- people outside it. A road declares which it can carry (rule 51); a connection names one of those. Not notifications' Audience, which is who a notice is for: same word, another question.

backend__features__notifications__schemas__Audience

string · enum
Enum values:
client
platform

backend__features__tools__schemas__Source

string · enum
Enum values:
builtin
custom
mcp

Where a tool comes from. Provenance matters to us (config/auth/billing), never to the LLM — which sees every source identically.

CUSTOM is retired and unreachable, and it is still here. It used to mean "code written for one organization", and runtime.execution._route refused it by name because nothing could run one. Code written for one organization now runs the way every other first-party tool does — BUILTIN, with config naming which built-in and the composition root binding it (ADR 0014, widened by ADR 0017) — so there is one dispatch path and this member names none of them. It is kept rather than removed because tool_source is a native Postgres enum: dropping a value means recreating the type, rewriting every dependent column and taking a lock on tools to delete a name no row has any reason to carry. backend/features/custom_tools/DECISIONS.md D10 records the trade. A row that somehow carries it is dispatched like any other non-MCP row and answers execute_http's "this tool has no destination configured", which is one answer rather than two.

backend__features__trunks__schemas__Direction

string · enum
Enum values:
inbound
outbound

Which way calls flow over the trunk.

On this page
  • AIProfileCapabilities
  • AbsoluteWindow
  • AccessLevel
  • ActRefusalOut
  • ActionRefusal
  • AgentCopy
  • AgentCreate
  • AgentMapOut
  • AgentOut
  • AgentVersionOut
  • Aggregation
  • AgreedVersionOut
  • AllTimeWindow
  • AnalystCostOut
  • AnswerField
  • AnsweredBy
  • AnsweredFolderOut
  • AnsweringRecipe
  • ApiDescriptionIn
  • ApiImportIn
  • ApiImportOut
  • ApiMissingOut
  • ApiOperationOut
  • ApiPreviewOut
  • ApiServerOut
  • ApiSuggestionOut
  • ApiUnimportableOut
  • ApplyTo
  • ArrangementAttempt
  • ArrangementIn
  • ArrangementOut
  • AssignIn
  • Attachment
  • AttachmentIn
  • Audience-Input
  • AuthorKind
  • AutomationCreate
  • AutomationCreated
  • AutomationMapOut
  • AutomationOut
  • AutomationRunOut
  • AutomationScope
  • AutomationUpdate
  • BlockedReason
  • BoardChartPlacement
  • BoardIn
  • BoardLayout
  • BoardOut
  • BoardUpdate
  • Body_import_records
  • Body_plan_import
  • Body_propose_import
  • Body_set_automation_enabled
  • Body_upload_document
  • BoxesOut
  • BridgeCeilingIn
  • BridgeCeilingOut
  • BrowserCall
  • BrowserCallRefusalOut
  • Bucket
  • BuiltinOut
  • BulkDeleteIn
  • BulkResult
  • BulkSetFieldIn
  • CallBackIn
  • CallBackOut
  • CallIn
  • CampaignIn
  • CampaignOut
  • CampaignStatus
  • CampaignSummaryOut
  • CampaignUpdate
  • CapabilityField
  • CatalogueVoice
  • ChangeKind
  • Channel
  • ChannelAccessBody
  • ChannelIn
  • ChannelKind
  • ChannelOut
  • ChannelScope
  • ChannelUpdate
  • ChartIn
  • ChartOption
  • ChartOut
  • ChartResult
  • ChartResultRow
  • ChartRunIn
  • ChartSeries
  • ChartSeriesResult
  • ChartSpec
  • ChartType
  • ChartUpdate
  • ChartWeek
  • ChoiceIn
  • ClassCapabilities
  • ClosedConversation
  • CollectionCreate
  • CollectionLink
  • CollectionNameOut
  • CollectionOut
  • CollectionQuestion
  • CollectionUpdate
  • Condition
  • ConditionOp
  • ConfirmationAnswer
  • ConfirmationDecision
  • ConfirmationMode
  • ConnectionIn
  • ConnectionOut
  • ConnectionUpdate
  • ContactFileOut
  • ContactIn
  • ContactOut
  • ContactSourceKind
  • ContactStatus
  • ContactsIn
  • ConversationAccessOut
  • ConversationAccessUpdate
  • ConversationLabels
  • ConversationMemoryOut
  • ConversationMode
  • ConversationState
  • CustomToolAuthor
  • CustomToolAuthorKind
  • CustomToolCreate
  • CustomToolDraftUpdate
  • CustomToolOut
  • CustomToolStatus
  • CustomToolVersionOut
  • CutCounts
  • DatasetDimensionOut
  • DatasetFilterOut
  • DatasetMeasureOut
  • DatasetOut
  • DecisionAct
  • DecisionOut
  • DecisionOutcome
  • DecisionReading
  • DeletedKey
  • DeletionRefusalOut
  • Dimension
  • DirectoryEntryIn
  • DirectoryEntryOut
  • DirectoryEntryUpdate
  • DirectoryHours
  • DirectoryTargetKind
  • DnsRecordOut
  • DocumentAttachmentOut
  • DocumentOut
  • DomainState
  • DraftUpdate
  • EdgeIn
  • EdgeOut
  • EnabledIn
  • EnabledModelOut
  • EndReason
  • EventType
  • ExampleSuggestionIn
  • ExampleSuggestionOut
  • FallbackCondition
  • FanOut
  • FeatureFlag
  • FieldDefault
  • FieldDefaultKind
  • FieldIn
  • FieldOut
  • FieldType
  • FieldUpdate
  • Filter
  • FilterKind
  • FilterOp
  • FindingAction
  • FindingActionOut
  • FindingKind
  • FindingMomentOut
  • FindingOut
  • FindingsOut
  • FlagsUpdate
  • FlowCreate
  • FlowEdgeIn
  • FlowEdgeOut
  • FlowEntryStepIn
  • FlowGraphOut
  • FlowOut
  • FlowStepIn
  • FlowStepOut
  • FlowStepStatus
  • FlowStepTrace
  • FlowStepUpdate
  • FlowTestIn
  • FlowTestOut
  • FlowTestOutcome
  • ForwardHealth
  • ForwardOrigin
  • ForwardOutcome
  • ForwardRecordOut
  • ForwardedAddressIn
  • ForwardedAddressOut
  • GoogleCapability
  • GoogleProduct
  • GrantedVia
  • GraphEdgeSpec
  • GraphNodeSpec
  • GraphOut
  • GraphReplace
  • GraphReplaced
  • HTTPValidationError
  • HandToAgentIn
  • HandedOverOut
  • HandoffAct
  • HandoffReasonCode
  • HeaderCollision
  • HealthStatus
  • HoursChange
  • HoursIn
  • HoursSlot
  • HungUpBy
  • ImportPlanOut
  • ImportProposalOut
  • ImportResult
  • ImportRunOut
  • ImportStatus
  • InboxItemOut
  • InboxViewUpdate
  • IngestionStatus
  • InstallIn
  • InstallState
  • InstalledRecipeOut
  • InteractionCount
  • InteractionEventOut
  • InteractionOut
  • InteractionTraceOut
  • InteractionTranscriptOut
  • InvitationCreate
  • InvitationOut
  • Kind
  • KnewNoteOut
  • LLMProvider
  • LLMSettingsIn
  • LLMSettingsOut
  • LLMSettingsUpdate
  • LabelChoice
  • LabelIn
  • LabelOut
  • LabelUpdate
  • LabelsChange
  • Language
  • LastLine
  • Layout
  • LegalDocument
  • LegalDocumentOut
  • LegalLanguage
  • LegalRecordOut
  • LineAbout
  • LineKind
  • LineType
  • LiveProvider
  • LiveSettingsIn
  • LiveSettingsOut
  • LiveSettingsUpdate
  • MailFolderOut
  • MailFoldersOut
  • MailLineOut
  • MailServerDiscoverIn
  • MailServerDiscoveryOut
  • MailServerEndpoint
  • MailServerFault
  • MailServerHealth
  • MailServerIn
  • MailServerOut
  • MailServerPasswordIn
  • MailServerSecurity
  • MailServerServersIn
  • MailServerSource
  • MailServerUpdate
  • MailboxAuthorizeIn
  • MailboxAuthorizeOut
  • MailboxDraftState
  • MailboxHealth
  • MailboxOut
  • MailboxUpdate
  • MapFrameOut
  • MapGroupOut
  • MapLanesOut
  • MapLineOut
  • MapOwner
  • MapPartOut
  • MapStepToolOut
  • McpImportIn
  • Measure
  • MediaKind
  • MemberRemoved
  • MembershipOut
  • MemoryBoundsOut
  • MemoryLineIn
  • MemoryLineOut
  • MemoryLinePatch
  • MemoryLinesOut
  • MemoryOut
  • MemoryPatch
  • MemoryPersonNotesOut
  • MemoryPersonOut
  • MemoryRefusal
  • MemoryRefusalOut
  • MemorySwitchIn
  • MemorySwitchOut
  • Message
  • MessageMark
  • MetricCategory
  • MetricOut
  • ModeUpdate
  • ModelsByKind
  • Moment
  • MomentLine
  • NoRunRule
  • NodeIn
  • NodeOut
  • NodeUpdate
  • NotAnswered
  • ObjectionIn
  • ObjectionPeriodOut
  • OpenCountOut
  • OpenRequest
  • OpenedConversation
  • OrgMapOut
  • OrganizationAgreementIn
  • OrganizationAskOut
  • OrganizationOut
  • OrganizationUpdate
  • Origin
  • OurSide
  • OutboundIn
  • Outcome
  • Owner
  • OwnerKind
  • Parameter
  • ParameterKind
  • ParametersIn
  • PartKind
  • PartOut
  • PartState
  • PauseOrigin
  • PendingOut
  • PersonAgreementIn
  • PersonAskOut
  • PersonGrantIn
  • PersonGrantOut
  • PersonState
  • PickupDecider
  • PickupDecision
  • PickupPath
  • PlacementOut
  • Position
  • PreferencesOut
  • PreferencesUpdate
  • PreparedOutdatedBy
  • PreparedReplyOut
  • PreparedSendIn
  • PreparedSentOut
  • PreparedState
  • ProfileCopy
  • ProfileIn
  • ProfileOut
  • ProfileUpdate
  • ProposedColumn
  • ProposedLabel
  • ProposedLabels
  • ProviderOut
  • ProviderPresence
  • ProvidersByKind
  • PublishRefusalOut
  • QuestionChartOut
  • QuestionKind
  • QuestionMatch
  • QuestionProposalOut
  • QuestionRefusal
  • QuestionRefusalOut
  • QuestionSearchIn
  • QuestionSearchOut
  • QuestionState
  • RailEntry
  • RailEntryKind
  • RailOut
  • ReachMode
  • Reachability
  • ReadFolderOut
  • ReaderCeilings
  • ReaderLimits
  • ReadingRefusal
  • ReadingRefusalOut
  • ReadingSwitchIn
  • ReadingSwitchOut
  • RecallIn
  • RecipeAccount
  • RecipeMark
  • RecipeOut
  • RecordCount
  • RecordFilter
  • RecordIn
  • RecordKind
  • RecordOut
  • RejectedRow
  • Rejection
  • RelativeWindow
  • RemovedFromCampaign
  • ReplyMode
  • ReplyOut
  • ResourceSummary
  • RetentionIn
  • RetentionOut
  • Role
  • RoleFlagsOut
  • RoleFlagsUpdate
  • RoleUpdate
  • RosterAddressIn
  • RosterAddressKind
  • RosterAddressOut
  • RosterEntryIn
  • RosterEntryOut
  • RosterEntryUpdate
  • Route
  • RouteKind
  • RunOutcome
  • RunSettings
  • STTProvider
  • STTSettingsIn
  • STTSettingsOut
  • STTSettingsUpdate
  • Scale
  • Scope
  • SearchCostOut
  • SearchHit
  • SearchIn
  • SelectionOfIds
  • SelectionOfMatching
  • SendOnChannelIn
  • SentFileOut
  • SentOut
  • Slot
  • Source-Input
  • SourceAddress
  • SourceFilter
  • SourceKind
  • Speaker
  • StandbyComponent
  • StateReason
  • Status
  • SummaryOut
  • SummaryScope
  • TTSProvider
  • TTSSettingsIn
  • TTSSettingsOut
  • TTSSettingsUpdate
  • TeachIn
  • TeachOut
  • TemplateIn
  • TestLink
  • TextSectionIn
  • TextSectionOut
  • TextStreamEnd
  • TextStreamError
  • TextStreamFrame
  • ThreadOut
  • ThreadSubjectOut
  • ToolIn
  • ToolOutcome
  • ToolPhase
  • ToolTestIn
  • ToolTestOut
  • ToolUpdate
  • ToolView
  • TrackedQuestionIn
  • TrackedQuestionOut
  • TrackedQuestionUpdate
  • TransferOutcome
  • TraversalEdgeOut
  • TraversalNodeOut
  • TraversalOut
  • TriggerConfig
  • TriggerKind
  • TrunkIn
  • TrunkOut
  • TrunkPeerOut
  • TrunkUpdate
  • TryIn
  • TryOut
  • TurnStages
  • UninstalledOut
  • UnreadCountOut
  • UsageSummaryOut
  • Utterance
  • VADCapabilities
  • VADProvider
  • VADSettingsIn
  • VADSettingsOut
  • VADSettingsUpdate
  • ValidationError
  • Verdict
  • VerdictsIn
  • VersionChange
  • VersionOut
  • VersionTextOut
  • ViewFilters
  • ViewIn
  • ViewOut
  • Visibility
  • VoiceSectionIn
  • WarnedRow
  • WindowUnit
  • WithdrawalIn
  • WriteConsent
  • WriteResult
  • backend__features__interactions__schemas__Direction
  • backend__features__interactions__schemas__Source
  • backend__features__messaging__schemas__Audience
  • backend__features__notifications__schemas__Audience
  • backend__features__tools__schemas__Source
  • backend__features__trunks__schemas__Direction