@anyformat/sdk is the TypeScript client for API v3. The wire types and the low-level HTTP client are generated from the API’s OpenAPI document; the layer you use (Anyformat, Schema, WorkflowBuilder, Workflow, Run, Result) is hand-written and mirrors the Python SDK method for method.
Install and auth
Requires Node.js 18 or later. The package ships ESM and CJS bundles with types. The 1.0 line is a release candidate on the npmrc tag; plain npm install @anyformat/sdk still resolves to the 0.x (v2) line until 1.0.0 final ships.
Client
new Anyformat(options) is the entry point. Every method returns a Promise.
Build a workflow
af.workflow(name, description?) returns a WorkflowBuilder. Chain one method per node, then await .create() to register the workflow and get a Workflow handle, or await .update(workflowId) to replace an existing workflow’s graph. .build() returns the request body without a network call.
The builder assigns node ids (parse_1, extract_1, …) and wires the edges. Pass { id } in the options to choose the id yourself. Each method builds the same typed node the API accepts, so the node schema is the reference for every option. A builder mistake, such as extract() before parse(), throws WorkflowBuilderError before any request is sent.
The builder has methods for Parse, Classify, Split, Extract and Validate. For a graph with an Edit, If/Else, Slack alert or Knowledge node, write the nodes and edges arrays yourself and send them with af.updateWorkflow(workflowId, definition), or call the create endpoint directly. The wire types (Edge, ClassifyCategory, SplitterRule, ValidationRule, the field and check types) are exported for that.
parse
Adds the one Parse node. Call it first and once. The options are amode-discriminated union: each tier only accepts its own knobs, so a wrong-tier knob is a compile-time error.
lite takes no knob of its own.
classify
Adds a Classify node after Parse. Takes an array ofClassifyCategory objects (id, name, description).
extract() that follows needs { branch }.
split
Adds a Split node. Takes an array ofSplitterRule objects (id, name, description, optional partition_key). After classify(), pass { routeFrom } to name the category that feeds the splitter.
extract
Adds an Extract node. Repeatable: one per branch.fields is an array built with Schema.
Schema has one factory per field type: string, integer, float, boolean, date, datetime, enum(name, description, options), multiSelect(name, description, options), and object(name, description, fields). Schema.option(name, description) builds an enum option. Every factory takes a trailing { source, persistentId }: source: "smart_lookup" or "lookup_if_missing" for a lookup field, persistentId to pin a field’s identity across updates.
validate
Adds a Validate node after the most recentextract(), or after the extract on { branch }. Takes an array of ValidationRule objects.
Run and read
Upload and run
workflow.run(files, opts?) uploads one File or an array of up to 10 as one document packet and starts a run. Every file needs a name so the server can detect its type: pass a File, not a bare Blob.
409; pass { onConflict: "rename" } to auto-rename instead.
To upload without spending credits, use workflow.upload(files, opts?). It returns an Upload with documentPacketId and files; call upload.run() later. workflow.uploadFromUrls(urls, opts?) imports up to 10 HTTPS URLs server-side into one packet.
idempotencyKey replays the original upload or run instead of creating and billing a second one.
wait
run.wait({ timeoutMs, pollMs }) polls GET /v3/runs/{run_id}/ until the status is terminal and resolves with a Result. Defaults: 300 000 ms budget, 3 000 ms between polls. A transient 429 or 5xx while polling is retried with backoff and honours Retry-After. wait() rejects with ExtractionFailedError on error, ExtractionCancelledError on cancelled, and SDKTimeoutError when the budget runs out.
Result
ParseView has no draw(); rendering pages needs PDF rasterising, which only the Python SDK ships.
Quick parse
For a one-off parse with no workflow,af.parse(file) runs the atomic parse operation and returns a job id. af.getParseMarkdown(jobId) returns the markdown once done, or null while it is still processing. This is the one call still served by the v2 operations surface.
Document packets and runs
Packets and runs stay addressable afterwait() resolves.
Packet statuses:
not_started, queued, in_progress, then processed, error or cancelled. Run statuses are the same set without not_started.
Manage workflows
Knowledge ask
The TypeScript client has noask() method yet. Call Ask with fetch:
Evals and datasets
A dataset holds documents with known answers. Upload one document, with optional ground truth, as one packet:groundTruth keys are the schema’s field identifiers. A scalar maps to string | null; an object field maps to an array of row objects. The call is atomic: a rejected file or a ground-truth failure stores nothing. Bulk upload loops one atomic call per document:
af.uploadToDataset(workflowId, files, opts) and af.uploadDocumentsToDataset(workflowId, documents). The client has no eval methods yet; launch and read evals through the endpoints.
Pagination
Every list resolves to aPage<T> with items and nextCursor. Pass nextCursor back as cursor until it is null. There are no totals or page numbers; limit is capped at 100.
iter* methods (iterWorkflows, iterDocumentPackets, iterRuns) do this loop for you and fetch each page lazily.
Errors and retries
Every HTTP failure rejects with a subclass ofAPIError carrying the v3 error envelope: .status, .errorCode, .retryable, .requestId and the raw .body.
Outside HTTP, all subclasses of
AnyformatError: RunFailedError when wait() sees a terminal failure, subclassed as ExtractionFailedError (error) and ExtractionCancelledError (cancelled), each with .runId and .status; SDKTimeoutError when wait() runs out of budget; WorkflowBuilderError for builder misuse.
wait() retries a transient 429 or 5xx itself, so those never reach your catch while polling.
Version pins
Upgrading from 0.x changes uploads, runs, pagination and workflow updates. Follow the 0.x to 1.0 migration guide.
Links
- npm
- Python SDK: the same shape in Python
- Node schemas: every option the builder maps to

