Amdahl Platform API
v1.0.0
REST API for Amdahl, an eval harness for GTM outputs. Three surfaces: **Evals** grades what your agents write against your own customer conversations, **Search** answers questions over the same corpus in one synchronous call, and **Agents** runs async investigations (multi-turn Chat, a reusable agent library, and cron-driven Routines).
Base URL https://app.amdahl.ai. All paths below include the full /api/platform/v1 prefix. Every operation has a Try it runner that calls the live API from your browser.
Need a key? Create one in the console: open console.amdahl.ai, then your workspace then Settings then Developer then Create key. Paste it into any Try it panel below (it is saved in this browser only, never sent anywhere but the API).
Workflows
Workflow orchestration endpoints.
/api/platform/v1/agents/{session_id}/resumeResume Agent Session
Resume an agent run that is paused on pending user input. Use when replying to an outline approval prompt, a clarification question, or a continue/finish decision. The input payload must match the run's `pending_input_schema`. After a successful resume the run transitions back to running and proceeds from where it paused.
Parameters
session_idpathstring <uuid>requiredRequest body
application/jsonrequired
inputobjectrequiredThe user's resume payload. Must match the run's persisted pending_input_schema.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/agents/{session_id}/cancelCancel Agent Session
Cancel an in-flight agent run. Use when the user wants to stop a running agent before it finishes, or abandon a run that has gone sideways. Cascades to any sub-agents spawned from this run via agents.delegate — every live descendant is cancelled atomically. The runner bails at the next iteration boundary after observing the status change. Already-completed runs cannot be canceled. Pass a reason to record WHY the run was aborted in the audit trail.
Parameters
session_idpathstring <uuid>requiredRequest body
application/json
reasonstringoptionalOptional operator note describing why the run was canceled.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/agentsList Workspace Agents
List the workspace agent roster - Amdahl-shipped library agents merged with tenant-authored ones - as lean rows of slug, name, description, and provenance. Read when choosing which named agent should handle a task or rendering the agent directory. Library entries are code-defined and locked; tenant rows carry their creation time.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/agentsCreate Workspace Agent
Save a new tenant-authored agent: a named, reusable prompt the workspace can dispatch later by its stable kebab-case slug. Use when a user wants to capture a custom instruction set as a workspace agent. Rejects a slug that shadows an Amdahl library agent (those ship in code and cannot be replaced) and surfaces tenant slug collisions as a typed conflict.
Request body
application/jsonrequired
slugstringoptionalOptional stable kebab-case dispatch key (lowercase letters, digits, single dashes). Omitted: derived from the name and uniquified automatically.
namestringrequiredDisplay name.
descriptionstringoptionalOptional one-line blurb for the agent directory.
tool_blocklistarray of stringoptionalOperation ids to subtract from this agent tool kit at run time (all kit tools are on by default; empty list = no restriction). Runner-inline tools are refused.
promptstringrequiredThe prompt the agent runs with.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/agents/{id}Get Agent By Id Or Slug
Fetch one named agent by slug or id, including the full prompt text it runs with. Resolves the Amdahl code library first, then falls back to the tenant row. Read when inspecting exactly what instructions an agent carries before dispatching, editing, or duplicating it. Returns null for another tenant or a missing id.
Parameters
idpathstringrequiredUUID or slug of the agent to read.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/agents/{id}Update Workspace Agent
Revise a tenant-authored agent's name, description, or prompt in place, keeping its slug stable. Call when refining how a saved agent behaves after seeing its output. Amdahl library agents are code-defined and immutable, so edits targeting one are refused with a locked error rather than silently ignored.
Parameters
idpathstringrequiredUUID or slug of the agent to patch.
Request body
application/json
namestringoptionalNew display name.
descriptionstringoptionalNew directory blurb.
promptstringoptionalNew prompt body.
tool_blocklistarray of stringoptionalOperation ids to subtract from this agent tool kit at run time (all kit tools are on by default; empty list = no restriction). Runner-inline tools are refused.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/agents/{id}Archive Workspace Agent
Retire a tenant-authored agent by soft-archiving it out of the roster; the row is retained and nothing is destroyed. Use when the workspace no longer dispatches a saved agent. Archived agents disappear from listings immediately, while Amdahl library agents cannot be archived at all - they are code-defined and always available.
Parameters
idpathstringrequiredUUID or slug of the agent to archive.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/agents/usageGet Build Usage
Workspace rollups of recent agent and routine activity over a fixed 30-day window: per-agent and per-routine run counts (7d / 30d), last run pointers, schedule counts, and workspace totals. Read when rendering the Agents & Routines page so operators can see which specialists and schedules are doing anything without inspecting each chat.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/routinesList Routines
List the workspace Routines, newest first: each entry carries the prompt, cadence (cron + timezone), agent pin, per-fire config, and last/next fire bookkeeping. Read when rendering the Routines page, checking what refreshes on a schedule, or picking one to update or fire now. Filter by enabled; paginate via limit + offset.
Parameters
enabledquerybooleanoptionalFilter by enabled state. As a query param, accepts ONLY the exact strings "true" or "false"; any other value (or omitted) applies no filter.
limitqueryintegeroptionaloffsetqueryintegeroptionalResponses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/routinesCreate Routine
Create a Routine: a cron that fires a Chat each occurrence, running one Master agent turn in a fresh named Chat. Use when the user asks for a standing refresh or a recurring investigation. Provide name, prompt, and cron (plus optional timezone and per-fire config like depth or an agents roster); the fire never pauses on a human and each occurrence opens a new Chat.
Request body
application/jsonrequired
namestringrequiredHuman label. Each fired Chat is named "{name} — {date}".
promptstringrequiredThe Chat input every fire starts the Master turn with.
cronstringrequiredCron expression (e.g. "0 9 * * 1"). Validated before the row is stored.
timezonestringoptionalIANA timezone the cron evaluates in. Defaults to UTC.
agentstringoptionalLEGACY single-agent pin the Master turn runs as (library slug/uuid or workspace agent id/slug). Prefer config.agents; passing both rejects.
configobjectoptionalPer-fire Chat config. on_question is always none (headless); agents picks the roster ("all", one pin, or a delegate allowlist).
depthstringoptionalquickstandarddeepagentsanyoptionalAgents the routine employs: "all" for unrestricted delegation, one ref to run AS that agent (pin), or several refs to restrict delegation to that roster.
actions_allowedarray of stringoptionalOutbound actions each fired run may invoke. Absent = ALL cataloged actions (default ON); pass a list to narrow, [] to disable.
external_searchbooleanoptionalAllow market fan-outs (external_search.execute in the kit). Default false.
include_divergencebooleanoptionalFuse the divergence map on external_search.execute (else evidence-only). Default false.
enabledbooleanoptionalWhether the routine fires. Defaults to true.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/routines/{id}Get Routine
Fetch one Routine in full: prompt, cadence, agent roster, per-fire config, enabled state, and the last fire pointers (Chat + run ids). Read when opening a routine detail view or before patching one. Unknown and cross-workspace ids return null.
Parameters
idpathstringrequiredResponses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/routines/{id}Update Routine
Patch an existing Routine: rename it, change its prompt or cadence, re-pin the agent, adjust per-fire config, or enable/disable it. Use when tuning a standing scheduled ask without recreating it. Only the supplied fields change; a cadence change is re-validated first and takes effect on the next scheduler tick.
Parameters
idpathstringrequiredThe routine id.
Request body
application/json
namestringoptionalNew human label.
promptstringoptionalNew Chat input for future fires.
cronstringoptionalNew cron expression (validated before storing).
timezonestringoptionalNew IANA timezone.
agentstring | nulloptionalLEGACY single-agent pin, or null to clear back to the plain Master. Prefer config.agents; passing both rejects.
configobjectoptionalReplacement per-fire config (replaces the whole object, including the agents roster).
depthstringoptionalquickstandarddeepagentsanyoptionalAgents the routine employs: "all" for unrestricted delegation, one ref to run AS that agent (pin), or several refs to restrict delegation to that roster.
actions_allowedarray of stringoptionalOutbound actions each fired run may invoke. Absent = ALL cataloged actions (default ON); pass a list to narrow, [] to disable.
external_searchbooleanoptionalAllow market fan-outs (external_search.execute in the kit). Default false.
include_divergencebooleanoptionalFuse the divergence map on external_search.execute (else evidence-only). Default false.
enabledbooleanoptionalEnable or disable firing.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/routines/{id}Delete Routine
Permanently remove a Routine so its cadence stops firing. Use when the user no longer wants the standing scheduled ask. Deletion affects only future fires - Chats and documents earlier fires produced are untouched. Prefer disabling (routines.update enabled=false) when the user may want it back.
Parameters
idpathstringrequiredThe routine id to delete.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/routines/{id}/run-nowRun Routine Now
Fire one Routine immediately, off-cadence: opens a fresh Chat and starts the Master turn exactly as the cron would. Use when the user wants the standing refresh right now instead of waiting for the next occurrence. Returns the Chat and run handles (stream/read/resume URLs) so the fire can be watched like any Chat.
Parameters
idpathstringrequiredThe routine id to fire.
Request body
application/json
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/subscriptionsList Subscriptions
List the workspace Subscriptions, newest first: each entry carries the source kind, per-kind config, prompt, agent pin, per-fire policy, and last-fire bookkeeping. Read when rendering the Subscriptions view, checking which events wake agents, or picking one to update or test-fire. Filter by source_kind or enabled; paginate via limit + offset.
Parameters
source_kindquerystringoptionalFilter to one source kind.
enabledquerybooleanoptionalFilter by enabled state. As a query param, accepts ONLY the exact strings "true" or "false"; any other value (or omitted) applies no filter.
limitqueryintegeroptionaloffsetqueryintegeroptionalResponses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/subscriptionsCreate Subscription
Create a Subscription: an event trigger that starts a headless Master agent turn in a fresh Chat when its source fires — e.g. a set lead time before each Google Calendar event. Use when the user wants an agent to react to something happening rather than to a clock. Provide name, source_kind, prompt, and the per-kind config; discover kinds and their config fields via subscription://kinds.
Request body
application/jsonrequired
namestringrequiredHuman label. Each fired Chat is named "{name} — {date}".
source_kindstringrequiredWhich event source fires this subscription (e.g. "calendar.event_upcoming"). Read subscription://kinds for the catalog + each kind's config fields.
promptstringrequiredThe Chat input every fire starts the Master turn with; the triggering event is appended after it automatically.
configobjectoptionalPer-kind source config, validated against the kind's schema (for calendar.event_upcoming: connection_id, lead_minutes, optional calendar_id + filters).
agentstringoptionalOptional agent pin the Master turn runs as (library slug/uuid or workspace agent id/slug).
run_configobjectoptionalPer-fire Chat config. on_question is always none (headless); every knob defaults conservative (standard depth, no market fan-out).
depthstringoptionalInvestigation depth per fire. Defaults to standard (a fire is a reaction).
quickstandarddeepactions_allowedarray of stringoptionalOutbound actions each fired run may invoke. Absent = ALL cataloged actions (default ON); pass a list to narrow, [] to disable.
external_searchbooleanoptionalAllow market fan-outs (external_search.execute in the kit). Default false.
include_divergencebooleanoptionalFuse the divergence map on external_search.execute (else evidence-only). Default false.
enabledbooleanoptionalWhether the subscription fires. Defaults to true.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/subscriptions/kindsList Subscription Kinds
Catalog of the event source kinds a Subscription can name, each with its config fields (key, type, required, description) so a subscription can be configured correctly from the catalog alone. Read when authoring or editing a subscription, or when the user asks what events can trigger an agent.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/subscriptions/{id}Get Subscription
Fetch one Subscription in full: source kind + per-kind config, prompt, agent pin, per-fire policy, enabled state, and the last fire pointers (Chat + run ids), plus a derived source_health block naming why a quiet fires ledger is quiet (e.g. the calendar credential needs reconnecting). Read when opening a subscription detail view, diagnosing missing fires, or before patching one. Unknown and cross-workspace ids return null.
Parameters
idpathstringrequiredResponses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/subscriptions/{id}Update Subscription
Patch one Subscription: rename it, change the prompt or agent pin, adjust the per-kind event config (lead time, filters), tune the per-fire policy, or flip enabled. Use when the user wants an existing event trigger changed rather than replaced; source_kind is immutable — delete and recreate to switch kinds. Only supplied fields change.
Parameters
idpathstringrequiredThe subscription id to patch.
Request body
application/json
namestringoptionalpromptstringoptionalconfigobjectoptionalReplacement per-kind config, validated against the subscription's existing source_kind.
agentstringoptionalNew agent pin, or null to clear it (plain Master).
run_configobjectoptionaldepthstringoptionalInvestigation depth per fire. Defaults to standard (a fire is a reaction).
quickstandarddeepactions_allowedarray of stringoptionalOutbound actions each fired run may invoke. Absent = ALL cataloged actions (default ON); pass a list to narrow, [] to disable.
external_searchbooleanoptionalAllow market fan-outs (external_search.execute in the kit). Default false.
include_divergencebooleanoptionalFuse the divergence map on external_search.execute (else evidence-only). Default false.
enabledbooleanoptionalResponses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/subscriptions/{id}Delete Subscription
Permanently remove a Subscription so its event source stops firing agents, taking its fire history with it. Use when the user no longer wants the event trigger at all. Chats and documents earlier fires produced are untouched. Prefer disabling (subscriptions.update enabled=false) when the user may want it back.
Parameters
idpathstringrequiredThe subscription id to delete.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/subscriptions/{id}/firesList Subscription Fires
The fire ledger of one Subscription, newest first: each row is one planned occurrence with its due time, status (pending, fired, skipped, failed, canceled), the event snapshot that drove it, and — once fired — the Chat + run it started. Read when checking whether a trigger is actually firing or diagnosing why an expected fire never ran.
Parameters
idpathstringrequiredlimitqueryintegeroptionalResponses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/subscriptions/{id}/test-fireTest-Fire Subscription
Fire one Subscription right now with a labeled synthetic event, exercising the identical path a real occurrence takes. Use when verifying a freshly created or edited event trigger end-to-end before its source fires for real. Returns the Chat and run handles (stream/read/resume URLs) so the fired run can be watched.
Parameters
idpathstringrequiredThe subscription id to test-fire.
Request body
application/json
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalData
Data exploration - SQL queries, hybrid search, and ML clusters.
/api/platform/v1/connections/catalogList Connector Catalog
Read the catalog of integrations a workspace can hook up - CRM, calls, comms, docs, support, social, and actions (outbound) - with each entry's connect flow, ownership, and the data streams it brings in. Read when rendering the "add a connection" picker or answering which providers are supported. Describes what CAN be connected, not what is already connected. Optional kind / category filters narrow the menu.
Parameters
kindquerystringoptionalFilter to one ingestion stack. first_party (amdahl-data pullers) is the only stack today; use category=social to filter the social UI grouping.
categoryquerystringoptionalFilter to one UI category.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/connectionsList Established Connections
Read the integrations a workspace has actually established, newest first, merging first-party data sources and tracked social accounts into one normalized list with a uniform status, scope, and last-synced field. Read when rendering the connections inventory or answering which integrations are live for the tenant. Defaults to ACTIVE connections; pass include_inactive=true to also return disconnected and never-synced sources.
Parameters
include_inactivequerybooleanoptionalInclude disconnected and never-synced (abandoned / in-flight) connections. Defaults to false, which returns only active connections (connected/syncing or ever-synced).
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/connectionsConnect an Integration
Establish a new integration for a workspace, routing on the connector type: collect an API key, kick off an OAuth redirect, or track a social handle. Use when a user picks a provider from the catalog and wants it connected. Returns the created connection, or an OAuth authorize URL to redirect to. Creates and stores the connection without starting a sync.
Request body
application/jsonrequired
connector_typestringrequiredCatalog connector type to connect (e.g. hubspot, fathom, twitter).
namestringoptionalOptional display label for this connection instance.
scopestringoptionalOwnership: business (workspace-wide) or personal. Defaults from the catalog.
businesspersonalapi_keystringoptionalFor api_key connectors: the API key / token to store (encrypted).
api_idstringoptionalFor an api_key connector whose credential is a PAIR (today only Aircall, whose API is HTTP Basic): the non-secret id half, pasted alongside the token in api_key. Read connections.list_catalog to see whether a connector asks for it.
base_urlstringoptionalFor the computer_agent connector: its deployment base URL (http/https).
account_handlestringoptionalFor social (handle) connectors: the resolved bare account handle.
external_account_idstringoptionalFor social (handle) connectors: the provider's stable account id.
display_namestringoptionalFor social (handle) connectors: display name snapshot.
avatar_urlstringoptionalFor social (handle) connectors: avatar image URL snapshot.
followersintegeroptionalFor social (handle) connectors: follower count snapshot.
return_tostringoptionalFor oauth connectors: console URL to redirect back to after the handshake (first-party origins only).
call_filtersobjectoptionalFor an api_key CALLS connector (e.g. Fireflies): an optional atomic pre-sync call filter applied in the SAME request, so the source connects AND its filter decision lands together (an enabled filter syncs filtered; { enabled: false } is the "ingest all" opt-out). Omit to leave the calls source paused until a filter is set later. Own domains are stamped server-side. Requires workspace admin.
enabledbooleanoptionalWhether the filter is active. false = the "ingest all calls" opt-out.
require_externalbooleanoptionalIngest only calls with at least one external attendee.
require_participantsarray of stringoptionalIngest only calls including at least one of these participant emails.
exclude_participantsarray of stringoptionalNEVER ingest a call one of these emails attended or hosted (exclusion wins over the include rules).
exclude_domainsarray of stringoptionalNEVER ingest a call with an attendee/host on one of these domains (suffix-aware; exclusion wins).
internal_domainsarray of stringoptionalOptional extra internal domains; the tenant own domains are merged in.
bot_domainsarray of stringoptionalMeeting-bot email domains to disregard when computing the roster.
modestringoptionalFail-closed posture; only "strict".
stricton_ambiguousstringoptionalAmbiguity resolution; only "exclude".
excludecrm_recencyobjectoptionalFor a CRM connector (Salesforce / HubSpot / Pipedrive): how much activity/comms history (emails, meetings, calls, notes, tasks) the first sync imports. window_months (1–120) OR all_history. Reference records (accounts, contacts, deals) always land in full. Omit for the 12-month default.
window_monthsintegeroptionalImport the last N months of CRM activity/comms.
all_historybooleanoptionalImport ALL activity history (unbounded). Mutually exclusive with window_months.
comms_filterobjectoptionalFor a connector that supports it (HubSpot and Salesforce emails; Zero meetings, whose transcripts follow their meeting): a SOFT filter over which comms records (e.g. emails or meetings) the first sync imports — keep only external / rep-involved messages, dropping internal-only or irrelevant ones before they land. Applies only to the named comms streams; account/contact/deal records are untouched. Own domains stamped server-side. Omit to import comms unfiltered.
enabledbooleanoptionalWhether the filter is active.
streamsarray of stringoptionalWhich comms streams to filter (e.g. ["emails"]).
require_externalbooleanoptionalKeep only records with at least one external party.
require_participantsarray of stringoptionalKeep only records including at least one of these rep emails.
exclude_participantsarray of stringoptionalAlways drop a record where one of these emails is a party.
exclude_domainsarray of stringoptionalAlways drop a record with a party on one of these domains.
internal_domainsarray of stringoptionalOptional extra internal domains; the tenant own domains are merged in.
on_ambiguousstringoptionalAmbiguity resolution; "include" (default) keeps a record when it cannot be judged.
includeexcludeResponses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/connections/{id}Get Connection By Id
Fetch one established integration by its id, scoped to the caller's workspace, with its newest successful provider check (warnings such as a key that lacks a scope; checked_at gives its age). Read when opening a connection detail view, confirming a specific integration's configuration, or finding why part of its data does not sync. Returns the normalized record, or null when the id belongs to another tenant or does not exist - a not-found that never reveals existence.
Parameters
idpathstringrequiredUUID of the connection to read.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/connections/{id}Update a Connection
Rename an established connection, or set/clear its owner - the workspace member a personal source (e.g. Gmail, Outlook) belongs to. Use when a user wants to relabel a connection to tell multiple instances apart, attribute a personal connection to a teammate, or clear that attribution. Owner applies to personal connectors only; returns the updated connection. A non-existent or cross-tenant id is a clean not-found.
Parameters
idpathstringrequiredUUID of the connection to update.
Request body
application/json
namestringoptionalNew display label for this connection instance (trimmed; must be non-empty). Renames any connection so multiple instances of the same connector type can be told apart.
owner_user_idstring | nulloptionalThe workspace member (auth user id) this personal connection belongs to. Pass null to clear the owner. Only valid for personal connectors (Gmail, Outlook).
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/connections/{id}Disconnect an Integration
Disconnect one established integration by its id, scoped to the workspace. Use when a user wants to stop an integration: the source (a CRM / calls / docs connector or a tracked X / LinkedIn account) is marked disconnected. Retains the row for re-connect; a non-existent or cross-tenant id is a clean not-found that never reveals another workspace.
Parameters
idpathstringrequiredUUID of the connection to disconnect.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/connections/{id}/statusGet Connection Sync State
Fetch the live sync state of one integration - its normalized health, the in-flight syncing flag, the last good sync time, and any last error - without pulling the whole record. Read when polling a connecting / syncing badge after a connect or refresh kicks off. Resolved across both stacks and scoped to the workspace; null when the id is not in the tenant.
Parameters
idpathstringrequiredUUID of the connection to read the sync state for.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/connections/{id}/runsList Connection Sync Runs
Read the recent sync-run history of one integration - each run's outcome, what triggered it, when it started and finished, how many streams ran or failed, the rows written, and a bucketed failure reason. Read when showing a connection activity log or diagnosing why data is not flowing. Scoped to the workspace; an id outside the tenant returns an empty list.
Parameters
idpathstringrequiredUUID of the connection to list sync runs for.
limitqueryintegeroptionalMax runs to return, newest first (default 25, capped at 100).
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/connections/{id}/summaryGet Connection Data Summary
Read a glanceable snapshot of one integration's own synced data - per-connector counts and totals (deals and pipeline value, meetings, messages, tickets), the most recent items, and a weekly activity sparkline - computed live from the raw tables its puller writes. Read when opening a connection detail to show what has actually flowed in, beyond status. Scoped to the workspace; null when the id is not in the tenant.
Parameters
idpathstringrequiredUUID of the connection to summarize.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/connections/{id}/reconnectReconnect an Integration
Restore a connection stuck in an error or needs-reauth state, in place on the existing data source so its sync history and id survive (no duplicate row). Use when a user clicks reconnect on a broken connection: an OAuth provider returns a fresh authorize URL to redirect through, an api_key connector takes a replacement key, and a tracked social handle simply re-triggers its sync. A non-existent or cross-tenant id is a clean not-found.
Parameters
idpathstringrequiredUUID of the connection to reconnect.
Request body
application/json
api_keystringoptionalFor api_key connectors: the replacement API key / token to store (encrypted) on the existing source. Ignored for oauth and social-handle connectors.
api_idstringoptionalFor an api_key connector whose credential is a PAIR (today only Aircall): the non-secret id half, re-stored alongside the replacement token. Required when the catalog entry asks for it.
return_tostringoptionalFor oauth connectors: console URL to redirect back to after the re-handshake (first-party origins only).
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/connections/{id}/call-filtersSet Call Filters
Configure the pre-sync call filter on a supported calls connection (Fathom, Fireflies, Gong, Grain) so only external and rep-attended calls are ever ingested — non-qualifying transcripts are never fetched. Use when a compliance-sensitive workspace must keep such call content from reaching Amdahl. Stamps the tenant own domains, validates against what the connector can enforce, and starts sync once an enabled filter is saved. Workspace admin only.
Parameters
idpathstringrequiredUUID of the calls connection to configure.
Request body
application/jsonrequired
configobjectrequiredThe call-filter config (inclusion polarity). Own domains are stamped server-side.
enabledbooleanoptionalWhether the filter is active. Only an enabled filter starts sync.
modestringoptionalFail-closed posture marker; only "strict".
strictrequire_externalbooleanoptionalIngest only calls with at least one external attendee.
require_participantsarray of stringoptionalIngest only calls including at least one of these participant emails.
exclude_participantsarray of stringoptionalNEVER ingest a call one of these emails attended or hosted (exclusion wins over the include rules).
exclude_domainsarray of stringoptionalNEVER ingest a call with an attendee/host on one of these domains (suffix-aware; exclusion wins).
internal_domainsarray of stringoptionalOptional extra internal domains; the tenant own domains are merged in.
bot_domainsarray of stringoptionalMeeting-bot email domains to disregard when computing the roster.
on_ambiguousstringoptionalAmbiguity resolution; only "exclude" (fail-closed).
excludeResponses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/connections/{id}/crm-recencySet CRM Recency Window
Set how much CRM activity history (emails, meetings, calls, notes, tasks) a Salesforce / HubSpot / Pipedrive connection imports on first sync — a month window or all-history — while account, contact and deal records always land in full. Use when a workspace wants to bound (or widen) how far back CRM communications are pulled; editing it re-pulls only the affected activity streams.
Parameters
idpathstringrequiredUUID of the CRM connection to configure.
Request body
application/jsonrequired
configobjectrequiredThe recency window: window_months (1–120) OR all_history. Omit both for the 12-month default.
window_monthsintegeroptionalImport the last N months of CRM activity/comms.
all_historybooleanoptionalImport ALL activity history (unbounded). Mutually exclusive with window_months.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/connections/{id}/comms-filterSet CRM Comms Filter
Filter which CRM activity records (today HubSpot and Salesforce emails, and Zero meetings with their transcripts) are imported to only external / rep-involved communications — internal-only or irrelevant messages are dropped before they land, while account, contact and deal records are untouched. Use when a workspace wants to keep internal or off-topic CRM comms out of Amdahl; it applies to new activity going forward (set it at connect to filter from the first sync).
Parameters
idpathstringrequiredUUID of the CRM connection to configure.
Request body
application/jsonrequired
configobjectrequiredThe comms filter: which streams + predicates. Own domains are stamped server-side.
enabledbooleanoptionalWhether the filter is active.
streamsarray of stringoptionalWhich comms streams to filter (e.g. ["emails"]).
require_externalbooleanoptionalKeep only records with at least one external party.
require_participantsarray of stringoptionalKeep only records including at least one of these rep emails.
exclude_participantsarray of stringoptionalAlways drop a record where one of these emails is a party.
exclude_domainsarray of stringoptionalAlways drop a record with a party on one of these domains.
internal_domainsarray of stringoptionalOptional extra internal domains; the tenant own domains are merged in.
on_ambiguousstringoptionalAmbiguity resolution; "include" (default) keeps a record when it cannot be judged.
includeexcludeResponses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/search/queryRouted Warehouse Search
One routed door over tenant warehouse data: each ask lands on typed filters, an NL-to-SQL pass, or vector similarity. Use when you hold a plain question OR typed predicates and want the cheapest lane picked automatically (force one via mode). Returns the lane, rows, any compiled SQL, and retry_guidance naming the exact params to re-send when part of a multi-part ask went unanswered. Act on retry_guidance instead of giving up. Lanes read different stores at different grains — read corpus.
Request body
application/json
querystringoptionalFree-text ask. Meaning-shaped wording ("what do customers say about…") routes to the semantic lane; other questions route to the NL-to-SQL fuzzy lane.
max_subqueriesintegeroptionalHow many independent sub-questions a multi-part ask may fan out into. Default 3; up to 5 synchronously and 12 with async:true. RAISE THIS when a reply comes back with retry_guidance.action = "raise_max_subqueries" — it means parts of your question were cut by this cap, and retry_guidance.suggested_params carries the value to use.
asyncbooleanoptionalRun as a background job instead of blocking. Returns {job:{job_id,status}} straight away; collect it by calling this verb again with that job_id and no query. USE THIS when a reply comes back with retry_guidance.action = "run_async" — the synchronous path is bounded at ~15s and your ask needs longer, and the async path also allows a higher max_subqueries. Fuzzy lane only; others name it in corpus.ignored_params.
job_idstringoptionalCollect a job started with async:true. Send this ALONE (no query). While status is "queued"/"running", poll again; on "complete" the full envelope is on detail.
modestringoptionalLane override. Default auto: filters-with-no-query → filter; meaning-shaped query → semantic; else fuzzy. Lanes read DIFFERENT stores at different grains — read `corpus` on the response to see which one answered.
autofilterfuzzysemanticsurfacestringoptionalWarehouse surface for the filter lane. Default interactions. Ignored by the fuzzy and semantic lanes, which report it back in corpus.ignored_params when you pass it.
interactionsdealsdeal_qualificationaudiencestringoptionalWho counts as the voice you want. Default "all". Use "customer_voice" to keep only genuine customer / prospect speech and drop investor, advisor, media, partner, vendor and competitor chatter that merely shares an account record — while STILL keeping a real buyer on such an account (someone on a live opportunity, or a scored champion / economic buyer). This applies the platform's own canonical rule server-side; it is a disjunction you cannot express with `filters`, which are ANDed. interactions only. Applied on the filter lane, and on the semantic lane with hydrate (turned on for you unless you send hydrate:false). A request that would land on the fuzzy lane, which writes its own SQL, is rerouted to semantic with hydrate and says so in rerouted_from and notes. Refused on the lexical lane, which matches exact terms.
allcustomer_voicehydratebooleanoptionalSemantic lane only. Semantic matches carry a truncated preview and no labels, so they cannot be quoted or attributed. Set true to bridge each match back to its full warehouse row — full `content` plus company, speaker title, pushback and buyer-signal columns. One row per match, containing the matched utterance, so a hit is citable. SPEAKER comes from the ATOMIC utterance that matched, not from the turn stamp: read speaker_attribution on each row. "utterance" = verified from the atomic source. "ambiguous" = two matched utterances share this row and disagree on speaker, so the speaker fields are NULL and nobody is named — check turn_speakers for who is in play. "turn" = the atomic source had no row, so the fields are the turn's FIRST member and are NOT verified. Separately, speaker_ambiguous says the row's TEXT spans more than one speaker (13.58% of turns on the tenant measured 2026-08-15) — a row can be cleanly attributed AND span two voices, so quoting the whole content as one person is still wrong there. Both fields ship on every turn-grain row including the clean ones; corpus.speaker_ambiguous_rows / corpus.speaker_unattributable_rows are the rates. speaker_ambiguous_source says where that verdict came from: "stamped" = read from the column the pipeline writes on the turn itself, "overlay" = reconstructed at read time because the pipeline recorded none for that turn. A missing record is NOT "one speaker" — permanently-excluded interactions never get one — so an unrecorded turn falls back rather than being reported unambiguous, and a "stamped" row also carries speaker_count. corpus.speaker_stamped_rows / corpus.speaker_fallback_rows are the split. Stakeholder columns (role_level, is_champion, is_economic_buyer, champion_score, inferred_persona) are derived from the turn stamp, so they are NULLED when the row is re-attributed to a different speaker rather than left describing someone else. Slower — two reads. Check corpus.hydrated_grain: "turn" is that row-per-match answer; "call" means the turn lookup failed and the rows are arbitrary turns from the right conversations (context, not the matches; also flagged corpus.degraded). Matches whose utterance belongs to no turn have no warehouse row and are counted in corpus.unresolved_matches; matches sharing one turn are corpus.collapsed_matches. Read those two directly rather than inferring a gap from a match_count/row_count delta — corpus.match_count is how many nearest neighbours the lane pulled and is BOUNDED BY limit, not a count of how many things genuinely matched. Caps at 200 distinct ids, reported as corpus.truncated.
coveragebooleanoptionalOpt into gap detection on corpus.coverage. Every response ALREADY carries corpus.coverage with max_timestamp / min_timestamp and an age histogram, computed free from the rows returned. This flag adds `gaps`: windows where the corpus HELD rows and this result set had none — the "your evidence has a hole in it" signal, as opposed to the ordinary quiet periods a plain empty-bucket scan would flag. Costs one extra bucketed read, so it is off by default. Read coverage.basis first: "matched_set" means the numbers describe your whole match set, "returned_rows" means they describe a truncated slice, and "unavailable" means coverage could not be measured — never read that as zero.
filtersarray of objectoptionalTyped predicates (ANDed — there is no OR; for the customer-voice disjunction use `audience`). Semantic mode supports only company_id / occurred_at / speaker_type, and cannot apply any of them on its theme-index fallback — check corpus.filters_applied. The filter lane supports every field the catalog advertises.
array items
fieldstringrequiredA field from search_field://list.
opstringrequiredeqneqinnot_incontainsgtgteltltebetweenis_nullnot_nullvalueanyoptionalScalar for eq/neq/gt/gte/lt/lte/contains; array for in/not_in; [low, high] for between; omitted for is_null/not_null.
order_byobjectoptionalFilter lane: order by a vocabulary field or a metric alias (e.g. "count").
fieldstringrequireddirstringrequiredascdesclimitintegeroptionalRow / match cap; max 1000. A larger value is clamped to 1000 rather than refused, and the response reports it on limit_clamped and in notes. Default is lane-dependent: 100 on the filter and semantic lanes, 50 on the fuzzy lane (where an unforced plain-language question usually lands), so pass it explicitly when the row count matters.
group_byarray of stringoptionalFilter lane: group fields (requires at least one metric).
metricsarray of objectoptionalFilter lane: aggregations (sum/avg need a numeric field).
array items
fnstringrequiredcountcount_distinctsumavgminmaxfieldstringoptionalRequired for every fn except count.
Responses
dataobjectrequiredsuccessbooleanrequiredmode_ranstringoptionalresultsarray of anyoptionalcompiledobjectoptionalfreshnessobjectoptionalcorpusobjectoptionaltimingobjectoptionalcachedbooleanoptionaldetailobjectoptionaljobobjectoptionalnotesarray of stringoptionallimit_clampedobjectoptionalrerouted_fromstringoptionalsetup_neededobjectoptionalPresent only when this workspace has no connected data source, so there is nothing to read. reason is no_sources_connected; message says so in plain words for the user; connect_url is the Connections page in the console. Give the user the message and the link. Absent when a source is connected.
reasonstringrequiredno_sources_connectedmessagestringrequiredconnect_urlstringrequirederrorobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/search/fieldsSearch Filter Fields
Read when composing typed predicates for the routed search verb: the per-surface field catalog (interactions, deals, deal_qualification) listing each field with its type, description, and the comparison operators it admits. Derived live from the warehouse schema catalog - exactly what the filter compiler validates against, so it can never drift from the accepted vocabulary.
Parameters
surfacequerystringoptionalList only this surface. Omit for every surface.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/search/overviewWorkspace Data Overview
Read when orienting on what a workspace holds before composing a search: live row counts and time spans for each queryable warehouse surface (conversations, CRM deals, per-company deal qualification), computed through the governed query gate. A surface whose read fails reports null rather than zero, so an outage is never mistaken for an empty corpus.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalConversations
/api/platform/v1/chatChat
Ask the workspace anything: opens or continues a named Chat and enqueues one Master agent turn. Use when starting an investigation over company data and connected sources. Always returns handles immediately (chat_id, run_id, stream/read/resume URLs) - subscribe to stream_url or poll read_url with wait_ms for the answer; never blocks.
Request body
application/jsonrequired
inputstringrequiredThe ask, in plain language. Becomes the Master turn user message.
chat_idstring <uuid>optionalContinue an existing Chat. Omit to open a new one.
namestringoptionalName for a NEW Chat. Omitted: auto-titled from the input.
agentstringoptionalOptional agent pin: an Amdahl library slug/uuid or workspace agent id/slug the Master turn runs as (Master privileges; the agent prompt is the specialization).
configobjectoptionalThe only knobs. No model, temperature, or stream fields exist.
depthstringoptionalquickstandarddeepactions_allowedarray of stringoptionalOutbound actions this run may invoke. Absent = ALL cataloged actions (default ON); pass a list to narrow, [] to disable.
on_questionstringoptionalask_a_human behavior: pause (interactive), auto (default), none (headless).
pauseautononeexternal_searchbooleanoptionalAllow market fan-outs (external_search.execute stays in the kit). Default false; deep depth forces true.
include_divergencebooleanoptionalFuse the divergence map on external_search.execute (else mode:evidence). Default false; deep depth forces true.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/chatsList Chats
List Chats for this workspace, newest activity first. Read when picking a Chat to continue, rendering a recents rail, or reviewing what one agent, routine, or subscription has been doing. Each entry is a lean snapshot (name, status, turn count, last activity, and how it was triggered); scope by agent_ref, routine_id, or subscription_id, filter by status, page via limit and offset.
Parameters
statusquerystringoptionalagent_refquerystringoptionalKeep only Chats this agent drove (slug or uuid — both forms of the same agent match).
routine_idquerystringoptionalKeep only Chats this routine fired.
subscription_idquerystringoptionalKeep only Chats this subscription fired.
limitqueryintegeroptionaloffsetqueryintegeroptionalResponses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/chats/{id}Get Chat
Fetch one Chat snapshot: name, status, its runs (with read handles), which runs are still in flight, and any pending human asks with their resume URLs. Read when rendering a Chat view, deciding whether to continue it, or surfacing an awaiting question to the human. Returns null for unknown or cross-workspace ids.
Parameters
idpathstringrequiredResponses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/chats/{id}Rename Chat
Rename one Chat so the thread is findable later. Use when a chat was auto-titled from its first ask and deserves a human name, or when its focus shifted. Renames the underlying conversation title in place; runs, substrate, and handles are untouched. Returns the updated chat snapshot.
Parameters
idpathstring <uuid>requiredChat UUID.
Request body
application/jsonrequired
namestringrequiredThe new chat name.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/chats/{id}/runs/{run_id}Get Chat Run
Read one Chat run snapshot: status, pause payload when awaiting input, the answer envelope (answer_text plus the ordered content_blocks with query and snapshot data), answered asks, and usage. Read when polling a started Chat for its result; pass wait_ms (max 30000) to long-poll until the run settles instead of tight-looping. Pass include=events to also get the persisted activity trace for transcript replay.
Parameters
idpathstringrequiredChat (conversation) UUID.
run_idpathstringrequiredRun (turn) UUID from the START response.
wait_msqueryintegeroptionalOptional long-poll budget in ms; hard-capped at 30000.
includequerystringoptionalOptional: 'events' includes the persisted activity trace (parent + sub-agent tool calls, capped) so a reloaded transcript can replay what the run did.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/chat/preferencesGet Chat Preferences
Fetch the calling user's saved Chat settings for this workspace: their default run-config knobs (depth, on_question, external_search, include_divergence, actions_allowed) and their agent block list. Read when opening the Chat settings surface to seed its controls. Returns defaults when nothing is saved yet.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/chat/preferencesUpdate Chat Preferences
Save the calling user's Chat settings for this workspace: their default run-config knobs and/or their agent block list (the agents the Master may not delegate to). Use when the user changes a control in the Chat settings surface. A top-level PATCH — only the keys you send change; the block list applies to new Chat turns server-side.
Request body
application/json
run_configobjectoptionalDefault Chat run knobs. Replaces the stored run_config wholesale when sent.
depthstringoptionalquickstandarddeepactions_allowedarray of stringoptionalon_questionstringoptionalpauseautononeexternal_searchbooleanoptionalinclude_divergencebooleanoptionalblocked_agentsarray of stringoptionalAgent slugs / refs the Master may NOT delegate to for this user. Empty = every agent usable.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/researchStart Research
Start a research job: a read-only investigation of one question over this workspace's own calls, CRM accounts and deals that settles on one cited answer. Use when a question needs several reads and a written synthesis that a single search cannot give. Returns a research_id at once; read it with wait_ms until it settles. The job may pause with a clarifying question, and at its budget it finishes with a partial answer marked partial.
Request body
application/jsonrequired
questionstringrequiredThe question, in plain language.
contextstringoptionalOptional background the job should know: who the answer is for, what you already know, the decision it feeds. Read as context, never as the question.
allow_questionsbooleanoptionalAllow the job to pause and ask you a clarifying question when the question is ambiguous in a way that changes the answer. Default true. Set false when nobody can answer; the job then proceeds on its best reading and states the assumption.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/research/{id}Get Research
Read one research job by id: its status (running, needs_input, complete, failed or canceled), the clarifying question when it is waiting on you, and once complete the answer with the evidence blocks it cites and whether it is partial. Read when polling a job you started; pass wait_ms (max 30000) to wait until it settles instead of looping fast.
Parameters
idpathstring <uuid>requiredThe research_id research.start returned.
wait_msqueryintegeroptionalOptional long-poll budget in ms, hard-capped at 30000. Blocks until the job is complete, failed, canceled or needs_input, then returns the same body.
Responses
dataobjectrequiredresearch_idstringrequiredchat_idstringoptionalstatusstringrequiredrunningneeds_inputcompletefailedcanceledquestionstring | nulloptionalquestion_for_youobject | nulloptionalSet while status is needs_input: answer it with research.respond.
answerobject | nulloptionalSet once status is complete: text (markdown), partial, partial_reason, content_blocks (the evidence), follow_ups.
answered_questionsarray of anyoptionalerrorstring | nulloptionalcreated_atstringoptionalstarted_atstring | nulloptionalcompleted_atstring | nulloptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/research/{research_id}/respondAnswer Research Question
Answer the clarifying question a research job paused on, and resume the job. Use when a research read shows status needs_input: send your answer as text, or pass option_id to pick one of the listed choices. The job resumes from where it paused and reads as running.
Parameters
research_idpathstring <uuid>requiredThe research_id research.start returned.
Request body
application/jsonrequired
answerstringrequiredYour answer. For a multiple-choice question, the label of your choice works.
option_idstringoptionalOptional: the id of a listed choice (question_for_you.options). `other` sends the answer as a write-in.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/research/{research_id}/cancelCancel Research
Stop a research job that is still running or waiting on its clarifying question. Use when the answer is no longer needed or the question was wrong; start a new job with the corrected question instead. A job that already finished is left as it is.
Parameters
research_idpathstring <uuid>requiredThe research_id research.start returned.
Request body
application/json
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalObservability
Audit logs and execution telemetry.
/api/platform/v1/evals/runRun Eval
Grade content against your workspace data and get a run id to poll. Use when scoring a prompt or a drafted message: pass inputs (prompt and/or message, optional audience, account, mode=rewrite|advisory|gate) and context.facts, what you know first-hand about the recipient. A blinded judge scores relevant positioning, grounding, verified specifics, differentiation and CTA clarity against real customer quotes. Returns eval-run://<id> plus a console_url to give the user.
Request body
application/json
evalstringoptionalEval slug or id. Default 'prompt-and-message-eval'. Discover options + their input fields via eval://list.
inputsobjectoptionalRun inputs for the eval's declared fields. prompt-and-message-eval requires at least one of prompt / message, and also accepts audience (a seniority or title to scope the cohort), account (the company it is going to — retrieves what THAT account said), and mode: rewrite (the default, a full improved prompt and message), advisory (anchored suggestions against what you wrote), or gate (grade ONLY what you sent and stop — read the verdict from eval-run://<id>/gate). Read the exact schema from eval://<slug>.
reusestringoptionalcached (default) reuses an active run for the same target (fingerprint); force always starts a fresh run.
cachedforceevidence_from_runstringoptionalRun id whose frozen customer quotes this run should be graded against, instead of retrieving fresh ones. Use to A/B two drafts: without it each run retrieves its own evidence, so the score difference mixes your edit with a different set of quotes. The source run must be in this workspace and have reached retrieval; the report states the pin and how old that evidence is.
include_externalbooleanoptionalAdd public web/market evidence to the grade (default false). One web fan-out runs and its snippets join the pool tagged EXTERNAL — the tier that backs market claims ("analysts report"), never customer voice. Needs the external_search:execute scope (refused without it, never silently dropped) and cannot combine with a pinned run — a pin holds the evidence frozen, and its pool already carries any external quotes it was graded on.
external_capintegeroptionalHow many external quotes to retrieve when include_external is set. Clamped to 1..25; default 15. The ack echoes the clamped value as external.cap.
generate_modelstringoptionalSTAGING ONLY, ignored in production: the Anthropic model id the writer (generate stage) uses on this run, for a writer bake-off without a redeploy. The judge never moves. Not part of the content address, so pass reuse=force or a cached run of another arm returns.
generate_thinkingstringoptionalSTAGING ONLY, ignored in production: how deeply the writer thinks on this run. Omitted uses the shipped depth (medium); default sends no thinking field. Same reuse=force caveat as generate_model.
offlowmediumhighdefaultcandidate_from_runstringoptionalRun id whose improved version this run should GRADE, instead of writing a new one. Use to re-run the judge over fixed text — the run makes no generate call, so a score difference between two such runs is the judge, not the writer. Pass reuse=force when sampling: two identical pinned requests share one content address, so on the default reuse=cached the second is served the first run and an N-sample spread reads as zero. Also pins that run's quotes (the citations in the frozen text address them), so pass the same id to evidence_from_run or omit it. Refused if this run submits different copy or a different mode than the pinned candidate was written for.
scopeobjectoptionalNarrow WHICH conversations the grade speaks for. Use when the verdict is about a cohort rather than your whole corpus. filters are AND-joined over interactions (utterance grain), deals or deal_qualification (account grain - these resolve to the matching accounts and scope the conversations to them). A slice below the evidence floors (3 speakers, 25 utterances, 2 companies) abstains to whole-corpus grading unless allow_thin_evidence, which grades it anyway and flags below_floors.
filtersarray of objectoptionalTyped predicates, AND-joined. At most 25. Read the filterable fields and their operators per surface from the search field catalog.
array items
surfacestringoptionalinteractionsdealsdeal_qualificationfieldstringrequiredopstringrequiredeqneqinnot_incontainsgtgteltltebetweenis_nullnot_nullvalueanyoptionalaudiencestringoptionalcustomer_voice restricts the slice to buyer speech; all (default) leaves it unrestricted.
allcustomer_voiceallow_thin_evidencebooleanoptionalGrade the slice even when it falls below the evidence floors, instead of abstaining to the whole corpus. The report carries the real counts and below_floors: true.
contextobjectoptionalOptional. What you know that the corpus cannot: facts, the CALLER evidence tier. A claim a fact states is not graded as fabricated; a claim that inflates one still is; a fact is never counted as a customer quote. Only facts is accepted here (rules and voice_examples belong to messages.optimize); any other key is refused. Graded by evals with an improvement_loop grader (prompt-and-message-eval); refused on any other.
factsarray of stringoptionalWhat you know first-hand about the recipient or situation that no call recording holds, one fact per entry, such as "Met Priya at the GTM dinner on Sep 24; she said her SDRs rewrite every AI draft". Graded as caller-supplied evidence (tier CALLER, not a customer quote): a claim a fact states is not marked fabricated, a claim that inflates one still is. Up to 10, each up to 1000 characters once trimmed.
recipientobjectoptionalBeta, per workspace: refused unless the workspace has recipient evidence on. Who the message is for: any of email (up to 320 characters), name and company (up to 200 each), domain (up to 253), linkedin_url (up to 300) and attributes. attributes is a flat map of text, number, true/false or null values, such as a whole Clay or CSV row: up to 100 keys of 100 characters, each text value up to 1000 characters. Any other key is refused, and so is an attribute key that names a business, user, tenant or workspace id.
emailstringoptionalnamestringoptionalcompanystringoptionaldomainstringoptionallinkedin_urlstringoptionalattributesmap<string, string | number | boolean | null>optionalrecipient_notesstringoptionalBeta, per workspace: refused unless the workspace has recipient evidence on. Free text about the recipient, such as your own research or a pasted CRM note. Up to 10000 characters.
message_typestringoptionalBeta, per workspace: ignored unless the workspace has recipient evidence on. The kind of message: cold, re-engage, follow-up, deal. follow-up is a reply in a thread that is in progress.
Responses
dataobjectrequiredsuccessbooleanrequiredrun_idstringoptionalreusedbooleanoptionalstatusstringoptionaleval_slugstringoptionaleval_versionstringoptionalresourcestringoptionalpollobjectoptionalHow to read the verdict back: tool (get_eval_run, the MCP tool; action status is the same read under its old MCP name), run_id, resource, report_resource, rest (method, url, report_url: the same two reads as REST paths, url already carrying wait_ms), and wait_ms_max - the largest long-poll budget the read honours, so a caller blocks once instead of looping. The run is queued when this returns; it is not the answer yet.
console_urlstringoptionalLink to this run's report in the workspace console. Give it to the user verbatim so they can open the run — run_id and resource are machine addresses they cannot follow. Works immediately (it shows the live grading trail, then the report). Absent when it could not be resolved; never invent one.
recipient_receivedobjectoptionalBeta, per workspace. Present only when the request carried a recipient field: fields (the request paths received), attribute_count, notes_chars and message_type. Names and counts only, never a value.
evidence_run_idstringoptionalBeta, per workspace. Present only when the request carried a recipient field: this run's recipient-evidence id (a UUID), the key of its lookup log. A reused run gives the id it stored, or none. run_id stays the eval run id.
ignored_paramsarray of stringoptionalPresent only when the request sent message_type to a workspace without recipient evidence: the params ignored. The run is the one the request would get without them.
setup_neededobjectoptionalPresent only when this workspace has no connected data source, so there is nothing to read. reason is no_sources_connected; message says so in plain words for the user; connect_url is the Connections page in the console. Give the user the message and the link. Absent when a source is connected.
reasonstringrequiredno_sources_connectedmessagestringrequiredconnect_urlstringrequirederrorobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/evalsList Evals
List every eval available to this workspace (prompt-and-message-eval, plus any you authored). Use when choosing which grader to run or rendering an eval picker. Returns slug, name, version, description, input_schema, and case count per eval; fetch eval://<slug> for the full cases + graders.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/evalsCreate Eval
Create a custom eval — a configured grading pipeline (declared input fields + cases + graders) stored for your workspace. Use when the built-in graders don't fit and you want your own scored report card. Pass slug, name, input_schema, and cases; the whole definition is validated and you get back the new eval's slug + id, or a structured validation_failed with the exact field errors. Built-in slugs are reserved.
Request body
application/jsonrequired
slugstringrequiredkebab-case identifier, unique in your workspace.
namestringrequireddescriptionstringoptionalversionstringoptionalsemver, default 1.0.0.
input_schemaobjectoptionalDeclared run-input fields ({ fields: [...] }).
casesarray of objectrequiredCases: each a subject + its graders.
reliabilityobjectoptionalRequired for any grader that reports a MAGNITUDE (judge / evidence_judge / improvement_loop): the measured flip rate and the floor a reported lift must clear. Validation refuses without it, so declare it rather than discovering the refusal.
Responses
dataobjectrequiredsuccessbooleanrequirederrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/evals/{slug}Get Eval
Fetch one eval by slug or id, with its input_schema and full cases — each case subject (a provided input field or a generated target) and the graders (rule checks, system-of-record anchors, judge and evidence-judge rubrics) applied to it. Use when building a run form or inspecting what a grader measures. Returns null for an unknown id.
Parameters
slugpathstringrequiredResponses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/evals/{slug}Update Eval
Revise one of your workspace's authored evals in place. Use when tuning a custom grader's rubric, thresholds, input fields, or cases. Pass the slug plus the fields to change; the merged definition is re-validated before it is stored, returning the slug + version or a structured validation error. Built-in evals cannot be edited.
Parameters
slugpathstringrequiredThe authored eval to revise.
Request body
application/json
namestringoptionaldescriptionstringoptionalversionstringoptionalinput_schemaobjectoptionalcasesarray of objectoptionalreliabilityobjectoptionalRequired for any grader that reports a MAGNITUDE (judge / evidence_judge / improvement_loop): the measured flip rate and the floor a reported lift must clear. Validation refuses without it, so declare it rather than discovering the refusal.
Responses
dataobjectrequiredsuccessbooleanrequirederrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/evals/{slug}Delete Eval
Archive one of your workspace's authored evals so it no longer lists or runs. Use when retiring a custom grader you no longer need; the slug frees up for re-use and a soft archive keeps the audit trail. Pass the slug. Built-in evals cannot be archived. Returns whether a matching eval was found and archived.
Parameters
slugpathstringrequiredResponses
dataobjectrequiredsuccessbooleanrequirederrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/evals/validateValidate Eval
Dry-run validate a custom eval definition without storing it. Use when building an eval in a form and you want author-time feedback before saving: it runs the same structural checks as creating one (input-schema shape, subject-to-input-field integrity, per-grader config) and returns valid plus a flat list of path + message errors. Never writes anything.
Request body
application/jsonrequired
slugstringrequirednamestringrequireddescriptionstringoptionalversionstringoptionalinput_schemaobjectoptionalcasesarray of objectrequiredreliabilityobjectoptionalRequired for any grader that reports a MAGNITUDE (judge / evidence_judge / improvement_loop): the measured flip rate and the floor a reported lift must clear. Validation refuses without it, so declare it rather than discovering the refusal.
Responses
dataobjectrequiredvalidbooleanrequirederrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/evals/exportExport Eval Runs
Pull this workspace's graded eval runs as flat rows for analysis elsewhere. Use when you need many runs at once rather than one verdict: filter by eval, version, date range, status and verdict, and read back a row per run carrying the score alongside the fields that qualify it. Pass count_only to learn how many runs match — and how many instrument versions they span — before pulling them.
Request body
application/json
evalstringoptionalEval slug or id. Default 'prompt-and-message-eval'. Required in effect — columns and versions are per-eval, so one export covers one eval.
eval_versionsarray of stringoptionalRestrict to these eval_version values. Omit for every version, but read the version spread on the count first: scores from different versions are produced by different instruments and are not comparable.
fromstringoptionalISO-8601 lower bound on created_at (inclusive).
tostringoptionalISO-8601 upper bound on created_at (exclusive).
statusesarray of stringoptionalRestrict to these run statuses. Omit for every status.
verdictsarray of stringoptionalRestrict to these verdict buckets. Omit for every bucket. `ungraded` is a pseudo-bucket for runs carrying no verdict at all - the ones whose overall_score cell is empty - so you can isolate or exclude them; it is not a value any run stores.
count_onlybooleanoptionalReturn only the count block (total, cap, version spread, date range) without the rows. Cheap enough to call on every filter change.
redact_quotesbooleanoptionalBlank the verbatim customer quotes in the exported verdict. Affects the raw JSONL download only; the flat rows never carry quote text.
allow_truncationbooleanoptionalProceed when more than 5000 runs match, taking the most recent 5000. Without it an over-cap response is refused rather than silently cut. The .csv / .jsonl file downloads stream and have no cap, so they never need this.
limitnumberoptionalTake at most this many runs, most recent first (max 5000 here). A deliberate top-N, not a truncation: it satisfies the cap on its own, so a limited call is never refused for matching a larger set.
Responses
dataobjectrequiredsuccessbooleanrequiredeval_slugstringoptionalcountobjectoptionaltotal, truncated, max_rows, versions (distinct eval_version values in the set), first_run_at and last_run_at. Read `versions` before pooling: more than one means the rows were scored by more than one instrument.
rowsarray of anyoptionalOne flat row per run. Every score ships with the fields that qualify it — overall_score with applicable, lift with lift_reportable, the submitted score with input_simulated. A null score is an empty value, never zero.
scopestringoptionalself (your own runs) or workspace (every run; workspace admins only).
errorobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/eval-runs/{id}/feedbackGet Eval Run Feedback
Fetch what was reported about one run after the fact: whether its recommendation was applied, how each writer knows, and whether the server could confirm it. Read when deciding whether a past recommendation already landed, or before reporting one as unused. verified is three-state — false means checked and absent, null means nothing checked, and the two are different answers.
Parameters
idpathstringrequiredResponses
dataobjectrequiredstatusstringrequiredThe RUN status, mirrored at the root so `data.status` resolves here as it does on every other eval read. The terminal success value is `complete` — never `completed`. It also reads meaningfully here: feedback on a run still `running` is about a recommendation that does not exist yet.
queuedrunningcompletefailedcanceledrun_idstringrequiredfeedbackarray of objectrequiredOne entry per (actor, subject). An actor re-reporting on the same subject replaces their earlier entry; two actors disagreeing appear as two entries, on purpose.
summaryobjectrequiredTallies only — no rate. A ratio over the handful of rows one run collects would be arithmetic on a denominator of two; the rate lives on eval://<slug>/adoption, where it has a population and an abstain floor.
errorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/eval-runs/{id}/feedbackRecord Eval Feedback
Report what happened AFTER an eval run — whether its recommendation was applied, and how you know. Call when you have acted on a report: rewritten a prompt, filed the improved copy, or decided against it. Requires evidence (did_it_myself | reported_to_me | inferred); report only what you observed, never what you assume a person did. Pass applied_to and the server checks the target actually contains it.
Parameters
idpathstringrequiredAlias for eval_run_id, carrying the REST path segment. Pass eval_run_id; this exists so POST /eval-runs/:id/feedback resolves.
Request body
application/jsonrequired
eval_run_idstringoptionalThe run this feedback is about.
outcomestringrequiredWhat happened to the recommendation. `unsure` is a real answer and is excluded from both halves of any adoption rate — prefer it to guessing.
usedused_with_editsnot_usedunsureevidencestringrequiredHow you know. `did_it_myself` = you performed the action in this session (the only value that can be verified). `reported_to_me` = a person told you. `inferred` = you concluded it; honest, but excluded from published rates. Never claim first-hand knowledge of something a person did outside your session.
did_it_myselfreported_to_meinferredsubject_keystringoptionalWhat the feedback is about. Omit (or '__run__') for the run as a whole; otherwise a stable key naming one recommendation, so the same recommendation across runs can be counted together.
what_happenedstringoptionalFree text, unconstrained. Say what you actually did, or why the recommendation was not usable — the reason is worth more than the verdict.
applied_toobjectoptionalWhere it was applied, e.g. { "kind": "agent", "id": "<uuid>" }. Recognised kinds (agent, routine, knowledge_doc) are dereferenced and checked; anything else is stored unverified rather than rejected.
Responses
dataobjectrequiredsuccessbooleanrequiredfeedback_idstringoptionalcreatedbooleanoptionalFalse when this replaced your earlier verdict on the same subject.
verifiedboolean | nulloptionalThree-state. true = the server found the recommendation in applied_to. false = it checked and it is absent. null = nothing was checked. null and false are different answers and must not be collapsed.
verification_notestring | nulloptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/evals/{slug}/kpiGet Eval KPI
Fetch the score trend for one eval as a time series over its completed runs: mean submitted-side score per day/week/month, the improved-side ceiling, refusal rate, and the current-vs-prior-window delta. Read when reporting whether writing quality is improving over time. The trend tracks the grade of what callers SUBMITTED, read from verdict.headline so it means one thing across an eval_version bump; windows under 3 scored runs abstain with a named reason rather than assert a mean.
Parameters
slugpathstringrequiredwindow_daysqueryintegeroptionalTrailing window to aggregate (default 90, clamped 7-365).
granularityquerystringoptionalResponses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/evals/{slug}/adoptionGet Eval Adoption
Fetch whether this eval changed anything: how many runs produced a recommendation, how many were reported on, how many were applied, and how often the server could confirm it. Read when asked whether an eval is worth running, or alongside the KPI — a rising score with no adoption means callers improved on their own. Every count states its denominator; the rate abstains rather than assert one on a thin window.
Parameters
slugpathstringrequiredwindow_daysqueryintegeroptionalTrailing window (default 90, clamped 7-365).
Responses
dataobjectrequiredadoptionobjectrequiredCounts, then the rate. `runs_completed` >= `runs_eligible` (refusals and gate-mode runs recommended nothing) >= `runs_with_signal` (someone reported) >= `runs_adopted`. `adoption_rate` is runs_adopted / runs_with_signal, or null with an `abstain_reason` — an abstain (`thin_window`, `no_signal`, `no_eligible_runs`) is never a zero. `counted_evidence` names which evidence values the rate counted; `inferred` is never one of them.
errorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/eval-runsList Eval Runs
List this workspace eval runs, newest first, filterable by eval_slug and status (queued|running|complete|failed|canceled). Use when rendering an eval run history or checking whether a report card has finished. Returns lean rows; fetch eval_run://<id> for the full verdict.
Parameters
eval_slugquerystringoptionalstatusquerystringoptionallimitqueryintegeroptionaloffsetqueryintegeroptionalincludequerystringoptionalResponses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/eval-runs/{id}Get Eval Run
Fetch one eval run by id: its status and, once complete, the verdict — verdict.headline carries the grade of the copy the caller SUBMITTED beside the grade of the version the eval wrote, plus the pass/partial/fail bucket and per-case + per-grader detail. Use when polling a run started via evals.run or rendering a report card; pass wait_ms (max 30000) to block until it finishes instead of tight-looping. Returns null for a missing id.
Parameters
idpathstringrequiredwait_msqueryintegeroptionalOptional long-poll budget in ms, hard-capped at 30000. The read blocks until the run reaches complete / failed / canceled, then returns the SAME body an immediate poll would have returned.
Responses
dataobjectrequiredstatusstringrequiredThe run status, mirrored at the ROOT so `data.status` resolves here exactly as it does on POST /evals/run and on /gate, /drafts, /improvement, /evidence. Identical to `run.status`, which is retained. The terminal success value is `complete` — never `completed`.
queuedrunningcompletefailedcanceledrunobjectrequiredThe run row. Null is returned for a missing or cross-tenant id — the two are deliberately indistinguishable.
idstringrequiredeval_slugstringoptionaleval_versionstringoptionalstatusstringrequiredLifecycle state. The terminal success value is `complete` — never `completed`.
queuedrunningcompletefailedcanceledverdictobjectoptionalThe graded report card. Null until the run reaches `complete`.
error_messagestring | nulloptionalcreated_atstringoptionalfinished_atstring | nulloptionalregradeobjectoptionalPresent exactly when this run’s evidence pool is pinnable: the parameter set that holds it fixed for the next call. Absent otherwise, never partial.
errorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/eval-runs/{id}/compare/{other_id}Compare Eval Runs
Compare two eval runs and report whether their difference is a result. Read when A/B-ing two drafts: each run retrieves its own quotes unless pinned, and a bumped eval_version re-means the stored score, so a raw difference can be either rather than your edit. Returns both verdicts, the shared-evidence overlap, any version boundary crossed, and a delta ONLY when the pair supports one — otherwise why not, and how to fix it.
Parameters
idpathstringrequiredThe baseline run id.
other_idpathstringrequiredThe candidate run id to compare against it.
Responses
dataobjectrequiredcomparisonobjectrequiredThe comparison. Null is never returned — a missing or cross-tenant id on either side resolves the whole read to a 404, so a compare cannot be used to probe which run ids exist in another workspace.
aobjectrequiredThe baseline run, with its own status and scores.
bobjectrequiredThe candidate run, same shape.
evidence_overlapobjectrequiredHow much of the two runs’ quote pools was shared.
delta_attributablebooleanrequiredWhether the difference can be attributed to the change in graded content. FALSE whenever shared evidence could not be DEMONSTRATED — including when it could not be measured. A comparison that cannot prove its controls is not a weak finding, it is not a finding.
score_deltanumberoptionalPRESENT ONLY when `delta_attributable`. Absent means the pair supports no delta — read `delta_withheld_reason`, NOT "no change".
submitted_score_deltanumberoptionalHow much the caller’s OWN copy moved, on [0,1] — the number an A/B is asking for. Derived from the improvement report rather than `overall_score`, so it means the same thing on both sides of a version boundary. Held to the same noise floor.
submitted_score_15_deltanumberoptionalThe same movement on the [1,5] axis every human-facing score is stated on.
delta_withheld_reasonstringoptionalWhy no delta shipped. Typed because the causes need DIFFERENT things from the reader and most are not fixed by pinning evidence — see `remedy`, which is keyed to this value.
not_scoredevidence_not_controlledartifact_type_mismatchstop_reason_mismatchinside_noise_flooreval_version_boundarynoise_floornumberoptionalThe floor |delta| had to clear, present whether or not it cleared — so a caller can see how close it came.
version_boundaryobjectoptionalThe eval_version transitions this pair straddles, with each one’s `kind`. Present whenever the two runs carry different versions. `recorded: false` means the ledger has no entry for one of them — "cannot say", not "nothing crossed".
caveatstringrequiredPlain-English statement of what this comparison does and does not support.
remedystringoptionalHow to get a reportable delta out of these two runs. Present exactly when `delta_withheld_reason` is.
errorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/eval-runs/{id}/gateGet Eval Run Gate
Fetch the machine verdict on the copy the caller SUBMITTED: passed, checks_passed/checks_total, score_15 against its threshold, and which failed. Use when wiring an eval into a pipeline as a send/hold decision: gate is the send/hold signal; overall_score and lift follow the rewrite. Pass wait_ms (max 30000) to block until the run settles. gate is null while running or when the run refused (not_applicable_reason says why); a refusal is not a fail.
Parameters
idpathstringrequiredwait_msqueryintegeroptionalOptional long-poll budget in ms, hard-capped at 30000. Blocks until the run reaches complete / failed / canceled, then returns the same body an immediate poll would.
Responses
dataobjectrequiredrun_idstringrequiredeval_slugstringoptionaleval_versionstringoptionalThe eval version this run was graded at. Gate thresholds are only comparable within one version — see the eval version ledger.
statusstringrequiredThe run status, at the ROOT of the body — unlike GET /eval-runs/{id}, which nests it under `run`. The terminal success value is `complete` — never `completed`.
queuedrunningcompletefailedcanceledgateobjectrequiredThe machine verdict on the copy the caller SUBMITTED. NULL means this run has no submitted-side grade to gate on — still running, refused (see `not_applicable_reason`), or a grader mix that produced none. Never treat null as a fail: `passed: false` is a verdict on the copy and a refusal is emphatically not one.
not_applicable_reasonstringoptionalPresent exactly when the run refused to grade, saying why. A refusal is not a fail.
error_messagestringoptionalPresent only on a failed run.
regradeobjectoptionalPresent exactly when this run’s evidence pool is pinnable: the parameter set that holds it fixed for the next call, so a held draft’s edit is graded against the same quotes. Absent otherwise, never partial.
errorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/eval-runs/{id}/reportGet Eval Run Report
Fetch the canonical report card for one eval run as ready-to-paste markdown: your draft score vs the improved one, the bar, the transition, cited customer quotes, and any confidence caveat. Read when presenting a result to a person — paste the markdown as written rather than summarising it, so the numbers reported are the ones Amdahl computed.
Parameters
idpathstringrequiredResponses
dataobjectrequiredstatusstringrequiredThe run status, mirrored at the root so `data.status` resolves here as it does on every other eval read. Identical to `report.status`. The terminal success value is `complete` — never `completed`.
queuedrunningcompletefailedcanceledreportobjectrequiredThe canonical report card. `markdown` is a string on every run state; `headline` and `findings` are null until the run reaches `complete`.
run_idstringrequiredeval_slugstringrequiredstatusstringrequiredmarkdownstringrequiredheadlineobject | nulloptionalfindingsobject | nulloptionalWhat did not hold up on the submitted copy: `failed[]` (one entry per rubric line missed, each `{dimension, reasoning}`), `contradicting[]`, and `truncated`. `dimension` is the rubric line’s STEM — the clause before the first colon — because the card renders it as a bold lead-in to that line’s `reasoning`, which carries the substance. The gate read names the SAME lines IN FULL: `gate.dimensions_failed` on GET /eval-runs/{id}/gate spells out each line’s whole text, because it travels without a reasoning field and the stem alone would drop the criterion. Same lines, two renderings, on purpose — a diff between the two strings is expected, not a bug. The two can also differ in LENGTH: `failed[]` cuts at 8 and says so via `truncated`, while `gate.dimensions_failed` is uncapped. On the shipped five-line rubric that cut never binds; a tenant-authored eval with a longer rubric can reach it.
errorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/eval-runs/{id}/evidenceGet Eval Run Evidence
Fetch the frozen customer-quote pool one eval run graded against: every quote with its id, tier, provenance and whether it reached the graded prompt, plus when the pool was frozen. Read when drafting an edit to re-grade against the SAME evidence (then pass evidence_from_run on the next run so the score delta is the edit, not a retrieval change), or when auditing what a verdict was scored on. evidence is null when the run never recorded a pool.
Parameters
idpathstringrequiredResponses
dataobjectrequiredrun_idstringrequiredeval_slugstringrequiredstatusstringrequiredThe run status, at the root as on every eval read. The terminal success value is `complete` — never `completed`.
queuedrunningcompletefailedcanceledevidenceobject | nulloptionalThe frozen quote pool this run was graded against. Null means no pool was recorded (the run predates evidence recording, or retrieval never froze a set) — a fact, not an error, and not pinnable.
regradeobjectoptionalThe pasteable `evidence_from_run` params for re-grading an edit against this same pool. Present exactly when the pool is pinnable on a settled run; absent otherwise.
errorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/eval-runs/{id}/draftsGet Eval Run Drafts
Fetch both graded artifacts of one eval run side by side: the prompt and message you submitted, the improved versions the eval wrote, each with its score, checks fraction and per-rubric-line reasoning. Read when comparing your draft against the rewrite line by line — this is the only read that carries the texts themselves. Cited quotes stay on the evidence read.
Parameters
idpathstringrequiredResponses
dataobjectrequiredrun_idstringrequiredeval_slugstringrequiredeval_versionstring | nulloptionalstatusstringrequiredThe run status, at the root as on every eval read. The terminal success value is `complete` — never `completed`.
queuedrunningcompletefailedcanceledsubmittedobjectoptionalThe prompt and message you sent, each with its score and per-line verdicts.
improvedobjectoptionalThe rewritten versions. `prompt` / `message` are null with an `absent_reason` on a run that correctly wrote no rewrite — never an empty string, never a zero score.
errorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/eval-runs/{id}/improvementGet Eval Run Improvement Report
Fetch the before/after improvement report of one eval run in a bounded shape: the per-facet graded pair with scores, per-rubric-line verdicts, cited quotes and the not-applicable counters, plus lift, transition, suggestions and confidence. Read when the full run payload is too large for your transport and you need the facets — no other narrow read carries them.
Parameters
idpathstringrequiredResponses
dataobjectrequiredrun_idstringrequiredeval_slugstringoptionaleval_versionstring | nulloptionalstatusstringrequiredqueuedrunningcompletefailedcanceledimprovementobject | nullrequiredThe before/after report. `facets[]` is the per-facet graded pair; each side carries score_15, checks_passed / checks_total, dimensions, quotes and the not-applicable counters. NULL — never an empty object, and never an empty `facets` — when the run produced no report (mode "gate", or any refusal); read `absent_reason` beside it.
omittedarray of objectrequiredThe fields this bounded shape leaves on the full-row read, each with why and where. Present on EVERY response, so the omission is readable rather than inferred from a payload that came back smaller than expected.
array items
fieldstringrequiredwhystringrequiredreadstringrequiredThe URI of this run that still carries the field.
absent_reasonstring | nulloptionalPresent only when `improvement` is null: why the run produced no report.
errorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/grader-kindsList Grader Kinds
List every grader kind an eval can use: deterministic (rule checks, no LLM), sor_anchored (compare a figure to a ground-truth warehouse count), and judge (LLM scores a rubric). Use when authoring or explaining an eval. Returns id, name, description, and whether the kind makes an LLM call.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/grader-kinds/{id}Get Grader Kind
Fetch one grader kind by id (deterministic / sor_anchored / judge). Use when rendering a single grader-kind detail card. Returns its name, description, and llm-usage flag, or null for an unknown id.
Parameters
idpathstringrequiredResponses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalAssets
Artifacts - unified output storage with version history and hierarchy.
/api/platform/v1/messages/optimizeOptimize Outbound Message
Write one outbound email or LinkedIn message in the sender's own voice from context (facts, rules, voice_examples) and return it with a line on what changed. Use when an outbound message needs writing or improving before it is sent. message (a draft) is optional; sent with facts or rules, it is the base. What the input cannot back is left out and listed in omitted (on_missing_input ask returns questions instead). evidence: "workspace" grades against synced conversations (slower). Sends nothing.
Request body
application/json
contextobjectoptionalWhat the message is written FROM, and the quality of the message follows the quality of this. facts are what the sender knows first-hand about the recipient: who they are, why write now, what is offered, the proof that may be cited. rules are what the message must and must not do, including the ask. voice_examples are the sender’s own messages, matched and never copied. keep_cta, max_words, banned_phrases and banned_punctuation are checked exactly. If all you have is the user’s own request, pass it as a fact rather than sending nothing. Blank entries are dropped and any other key is refused.
rulesarray of stringoptionalHard constraints on the rewrite, such as "no pricing". They steer the rewrite and are not rewritten or quoted in the message. Up to 20, each up to 500 characters once trimmed.
voice_examplesarray of stringoptionalMessages the sender wrote themselves. The rewrite matches the sender's voice and does not copy their words. Up to 5, each up to 4000 characters once trimmed.
factsarray of stringoptionalWhat you know first-hand about the recipient or situation that no call recording holds, one fact per entry, such as "Met Priya at the GTM dinner on Sep 24; she said her SDRs rewrite every AI draft". Graded as caller-supplied evidence (tier CALLER, not a customer quote): a claim a fact states is not marked fabricated, a claim that inflates one still is. Up to 10, each up to 1000 characters once trimmed.
keep_ctabooleanoptionaltrue to keep the draft's closing ask (its last sentence that asks for something) word for word. Checked exactly on every rewrite; the rewrite works on the rest of the message.
max_wordsintegeroptionalThe most words the returned message may have, checked exactly on every rewrite. 10 to 1000.
banned_phrasesarray of stringoptionalWords or phrases the rewrite leaves out, such as "just" or "reaching out", matched as whole words in any case and checked exactly on every rewrite. Up to 30, each up to 60 characters once trimmed.
banned_punctuationarray of stringoptionalPunctuation the rewrite leaves out: any of colon, semicolon, exclamation. Checked exactly on every rewrite; a colon in a time like 2:30pm or in a link does not count.
messagestringoptionalOptional. A draft, verbatim. Sent with context.facts, context.rules or a recipient, it is the starting point of the message. It is the sender's own word, so what it states may be stated, and its substance, order, layout and voice are kept. Facts add to it and rules shape it. A rule that needs a fact neither the draft nor a fact gives is listed in omitted, or asked as a question with on_missing_input ask. A draft sent with no facts, no rules and no recipient is improved instead, with a draft_improved_no_context note. Put what you know in context.
channelstringoptionalWhere the message will be sent. Send it: with no draft there is nothing to infer it from, and the channel sets the length and format the message is written and judged against. linkedin means a connection note, which carries no sign-off.
emaillinkedinon_unsupportedstringoptionalWhat to do with a claim in the draft that nothing in the workspace conversations backs. "flag" (the default) keeps it and lists it in unsupported_claims; "remove" lets the rewrite soften or cut it. Only checked when the workspace has connected conversations.
flagremoveevidencestringoptionalOptional, default off. workspace grades the draft against what this workspace's customers said in its synced conversations (positioning, grounding, specifics, differentiation), which is slower; off is a fast style pass. Where no conversations can be read the run is graded as off and evidence.reason says why.
workspaceoffon_missing_inputstringoptionalWhat to do when the input cannot back everything the message should say. "best_effort" (the default) writes the best message the input supports and lists what it could not do in omitted, each with why; nothing is invented. "ask" writes nothing it would have to guess and returns outcome needs_input with ask, the questions only the sender can answer.
best_effortaskinclude_triesbooleanoptionalOptional, default false. When true, the result also carries `tries`: every version the run scored or checked, the draft first, each with its text, the optimizer's own score and human_tone (1 to 5), the dimensions it aimed at, and which one was returned. It shows how the draft progressed. Those scores come from the judge the rewrites were optimized against, so `lift`, not them, is the before-and-after. A version written from context is checked, not scored: its score can be null, and `checks_open` counts the checks it still fails.
recipientobjectoptionalBeta, per workspace: refused unless the workspace has recipient evidence on. Who the message is for: any of email (up to 320 characters), name and company (up to 200 each), domain (up to 253), linkedin_url (up to 300) and attributes. attributes is a flat map of text, number, true/false or null values, such as a whole Clay or CSV row: up to 100 keys of 100 characters, each text value up to 1000 characters. Any other key is refused, and so is an attribute key that names a business, user, tenant or workspace id.
emailstringoptionalnamestringoptionalcompanystringoptionaldomainstringoptionallinkedin_urlstringoptionalattributesmap<string, string | number | boolean | null>optionalrecipient_notesstringoptionalBeta, per workspace: refused unless the workspace has recipient evidence on. Free text about the recipient, such as your own research or a pasted CRM note. Up to 10000 characters.
message_typestringoptionalBeta, per workspace: ignored unless the workspace has recipient evidence on. The kind of message: cold, re-engage, follow-up, deal. follow-up is a reply in a thread that is in progress.
coldre-engagefollow-updealResponses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/messages/optimize/batchOptimize Outbound Messages In A Batch
Queue many outbound drafts for the optimizer in one request and get a batch_id back at once. Use when a campaign has more drafts than the per-workspace limits on single optimizes allow, or nobody is waiting on each result. Items run later on spare capacity, behind interactive calls, and are never refused as busy. Poll the batch for counts, an estimated finish time and each result. Only items that succeed count as usage.
Request body
application/jsonrequired
itemsarray of objectrequiredThe drafts, up to 500. Each is the body of one single optimize (message, channel, context, evidence, on_unsupported, include_tries, all optional) plus an optional item_id of your own. Results come back in this order.
array items
item_idstringoptionalYour own id for this draft, returned with its result. Unique within the batch.
contextobjectoptionalWhat the message is written FROM, and the quality of the message follows the quality of this. facts are what the sender knows first-hand about the recipient: who they are, why write now, what is offered, the proof that may be cited. rules are what the message must and must not do, including the ask. voice_examples are the sender’s own messages, matched and never copied. keep_cta, max_words, banned_phrases and banned_punctuation are checked exactly. If all you have is the user’s own request, pass it as a fact rather than sending nothing. Blank entries are dropped and any other key is refused.
rulesarray of stringoptionalHard constraints on the rewrite, such as "no pricing". They steer the rewrite and are not rewritten or quoted in the message. Up to 20, each up to 500 characters once trimmed.
voice_examplesarray of stringoptionalMessages the sender wrote themselves. The rewrite matches the sender's voice and does not copy their words. Up to 5, each up to 4000 characters once trimmed.
factsarray of stringoptionalWhat you know first-hand about the recipient or situation that no call recording holds, one fact per entry, such as "Met Priya at the GTM dinner on Sep 24; she said her SDRs rewrite every AI draft". Graded as caller-supplied evidence (tier CALLER, not a customer quote): a claim a fact states is not marked fabricated, a claim that inflates one still is. Up to 10, each up to 1000 characters once trimmed.
keep_ctabooleanoptionaltrue to keep the draft's closing ask (its last sentence that asks for something) word for word. Checked exactly on every rewrite; the rewrite works on the rest of the message.
max_wordsintegeroptionalThe most words the returned message may have, checked exactly on every rewrite. 10 to 1000.
banned_phrasesarray of stringoptionalWords or phrases the rewrite leaves out, such as "just" or "reaching out", matched as whole words in any case and checked exactly on every rewrite. Up to 30, each up to 60 characters once trimmed.
banned_punctuationarray of stringoptionalPunctuation the rewrite leaves out: any of colon, semicolon, exclamation. Checked exactly on every rewrite; a colon in a time like 2:30pm or in a link does not count.
messagestringoptionalOptional. A draft, verbatim. Sent with context.facts, context.rules or a recipient, it is the starting point of the message. It is the sender's own word, so what it states may be stated, and its substance, order, layout and voice are kept. Facts add to it and rules shape it. A rule that needs a fact neither the draft nor a fact gives is listed in omitted, or asked as a question with on_missing_input ask. A draft sent with no facts, no rules and no recipient is improved instead, with a draft_improved_no_context note. Put what you know in context.
channelstringoptionalWhere the message will be sent. Send it: with no draft there is nothing to infer it from, and the channel sets the length and format the message is written and judged against. linkedin means a connection note, which carries no sign-off.
emaillinkedinon_unsupportedstringoptionalWhat to do with a claim in the draft that nothing in the workspace conversations backs. "flag" (the default) keeps it and lists it in unsupported_claims; "remove" lets the rewrite soften or cut it. Only checked when the workspace has connected conversations.
flagremoveevidencestringoptionalOptional, default off. workspace grades the draft against what this workspace's customers said in its synced conversations (positioning, grounding, specifics, differentiation), which is slower; off is a fast style pass. Where no conversations can be read the run is graded as off and evidence.reason says why.
workspaceoffon_missing_inputstringoptionalAlways best_effort in a batch: each item writes what its input supports and lists the rest in omitted. "ask" is refused, since a queued item cannot stop to ask its sender.
best_effortaskinclude_triesbooleanoptionalOptional, default false. When true, the result also carries `tries`: every version the run scored or checked, the draft first, each with its text, the optimizer's own score and human_tone (1 to 5), the dimensions it aimed at, and which one was returned. It shows how the draft progressed. Those scores come from the judge the rewrites were optimized against, so `lift`, not them, is the before-and-after. A version written from context is checked, not scored: its score can be null, and `checks_open` counts the checks it still fails.
webhook_urlstringoptionalOptional. An https URL that gets one signed POST when the batch completes, carrying the batch id and its counts, never a draft or a rewrite.
idempotency_keystringoptionalOptional. Send the same key to retry a submit safely: the same key and body return the first batch and queue nothing new.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/messages/optimize/batches/{id}Get Optimize Batch
Read one optimize batch you submitted: how many items are queued, running, done and failed, its place in the queue, an estimated time to finish, and a page of items, each with the same result a single optimize returns once it has run. Read when polling a batch_id; page with limit and cursor, and filter items by status to collect only the finished ones.
Parameters
idpathstring <uuid>requiredThe batch_id the batch submit returned.
limitqueryintegeroptionalItems per page, up to 100.
cursorquerystringoptionalThe next_cursor of the previous page. Omit for the first page.
statusquerystringoptionalReturn only items in this state. Omit for every item, in submitted order.
Responses
dataobjectrequiredbatch_idstringrequiredstatusstringrequiredqueuedrunningcompletedcreated_atstringoptionalcompleted_atstring | nulloptionalcountsobjectrequiredtotal, queued, running, done, failed.
queue_positionintegeroptionalAn estimate: queued items, in any workspace, expected to start before this batch's next one. 0 when none of its items is waiting.
etaobject | nulloptionalAn estimate, null once nothing is left to run: seconds, estimate (always true), items_ahead, median_run_seconds, slots.
webhookobjectoptionalitemsarray of anyrequiredindex, item_id, status, and result: the single-optimize result, null until settled.
next_cursorstring | nulloptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional/api/platform/v1/messages/optimize/runs/{id}Get Optimize Run
Read one optimize run you made: the draft you sent in message, the context you sent, and the message Amdahl returned, so the two versions can be compared. Read when you need to show a past run again; pass the run_id an optimize returned.
Parameters
idpathstring <uuid>requiredThe run_id an optimize returned.
Responses
dataobjectrequiredrun_idstringrequireddraftstring | nullrequiredYour own version, as you sent it in message. null when you sent none.
contextobject | nullrequiredThe context you sent: facts, rules, voice_examples and limits.
messagestring | nullrequiredThe message Amdahl returned. null when nothing was written.
created_atstring | nullrequirederrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalContext
Business context, substrate intelligence, remembered evidence, and Q&A.
/api/platform/v1/setup/statusSetup Status
Check the calling credential end to end: the workspace and role it acts as, its effective scopes, whether the message optimizer would run for it (and the one blocker if not), and which data connections need attention. Use when a call is refused for scope, role or quota, or to confirm a new connection works before relying on it.
Responses
dataanyoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptionalerrorobjectrequiredcodestringrequiredmessagestringrequireddetailsobjectoptional