OpenAI-Compatible API
samreshuuu exposes a drop-in chat/completions endpoint. If you already use the OpenAI SDK, change the base URL and the API key; nothing else.
- Base URL:
https://samreshuuu.ru/v1 - Auth: an organization key with the
chat:writescope (see Authentication) - model: the handle of the agent that answers, from
GET /v1/models
Drop-In Example
Set SAMRESHUUU_API_KEY in your environment to the organization key before running any of these examples.
About the model field and other OpenAI fields
model addresses an agent, not an LLM. GET /v1/models lists the agents the key's organization can talk to: the built-in assistant (samreshu, owned_by: "system") first, then the organization's own and shared agents (owned_by is the organization id). The agent's own model setting picks the LLM, the request cannot override it. Every response and every chunk carries the handle of the agent that answered. A handle that does not exist or that the key's creator cannot reach is refused with 404 DATA_NOT_FOUND and param: "model", with the message "The model x does not exist or you do not have access to it. List models with GET /v1/models."; there is no fallback to another agent, so a stock gpt-4o fails loudly. A leading @ is accepted. These OpenAI fields are accepted so stock SDK calls keep working, and are not acted on: tools, tool_choice, parallel_tool_calls, temperature, top_p, max_tokens, max_completion_tokens, stop, seed, user, stream_options. Two fields that would change the shape of the answer are refused with 400 DATA_INVALID_PARAMETER: response_format of type json_object or json_schema, and n above 1. response_format: {"type": "text"} and n: 1 pass.
Request Fields
| Field | Type | Description |
|---|---|---|
| modelrequired | string | The handle of the agent that answers, an id from GET /v1/models. A leading @ is accepted. |
| messagesrequired | array | Chat messages; at least one has role user, else 400 with param: messages. The last user message drives the turn. Earlier user and assistant messages become the history of a new conversation; a session that already exists keeps its own history, and earlier messages are not read. system, developer and tool messages are not written to the history: the agent has its own instructions and tools. content is a string, an array of text parts ({type: text or input_text, text}) or null (an assistant message with tool_calls). An image, file or audio part is refused with 400 and param: messages[i].content. name, tool_calls and tool_call_id are accepted and ignored. |
| stream | boolean | When true, stream OpenAI chat.completion.chunk frames. Default false. |
| session_id | string (UUID) | A conversation UUID you generate yourself; send the same one to continue. Omit to start fresh. A conversation belongs to the agent it started with and to the key's organization: naming it with another model or from another key returns 409 DATA_CONFLICT with param: session_id. Not an OpenAI field: pass it via extra_body in the Python SDK, directly in the body in Node.js. |
| max_iterations | integer | Agent loop cap. Bounds and default are below. Pass it like session_id. |
| tool_events | boolean | When true (with stream: true), the stream also carries named tool events, see below. Default false. A top-level field: pass it via extra_body in the Python SDK. |
max_iterations accepts 1 to 50 and defaults to 25.
The last user message is at most 200000 characters. A longer one is refused with 400 DATA_INVALID_PARAMETER, and the error message names the limit. Send large data in parts across several turns of the same session.
This key acts on the whole account
A chat:write key drives your agents as the workspace does: the agent can reach the entire tool registry, including the organization's shared connected accounts and code execution. The key creator's personal accounts and memory stay closed to it. Treat the key like account credentials: scope it per integration, store it server-side only, and rotate it if exposed. There is no read-only mode on this endpoint.
Streaming
Set stream: true to receive Server-Sent Events in OpenAI's chunk format, terminated by a final data: [DONE] frame. The first chunk carries delta: {"role": "assistant", "content": ""}. Every 10 seconds an SSE comment starting with : ping (a timestamp follows) arrives; parsers must skip lines starting with :.
Tool events (opt-in)
Pass tool_events: true to receive samreshuuu.tool events interleaved with the standard chunks. They arrive as named SSE events (event: samreshuuu.tool). The official OpenAI SDKs do not filter by event name: they yield these frames as chunks without choices, so guard the loop (if not chunk.choices: continue). In the Node.js SDK read them only with for await over create({ stream: true }) or with your own SSE parser: the .stream() and runTools helpers expect every frame to be a chunk and break on these events. When the field is off, the stream carries chunks only.
tool_started carries type, tool_id, tool_name, iteration and label (the tool's display name); tool_completed adds success and duration_ms. tool_id pairs the two events of one call. A call answered without running (a repeat served from the run's earlier result, a call refused before it ran) sends only tool_completed. The agent's internal end-of-turn tool does not appear in these events.
Errors mid-stream
Once the stream has started, the status line is already 200, so a failure arrives as a final chunk carrying an error object instead of choices: {"type":"error","error":{"type","code","message","param"},"request_id"}. The Python openai client raises it as APIError, and data: [DONE] closes the stream after it; a raw SSE parser should check each data: payload for error before reading choices.
Differences from OpenAI
- Endpoints: only
POST /v1/chat/completionsandGET /v1/models. There is noresponses,embeddingsorcompletions. - Duration: an agent turn can run for a long time, up to 1800 seconds. Without streaming the connection stays silent the whole time: raise the client timeout well above the OpenAI SDK default, or set
stream: trueand rely on the ping. With streaming, keep the read timeout above the ping interval and the total request timeout above the turn's limit: a client that gives up after two minutes cuts a long reconciliation off midway, and the dropped connection stops the turn. - usage: returned only in the non-streaming response. The stream has no usage chunk.
- finish_reason is
stop. A failure after the stream started is theerrorchunk above, not afinish_reason. - A dropped connection stops the turn. Closing the stream mid-answer stops the agent and records the turn as stopped, not failed; continue with the same session.
- A refusal before the turn starts is an HTTP error whose body is the same
errorobject:402 DATA_USAGE_QUOTA_EXCEEDEDwhen the weekly limit is spent,429on rate limit,403without thechat:writescope,404for an unknownmodel,409for asession_idof another agent or key,409 SESSION_BUSYwhile another turn is running in that conversation (retry after it; withstream: truetoo, before the stream opens),400for a refused field,422for a body that fails the schema. See Errors & Rate Limits. - Concatenated chunks may differ from the saved answer: the agent sometimes rewrites text mid-turn. The non-streaming response returns the authoritative text.
Multi-turn
Generate a UUID and pass it as session_id on every request of the conversation, with the same model. The response does not return a session id, so keep it on your side.
Frequently asked questions
What do I change to use the OpenAI SDK with samreshuuu?
Only the base URL (https://samreshuuu.ru/v1) and the API key. Existing chat.completions code works unchanged once model names an agent, and the key needs the chat:write scope.
What goes in the model parameter?
The handle of the agent that should answer, as listed by GET /v1/models (the built-in assistant is samreshu). The agent's own model setting picks the LLM. An unknown or inaccessible handle is refused with 404 DATA_NOT_FOUND, there is no fallback. Sampling parameters such as temperature and max_tokens are accepted for compatibility but not honored; response_format json_object or json_schema and n above 1 are refused with 400 DATA_INVALID_PARAMETER.
How do I keep context across requests?
Generate a UUID on your side and pass it as session_id on every call. The response does not return a session id: without session_id each call starts a new conversation.
What can a chat:write API key do?
It drives your organization's agents as in the workspace, with access to the full tool registry including the connected accounts and code execution. There is no read-only mode: treat the key like account credentials, keep it server-side, and rotate it if exposed.