Reference
Base URL: https://chat.app.urai.dev/api/v1. Every request carries
Authorization: Bearer sk-urai-....
Endpoints
Section titled “Endpoints”| Method and path | Purpose |
|---|---|
POST /extractions |
Send a file. Returns 202 and a pending record, or 200 and an earlier result for identical bytes. |
GET /extractions/{id} |
Read one record. Carries markdown when the status is ready. |
DELETE /extractions/{id} |
Delete the record and the stored bytes. Returns 204. |
There is no list endpoint. Keep the id from the POST reply.
Request body
Section titled “Request body”POST /extractions takes multipart/form-data.
| Part | Required | Notes |
|---|---|---|
| The file | yes | The first part that has a filename. The part name does not matter. Urai reads the filename and the content type from it. |
A second part with a filename fails the request. A part with no filename is ignored.
The extraction object
Section titled “The extraction object”{ "id": "8f0a2c31-4d7e-4f0a-9a6e-6b0d2f8c5411", "status": "ready", "name": "billing-policy.pdf", "mime_type": "application/pdf", "size_bytes": 184320, "page_count": 18, "chunk_count": 96, "markdown": "# Billing Policy 2026\n...", "created_at": "2026-08-14T18:45:53Z", "expires_at": "2026-08-15T18:45:53Z"}| Field | Type | Notes |
|---|---|---|
id |
string | UUID. Use it to poll and to delete. |
status |
string | See the table below. |
name |
string | The filename you sent. |
mime_type |
string | The type Urai detected, which can differ from the type you declared. |
size_bytes |
integer | Size of the file you sent. |
page_count |
integer | Present for PDF and PowerPoint only. |
chunk_count |
integer | Pieces the text was split into. Reported as a shape signal. Nothing is indexed. |
markdown |
string | The extracted text. Present when status is ready. |
error |
string | Why the read failed. Present when status is failed. |
created_at |
string | RFC 3339 timestamp. |
expires_at |
string | When Urai deletes the result. |
A field with no value is left out. Your code reads status first, then the field
that belongs to that status.
Statuses
Section titled “Statuses”| Status | Meaning |
|---|---|
pending |
Accepted and queued. |
processing |
Urai is reading the file. |
ready |
Terminal. markdown carries the text. |
failed |
Terminal. error says what went wrong. |
Poll until the status is ready or failed. No other value is terminal.
Errors
Section titled “Errors”The error shape is the one the Chat Completions API uses.
{ "error": { "message": "No such extraction. It may have expired.", "type": "invalid_request_error", "code": "extraction_not_found" }}| Status | Code | Cause |
|---|---|---|
| 400 | missing_file |
No part of the request had a filename. |
| 400 | too_many_files |
More than one file in the request. |
| 400 | invalid_body |
Urai could not read the multipart body. |
| 400 | empty_document |
The file has no bytes. |
| 401 | missing_api_key |
No Authorization header. |
| 401 | invalid_api_key |
Urai does not recognise the key. |
| 401 | api_key_disabled |
Someone disabled the key. |
| 401 | expired_api_key |
The key is past its expiry. |
| 403 | product_not_enabled |
The key is not enabled for the extraction product. |
| 404 | extraction_not_found |
Unknown id, an expired result, or an id from another organization. |
| 415 | unsupported_file_type |
No reader handles this file type. |
| 500 | (none) | Server error. Urai logs the detail and does not return it. |
A file that Urai accepts and then cannot read is not an HTTP error. The request
returns 202, and the record ends as failed with the reason in error.
404 gives the same answer for an expired result, another organization’s id, and
an id that never existed. One caller cannot learn that another caller’s document
exists.
Limits
Section titled “Limits”| Limit | Value |
|---|---|
| Files in one request | 1 |
| Size of one file | 100 MB |
| Retention | 24 hours from created_at |
| Billing unit | One page. Word and Markdown are counted at 3,000 characters for one page, one page at least. |