Invoke Braintrust experts programmatically via REST API.
Braintrust experts can be invoked programmatically via REST API, enabling automated pipelines, CI/CD integrations, custom applications, and webhook-driven workflows.
Extra background context injected into the conversation
attachments
Attachment[]
No
Array of file attachments uploaded via the presigned-URL flow. Each entry references a previously uploaded file by s3_key (returned by the upload endpoint). Each entry contains exactly s3_key, type, name. Maximum 20 files. See Attachments for the per-entry schema.
deliver_to.gchat_space_id
string
No
Deliver output to a Google Chat Space (e.g. spaces/AAQAvU5RLdI)
deliver_to.agent_dm
boolean
No
Deliver output as a DM from the agent’s identity
* Use either agent or agents, never both.
Fields async and wait_for_completion are not supported — use stream only.
Each entry in the attachments array is an object with the following fields:
Field
Type
Required
Description
s3_key
string
Yes
Must start with invoke-uploads/ and come from the presigned-URL flow below. Arbitrary S3 paths return 400.
type
string
Yes
MIME type of the uploaded file (e.g. application/pdf).
name
string
Yes
Original basename of the file.
No other fields are accepted — extra keys return 400. Maximum 20 attachments per invoke.
Files must be uploaded before invoking the expert. Use POST /api/agents/invoke/attachments/upload-url to obtain a presigned URL, upload the file to that URL, then include the returned attachment object (s3_key, type, name) in your invoke request. The s3_key must start with invoke-uploads/ — arbitrary S3 paths are rejected with 400.
When stream is omitted or true, the response is a Server-Sent Events (SSE) stream at HTTP 200 with Content-Type: text/event-stream.Lines beginning with data: are JSON payloads. Lines beginning with : keepalive are heartbeat comments — ignore them when parsing.Streaming responses include an X-Request-Id response header containing the server-generated request correlation ID.
Log X-Request-Id from every streaming response alongside your task_id and thread_token. If a request behaves unexpectedly, the X-Request-Id value lets support engineers locate the exact server-side trace without needing full request payloads.
Every successful invoke turn gets a UUID request_id echoed in meta, done, async 202, poll responses, and the X-Request-Id response header. Save it to look up the LangSmith trace timeline (see Traces).
curl -s -N -X POST "https://braintrust.ti.trilogy.com/api/agents/invoke" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"agent":"AGENT_ID","message":"What can you help me with?"}'
If you see meta → chunk lines → done, your integration is working. Save the thread_token from meta or done for follow-up messages.
UUID correlation ID for this turn — echoed in meta/done events, this 202 body, poll responses, and the X-Request-Id streaming response header. Use it with the trace timeline endpoint (see Traces).
status_url
Relative URL for polling; prepend the base URL
thread_token
Conversation continuity handle for follow-up messages
thread_token keeps messages connected into a single conversation thread.
Scenario
What to do
First message, new thread
Omit thread_token — server mints a UUID and returns it in meta / done / 202
Bring your own ID
Send any stable opaque string (ticket ID, session ID, etc.) — it is accepted as-is
Follow-up message
Send the same thread_token used on the first turn
Start over
Omit thread_token or use a new custom string
If you change the agents array while reusing the same thread_token, the conversation will not continue from the previous thread. Keep the same agent roster across follow-up messages to maintain continuity.
# First message — server mints the thread_tokencurl -s -N -X POST "https://braintrust.ti.trilogy.com/api/agents/invoke" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"agent":"AGENT_ID","message":"Summarise last week sales data"}'# Follow-up — continue the same threadcurl -s -N -X POST "https://braintrust.ti.trilogy.com/api/agents/invoke" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"agent":"AGENT_ID","thread_token":"<token-from-first-response>","message":"Now break that down by region"}'
Streaming done events may include delivery_error (string) when posting to the configured destination failed, or delivered_to (object) confirming where the result was sent. Polled GET task-status responses do NOT carry a separate delivery_error field on success — use streaming if you need delivery diagnostics in the HTTP body.
GET/api/agents/invoke/{task_id}Retrieve status and output for any invocation (streaming or async) by task_id. The task_id is returned in the streaming meta event and in the async 202 response.
Every invoke turn returns a UUID request_id (in meta/done, the async 202 body, poll responses, and the X-Request-Id response header). Use it to fetch the LangSmith trace timeline for that turn. Same x-api-key as invoke; you must have hired the expert.GET/api/agents/{agent_id}/traces/{request_id}/timelineReturns run header fields (name, status, start_time, end_time, error) and an interleaved timeline of model and tool events. When truncated is true, the trace exceeded internal fetch limits.
Bad JSON, missing message, invalid agent/agents, invalid attachments (wrong s3_key, extra fields, or more than 20 files), or unsupported extra fields.
Check JSON, message, and agent/agents usage. Verify each attachment’s s3_key starts with invoke-uploads/ and came from the upload-url endpoint. Remove unsupported flags like async or wait_for_completion.
401
Missing or invalid API key
Check the x-api-key header name and key value
403
Agent not hired or inactive
Hire the agent first; response may include hire_url
404
Wrong agent ID or task ID
Verify the IDs
409
Cancel not allowed for this task state — record exists but is in a terminal state or has no stored internal message id.
Use the cancel endpoint only for queued or running tasks; check cancel_applied and reason in the 200 response for diagnostic detail.