1. Threads
  2. Submit Tool Outputs
POST
/threads/tool-outputs
curl --request POST \
     --url https://app.backboard.io/api/threads/tool-outputs \
     --header 'X-API-Key: <api-key>' \
     --header 'authorization: <authorization>' \
     --header 'x_session_token: <x_session_token>' \
     --header 'Content-Type: application/json' \
     --data '{
  "thread_id": "<uuid>",
  "tool_outputs": [
    {
      "tool_call_id": "string",
      "output": "string"
    }
  ],
  "stream": true
}'

​
Overview

The recommended way to submit tool call results. Unlike the original endpoint, you never need to track run_id — the server resolves it automatically from the latest REQUIRES_ACTION message on the thread.

Three fields, no escape hatches:

FieldTypeRequiredDescription
thread_iduuidYesThread the outputs belong to
tool_outputsarrayYes[{tool_call_id, output}, ...]
streambooleanNoStream the response (default false)

​
Quick Start

import json
from backboard import BackboardClient

client = BackboardClient(api_key="YOUR_API_KEY")

# 1. Send a message that triggers a tool call
r = await client.send_message(
    "What's the weather in SF?",
    thread_id=thread_id,
    tools=[weather_tool],
)

# 2. Execute the tool
if r.status == "REQUIRES_ACTION" and r.tool_calls:
    outputs = []
    for tc in r.tool_calls:
        result = my_tool_handler(tc.function.name, tc.function.parsed_arguments)
        outputs.append({"tool_call_id": tc.id, "output": json.dumps(result)})

    # 3. Submit outputs — no run_id needed
    final = await client.submit_tool_outputs_simple(
        thread_id=r.thread_id,
        tool_outputs=outputs,
    )
    print(final.content)

​
Streaming

async for chunk in await client.submit_tool_outputs_simple(
    thread_id=thread_id,
    tool_outputs=outputs,
    stream=True,
):
    if chunk.get("type") == "content_streaming":
        print(chunk.get("content", ""), end="", flush=True)

​
Chained Tool Calls

The model may need multiple rounds. Loop until status is COMPLETED:

r = await client.send_message("Get weather + forecast for SF", thread_id=tid, tools=tools)

while r.status == "REQUIRES_ACTION" and r.tool_calls:
    outputs = [
        {"tool_call_id": tc.id, "output": json.dumps(dispatch(tc))}
        for tc in r.tool_calls
    ]
    r = await client.submit_tool_outputs_simple(
        thread_id=r.thread_id,
        tool_outputs=outputs,
    )

print(r.content)

​
Comparison with Original Endpoint

This endpointOriginal (/threads/{id}/runs/{run_id}/submit-tool-outputs)
run_idAuto-resolvedMust be provided
tools overrideNot supportedSupported
thinking (reasoning)Not supportedSupported — per-step reasoning on continuation
SchemaStrict (extra="forbid")Flexible
Best forMost integrationsAdvanced: pin a run, swap tools, or tune reasoning after tools

For the original endpoint, see Submit Tool Outputs.

​
Error Codes

StatusCause
404Thread not found, or no pending REQUIRES_ACTION on the thread
422tool_outputs is empty or contains invalid entries
400Extra fields in request body (strict schema)

​
Authorizations

X-API-Key
required
string
API Key authentication

​
Query Parameters

authorization
x_session_token

​
Body

application/json
thread_id
required
string

Thread UUID the outputs belong to.

tool_outputs
required
array

Tool outputs to submit. Each item is {tool_call_id, output}.

stream
boolean | null

Whether to stream the response.

​
Response

application/json
  • 200

  • 422

Successful Response

message
required
string

Message

thread_id
required
string

Thread Id

run_id
required
string

Run Id

timestamp
required
string

Timestamp

content
string | null

Content

message_id
string | null

Message Id

role
string | null
status
string | null
tool_calls
array | null

Tool Calls

memory_operation_id
string | null

Memory Operation Id

retrieved_memories
array | null
retrieved_files
array | null

Retrieved Files

retrieved_files_count
integer

Retrieved Files Count

reasoning
string | null

Reasoning

model_provider
string | null

Model Provider

model_name
string | null

Model Name

input_tokens
integer | null

Input Tokens

output_tokens
integer | null

Output Tokens

total_tokens
integer | null

Total Tokens

created_at
string | null

Created At

generated_media
array | null
context_usage
object | null

Context Usage