🚀 Simplify your work by integrating Haufe Copilot via API
Guides

Use Your Own Documents

Learn how to upload text documents and integrate them to enable your assistant to deliver context-aware responses based on the content of your files.

You can upload files. The Copilot uses them as extra context when it generates answers. This guide describes the full lifecycle of a file.

Supported File Types

TypeMIME Type
PDFapplication/pdf
Word (DOCX)application/vnd.openxmlformats-officedocument.wordprocessingml.document
Excel (XLSX)application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
CSVtext/csv
Plain Texttext/plain

Maximum file size: 10 MB.

Get a Signed Upload URL

Request a pre-signed URL to upload your file. The API returns a file_id and a file_url. Use the file_url to PUT the file content.

GET https://api.haufe.ai/agents/v1/files/signed-url

Query Parameters

ParameterTypeRequiredDescription
filenamestringYesName of the file including extension
persistbooleanNoWhen true (default), the file is stored permanently. When false, the file is automatically deleted after 24 hours.
Get a signed upload URL
curl --request GET \
  --url "https://api.haufe.ai/agents/v1/files/signed-url?filename=report.pdf&persist=true" \
  --header 'api-key: <API_KEY>' \
  --header 'user-id: <USER_ID>'

Response

{
  "file_url": "https://example-bucket.s3.amazonaws.com/example-file.txt",
  "expires_in": 600,
  "file_id": "123e4567-e89b-12d3-a456-426614174000",
  "file_limits": {
    "max_size": 10485760,
    "allowed_types": [
      "text/csv",
      "text/plain",
      "application/pdf",
      "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
      "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
    ]
  }
}
info

The signed URL expires after the time in expires_in (typically 600 seconds). If the upload does not finish in time, request a new URL.

Upload the File

Use the returned file_url to upload the file content via a PUT request.

Upload the file
curl --request PUT \
  --url "<FILE_URL>" \
  --header 'Content-Type: application/pdf' \
  --data-binary @report.pdf
warning

Make sure the Content-Type header matches one of the allowed_types from the signed URL response. A file with an unsupported type causes a processing error.

Check Processing Status

After the upload, the service processes the file asynchronously. Check the status to confirm the file is ready.

GET https://api.haufe.ai/agents/v1/files/{file_id}/status

Check processing status
curl --request GET \
  --url "https://api.haufe.ai/agents/v1/files/<FILE_ID>/status" \
  --header 'api-key: <API_KEY>' \
  --header 'user-id: <USER_ID>'

Response

{
  "processing_status": "PROCESSED",
  "warnings": {
    "length": false,
    "infected": false,
    "empty": false
  }
}

processing_status currently takes one of these values:

processing_statusMeaning
PROCESSEDTerminal. The file is ready to be referenced in a message
FAILEDTerminal. Processing did not succeed — see processing_steps and validation_steps for the cause
PENDING_UPLOADIn progress. The upload has not been completed yet
PROCESSINGIn progress. Text extraction is running
ANTIVIRUS_SCANNINGIn progress. The file is being scanned
info

Poll this endpoint until processing_status is PROCESSED or FAILED. Do this before you reference the file in a message. Treat every other value as "not finished yet". Do not branch on the individual in-progress states — they are short-lived, and the list above can grow without notice. On FAILED, read the processing_steps and validation_steps arrays to find the cause. On PROCESSED, also check the warnings object. The service rejects a file with empty: true or length: true when you attach it to a message. See Message Validation for the error codes.

Verify the Upload

List All Files

Retrieve a paginated list of all persisted files for your user. This list includes only files uploaded with persist=true. Temporary files (persist=false) do not appear.

GET https://api.haufe.ai/agents/v1/files

Query Parameters

ParameterTypeRequiredDescription
pageintegerNoPage number (1-based). Defaults to 1
limitintegerNoNumber of files per page (1–100). Defaults to 30
List all files
curl --request GET \
  --url "https://api.haufe.ai/agents/v1/files?page=1&limit=30" \
  --header 'api-key: <API_KEY>' \
  --header 'user-id: <USER_ID>'
Response
{
  "files": [
    {
      "file_id": "9c3e00fe-1b42-413a-bf44-39ad9491ad86",
      "file_name": "report.pdf"
    },
    {
      "file_id": "cbfe557e-144e-43f3-823f-d72be00084a2",
      "file_name": "expenses.xlsx"
    }
  ],
  "page": 1,
  "limit": 30,
  "total": 2
}

Retrieve File Content

Fetch the extracted text of a file to confirm the service processed it correctly.

GET https://api.haufe.ai/agents/v1/files/{file_id}

Retrieve file content
curl --request GET \
  --url "https://api.haufe.ai/agents/v1/files/<FILE_ID>" \
  --header 'api-key: <API_KEY>' \
  --header 'user-id: <USER_ID>'

The endpoint returns the extracted text of the file as a plain string.

Use with Chat Completions

After the service processes a file, you can reference it in a single stateless request. Add the file as an attachments entry on a user message:

warning

The top-level user_id must match the user-id header you used during file upload. Do not set user_id on the attachment itself. At most 3 attachments per message are allowed.

Chat completions with attachment
curl --request POST \
  --url https://api.haufe.ai/agents/v1/chat/completions \
  --header 'content-type: application/json' \
  --header 'api-key: <API_KEY>' \
  --data '{
    "assistant_id": "<ASSISTANT_ID>",
    "user_id": "<USER_ID>",
    "messages": [
      {
        "role": "user",
        "content": "Summarize the attached report.",
        "attachments": [
          {
            "file_id": "<FILE_ID>"
          }
        ]
      }
    ],
    "meta_data": {
      "user_data": {
        "licence": "<LICENSE_ID>"
      }
    }
  }'

Use with Threads

For conversational workflows, create a thread, post a message with the attachment, and run the thread to get a response.

warning

The user_id on the thread must match the user-id header you used during file upload. Do not set user_id on the attachment itself. At most 3 attachments per message are allowed.

1. Create a thread:

Create a thread with user_id
curl --request POST \
  --url https://api.haufe.ai/agents/v1/threads \
  --header 'content-type: application/json' \
  --header 'api-key: <API_KEY>' \
  --data '{
    "assistant_id": "<ASSISTANT_ID>",
    "user_id": "<USER_ID>"
  }'

2. Post a message with the attachment:

Thread message with attachment
curl --request POST \
  --url "https://api.haufe.ai/agents/v1/threads/<THREAD_ID>/messages" \
  --header 'content-type: application/json' \
  --header 'api-key: <API_KEY>' \
  --data '{
    "role": "user",
    "content": "Summarize the attached report.",
    "attachments": [
      {
        "file_id": "<FILE_ID>"
      }
    ]
  }'

3. Run the thread:

Run the thread
curl --request POST \
  --url "https://api.haufe.ai/agents/v1/threads/<THREAD_ID>/run" \
  --header 'content-type: application/json' \
  --header 'api-key: <API_KEY>' \
  --data '{
    "meta_data": {
      "user_data": {
        "licence": "<LICENSE_ID>"
      }
    }
  }'

Check Which Attachments Were Used

The service loads the file content when it generates the answer, not when you attach the file. So the assistant message reports, per file, whether its content reached the answer. This is in meta_data.attachments:

{
  "role": "assistant",
  "content": "The report covers ...",
  "meta_data": {
    "attachments": [
      { "file_id": "<FILE_ID_1>", "file_name": "report.pdf", "status": "ok" },
      { "file_id": "<FILE_ID_2>", "file_name": "notes.txt", "status": "not_found" }
    ]
  }
}
FieldMeaning
file_idThe file_id you attached to the message
file_nameOriginal name of the file, or null when it is unknown to the service
statusWhether the content was loaded and considered for the answer, see below
statusMeaning
okThe content was loaded and considered for the answer
not_foundThe file no longer exists on the storage service and was therefore not considered
errorThe file exists but could not be loaded, e.g. the storage service was temporarily unreachable
emptyThe file exists but no text could be extracted from it

error and empty do not mean the file is gone. The file is still there, but its content could not be used for this answer.

info

A file that you deleted via DELETE /files/{file_id} does not appear in the list. The service skips it on purpose and does not report it as a failure. In streaming mode this data arrives in a separate chunk with an empty content.

Like the rest of meta_data, this field is part of the assistant response schema. Use GET /assistants/{assistant_id} to check whether your assistant returns it.

Delete a File

Remove a file that you no longer need. After deletion, you cannot reference the file in new messages. The service also stops using it for response generation in threads that contained it.

DELETE https://api.haufe.ai/agents/v1/files/{file_id}

Delete a file
curl --request DELETE \
  --url "https://api.haufe.ai/agents/v1/files/<FILE_ID>" \
  --header 'api-key: <API_KEY>' \
  --header 'user-id: <USER_ID>'

Returns 204 No Content on success with an empty response body.

Next Steps

On this page