Guides
UniAI has one endpoint. Everything below is the same request with a different type in the path or a different flag in the form.
curl -X POST https://cloud.unidoc.io/api/uniai/extract/{type} \
-H "X-API-KEY: $UNIDOC_LICENSE_API_KEY" \
-F "[email protected]"New to UniAI? Getting started covers creating a key and making the first call.
The request
| Part | Where | Notes |
|---|---|---|
| API key | X-API-KEY header | Required. Not an Authorization header. |
{type} | Path | invoice or resume. |
file | Form field | Required. A PDF, three pages or fewer. |
is_scanned | Form field | true renders the pages as images as well. |
debug_prompt | Form field | true prefixes the response with the prompt, which makes the body non-JSON. Debugging only. |
The body is multipart/form-data. Anything other than invoice or resume in the path is
rejected before the file is read.
The response
A success is HTTP 200 with a JSON object in the body, assuming the default request. Two things about it will catch out a client written against the obvious assumptions.
The Content-Type is text/plain; charset=utf-8, not application/json, even though the
body is JSON. Parse the body and ignore the header.
The body is streamed as the model produces it, and the 200 is sent before generation starts. A failure partway through cannot change the status code, so a truncated body is possible on a response that looked successful. Decoding the JSON is what tells you the response is complete; a decode error on a 200 means you got a partial answer.
Retrying that is reasonable, and it is billable. Credits are committed before generation, so the truncated attempt was charged and each retry charges the pages again. Cap the attempts.
debug_prompt=true breaks this contract on purpose: it writes the prompt text ahead of the
model output, so the body is no longer a standalone JSON object and the decode test above will
always fail. Use it while debugging by eye, never in a client that parses the response.
Errors are a JSON envelope instead:
{"success":false,"message":"file is required","errors":null}Where to look
| Guide | Covers |
|---|---|
| Extract an Invoice | The invoice schema field by field, and what sparse output looks like. |
| Extract a Resume | The resume schema, and why the arrays are the useful part. |
| Scanned Documents | Telling a scan from a text PDF, and the is_scanned flag. |
Fields are best-effort
This applies to both types, so it goes here once rather than on both pages. The response contains what the model found and nothing else. Keys are omitted rather than set to null, and that includes fields the schema marks required. Any client that indexes straight into the response will panic on a document that happened to be missing a total.
Check before reading, and treat a missing field as “not found in this document” rather than as an error. Why is a field missing? goes into what tends to cause it.