Use the AI SDK¶
The package @doc-cheap/ai-sdk turns this API into five tools for the
AI SDK. A model that has them can recognize a passport,
national ID card or driver licence and read the balance. It can also list,
read back and delete the scans an account has stored. This guide covers installing it,
passing a key, what each tool sends, and what the model sees of the answer.
The tools are a thin client of the public HTTP API. They hold no data of their
own. One recognized document costs one credit, $0.01. An unreadable image, an
empty frame or an unsupported document costs nothing, and meta.billed says
which happened.
Install it¶
npm install @doc-cheap/ai-sdk ai zodai (version 6 or 7) and zod (3.25.76 or later, or 4) are peer
dependencies, so the package uses the copies your project already has. It
needs Node.js 20 or later.
Give the tools to a model¶
import { generateText, isStepCount } from "ai";
import { docCheapTools } from "@doc-cheap/ai-sdk";
const { text } = await generateText({
model: "openai/gpt-5-mini",
tools: docCheapTools(),
stopWhen: isStepCount(3),
prompt:
"Read the passport at https://example.com/passport.jpg and tell me " +
"the holder's name and when the passport expires.",
});docCheapTools() returns all five tools under the names below. To give a
model only some of them, build each one on its own:
import { getUsage, scanDocument } from "@doc-cheap/ai-sdk";
const tools = { scanDocument: scanDocument(), getUsage: getUsage() };Pass a key¶
Set DOC_CHEAP_API_KEY to a live key, or pass apiKey to the builder. The
environment is read when a tool runs, not when it is built. Every builder takes
the same three options:
| Option | Default | Meaning |
|---|---|---|
apiKey |
DOC_CHEAP_API_KEY, then sk_sandbox_public |
The key sent as a Bearer token. |
baseUrl |
DOC_CHEAP_API_BASE, then https://api.doc.cheap |
The API's address. |
fetch |
the runtime's own | The fetch the API calls go through. |
Without a key, the public sandbox key is used. It gives 10 free recognized documents per address in all, and at most 10 requests per address an hour, whatever their answer. Registering gives 100 free documents every month. See from sandbox to live for the switch.
The tools¶
| Tool | Request | What it does |
|---|---|---|
scanDocument |
POST /v1/scans |
Recognizes a passport, ID card or driver licence. |
getScan |
GET /v1/scans/{id} |
Reads back a stored result. Never charges. |
deleteScan |
DELETE /v1/scans/{id} |
Deletes a stored result for good. |
listScans |
GET /v1/scans |
Lists stored results, newest first, 1 to 100 a page. |
getUsage |
GET /v1/usage |
The balance and this period's counters. |
scanDocument takes the image as exactly one of imageUrl and imageBase64.
The other inputs map onto the request. expectCountry, returnPortrait and
retainHours become the scan options, and
reference is echoed back. idempotencyKey is sent as the Idempotency-Key
header, so a retried call cannot charge twice.
The package checks the image before it uploads anything. Only JPEG and PNG are
sent, and 25 MiB at most. An imageUrl is downloaded by the package itself,
over https: only. A URL that points at a private, loopback or link-local
address is refused, and so is a redirect to one.
A result is stored only when the scan was made with a live key under a
non-zero retention window. Under a sandbox
key nothing is stored, so getScan and deleteScan answer not_found and
listScans is empty.
What the model sees¶
Your code gets the whole result of every tool, exactly as the API returned it.
The copy of a scanDocument or getScan result that goes back to the model
has each image crop replaced by a short note of its size. A model cannot look
at base64 text, and the crops would fill its
context window.
When a call fails¶
A failed call throws a DocCheapError with the HTTP status (or null when
no answer came back) and the API's own code. The AI SDK hands its message to
the model as the tool's error result, so the model can react to it. The codes
and what to do with each are in handle errors. Two
more come from the package itself: invalid_input for arguments it refused
before sending, and image_refused for an imageUrl it could not or would not
fetch.
Every request carries User-Agent: doc-cheap-ai-sdk/<version>. Nothing else
about your application is sent.