Skip to main content
@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 npm rc tag; plain npm install @anyformat/sdk still resolves to the 0.x (v2) line until 1.0.0 final ships.
Pass the API key to the constructor. The client does not read the environment for you.
See Authentication for how to mint a key.

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 a mode-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 of ClassifyCategory objects (id, name, description).
Every extract() that follows needs { branch }.

split

Adds a Split node. Takes an array of SplitterRule 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 recent extract(), 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.
Filenames are unique within a workflow. A colliding name is rejected with 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.
Retrying with the same 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 after wait() 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 no ask() 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:
Both are also on the client: 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 a Page<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.
The 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 of APIError 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.