Agents and assistants over the API
An application talks to an agent through the assistant that publishes it. The assistant’s chat endpoint takes the assistant’s own key, needs no sign-in, streams its answer, and runs the agent with its knowledge, tools, skills, collaborators and approvals. This page documents that endpoint, and the separate endpoint builders use to run an agent from a script.
| You want to | Endpoint | Credential |
|---|---|---|
| Put an agent in front of your customers or staff, from your own application or site | POST /api/v1/public/assistants/<slug>/chat |
The assistant’s key, from its share link |
| Run an agent from a script while you build or test it | POST /api/v1/tenants/<tenant-id>/agents/<agent-id>/run |
A builder’s sign-in token |
Both are on the console host, not the gateway.
Talk to a published assistant
Section titled “Talk to a published assistant”The key
Section titled “The key”An Admin creates an assistant’s share link when it is published, or later with Get share link. The link has this shape:
https://<your-console-host>/a/<slug>#k=<access-key>The part after #k= is the assistant’s key. It is shown once and stored nowhere, so copy it then. It can call only that assistant’s model, only for that assistant, in that tenant; the tenant’s budgets and guardrails apply, and every turn is metered against the tenant’s first project. Creating a new link revokes the previous key. See Publish and share.
Send a message
Section titled “Send a message”POST https://<your-console-host>/api/v1/public/assistants/<slug>/chatContent-Type: application/jsonx-api-key: <access-key>| Field | Required | Notes |
|---|---|---|
message |
Yes | The person’s message, at most 4,000 characters. |
threadId |
No | Leave it out to start a conversation; send the one you received to continue it. |
endUserRef |
No | Your opaque handle for the person. See The person’s reference. |
apiKey |
No | The key, if you cannot send the x-api-key header. |
curl -N -s https://<your-console-host>/api/v1/public/assistants/<slug>/chat \ -H "x-api-key: $ASSISTANT_KEY" \ -H 'content-type: application/json' \ -d '{"message":"How do I file a claim?","endUserRef":"cust-4821"}'import json, os, requests
resp = requests.post( "https://<your-console-host>/api/v1/public/assistants/<slug>/chat", headers={"x-api-key": os.environ["ASSISTANT_KEY"]}, json={"message": "How do I file a claim?", "endUserRef": "cust-4821"}, stream=True, timeout=200,)resp.raise_for_status()for line in resp.iter_lines(decode_unicode=True): if not line or not line.startswith("data: "): continue frame = json.loads(line[len("data: "):]) if "delta" in frame: print(frame["delta"], end="", flush=True) elif frame.get("done"): print("\n", frame.get("usage")) thread_id = frame.get("threadId")The answer
Section titled “The answer”The answer is a stream of server-sent events. Each event is one line, data: <json>, and the JSON is one of these frames, in this order:
| Frame | Meaning |
|---|---|
{"threadId": "..."} |
The conversation this turn belongs to. Keep it to continue. |
{"sources": [...]} |
The passages the answer is grounded on, when the agent has knowledge. |
{"step": {...}, "index": n} |
A step the agent took, such as a tool call, when the assistant runs its agent with tools. |
{"delta": "..."} |
The next piece of the answer. |
{"done": true, "usage": {...}, ...} |
The end of the turn, with promptTokens, completionTokens and totalTokens. |
{"error": "...", "detail": "..."} |
The turn failed; detail says why. |
When the agent asks for a tool that waits for a person, the done frame also carries "waiting": true and the pending approvals. The answer arrives once an Admin or Reviewer decides; read the conversation back to get it.
Read a conversation back
Section titled “Read a conversation back”GET https://<your-console-host>/api/v1/public/assistants/<slug>/threads/<thread-id>x-api-key: <access-key>The response is the conversation with its messages. Use it after a turn that was waiting for an approval, or to restore a conversation in your interface.
The person’s reference
Section titled “The person’s reference”A conversation is erasable by person only if it was stored with a reference to that person. Send endUserRef on the first turn of each conversation: the opaque handle your application already uses for the person, such as a customer number. It must not be personal data: no @, at most 200 characters; otherwise the turn is refused with 400 bad_end_user_ref.
If you send none, Olympusx AI issues a reference for the conversation and returns it in the X-Olympus-Visitor-Ref response header. Keep it with the conversation: it is what Govern › Erasure needs to find it.
On the share link, the same handle goes in the fragment as u:
https://<your-console-host>/a/<slug>#k=<access-key>&u=<your-handle-for-this-person>See Erasure.
Rate limit
Section titled “Rate limit”Public assistant chat is limited per tenant, across all its assistants and links: 60 requests a minute by default, unless the tenant has a rate limit of its own. Over it, a turn is refused with HTTP 429, the OpenAI error object with code request_rate_exceeded, and a Retry-After header in seconds. Your platform administrator can change the default. See Limits.
Errors
Section titled “Errors”| Status | error or code |
Cause |
|---|---|---|
400 |
message_required, message_too_long, bad_end_user_ref, bad_json |
The request body. |
401 |
key_required, invalid_key, key_expired, key_disabled |
The key is missing, wrong, expired or was replaced by a new link. |
403 |
key_not_for_assistant, wrong_tenant |
The key belongs to another assistant or tenant. |
403 |
model_not_granted, guardrails_blocked |
The tenant lost the model, or the policy blocked the message. |
404 |
not_found |
No assistant with that slug, or it is disabled. |
429 |
budget_exceeded, request_rate_exceeded |
A budget is spent, or the rate limit is reached. |
Run an agent while you build it
Section titled “Run an agent while you build it”Builders and Admins can run an agent from a script, with their own sign-in token, the same way Preview does. It is meant for testing and automation by the people who build agents, not for application traffic: a person’s token expires within minutes and carries their role.
curl -s -X POST https://<your-console-host>/api/v1/tenants/<tenant-id>/agents/<agent-id>/run \ -H "Authorization: Bearer $TOKEN" \ -H 'content-type: application/json' \ -d '{"message":"Which claims are waiting on documents?","maxToolCalls":10}'| Field | Notes |
|---|---|
message |
Required. |
sessionId |
Continue a conversation with this agent; leave out to start one. |
stream |
true for server-sent events, as with Accept: text/event-stream. |
maxToolCalls, maxModelCalls, maxTokens |
Ceilings for this run. Left out, the agent’s own ceilings apply, or the defaults of 20, 25 and 200,000. |
The response holds runId, sessionId, outcome (ok, ceiling, failed or needs_approval), answer, toolCalls, modelCalls, tokens, sources, steps, elapsedMs, and for a held call, pending with each approval’s id, tool and expiresAt. A run lasts at most 5 minutes. The request body is at most 1 MiB. Every run appears in Operate › Runs.