Skip to content

Assistants

Manage documents

Upload a document once to your library, attach it to as many assistants as need it, and control each attachment independently.

A document lives in one place and gets used in many. You upload it once to your library, then attach it to whichever assistants should answer from it. Each of those attachments can be switched off without touching the document or the other assistants.

That split runs through all ten endpoints, so it is worth getting straight before you write any code. Every parameter and response field is also listed in the Documents API reference.

Two resources, one document#

The document is the content: a name, some text, a size, and timestamps. It exists once, under /documents, and it is the only place the text lives.

The attachment is the link between one document and one assistant. It lives under /assistants/{id}/documents, carries its own enabled flag, and holds no text of its own.

text
                    ┌── attachment ──► Bookings assistant    (enabled)
 "Opening hours" ───┼── attachment ──► Support assistant     (enabled)
   one document     └── attachment ──► Out-of-hours line     (disabled)

Editing the text changes what every attached assistant answers from. Disabling an attachment changes one assistant and leaves the rest alone. Detaching removes the link and the document survives. Those are three different operations on two different resources, and mixing them up is the usual source of confusion here.

Library endpoints#

MethodPathPurpose
POST/documentsCreate a document from inline text or a file upload
GET/documentsList documents, with search and filtering
GET/documents/{id}Get one document's metadata, including where it is used
GET/documents/{id}/contentRead the document's stored text
PATCH/documents/{id}Update the name and/or the content
DELETE/documents/{id}Delete the document (blocked while it is attached)

Attachment endpoints#

MethodPathPurpose
GET/assistants/{id}/documentsList the documents attached to an assistant
POST/assistants/{id}/documentsAttach an existing document
PATCH/assistants/{id}/documents/{documentId}Enable or disable one attachment
DELETE/assistants/{id}/documents/{documentId}Detach the document from the assistant

Responses that carry a payload use the standard response envelope (success, data, metadata), and failures replace data with the error object.

The document library#

Create a document#

POST /documents accepts either a JSON body with the text inline, or a multipart/form-data file upload.

FieldTypeRequiredDescription
namestringYesDisplay name for the document, max 200 characters
filenamestringJSON onlyMust end in .txt, max 255 characters
contentstringJSON onlyThe document text, up to 1 MB
filefileMultipart onlyThe plain-text file to upload

These limits are enforced but absent from the spec

The OpenAPI document declares no request body for POST /documents or PATCH /documents/{id}, so the constraints below cannot be read from it. They are enforced on the server:

FieldConstraint
name1 to 200 characters, required
filename1 to 255 characters, required, and must end in .txt (case-insensitive)
content1 to 1,048,576 characters (a 1 MB ceiling), required

The .txt requirement is a real filename check, not a convention: any other extension is rejected with Filename must end in .txt. Both content types the spec describes (JSON and multipart) accept the same three fields.

bash
curl -X POST https://core-api.heysadie.ai/documents \
  -H "Authorization: ApiKey YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Opening hours",
    "filename": "opening-hours.txt",
    "content": "We are open Monday to Friday, 9am to 5pm."
  }'

Or as a file upload:

bash
curl -X POST https://core-api.heysadie.ai/documents \
  -H "Authorization: ApiKey YOUR_API_KEY" \
  -F "name=Opening hours" \
  -F "[email protected]"

The response carries the document object, the same shape returned by GET /documents/{id} and PATCH /documents/{id}.

Response
{
  "success": true,
  "data": {
    "id": "0195f1e2-0000-0000-0000-000000000000",
    "name": "Opening hours",
    "sizeBytes": 41,
    "createdAt": "2026-08-17T09:12:04.000Z",
    "updatedAt": "2026-08-17T09:12:04.000Z",
    "usedByCount": 0,
    "usedByAssistantIds": []
  },
  "metadata": {
    "timestamp": "2026-08-17T09:12:04.000Z",
    "version": "1.0",
    "path": "/documents"
  }
}
FieldTypeDescription
idstringThe document's UUID. Use it everywhere else on this page.
namestringThe display name you gave it.
sizeBytesnumberSize of the stored content, in bytes.
createdAtstringISO 8601 UTC. See conventions.
updatedAtstringISO 8601 UTC. Moves every time you update the document.
usedByCountnumberHow many assistants the document is attached to.
usedByAssistantIdsarrayThe IDs of those assistants.

List documents#

GET /documents returns a page of documents plus a total.

ParameterTypeRequiredDescription
limitnumberNoPage size. Default 50, maximum 200.
offsetnumberNoItems to skip. Default 0.
searchstringNoReturn only documents matching this text.
usedByAssistantstringNoReturn only the documents attached to this assistant, by assistant ID.
bash
curl "https://core-api.heysadie.ai/documents?limit=25&search=hours" \
  -H "Authorization: ApiKey YOUR_API_KEY"
Response
{
  "success": true,
  "data": {
    "documents": [
      {
        "id": "0195f1e2-0000-0000-0000-000000000000",
        "name": "Opening hours",
        "sizeBytes": 41,
        "createdAt": "2026-08-17T09:12:04.000Z",
        "updatedAt": "2026-08-17T09:12:04.000Z",
        "usedByCount": 2
      }
    ],
    "total": 1
  },
  "metadata": {
    "timestamp": "2026-08-17T09:20:11.000Z",
    "version": "1.0",
    "path": "/documents"
  }
}

Two things the list does not carry. It returns usedByCount but not usedByAssistantIds; to learn which assistants use a document, fetch it individually. And it never returns the text; that is what GET /documents/{id}/content is for. Page through a long library by advancing offset until you have seen total items, as described in conventions.

Get one document#

GET /documents/{id} returns the full document object, including usedByAssistantIds.

bash
curl https://core-api.heysadie.ai/documents/0195f1e2-0000-0000-0000-000000000000 \
  -H "Authorization: ApiKey YOUR_API_KEY"

usedByAssistantIds is the field that makes this endpoint worth calling before you change anything. It answers "which assistants would this edit affect?" and it is the exact list you have to work through before a document can be deleted.

Read a document's text#

GET /documents/{id}/content returns the document's stored text rather than a metadata record.

bash
curl https://core-api.heysadie.ai/documents/0195f1e2-0000-0000-0000-000000000000/content \
  -H "Authorization: ApiKey YOUR_API_KEY"

Raw text response

This endpoint returns 200 with Content-Type: text/plain; charset=utf-8. The body is the stored document text itself, outside the usual JSON envelope, so read it with response.text().

Update a document#

PATCH /documents/{id} updates the name, the content, or both, using the same JSON or multipart body as create.

bash
curl -X PATCH https://core-api.heysadie.ai/documents/0195f1e2-0000-0000-0000-000000000000 \
  -H "Authorization: ApiKey YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "content": "We are open every day, 9am to 9pm." }'

The response is the document object again, with a new updatedAt and, if you replaced the content, a new sizeBytes. The id does not change, so every existing attachment still points at the same document; there is nothing to re-attach after an edit.

Delete a document#

DELETE /documents/{id} soft-deletes the document. The spec publishes a success response with no schema for this operation, so branch on the status code rather than on a body.

bash
curl -X DELETE https://core-api.heysadie.ai/documents/0195f1e2-0000-0000-0000-000000000000 \
  -H "Authorization: ApiKey YOUR_API_KEY"

Detach before you delete

The request fails with 409 while the document is still attached to any assistant. Read usedByAssistantIds from GET /documents/{id}, detach the document from each assistant on that list, then delete it. If you only want an assistant to stop using the document, disabling the attachment is the smaller and reversible move.

Attaching documents to an assistant#

An attachment carries no content of its own: the assistant exists, the document exists, and the attachment links the two and records whether the link is live. That is why attaching takes a single field.

Attach a document#

POST /assistants/{id}/documents

FieldTypeRequiredDescription
documentIdstringYesUUID of the document to attach
bash
curl -X POST https://core-api.heysadie.ai/assistants/0195f1e2-1111-0000-0000-000000000000/documents \
  -H "Authorization: ApiKey YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "documentId": "0195f1e2-0000-0000-0000-000000000000" }'

Empty success responses

  • Attaching a document returns 201 with an empty body.
  • Detaching a document returns 204 with an empty body.
  • Deleting an unattached document returns 204 with an empty body.

Check response.ok; do not call response.json() for these operations. Use GET /assistants/{id}/documents when you need the resulting attachment state.

A new attachment starts with enabled: true, appears in GET /assistants/{id}/documents, and counts toward the document's usedByCount.

List an assistant's documents#

GET /assistants/{id}/documents returns what this one assistant is attached to.

bash
curl https://core-api.heysadie.ai/assistants/0195f1e2-1111-0000-0000-000000000000/documents \
  -H "Authorization: ApiKey YOUR_API_KEY"
Response
{
  "success": true,
  "data": {
    "documents": [
      {
        "id": "0195f1e2-0000-0000-0000-000000000000",
        "name": "Opening hours",
        "sizeBytes": 41,
        "attachedAt": "2026-08-17T09:31:47.000Z",
        "enabled": true
      }
    ]
  },
  "metadata": {
    "timestamp": "2026-08-17T09:40:02.000Z",
    "version": "1.0",
    "path": "/assistants/0195f1e2-1111-0000-0000-000000000000/documents"
  }
}

This is the attachment view, and its shape differs from the library list:

FieldTypeDescription
idstringThe document's UUID.
namestringThe document's display name.
sizeBytesnumberSize of the stored content, in bytes.
attachedAtstringISO 8601 UTC (when this assistant was attached to the document).
enabledbooleanWhether this attachment is live for this assistant.

There is no total here and no usedByCount: this endpoint answers "what does this assistant know?", not "how widely is this document used?". For the latter, use GET /documents/{id}.

Enable or disable an attachment#

PATCH /assistants/{id}/documents/{documentId} flips one attachment on or off without removing it.

FieldTypeRequiredDescription
enabledbooleanYesWhether the attached document is included in the assistant's knowledge base
bash
curl -X PATCH https://core-api.heysadie.ai/assistants/0195f1e2-1111-0000-0000-000000000000/documents/0195f1e2-0000-0000-0000-000000000000 \
  -H "Authorization: ApiKey YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": false }'

The response returns the attachment itself, nested under data.document:

Response
{
  "success": true,
  "data": {
    "document": {
      "assistantId": "0195f1e2-1111-0000-0000-000000000000",
      "documentId": "0195f1e2-0000-0000-0000-000000000000",
      "enabled": false,
      "attachedAt": "2026-08-17T09:31:47.000Z"
    }
  },
  "metadata": {
    "timestamp": "2026-08-17T10:02:19.000Z",
    "version": "1.0",
    "path": "/assistants/0195f1e2-1111-0000-0000-000000000000/documents/0195f1e2-0000-0000-0000-000000000000"
  }
}

Both IDs come back, which makes the change easy to log: assistantId and documentId identify the attachment, enabled is its new state, and attachedAt is unchanged; toggling does not re-attach anything. The flag applies to this assistant only; every other assistant attached to the same document is unaffected.

Detach a document#

DELETE /assistants/{id}/documents/{documentId} removes the link.

bash
curl -X DELETE https://core-api.heysadie.ai/assistants/0195f1e2-1111-0000-0000-000000000000/documents/0195f1e2-0000-0000-0000-000000000000 \
  -H "Authorization: ApiKey YOUR_API_KEY"

The document stays in your library and every other assistant keeps its own attachment. Detach when an assistant should stop using a document for good, or as the step that unblocks DELETE /documents/{id}. Like attaching, this operation publishes no response schema, so confirm the result with GET /assistants/{id}/documents.

Next steps#