Documents API
Upload, list, read, update, delete, download and search documents with the Muin REST API: endpoints, parameters, example requests and responses, and errors.
The Documents API lets your own code put files into Muin’s document library and read them back, including the data Muin extracts from each file.
When you’d use this
- Send scanned receipts or invoices from another system into Muin for extraction.
- Pull a list of new documents, with their extracted data, into a report or spreadsheet.
- Tag or reclassify documents in bulk from a script.
- Search the text of your documents from an external tool.
Before you start
| Item | Value |
|---|---|
| Base URL | https://muin-api.falaah.ai/api/v1 |
| Header | Authorization: Bearer muin_<64 hex characters> |
Read endpoints (GET) |
A key with the Read or Write scope |
POST, PATCH, DELETE |
A key with the Write scope |
| Module access | The person who created the key needs access to the Documents module. The key acts with that person’s role. |
How to create a key, the scopes, and the daily request limit: Authentication.
The examples below read the key from an environment variable:
export MUIN_API_KEY="muin_your_api_key_here"
Endpoints
| Action | Request |
|---|---|
| Upload a document | POST /api/v1/documents/upload |
| List documents | GET /api/v1/documents/ |
| Get one document | GET /api/v1/documents/{document_id} |
| Update a document | PATCH /api/v1/documents/{document_id} |
| Delete a document | DELETE /api/v1/documents/{document_id} |
| Get a download link | GET /api/v1/documents/{document_id}/download |
| Search document text | POST /api/v1/documents/search |
Upload a document
POST /api/v1/documents/upload
Content-Type: multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
file |
file | Yes | The file. Up to 50 MB. Empty files are refused. |
document_type |
string | No | A document type key such as invoice. Leave it out and Muin classifies the file during processing. |
Accepted file types: PDF, DOC, DOCX, XLS, XLSX, ODT, ODS, ODP, CSV, TXT, MD, RTF, JPG, JPEG, PNG, GIF, WEBP, BMP, TIFF, SVG and HEIC.
Muin checks the file type from the Content-Type of the file part, and checks that the file’s content matches it. Set the type explicitly in curl with ;type=... so it is not sent as application/octet-stream.
Uploads are limited to 30 per hour per user. Each upload also counts against your plan’s document and storage limits.
curl -X POST https://muin-api.falaah.ai/api/v1/documents/upload \
-H "Authorization: Bearer $MUIN_API_KEY" \
-F "file=@invoice-1042.pdf;type=application/pdf" \
-F "document_type=invoice"
Response (201 Created):
{
"id": "3f6c1e8a-9b2d-4c7e-8f10-5a2b7d9e4c31",
"filename": "invoice-1042.pdf",
"status": "pending",
"message": "Document uploaded successfully"
}
Knowing when processing is done
Processing runs in the background after the upload returns. Read the document’s status with Get one document, or subscribe to the document.processed webhook instead of polling.
status |
Meaning |
|---|---|
pending |
Uploaded, not yet picked up for processing |
processing |
Muin is extracting data. Also shown while a failed extraction is being retried automatically. |
ready |
Processing finished. extracted_data is filled in. |
review_needed |
Processing finished, but Muin’s classification has low confidence and a person should check it |
reviewed |
A person confirmed the classification |
extraction_failed |
Extraction failed and all automatic retries are used up |
error |
Processing failed. See error_message. |
Processing is done once status is anything other than pending or processing.
List documents
GET /api/v1/documents/
Returns the documents in your organization, 20 per page by default, newest first unless you set sort_by.
| Parameter | Type | Required | Description |
|---|---|---|---|
page |
integer | No | Page number, starting at 1. Default 1. |
page_size |
integer | No | Items per page, 1 to 100. Default 20. (per_page is accepted as an alias.) |
status |
string | No | Processing status. Comma-separated for several, for example ready,review_needed. |
document_type |
string | No | Document type key. Comma-separated for several. |
module |
string | No | Business module, for example finance. Comma-separated for several. |
business_status |
string | No | Workflow status, for example pending_review or approved. Comma-separated for several. |
ai_audience |
string | No | none, internal or public. Who the AI assistant may quote the document to. |
uploaded_by |
UUID | No | Only documents uploaded by this user. |
tags |
string | No | Comma-separated tags. A document must have all of them. |
date_from |
date | No | Created on or after this date (YYYY-MM-DD). |
date_to |
date | No | Created on or before this date (YYYY-MM-DD). |
origin |
string | No | uploaded, template, composer, connector or all. |
filter |
string | No | Free-text match on filename, type, tags, notes, extracted data and document text. |
sort_by |
string | No | name, created_at, updated_at, module, document_type or file_size. |
sort_order |
string | No | asc or desc. |
curl "https://muin-api.falaah.ai/api/v1/documents/?status=ready&document_type=invoice&page_size=50" \
-H "Authorization: Bearer $MUIN_API_KEY"
Response (trimmed to one document):
{
"documents": [
{
"id": "3f6c1e8a-9b2d-4c7e-8f10-5a2b7d9e4c31",
"filename": "invoice-1042.pdf",
"original_filename": "invoice-1042.pdf",
"mime_type": "application/pdf",
"file_size": 184233,
"status": "ready",
"document_type": "invoice",
"module": "finance",
"tags": ["utilities"],
"created_at": "2026-10-05T17:30:28Z",
"updated_at": "2026-10-05T17:31:02Z"
}
],
"total": 1,
"page": 1,
"page_size": 50,
"total_pages": 1,
"has_more": false,
"total_unfiltered": null
}
Each item in documents has the same fields as Get one document. Keep requesting the next page while has_more is true.
Get one document
GET /api/v1/documents/{document_id}
| Parameter | Type | Required | Description |
|---|---|---|---|
document_id |
UUID (path) | Yes | The document’s id. |
curl https://muin-api.falaah.ai/api/v1/documents/3f6c1e8a-9b2d-4c7e-8f10-5a2b7d9e4c31 \
-H "Authorization: Bearer $MUIN_API_KEY"
Response (trimmed):
{
"id": "3f6c1e8a-9b2d-4c7e-8f10-5a2b7d9e4c31",
"filename": "invoice-1042.pdf",
"original_filename": "invoice-1042.pdf",
"mime_type": "application/pdf",
"file_size": 184233,
"status": "ready",
"document_type": "invoice",
"module": "finance",
"extracted_data": {
"vendor_name": "City Water Utility",
"total_amount": 412.5,
"invoice_date": "2026-09-28"
},
"tags": ["utilities"],
"notes": null,
"ai_audience": "internal",
"extraction_quality": 0.93,
"error_message": null,
"business_status": "pending_review",
"business_status_updated_at": "2026-10-05T17:31:02Z",
"workflow_instance_id": null,
"created_at": "2026-10-05T17:30:28Z",
"updated_at": "2026-10-05T17:31:02Z",
"uploaded_by": {
"id": "a1d4b7c2-0e5f-4a83-9c61-7b2e8f3d1a90",
"name": "Aisha Rahman"
},
"source_template_id": null,
"source_assembly_project_id": null,
"source_connector_id": null,
"source_assembly_project": null
}
| Field | Description |
|---|---|
status |
Processing status (see the table under Upload). |
extracted_data |
The structured data Muin extracted. Its keys depend on the document type. |
extraction_quality |
Confidence from 0.0 to 1.0. null until processed. |
error_message |
Why processing failed. null on success. |
business_status |
Workflow status, for example pending_review, approved, rejected or paid. |
uploaded_by |
The uploader. null for documents created by the system or a connector. |
source_template_id, source_assembly_project_id, source_connector_id |
Set when the document came from a template, the document composer, or a cloud connector. |
Update a document
PATCH /api/v1/documents/{document_id}
Content-Type: application/json
Changes the document’s details, not the file. Send only the fields you want to change. Only the uploader, someone the document is shared with for editing, or an owner or admin can update it.
| Field | Type | Required | Description |
|---|---|---|---|
document_type |
string | No | A registered document type key, for example invoice. Unknown keys are refused. |
module |
string | No | Business module, for example finance. |
tags |
array of strings | No | Replaces the document’s tags. |
notes |
string | No | Up to 2,000 characters. |
extracted_data |
object | No | Corrected extracted data. |
ai_audience |
string | No | none, internal or public. public lets the AI assistant quote this document to anyone, including visitors who are not signed in. |
curl -X PATCH https://muin-api.falaah.ai/api/v1/documents/3f6c1e8a-9b2d-4c7e-8f10-5a2b7d9e4c31 \
-H "Authorization: Bearer $MUIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tags": ["utilities", "fy2026"],
"notes": "Paid by check 2214"
}'
The response is the full updated document, in the same shape as Get one document.
Delete a document
DELETE /api/v1/documents/{document_id}
Only the uploader or an owner or admin can delete a document. Muin marks the record as deleted (it no longer appears in lists or lookups) and removes the file from storage. Any signature requests still open on the document are voided.
| Parameter | Type | Required | Description |
|---|---|---|---|
document_id |
UUID (path) | Yes | The document’s id. |
curl -X DELETE https://muin-api.falaah.ai/api/v1/documents/3f6c1e8a-9b2d-4c7e-8f10-5a2b7d9e4c31 \
-H "Authorization: Bearer $MUIN_API_KEY"
Response:
{
"message": "Document deleted successfully"
}
Download a document
GET /api/v1/documents/{document_id}/download
Returns a signed link to the original file. It does not return the file itself.
| Parameter | Type | Required | Description |
|---|---|---|---|
document_id |
UUID (path) | Yes | The document’s id. |
expires |
integer (query) | No | Seconds the link stays valid, 60 to 86,400. Default 3600. |
intent |
string (query) | No | preview (default) or download. download records the download in the audit log. |
curl "https://muin-api.falaah.ai/api/v1/documents/3f6c1e8a-9b2d-4c7e-8f10-5a2b7d9e4c31/download?expires=600&intent=download" \
-H "Authorization: Bearer $MUIN_API_KEY"
Response:
{
"url": "https://.../invoice-1042.pdf?...",
"expires_in": 600
}
Fetch url before it expires. The link is signed, so it does not need the Authorization header:
curl -o invoice-1042.pdf "<url from the response>"
Search document text
POST /api/v1/documents/search
Content-Type: application/json
Finds passages in your documents that are close in meaning to your query. Results only include documents from modules the key’s creator can view. This is a POST, so it needs a key with the Write scope even though it changes nothing.
| Field | Type | Required | Description |
|---|---|---|---|
query |
string | Yes | What to look for, 1 to 1,000 characters. |
limit |
integer | No | Maximum results, 1 to 50. Default 10. |
threshold |
number | No | Minimum similarity, 0.0 to 1.0. Default 0.7. Lower it to get more, looser matches. |
curl -X POST https://muin-api.falaah.ai/api/v1/documents/search \
-H "Authorization: Bearer $MUIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "water bill for September",
"limit": 5
}'
Response:
{
"query": "water bill for September",
"results": [
{
"document_id": "3f6c1e8a-9b2d-4c7e-8f10-5a2b7d9e4c31",
"document_filename": "invoice-1042.pdf",
"chunk_index": 0,
"content": "City Water Utility. Service period September 1 to September 30...",
"similarity": 0.84
}
],
"total": 1
}
Only processed documents can be found. A document still in pending or processing has no searchable text yet.
When it goes wrong
Errors from these endpoints return a JSON body with an error object holding code, message and status. Request-format errors (422 from a bad parameter) list the field that failed.
| Status | Why | What to do |
|---|---|---|
400 |
The uploaded file is empty. | Send a file with content. |
401 |
The key is missing, wrong, revoked or expired. | See Authentication. |
403 |
The key has only Read scope and you sent POST, PATCH or DELETE. |
Use a key with Write. |
403 |
The key’s creator has no access to the Documents module, or is not allowed to edit or delete this document. | Create the key as an owner or admin, or as someone with access to the document. |
403 |
Your plan’s document or storage limit is reached. | Delete documents or upgrade your plan. |
404 |
No document with that id in your organization, or it was deleted. |
Check the id with List documents. |
422 |
Upload: unsupported file type, file over 50 MB, or the content does not match the declared type. | Send an accepted type with the correct type= in the file part. |
422 |
A parameter or body field is invalid, for example a malformed UUID, an unknown document_type on update, or page_size over 100. |
Fix the field named in the error. |
429 |
More than 30 uploads in an hour, too many searches in a short time, or your plan’s daily request limit is reached. | Wait and retry. For the daily limit, see Authentication. |
Next step
- Authentication: create the API key these calls need, and choose its scope.
- Webhooks: get a
document.processedevent instead of polling forstatus.