HTTP API and client integrations
Owner API, channel-compatible chat, asynchronous runs, and idempotent requests.
Jian exposes two different integration surfaces: the owner API for administering and driving a gateway, and an OpenAI-compatible chat surface belonging to an API channel. Use the credential appropriate to the surface.
The authoritative schema for a running gateway is GET /openapi.json. The repository generates it from Zod contracts and generates a TypeScript client from the same contract. This guide is a working orientation, not a replacement for your installed version's schema.
Addresses and authentication
| Surface | Base/path | Credential |
|---|---|---|
| Owner API | /v1/... | Authorization: Bearer <host token> |
| OpenAI-compatible API channel | /v1/channels/{channelId}/openai | Channel token as a bearer |
| Native channel ingress | /v1/ingress/{channelId} | x-jian-channel-token |
| Public probes | /health, /openapi.json | None |
Use the public web-panel origin for a server deployment. The web host forwards these requests to its gateway. Never put owner tokens in a frontend bundle or a public URL.
Inspect an existing installation
Set JIAN_HOST_TOKEN securely in the shell before using these examples. It is an example client variable, not an additional gateway setting. All example domains and IDs below are fictional.
JIAN_BASE_URL='https://jian.example.com'
curl --fail-with-body "$JIAN_BASE_URL/health"
curl --fail-with-body \
-H "Authorization: Bearer $JIAN_HOST_TOKEN" \
"$JIAN_BASE_URL/v1/profiles"Take the intended profile ID from the returned data. Creating a conversation and submitting a message are writes and can consume model usage.
Open a conversation and submit a turn
A panel-style conversation is created through gateway-sessions. Its body can select projectId or remoteMachineId; an empty object uses the default target.
JIAN_PROFILE_ID='11111111-1111-4111-8111-111111111111'
curl --fail-with-body \
-H "Authorization: Bearer $JIAN_HOST_TOKEN" \
-H 'Content-Type: application/json' \
-d '{}' \
"$JIAN_BASE_URL/v1/profiles/$JIAN_PROFILE_ID/gateway-sessions"Use the returned session ID in the next path. Prepare the request body as message.json:
{
"text": "Summarize the three next steps for Harbor Studio. Do not change any external data.",
"requestKey": "harbor-planning-001"
}JIAN_SESSION_ID='22222222-2222-4222-8222-222222222222'
curl --fail-with-body \
-H "Authorization: Bearer $JIAN_HOST_TOKEN" \
-H 'Content-Type: application/json' \
--data-binary @message.json \
"$JIAN_BASE_URL/v1/profiles/$JIAN_PROFILE_ID/sessions/$JIAN_SESSION_ID/messages"Submission returns 202 with run information. It means work was accepted, not that the final answer or an external effect has completed. Reuse the same request key when retrying the same submission after an uncertain network result. Give genuinely new messages new keys.
The message contract allows text up to 8,000 characters and a request key up to 120. Attachments use previously stored media IDs; a message needs text or an attachment.
Follow execution
Method and path suffix under /v1/profiles/{profileId} | Purpose |
|---|---|
GET /runs/{runId} | Current run state/result |
POST /runs/{runId}/cancel | Request cancellation |
GET /runs/{runId}/checkpoints | Inspect persisted execution checkpoints |
POST /runs/{runId}/approval | Decide an approval using the contract's decision body |
POST /runs/{runId}/answer | Answer a structured question |
GET /sessions/{sessionId}/messages | Conversation messages |
GET /sessions/{sessionId}/timeline | Conversation activity |
GET /sessions/{sessionId}/context | Context usage and compaction information |
PUT /sessions/{sessionId}/protection | Set ask, auto, or full for an owner-controlled session |
PUT /sessions/{sessionId}/objective | Manage a persistent objective |
Use /openapi.json for the exact approval, answer, media, and objective schemas. A run can wait for human input; clients should not classify every non-completed state as failure or start duplicate work to escape a wait.
OpenAI-compatible chat
Create an API channel on a server agent. Configure a client with:
Base URL: https://jian.example.com/v1/channels/33333333-3333-4333-8333-333333333333/openai
API key: the channel token
Model: default, or a model ID returned by GET /modelsGET /models lists default and available model IDs. POST /chat/completions accepts a body such as:
{
"model": "default",
"user": "harbor-client",
"metadata": { "chat_id": "harbor-planning" },
"messages": [
{ "role": "user", "content": "What should I prepare for the project review?" }
],
"stream": true
}The example can be sent with curl using the channel token stored privately in JIAN_CHANNEL_TOKEN:
curl --no-buffer --fail-with-body \
-H "Authorization: Bearer $JIAN_CHANNEL_TOKEN" \
-H 'Content-Type: application/json' \
--data-binary @chat.json \
"$JIAN_BASE_URL/v1/channels/$JIAN_CHANNEL_ID/openai/chat/completions"Save the JSON above as chat.json and set JIAN_CHANNEL_ID to the API channel's ID before running it. Streaming returns text/event-stream chunks, ending with data: [DONE] on normal completion. Errors after the stream starts arrive in the stream rather than changing the already-sent HTTP status.
Compatibility boundaries
This endpoint drives a persistent Jian agent; it is not a transparent proxy to the complete OpenAI API.
- Only the last user message's text becomes the new request. Image parts are accepted but ignored by this compatibility surface.
metadata.chat_idkeeps conversation identity stable. Without it, Jian derives an identity from the channel and opening system/user messages.useridentifies the channel contact; absent it, the adapter uses its default actor.- Resent history helps identify the conversation but is not installed wholesale as a replacement for gateway history. Client system instructions do not replace the agent's configured identity.
- Fields such as
temperature,tools, orncan be tolerated without being implemented. The agent's runtime and settings control execution. reasoning_effortand the Jianprotectionextension are supported by the contract. API channels refusefullprotection.- The body limit is 4 MB with up to 2,000 messages; the new user text remains capped at 8,000 characters.
Use normal media APIs/channels for attachments requiring full Jian processing. Do not infer support for Responses, embeddings, or another API merely from Chat Completions compatibility.
Contact gates and client disconnects
A pending API contact returns 403 with an approval explanation. A paused conversation can return 409; missing model setup can return 503. Resolve the actual state in Jian instead of rotating unrelated provider keys.
If a client disconnects, the gateway run continues. Its result remains in the conversation. After an uncertain request, inspect or retry the same logical turn rather than inventing a new conversation ID.
Native ingress
Native ingress is useful when you control the channel integration. A minimal body is:
{
"actorId": "harbor-client",
"chatId": "harbor-planning",
"text": "Prepare a concise project review outline.",
"requestKey": "harbor-review-001"
}POST /v1/ingress/{channelId} authenticates with x-jian-channel-token. A 202 response can still contain accepted: false and contact: pending. Check the body and run ID: HTTP receipt alone is not proof of model execution or reply delivery.
Implementation reference
Session routes, OpenAI-compatible routes, and compatibility contract.