Skip to main content
The Cominty API is a standard HTTPS/JSON API for chatting with agents, managing conversation threads, handling file uploads/downloads and message exports, storing memory files in named namespaces, and configuring what a signed-in admin configures in the console: MCP integrations, agents, skills, document sources, and documents. Every endpoint below is called by the Python SDK and the JavaScript SDK. Use an SDK for the smoothest experience, or call them directly.

Base URL

Authentication

Send your API token in the x-cominty-token header on every request. Create a token on the API keys page at platform.cominty.ai.
A key created now starts with sk-cmt-. Paste it into the header exactly as you received it, with or without a single Bearer in front. Both forms work:
The secret is shown once, when you create the key. Copy it then. A key created before the prefix existed has no sk-cmt- and keeps working when sent as is.
SK-CMT- (wrong case), and bearer or a double space after Bearer are all rejected. If your client adds the prefix itself, add it only when the stored value does not already start with sk-cmt-.

Authentication errors

Chat and threads are scoped to one end user via user_id. Chat start sends it in the body (options.user_id). Thread endpoints send it as a query parameter. The SDKs fill this in from the client, so you never pass it on those calls. Memory endpoints may accept user_id as an optional query, but it does not select a memory namespace. See Memory endpoints.

Endpoints

Response shapes differ per endpoint. POST /chat returns a full thread, but POST /chat/{id} returns a bare message. GET /chat/{id} returns a full thread, but PUT /chat/{id} returns a summary with no messages. GET /memory returns file summaries with no content; GET /memory/file, POST /memory, and PUT /memory/file all return the full file. Check each endpoint page for its exact response.

Memory endpoints

Memory files live in a namespace you choose. For the concepts (identity of a file, isolating users, attaching a thread), see Memory namespaces. This section covers what is specific to the endpoints. The endpoint pages list namespace as optional (max 128 characters). On the platform API it is required for create, get, update, and delete: omit it and the call returns 400 Missing namespace. POST /memory sends namespace in the JSON body, next to path, purpose, and content. The other memory calls send it as a query parameter. user_id may still appear as an optional query. It does not choose the namespace. A user_id in the body of POST /memory is ignored, with no error. A thread’s memory namespace is a capability: send options.capabilities.memory on POST /chat. See Capabilities. options.memory_namespace still works but is deprecated. POST /chat/{thread_id} accepts neither.
path (in POST /memory’s body, and as a query parameter everywhere else) may have at most one folder segment. "notes/todo.md" is fine, "a/b/todo.md" returns 422 ("Maximum folder depth is 1"). content may be an empty string. There’s no minimum length.
PUT /memory/file requires a version query parameter: the opaque token from your last read of that file. A stale version (the file changed since you read it) returns 409 Conflict. A malformed one (not a real timestamp) returns 422 instead. Round-trip it unchanged. Do not parse or compare it.
PUT /memory/file’s body accepts content/purpose as null, but sending null currently has no effect: the existing value is left untouched (same as omitting the key), the version doesn’t change either, and the request still returns 200. There is currently no way to clear either field once set.
DELETE /memory/file is not idempotent: deleting an already-deleted (or never-existing) path returns 404, not a repeated 204.
GET /memory/namespaces can still return stored names such as user_abc::default. They hold files written before namespaces existed. See Files written before namespaces existed.

Capabilities

Capabilities decide what the agent can use: web, indexed_documents, mcp, skills, image_generation, video_generation, music_generation, and memory. For the concepts, see Capabilities. This section covers what is specific to the endpoints.
  • Each capability in options.capabilities is an object with a required activation: always, on_request, or never. skills and memory accept only always and never. A never entry takes no other field.
  • A capability you send replaces the agent’s value entirely. An omitted filter goes back to "*" (no restriction). A capability you do not send keeps the agent’s value.
  • Filters (source_ids, document_ids, connections, include) are either the string "*" or a list with at least one item. null and [] return 422.
  • Each entry in message.scope is an object with a required enabled. The skills and memory keys return 422.
  • A scope can only narrow. It must stay inside the thread’s list.
  • options.capabilities on POST /chat/{thread_id} returns 422. The OpenAPI schema lists the field there, but a follow-up cannot change the thread.
  • The thread’s capabilities are not returned by any endpoint.

Errors

The error detail has two shapes. Mixing the new and the legacy fields also returns 422: options.capabilities with options.memory_namespace, or message.scope with message.disabled_tools, message.source_ids or message.document_ids. The legacy fields are deprecated and will be removed in a later version.
Ids and names in filters are not checked. An unknown source id, document id, connection name, or skill id is accepted and matches nothing.

Configure with an API key

An API key acts as an admin of its own organization. It can call the agent, skill, drive, source, and document endpoints below, and read the model catalog. These endpoints do not take user_id. For the concepts and the SDKs, see Custom agents, Skills, and Sources. This section covers what is specific to the endpoints.
  • Everything a key creates is owned by the user who created the key. owner on an agent and created_by on a source are that user’s id, not the key’s.
  • A key never sees another member’s private agents, skills, or drives. A direct read, update, or delete returns 404 Not Found, and they are absent from lists.
  • A key only sees its own product surface. A Platform key does not see agents created on the other surface.
  • Send one credential. If x-cominty-token is present, an Authorization header is ignored.

MCP endpoint

GET /mcp/manifest returns {"servers": [...]}. Use a server’s name in capabilities.mcp.connections. Servers that are off are still listed, with is_enabled false. The optional fields (connection_status, icon_uri, mcp_url, owner_id, created_at, updated_at) are omitted when unknown, not null. A manifest that cannot be loaded returns 500 with the plain text Internal Server Error, so do not parse that body as JSON.

Agent endpoints

  • PUT /agents/{agent_pid} is partial. A JSON null clears description, instructions, and capabilities. capabilities is merged capability by capability: each one you send replaces the stored one, the others stay.
  • Omit model_ids on POST /agents to use the organization’s default model chain. The chain is a failover order: the first model answers.
  • On a built-in agent, PUT /agents/{agent_pid}/models changes the chain for the whole organization.
  • An organization agent owned by someone else can be updated and deleted with the key.
  • execution_options and memory_namespace are deprecated. Use capabilities (capabilities.memory sets the namespace).

Skill endpoints

POST /skills and POST /skills/{skill_pid}/versions are multipart/form-data. Send the zip in the file field. POST /skills also takes display_name (1 to 100 characters) and scope.
DELETE /skills/{skill_pid} returns 200 with a null body and removes every version. Do not send a surface field: an API key cannot choose a surface, and the call returns 400 Forbidden field.

Source endpoints

A source indexes the files of a drive. With an API key, only store: "draganddrop" works. Creating one is a 5-step flow, like a file upload:
  1. POST /drives returns the drive, with its id.
  2. GET /drives/{drive_pid}/upload?filename=...&mimetype=... returns a presigned url and fields. filename cannot contain /.
  3. POST the file to that url as multipart/form-data, with fields as form fields, and read the ETag response header. Do not send x-cominty-token to this host.
  4. POST /drives/upload with {"etag": ..., "key": ...}. The key comes from the fields of step 2. The response is the file.
  5. POST /dc/sources with store, scope, and config.drive_id. The first sync starts at once.
The response has status pending or syncing. Poll GET /dc/sources until the source is synced, then search it.
  • Two ids. id is a number: capabilities and scopes take it in source_ids. pid is a string: every path takes it.
  • No single read. There is no GET /dc/sources/{pid}. Read the list and pick. GET /dc/sources includes archived sources. archived=true keeps only those, archived=false hides them.
  • Files. DELETE /files/{file_pid} returns 200 with a null body. GET /files/{file_pid} returns a download URL as a bare JSON string, valid for 5 minutes. Fetch it without the key. After you add or delete a file, call the sync operation.
PUT /dc/sources/{source_pid}/status?operation=... runs one operation: Any other combination returns 400 Operation is not valid. An unknown operation returns 422.
A source created with a key is private to the key owner. A chat that searches it must run with the owner’s user_id, as the agent’s owner shows. With another user_id, the agent’s document tool reports that it has no authorized source. See Sources.

Document endpoint

GET /documents?id=... resolves one document id, such as the one in a <cite document_id="2292#8058" .../> tag of an answer. The id contains a #, so URL-encode it (2292%238058). There is no list. An unknown id returns 404 Not Found.

Tool round cap

POST /chat and POST /chat/{thread_id} accept options.max_steps: an integer >= 1 that caps the tool rounds of that message. Omit it and the server applies its default (60 today, and it may change). The cap is per message and is not stored or returned. Each sub-agent has its own counter. For the behavior, see Limiting agent steps. Reaching the cap is not an error. The message ends with status success and error_code null. Continue with POST /chat/{thread_id}. No thread or message is created when the request is rejected.

One message at a time

A thread processes one message at a time. After POST /chat or POST /chat/{thread_id}, the assistant message is pending (live: true). It only finishes when you read its events to the end with GET /chat/messages/{message_id}/stream, or when you cancel it. Until then, posting another message to the same thread returns 400:
Closing the stream connection does not stop the message. The server keeps it pending. To free the thread:
  1. Find the pending message: GET /chat/{thread_id} returns each message with live. A message with live: true is still pending.
  2. Read it to the end with GET /chat/messages/{message_id}/stream, or cancel it with POST /chat/messages/{message_id}/cancel.
  3. Post the next message.
This 400 is not the concurrency rate limit, which returns 429 Too many concurrent requests when your plan’s limit on simultaneous chats is reached.

Cancel and export a message

Cancelling is not idempotent: a second cancel returns 400. The export response carries no file name. Choose one when you save the bytes.

Files and exports

Uploading a file is a 3-step flow, not a single call:
  1. GET /chat/files/upload: get a presigned upload permission (url + fields) for your filename/mimetype.
  2. Upload the file directly to that url (a storage host, not ds.cominty.com) as a multipart/form-data POST, including fields as form fields alongside the file. Don’t send x-cominty-token to this URL. It’s a different host and doesn’t expect it.
  3. POST /chat/files with the resulting ETag (as etag) and the key from step 1’s fields, to register the file and get back a ConversationFile. Its id is what feeds file_ids on POST /chat or POST /chat/{thread_id}.
GET /chat/files/{file_pid} doesn’t return file bytes. It returns a presigned download URL as a bare JSON string. Fetch that URL directly (again, no x-cominty-token needed there) to get the actual content.
GET /chat/messages/{message_id}/export returns the exported file’s raw bytes (a PDF or DOCX), not JSON. The response content-type shown on this page is a placeholder from the spec, not what you’ll actually receive.
Limits to plan for:
  • A file is at most 500 MiB on the hosted API. The storage host refuses a bigger one, and another deployment may set a lower limit.
  • Your organization has a daily upload quota. When it is used up, the storage host refuses the upload.
  • The download URL is short-lived (5 minutes). Fetch it right away.
  • No endpoint lists or deletes uploaded files.
  • A presigned URL carries a signature in its query string. Do not log it.
Each endpoint page below includes a live playground. Add your token and try a real request.