@docmana/sdk
v0.11.0
Published
Official Docmana SDK — run document-analysis flows with a single call.
Keywords
Readme
@docmana/sdk
Official Docmana SDK for calling docmana-consumer-edge-api.
The SDK sends documents to Consumer, starts an async flow, polls Consumer for status, and returns the mapped Consumer execution DTO.
Installation
npm install @docmana/sdkRequires Node.js 20+.
Quickstart
import { Docmana } from "@docmana/sdk";
const docmana = new Docmana({
apiBaseUrl: process.env.DOCMANA_API_URL!,
accessToken: process.env.DOCMANA_ACCESS_TOKEN!,
});
const result = await docmana.runFlow("<flow-id>", {
files: ["./contract.pdf"],
context: { contract: { destination: "Canada" } },
useDraftVersion: true,
});
console.log(result.status, result.executionId);Authentication
The public SDK is Bearer-only. It does not acquire OAuth tokens and it never
calls docmana-api directly. Provide either a static accessToken or a
getAccessToken provider:
const docmana = new Docmana({
apiBaseUrl: "https://consumer.example.com/v1",
getAccessToken: async ({ forceRefresh } = {}) => {
return forceRefresh ? refreshTokenSomehow() : currentToken();
},
});If Consumer returns 401 and getAccessToken is configured, the SDK calls
getAccessToken({ forceRefresh: true }) once and retries the request. With a
static accessToken, 401 is returned as an auth error without retry.
Configuration
| Field | Type | Required | Description |
| ------------------- | --------------------------------------------------------------------- | -------- | ------------------------------------------------------------ |
| apiBaseUrl | string | Yes | Base URL of Consumer API, including version when applicable. |
| accessToken | string | One of | Static Bearer token sent to Consumer. |
| getAccessToken | (options?: { forceRefresh?: boolean }) => string \| Promise<string> | One of | Token provider for renewable Bearer tokens. |
| timeoutMs | number | No | Maximum polling time for runFlow. Default: 5 minutes. |
| pollIntervalMs | number | No | Fallback minimum delay between status polls. Default: 2s. |
| pollMaxIntervalMs | number | No | Fallback maximum polling delay. Default: 10s. |
| fetch | typeof fetch | No | Custom fetch implementation. |
| headers | Record<string, string> | No | Extra headers sent on every Consumer request. |
apiBaseUrl is required so a migrated SDK cannot accidentally call
https://api.docmana.ai.
Run flow params
runFlow(flowId, params, options) and runFlowAsync(flowId, params, options)
use RunFlowParams for request data:
| Field | Type | Description |
| ----------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| files | (string \| { path: string } \| { data, filename, contentType? })[] | Documents to upload. A string or { path } is treated as a file path. |
| context | Record<string, unknown> | Business context serialized as multipart jsonContext. |
| language | string | Optional result language passed to Consumer. |
| useDraftVersion | boolean | Runs the current draft flow version. |
| callbackUrl | string | Async-only callback URL, serialized as callbackUrl. |
runFlow uses RunFlowOptions for local execution controls:
| Field | Type | Description |
| ------------------- | ----------------- | ------------------------------------------------------------ |
| signal | AbortSignal | Cancels the in-flight request/polling. |
| timeoutMs | number | Overrides configured polling timeout for this call. |
| pollIntervalMs | number | Overrides configured fallback minimum polling interval. |
| pollMaxIntervalMs | number | Overrides configured fallback maximum polling interval. |
| onPoll | (event) => void | Called after each status poll. |
| onRateLimit | (event) => void | Called when polling receives 429; includes retryAfterMs. |
runFlowAsync accepts RunFlowAsyncOptions with signal.
once was removed. Use useDraftVersion.
Methods
listFlows()callsGET /v1/flowsand returns{ id, name, state }[]for the flows available to the Bearer token. Missing flow states are returned as"unknown".listOrganizations()callsGET /v1/organizationsand returns{ id, name }[]for the organizations available to the Bearer token.getFlowMetadata(flowId)callsGET /v1/flows/:flowId/metadataand returns{ flowId, name?, jsonContextSchema, languages, minScore?, documents }.getFlowDefinition(flowId)callsGET /v1/flows/:flowId/definitionand returns the normalized flow definition{ flowId, name?, languages, minScore?, nodes }.listOrganizationDocuments({ prefix? })callsGET /v1/organization-documentsand returns{ configured, documents }.downloadOrganizationDocument(path)callsGET /v1/organization-documents/download?path=...and returns{ bytes, filename, contentType? }.downloadOrganizationDocuments(paths)downloads each requested path.runFlowAsync(flowId, params, options?)posts multipart files toPOST /v1/flows/:flowId/execute-asyncand returns{ executionResultId, fileIds }.getStatus(executionResultId)callsGET /v1/executions/:executionResultId/status.getResult(executionResultId, signal?, language?)callsGET /v1/executions/:executionResultId/result.runFlow(flowId, params, options?)starts async execution, polls status, fetches the result, respectsX-Poll-Afterand429 Retry-Afterwhile polling, and throwsDocmanaExecutionErrorif the result status isFAILED.
Consumer is the polling authority. When GET /status returns X-Poll-After,
the SDK waits that many milliseconds before the next status call. If the header
is absent, the SDK falls back to capped exponential full jitter between
pollIntervalMs and pollMaxIntervalMs. Retry-After on 429 always wins.
const flows = await docmana.listFlows();
console.log(flows.map((flow) => `${flow.id}: ${flow.name} (${flow.state})`).join("\n"));const organizations = await docmana.listOrganizations();
console.log(organizations.map((org) => `${org.id}: ${org.name}`).join("\n"));const metadata = await docmana.getFlowMetadata("<flow-id>");
console.log(metadata.jsonContextSchema);
const definition = await docmana.getFlowDefinition("<flow-id>");
console.log(definition.nodes.map((node) => node.label).join("\n"));const depot = await docmana.listOrganizationDocuments({ prefix: "folder/" });
if (depot.configured) {
const file = await docmana.downloadOrganizationDocument(depot.documents[0].path);
console.log(file.filename, file.bytes.byteLength);
}const { executionResultId } = await docmana.runFlowAsync("<flow-id>", {
files: ["./contract.pdf"],
});
let status = "PENDING";
while (status !== "COMPLETED" && status !== "FAILED") {
await new Promise((r) => setTimeout(r, 2000));
({ status } = await docmana.getStatus(executionResultId));
}
const result = await docmana.getResult(executionResultId, undefined, "fr");Webhook callbacks
runFlowAsync(flowId, params, options?) accepts callbackUrl and sends it as
callbackUrl. It returns immediately and does not poll.
const { executionResultId } = await docmana.runFlowAsync("<flow-id>", {
files: ["./contract.pdf"],
callbackUrl: "https://your-service.example.com/webhooks/docmana/executions",
language: "fr",
});Consumer posts the mapped result DTO to your callback URL when the execution is
finished. Keep executionResultId if you also want to poll as a fallback.
CLI
The CLI can still acquire and cache a token through OAuth:
docmana config init
docmana logindocmana login reads clientId, clientSecret, tokenEndpoint, and scope
from flags, environment variables, or docmana.config.json, then stores the
access token in docmana.token.json.
docmana flow run always passes a Bearer token to the SDK. It resolves the token
in this order:
--access-tokenDOCMANA_ACCESS_TOKEN- valid
docmana.token.json - OAuth refresh using CLI/config credentials
DOCMANA_API_URL="https://consumer.example.com/v1"
docmana flow listDOCMANA_API_URL="https://consumer.example.com/v1"
docmana organization listDOCMANA_API_URL="https://consumer.example.com/v1"
docmana organization document list --prefix "folder/"
docmana organization document download "folder/invoice.pdf"
docmana organization document download "folder/invoice.pdf" --output "./invoice.pdf"DOCMANA_API_URL="https://consumer.example.com/v1"
docmana flow metadata "<flow-id>"
docmana flow definition "<flow-id>"DOCMANA_API_URL="https://consumer.example.com/v1"
docmana flow run "<flow-id>" --files "./contract.pdf"You can also run without the cache:
docmana flow run "<flow-id>" \
--api-url "https://consumer.example.com/v1" \
--access-token "$DOCMANA_ACCESS_TOKEN" \
--files "./contract.pdf,./appendix.pdf" \
--draft \
--jsonUse --draft to run the current draft flow version.
Use docmana flow list --json to print the raw flow list returned by Consumer.
Use docmana organization list --json to print the organization list as JSON.
Use docmana organization document list --json for raw organization document JSON.
docmana flow metadata and docmana flow definition print JSON by default.
The CLI no longer sends X-Selected-Organization; Consumer derives identity
from the Bearer token.
Error handling
Every SDK error extends DocmanaError, which carries .status, .code, and an
optional .requestId when available.
| Error | Thrown when |
| ------------------------ | -------------------------------------------------------------------------- |
| DocmanaRequestError | 400 bad request. |
| DocmanaAuthError | 401 rejected or expired Bearer token. |
| DocmanaPermissionError | 403 missing permission. |
| DocmanaNotFoundError | 404 flow or execution not found. |
| DocmanaExecutionError | The flow finished with status "FAILED". Carries .errors and .result. |
| DocmanaTimeoutError | Polling exceeded timeoutMs. |
| DocmanaAbortError | The provided AbortSignal fired. |
Local development with yalc
In this library:
npm run devIn the consuming project:
yalc add @docmana/sdk
npm installFor a one-shot publish to the local yalc store:
npm run yalc:publishLicense
MIT
