Workspace API
Build with Tracer
Ask questions and run work using your company’s context, connected tools and a model you choose.
- Host
https://echo.tracerml.ai- Authentication
- Workspace Bearer key
- Model
- Choose an available model ID
- Progress
- Poll a run or subscribe to SSE
Each request creates a saved run. Keep its ID to read progress, retrieve the answer or request a stop.
Access
Your workspace key
Open API access, select your workspace, and create a key. Copy the workspace base URL from its API reference. The key secret is shown once. A provider key used for BYOK is separate from this Tracer API key.
Choose read access for analysis, or action access for supported changes in connected tools. The key uses its owner’s permissions in that workspace. You can restrict models, set a usage limit and choose an expiry.
Authorization: Bearer YOUR_TRACER_API_KEY
Keep the key on your server. Replacing its secret invalidates the old secret immediately. Revoking the key denies new requests and stops running work before further actions.
First request
Start and read a run
Set TRACER_API_BASE to the full workspace base URL copied from API access. Load TRACER_API_KEY from your secret store.
First check GET /models for the IDs allowed by your key. The examples use openai/gpt-5.6-sol; replace it if needed.
curl --fail-with-body --silent --show-error "$TRACER_API_BASE/models" \
-H "Authorization: Bearer $TRACER_API_KEY"
Set TRACER_REQUEST_ID to a unique value for this logical request. Save it with your request body so a network retry uses the same value. Generate a new one only for new work.
export TRACER_REQUEST_ID="$(python3 -c 'import uuid; print(uuid.uuid4())')"
curl --fail-with-body --silent --show-error "$TRACER_API_BASE/runs" \
-H "Authorization: Bearer $TRACER_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $TRACER_REQUEST_ID" \
--data '{
"model": "openai/gpt-5.6-sol",
"input": "Summarize the open questions about Vela."
}'
# Copy the returned run.id, then read its progress.
curl --fail-with-body --silent --show-error \
"$TRACER_API_BASE/runs/RUN_ID" \
-H "Authorization: Bearer $TRACER_API_KEY"
import json
import os
import time
from urllib.request import Request, urlopen
base = os.environ["TRACER_API_BASE"].rstrip("/")
headers = {"Authorization": "Bearer " + os.environ["TRACER_API_KEY"]}
def call(path, body=None):
extra = {} if body is None else {
"Content-Type": "application/json",
"Idempotency-Key": os.environ["TRACER_REQUEST_ID"],
}
request = Request(base + path, headers={**headers, **extra},
data=None if body is None else json.dumps(body).encode())
with urlopen(request, timeout=30) as response:
return json.load(response)["run"]
run = call("/runs", {
"model": "openai/gpt-5.6-sol",
"input": "Summarize the open questions about Vela.",
})
print("Run:", run["id"])
deadline = time.monotonic() + 120
while run["status"] in ("queued", "running", "stopping"):
if time.monotonic() >= deadline:
print("Still active. Read the same run ID later, or cancel it.")
break
time.sleep(2)
run = call("/runs/" + run["id"])
print(run["status"], run.get("error_code"), run["output"])
const base = process.env.TRACER_API_BASE.replace(/\/$/, "");
const headers = { Authorization: `Bearer ${process.env.TRACER_API_KEY}` };
async function call(path, body) {
const response = await fetch(base + path, {
method: body === undefined ? "GET" : "POST",
headers: body === undefined ? headers : {
...headers,
"Content-Type": "application/json",
"Idempotency-Key": process.env.TRACER_REQUEST_ID,
},
body: body === undefined ? undefined : JSON.stringify(body),
signal: AbortSignal.timeout(30_000),
});
const result = await response.json();
if (!response.ok) throw new Error(result.error?.message || `HTTP ${response.status}`);
return result.run;
}
let run = await call("/runs", {
model: "openai/gpt-5.6-sol",
input: "Summarize the open questions about Vela.",
});
console.log("Run:", run.id);
const deadline = Date.now() + 120_000;
while (["queued", "running", "stopping"].includes(run.status)) {
if (Date.now() >= deadline) {
console.log("Still active. Read the same run ID later, or cancel it.");
break;
}
await new Promise(resolve => setTimeout(resolve, 2000));
run = await call("/runs/" + run.id);
}
console.log(run.status, run.error_code, run.output);
A 202 response means accepted, not finished. The Python and JavaScript examples poll for up to two minutes. Ending the client’s wait does not cancel the run.
Contract
Request and retry behavior
POST /runs accepts these fields. Send the Bearer key and an Idempotency-Key header with every start request.
| Field | Type | Behavior |
|---|---|---|
model | string | Required. An exact ID from GET /models. Workspace model and spend policies also apply when work starts. |
input | string | Required. Non-empty text, up to 100,000 characters. |
conversation_id | string | Optional. Use an ID previously returned to this same API key for a follow-up in its saved conversation. |
The idempotency value accepts 1–128 letters, digits, dots, colons, underscores or hyphens. The same key and body return the existing run. Reusing the key with a different body returns 409. A follow-up uses a new idempotency value and the previous conversation_id.
Use these HTTP endpoints directly. The workspace API has a saved-run contract; OpenAI Chat Completions SDK request fields do not apply.
Progress
Run status and output
Start, read and cancel responses wrap the run in {"run": ...}. Read the answer from run.output. The start response also returns a Location header and X-Tracer-Request-ID.
{
"run": {
"id": "api_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"conversation_id": "api-chat-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"model": "openai/gpt-5.6-sol",
"status": "queued",
"output": "",
"started_at": null,
"completed_at": null,
"tool_activity": [],
"usage": [],
"trace_id": null,
"error_code": null
}
}
| Status | Meaning | Next step |
|---|---|---|
queued, running, stopping | Active | Poll GET /runs/{id} or read events. |
completed | Finished | Read output and retain the run ID. |
needs_attention | Paused or failed | Inspect error_code and saved output before deciding what to do next. |
cancelled | Stopped | Completed output and tool activity remain available. |
To stop active work, send POST /runs/{id}/cancel with the same Bearer key. Keep reading until the status is terminal. A stop request cannot undo actions already completed.
curl --fail-with-body --silent --show-error -X POST \
"$TRACER_API_BASE/runs/RUN_ID/cancel" \
-H "Authorization: Bearer $TRACER_API_KEY"
Replaying a finished or paused run’s idempotency value reads that run; it does not start it again. For a follow-up, send a new input and the saved conversation ID with a new idempotency value.
Events
Stream progress
Connect to GET /runs/{id}/events with the same Bearer key. Each event: run contains the full run object as its JSON data, without the outer run wrapper. Replace your displayed snapshot rather than appending its output repeatedly.
curl --no-buffer --fail-with-body --silent --show-error \
"$TRACER_API_BASE/runs/RUN_ID/events" \
-H "Authorization: Bearer $TRACER_API_KEY"
Buffer until a blank-line delimiter and ignore heartbeat comments beginning with :. Save each event’s id. If the stream closes while the run is active, reconnect with Last-Event-ID; streams last up to two minutes. A terminal status ends monitoring.
An event: error may arrive after HTTP 200. Its data contains code and message. Resolve that error before reconnecting.
Reference
Available endpoints
All paths below are relative to your workspace base URL and require a valid Bearer key.
| Method | Path | Returns |
|---|---|---|
| GET | /models | Available catalog entries allowed by the key’s model list. |
| GET | /context?query=... | Company context permitted for the key’s owner. |
| GET | /connections | Connected accounts available to the key’s owner. |
| POST | /runs | A saved run; 202 while active, otherwise 200. |
| GET | /runs/{id} | Status, output, tool activity, usage and error code. |
| GET | /runs/{id}/events | Server-sent progress snapshots. |
| POST | /runs/{id}/cancel | The run after requesting cancellation. |
Usage
Workspace billing
API work uses the same workspace billing and spending policies as chat. Model calls can incur charges even if the overall run is later stopped or needs attention. Saved tool activity and usage remain attached to the run.
Review confirmed charges and available credit in Usage & billing. If credit is exhausted, an authorized workspace administrator can top up from Pricing. Check the response’s error code before retrying.
A key’s usage limit restricts that key; it does not add credit to the workspace. See your workspace’s Pricing page for current model usage and BYOK terms.
Recovery
Handle errors
Request errors return a message and code. Malformed JSON or framework validation may instead return a 422 validation response. An accepted run can later finish with needs_attention; check its status as well as the initial HTTP response.
{
"error": {
"message": "Specify an available model explicitly. See the models endpoint.",
"code": "model_required",
"actions": []
}
}
400Invalid requestCorrect the model, input, conversation ID or idempotency header.
401Key unavailableCheck the secret, expiry, revocation and workspace.
402Credit or spending limitRead the error, review workspace credit and adjust the applicable limit before new work.
403Access deniedCheck member, model and action permissions.
404Run unavailableUse the key that created it. Deleted conversations are not restored by retrying.
409Conflicting requestInspect the code. An idempotency conflict means that value was used with a different body.
429Temporarily limitedHonor Retry-After when provided; otherwise use a bounded delay before retrying.
503Temporarily unavailableRetry with backoff and the same idempotency value for the same request.
If a start request times out, retry with the same body and idempotency value to recover its run ID. If polling or events fail, reconnect to the existing run. Generating a new idempotency value can start separate, billable work.
Permissions
Keep access scoped
Keys are tied to a member and workspace. One key cannot retrieve another key’s runs or use its conversation IDs. Connected accounts and source permissions still apply. Manage keys from the signed-in API access page.
Store secrets server-side, keep authorization headers out of logs, and use the smallest access level your integration needs. Render generated content safely in your application.
Integrations must follow the existing API attribution terms, including the required “Echo by Tracer” attribution unless a separate written agreement applies.