API v3 is available. This SDK targets the v2 API, which keeps working through its deprecation window. A new SDK major targeting v3 is in progress — see the v3 SDKs page and the v3 API reference.
Anyformat, Schema, WorkflowBuilder, Result) is hand-written and mirrors the Python SDK.
Installation
Requires Node 18+.Authentication
Pass the API key to theAnyformat constructor, or read it from the environment.
baseUrl if you’re running against a non-production deploy. See Authentication for how to mint an API key.
Basic usage
Full flow: build a workflow with a fluent builder, submit a document, await the typed result.create() returns a Workflow handle whose run(file) reuses the existing workflow id — so processing N documents through the same workflow costs one POST /v2/workflows/ plus N POST /v2/workflows/{id}/run/, not N create+run pairs.
wait options
wait defaults: 300s overall timeout, 3s poll interval. Override either:
SDKTimeout is thrown if the overall deadline expires before the run completes. The internal poll handles 412 (still processing) automatically.
Builder methods
All node types in the typed graph are exposed as fluent methods.parse is required; the others are optional and can be chained in any topology the API allows.
| Method | What it adds | Notes |
|---|---|---|
.parse(opts?) | The required parse node | opts is a mode-discriminated union: { mode?: "standard", promptHint?, figureEnhancement?, cache? }, { mode: "agentic", effort? } ("low"|"mid"|"accurate"), or { mode: "lite", ocrEffort?, region?, skipRoutingReview?, figures?, textFormatting?, routingEffort? }. A wrong-mode knob is a compile-time error. |
.classify(categories, opts?) | A classify node | Categories from ClassifyCategory objects (id, name, description) |
.split(rules, opts?) | A splitter node | opts.routeFrom wires which classify branch fans out |
.extract(fields, opts?) | An extract node | opts.branch required after .classify() / .split(); build fields with Schema.*. opts.mode ("standard"|"agentic"|"max"|"lite"), opts.useImages, and — for smart lookup — opts.lookupFiles + opts.lookupReasoningEffort ("minimal"|"low"|"medium"|"high") + a { lookup: true } field. |
.validate(rules, opts?) | A validate node | Attaches to the most recent extract (or opts.branch) |
.build() | Same shape, no network call | Returns a WorkflowCreateRequest |
.create() | Persists the workflow | Returns a Workflow handle (id + .run(...)) |
Workflow handle returned by .create() has one method:
| Method | What it does | Notes |
|---|---|---|
.run(file, opts?) | Submits a document against the existing workflow id | opts.text to submit raw text instead of a file; pass null as the file |
Workflow.run returns a Run; Run.wait(opts?) returns a Result once processing finishes.
Smart lookup
A smart-lookup field is resolved by matching the document against a reference file (a CSV/catalog) instead of being read off the page. Flag the field with{ lookup: true }, and pass the reference file to .extract(..., { lookupFiles: [...] }) — the SDK reads each path and uploads it with the workflow. Here the model reads vendor_name, then resolves the canonical vendor_id from a vendor catalog:
{ lookup: true } is accepted on every Schema.* factory. The looked-up field comes back via result.field(...) alongside the others — there’s no separate section for it. An extract with a lookup field but no lookupFiles is rejected with 400. By default the lookup overwrites the field; add lookupOnlyIfMissing: true to extract it from the document too and let the lookup fill it only where extraction found no value.
Managing workflows
The client also lists and deletes workflows:| Method | What it does | Returns |
|---|---|---|
af.listWorkflows({ page?, pageSize?, status? }) | One page of your workflows | Promise<Workflow[]> — each is .run(...)-able (pageSize max 100) |
af.deleteWorkflow(workflowId) | Soft-deletes a workflow | Promise<string> — the deleted id (a 404 throws NotFound) |
Reading results
Error handling
The SDK exports typed error classes:EXTRACTION_FAILED, RATE_LIMITED) are documented at Errors. The APIError.body field holds the parsed JSON body the server returned.
Webhooks (not on the SDK surface yet)
Anyformat doesn’t expose webhook endpoints directly today. Until it does, register and delete webhooks with fetch or the generated low-level client:
Links
- npm —
npm install @anyformat/sdk - Python SDK — the same fluent shape in Python
- Coding assistant — let Claude drive anyformat from your editor
