/api/csrfIssue a CSRF token
Response 200Token plus the header name to send on mutating requests
| Field | Type | Notes |
|---|---|---|
| tokenrequired | string | |
| headerNamerequired | string |
Frontend contract
HTTP routes for a custom frontend. They run the same server functions as the live dashboard.
POST /api/v1/auth/login. The session is an HTTP-only cookie, so send cookies with every request./api/v1/assistants routes also need CSRF: call GET /api/csrf (it sets a cookie too) and send the returned token in x-csrf-token./api/conversations routes need the session cookie only.GET /api/csrf, then POST /api/v1/auth/login with x-csrf-token.GET /api/v1/assistants and show Shared vs Private from plans.POST /api/v1/assistants/claim with tier and durationHours only. Allow at least 180 s.GET /api/v1/assistants until current.status is READY.current.launchPath for the managed assistant.POST /api/conversations and POST /api/conversations/{id}/messages; these store messages and do not call the model.POST /api/v1/assistants/release to end the session early and get unused time refunded.CSRF, register, session
/api/csrf| Field | Type | Notes |
|---|---|---|
| tokenrequired | string | |
| headerNamerequired | string |
/api/register| Field | Type | Notes |
|---|---|---|
| usernamerequired | string | |
| emailrequired | string | |
| passwordrequired | string | min length 8 |
| acceptTerms | boolean | Acceptance of the Terms of Service and Privacy Policy. Web sign-ups require it and always send true; when true the acceptance time is stored. Optional for API clients. |
| Field | Type | Notes |
|---|---|---|
| successrequired | boolean | |
| messagerequired | string |
Errors:400403409429503with an error body
/api/v1/auth/loginSame code as the dashboard's login() function. Checks the username or email and password and sets the session cookie.
| Field | Type | Notes |
|---|---|---|
| usernamerequired | string | Username or email |
| passwordrequired | string |
| Field | Type | Notes |
|---|---|---|
| successrequired | boolean | |
| rolerequired | string | One of USER, ADMIN |
Errors:400401403with an error body
/api/v1/auth/password-reset/requestSend email or username. The answer is always the same 200, whether or not an account matches, and mail is only sent when the server has SMTP configured. The link in the mail is valid for 1 hour. Limited to 10 requests per hour per IP and 3 per hour per identifier.
| Field | Type | Notes |
|---|---|---|
| string | ||
| username | string |
| Field | Type | Notes |
|---|---|---|
| successrequired | boolean | |
| messagerequired | string |
Errors:400403429with an error body
/api/v1/auth/password-reset/confirmSend the token from the reset link and the new password. A successful reset signs out every older session.
| Field | Type | Notes |
|---|---|---|
| tokenrequired | string | |
| passwordrequired | string | min length 8 |
| Field | Type | Notes |
|---|---|---|
| successrequired | boolean | |
| messagerequired | string |
Errors:400403500with an error body
/api/v1/auth/verify-emailSend the token from the verification link. A token works once and is valid for 24 hours.
| Field | Type | Notes |
|---|---|---|
| tokenrequired | string |
| Field | Type | Notes |
|---|---|---|
| successrequired | boolean | |
| messagerequired | string |
Errors:400403500with an error body
/api/v1/auth/verify-email/resendSends a new verification link to the signed-in user when their email still needs verification. Limited to 3 per hour per user.
| Field | Type | Notes |
|---|---|---|
| successrequired | boolean | |
| messagerequired | string |
Errors:401403429with an error body
/api/v1/auth/logoutClears the session cookie.
| Field | Type | Notes |
|---|---|---|
| successrequired | boolean |
/api/v1/me| Field | Type | Notes |
|---|---|---|
| idrequired | string | |
| usernamerequired | string | |
| string | null | ||
| rolerequired | string | One of USER, ADMIN |
| creditsrequired | number | |
| emailVerifiedrequired | boolean |
Errors:401with an error body
/api/userExisting dashboard JSON route. Prefer GET /api/v1/me, which adds credits.
Errors:401with an error body
Shared vs Private claim, session, launch
/api/v1/assistantsReturns the plans you can claim and your current assistant, if any. Poll until current.status is READY, then open current.launchPath. READY always includes a non-null expiresAt; while the pod is healthy but the billing clock has not started yet, status stays STARTING and expiresAt stays null. While STARTING, current.provisioning reports the pod's own status, when it was claimed, and warmupDeadlineAt, the point by which the claim is fully refunded and the pod released if it still has not come up. Do not call release or upgrade while current.status is STARTING before provisioning.warmupDeadlineAt. FAILED is terminal: the claim could not be set up and error says why. The charge is refunded automatically; a refund that fails is retried every monitoring cycle until it succeeds. It stays visible for about 10 minutes, then current becomes null.
| Field | Type | Notes |
|---|---|---|
| creditsrequired | number | |
| plansrequired | Plan[] | |
| plans[].tierrequired | string | One of PRIVATE, SHARED |
| plans[].availablerequired | boolean | |
| plans[].creditRaterequired | number | |
| currentrequired | object |
Errors:401403with an error body
/api/v1/assistants/claimThe server picks a free assistant of the requested tier. Send only tier and durationHours. The request returns at once with status STARTING once the charge and assignment commit; the workspace is prepared in the background. Poll GET /api/v1/assistants until current.status is READY (or FAILED, whose charge is refunded automatically). Do not retry blindly: retrying a claim that already succeeded returns 409 with a clear already-held message and never charges twice.
| Field | Type | Notes |
|---|---|---|
| tierrequired | string | One of PRIVATE, SHARED |
| durationHoursrequired | integer | min 1. max 168 |
| Field | Type | Notes |
|---|---|---|
| successrequired | boolean | |
| assistantrequired | Assistant | |
| assistant.assignmentIdrequired | string | |
| assistant.tierrequired | string | One of PRIVATE, SHARED |
| assistant.statusrequired | string | One of READY, STARTING, FAILED |
| assistant.creditRaterequired | number | |
| assistant.expiresAtrequired | string | null | date-time. Session end time stamped by the billing worker. Always non-null when status is READY; null while STARTING. |
| assistant.durationHoursrequired | integer | null | |
| assistant.launchPathrequired | string | |
| assistant.error | string | Present only when status is FAILED |
| assistant.provisioningrequired | object | null | Set while status is STARTING and the billing clock has not started, including a RUNNING pod still waiting on its first billing tick; null once expiresAt is set. |
| assistant.provisioning.podStatusrequired | string | One of IDLE, ERROR, PENDING, HIBERNATED, RUNNING |
| assistant.provisioning.claimedAtrequired | string | date-time |
| assistant.provisioning.warmupDeadlineAtrequired | string | date-time |
| remainingCredits | number | |
| message | string |
Errors:400401403409503with an error body
/api/v1/assistants/releaseEnds the caller's current session and refunds unused time. The server finds the session, so the client sends no id.
| Field | Type | Notes |
|---|---|---|
| successrequired | boolean | |
| refundedrequired | number |
Errors:401403409with an error body
/api/v1/assistants/extendAdds hours to the caller's current session and charges credits for them.
| Field | Type | Notes |
|---|---|---|
| durationHoursrequired | integer | min 1. max 168 |
| Field | Type | Notes |
|---|---|---|
| successrequired | boolean | |
| remainingCreditsrequired | number | |
| expiresAtrequired | string | null | date-time |
| durationHoursrequired | integer |
Errors:400401409with an error body
/api/v1/assistants/upgradeMemory stays on the account. Unused shared time is refunded first. The request can take up to ~3 minutes while the private pod is prepared: use a client timeout of at least 180 s. If your client times out anyway, the upgrade still completes; do not retry blindly, instead GET /api/v1/assistants to read the resulting assignment.
| Field | Type | Notes |
|---|---|---|
| successrequired | boolean | |
| assistantrequired | Assistant | |
| assistant.assignmentIdrequired | string | |
| assistant.tierrequired | string | One of PRIVATE, SHARED |
| assistant.statusrequired | string | One of READY, STARTING, FAILED |
| assistant.creditRaterequired | number | |
| assistant.expiresAtrequired | string | null | date-time. Session end time stamped by the billing worker. Always non-null when status is READY; null while STARTING. |
| assistant.durationHoursrequired | integer | null | |
| assistant.launchPathrequired | string | |
| assistant.error | string | Present only when status is FAILED |
| assistant.provisioningrequired | object | null | Set while status is STARTING and the billing clock has not started, including a RUNNING pod still waiting on its first billing tick; null once expiresAt is set. |
| assistant.provisioning.podStatusrequired | string | One of IDLE, ERROR, PENDING, HIBERNATED, RUNNING |
| assistant.provisioning.claimedAtrequired | string | date-time |
| assistant.provisioning.warmupDeadlineAtrequired | string | date-time |
| remainingCredits | number | |
| message | string |
Errors:401409503with an error body
/api/v1/assistants/chatSends messages to your own READY assistant and returns the answer. Needs the session cookie and the x-csrf-token header; the server holds the inference key, so none is ever sent to or returned to the browser. Set stream to true for token-by-token OpenAI-style server-sent events (data: lines ending with data: [DONE]); otherwise one JSON chat completion is returned. Any model, tools or other field you send is ignored: the model is fixed to your assistant's. The inference service raises max_tokens below 4096 to 4096. Errors before the answer starts carry error and code; 409 means no assistant or one that is not READY yet (poll GET /api/v1/assistants), 429 includes Retry-After, 503 is retryable. If a stream breaks after it started, a final data event with an error object and data: [DONE] close it. Closing the connection cancels the request. Nothing is stored; use POST /api/conversations/{id}/messages for history.
| Field | Type | Notes |
|---|---|---|
| messagesrequired | object[] | |
| messages[].rolerequired | string | One of system, user, assistant |
| messages[].contentrequired | string | max length 32000 |
| stream | boolean | Default false. true returns server-sent events. |
| max_tokens | integer | min 1. max 32768. The inference service raises values below 4096 to 4096. |
| temperature | number | min 0. max 2 |
Errors:400401402403409429502503504with an error body
/api/agent-zero/launchSame URL the live dashboard Open assistant button uses. Requires a session cookie. Returns 302 to the auto-login entrypoint when READY, or an HTML wait page while starting. Use from a top-level navigation or window.open, not as JSON.
Errors:403503with an error body
/api/pods/{id}/resumeUsed by the built-in dashboard. {id} is the internal pod id, which the customer API does not return, so a custom frontend should not call this; poll GET /api/v1/assistants after claim instead.
| Field | Type | Notes |
|---|---|---|
| id (path)required | string | Internal pod id. The live dashboard still sends this. |
Errors:401403with an error body
Conversation history stored on the account. Works without an active assistant, does not need CSRF, and is readable after a session ends. File upload is not available to customers yet.
/api/conversationsOrdered by updatedAt, newest first, so the conversation with the latest message comes first. updatedAt moves when a message is added (conversations that have had no message since they were created keep their creation time). Returns at most limit (up to 100) conversations, with no paging past that. Metadata only; read a conversation's messages with GET /api/conversations/{id}/messages. Delete one with DELETE /api/conversations/{id}. Admin accounts get 403.
| Field | Type | Notes |
|---|---|---|
| limit (query) | integer | min 1. max 100. default 30 |
| Field | Type | Notes |
|---|---|---|
| conversationsrequired | ConversationSummary[] | |
| conversations[].idrequired | string | |
| conversations[].titlerequired | string | null | |
| conversations[].createdAtrequired | string | date-time |
| conversations[].updatedAtrequired | string | date-time |
| conversations[].messageCountrequired | integer |
Errors:401403500with an error body
/api/conversationsDoes not start a model reply. A JSON body is required; send {} to create a conversation without a title, which is stored as null. There is no rename yet.
| Field | Type | Notes |
|---|---|---|
| title | string | max length 120 |
| Field | Type | Notes |
|---|---|---|
| idrequired | string | |
| titlerequired | string | null | |
| createdAtrequired | string | date-time |
| updatedAtrequired | string | date-time |
Errors:400401403500with an error body
/api/conversations/{id}/messagesNewest first: the first message in the list is the latest. Returns the newest limit messages (up to 200); pass order=asc to list them oldest first instead. hasMore is true when older messages exist, and nextBefore is then the id to pass as before to load the next older page (it is null on the last page). before returns the limit messages just before that message, in the same order setting. An unknown before id, or an order other than asc or desc, gets 400. Each message lists its attached files and generated artifacts.
| Field | Type | Notes |
|---|---|---|
| id (path)required | string | |
| limit (query) | integer | min 1. max 200. default 50 |
| before (query) | string | Id of a message in this conversation. Returns the messages just before it. Use nextBefore from the previous response. |
| order (query) | string | Order of messages inside the page: desc lists the newest first (default), asc the oldest first.. One of desc, asc. default desc |
| Field | Type | Notes |
|---|---|---|
| messagesrequired | Message[] | |
| messages[].idrequired | string | |
| messages[].rolerequired | string | One of SYSTEM, USER, ASSISTANT, TOOL |
| messages[].contentrequired | string | |
| messages[].podIdrequired | string | null | |
| messages[].createdAtrequired | string | date-time |
| messages[].uploadedFilesrequired | MessageFile[] | |
| messages[].uploadedFiles[].idrequired | string | |
| messages[].uploadedFiles[].originalNamerequired | string | |
| messages[].uploadedFiles[].mimeTyperequired | string | |
| messages[].uploadedFiles[].sizeBytesrequired | integer | |
| messages[].uploadedFiles[].statusrequired | string | One of PENDING, AVAILABLE, FAILED, DELETED |
| messages[].artifactsrequired | MessageArtifact[] | |
| messages[].artifacts[].idrequired | string | |
| messages[].artifacts[].typerequired | string | One of TEXT, JSON, IMAGE, FILE |
| messages[].artifacts[].mimeTyperequired | string | |
| messages[].artifacts[].sizeBytesrequired | integer | |
| messages[].artifacts[].createdAtrequired | string | date-time |
| hasMorerequired | boolean | True when older messages exist beyond this page. |
| nextBeforerequired | string | null | Id to pass as before to load the next older page; null when hasMore is false. |
Errors:400401403404with an error body
/api/conversations/{id}/messagesPersists history. It does not call the GPU model. content is trimmed before it is stored and must not be empty after trimming. Leave podId and uploadedFileIds out: the customer API exposes neither pod ids nor file upload yet; both are only used by the built-in dashboard. 403 covers admin accounts, a pod that is not assigned to the caller, and unavailable files.
| Field | Type | Notes |
|---|---|---|
| id (path)required | string |
| Field | Type | Notes |
|---|---|---|
| rolerequired | string | One of SYSTEM, USER, ASSISTANT, TOOL |
| contentrequired | string | min length 1. max length 32000 |
| podId | string | Internal; customers leave it out. |
| uploadedFileIds | string[] | Internal; customers leave it out. |
| Field | Type | Notes |
|---|---|---|
| idrequired | string | |
| rolerequired | string | One of SYSTEM, USER, ASSISTANT, TOOL |
| contentrequired | string | |
| podIdrequired | string | null | |
| createdAtrequired | string | date-time |
Errors:400401403404with an error body
/api/conversations/{id}Deletes one of your conversations together with its messages. Files you attached stay available but are no longer linked to it. 404 covers an unknown id and a conversation that belongs to someone else. Admin accounts get 403.
| Field | Type | Notes |
|---|---|---|
| id (path)required | string |
| Field | Type | Notes |
|---|---|---|
| successrequired | boolean |
Errors:401403404500with an error body
Credit top-ups through Stripe Checkout and payment history
/api/v1/billing/checkoutCreates a Stripe Checkout session for one credit pack and returns its url; send the customer there to pay. Credits are added once Stripe confirms the payment, never from the redirect back. Answers 503 with code PAYMENTS_DISABLED until payments are enabled, and 403 for admin accounts and for unverified email addresses when verification is required.
| Field | Type | Notes |
|---|---|---|
| amountrequired | integer | Pack size in currency units; one of BillingOverview.amounts |
| Field | Type | Notes |
|---|---|---|
| urlrequired | string |
Errors:400401403429500502503with an error body
/api/v1/billing/paymentsReturns whether payments are enabled, the pack amounts you can buy, the currency, and your 50 most recent payments, newest first.
| Field | Type | Notes |
|---|---|---|
| enabledrequired | boolean | |
| amountsrequired | integer[] | |
| currencyrequired | string | |
| paymentsrequired | Payment[] | |
| payments[].idrequired | string | |
| payments[].kindrequired | string | One of TOPUP, SUBSCRIPTION |
| payments[].statusrequired | string | One of PENDING, PAID, FAILED, EXPIRED, REFUNDED |
| payments[].amountrequired | number | Price in currency units |
| payments[].currencyrequired | string | |
| payments[].creditsrequired | number | |
| payments[].createdAtrequired | string | date-time |
| payments[].paidAtrequired | string | null | date-time |
Errors:401with an error body
Liveness and readiness
/api/proxy/healthReports whether the inference proxy is reachable.
/api/health/live/api/health/readyErrors:503with an error body
/api/openapi.jsonManaged OpenAI-compatible proxy on api.aicharli.com
/v1/chat/completionson https://api.aicharli.comManaged assistant inference. Authorization is a Bearer key minted for the claimed assistant, not a customer-supplied key. Prefer launchPath for the product chat UI.
| Field | Type | Notes |
|---|---|---|
| model | string | |
| messagesrequired | object[] | |
| stream | boolean | default false |
Errors:401with an error body
/v1/modelson https://api.aicharli.com/v1/memory/searchon https://api.aicharli.com| Field | Type | Notes |
|---|---|---|
| queryrequired | string | |
| limit | integer | min 1. max 50. default 5 |
error is always present. code and retryable appear only on errors that define them (for example GPU_ADMISSION_CLOSED and CLAIM_IN_PROGRESS); a plain 401 is {"error":"Unauthorized"}.
| Field | Type | Notes |
|---|---|---|
| errorrequired | string | |
| code | string | |
| retryable | boolean |