npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@docmana/sdk

v0.25.0

Published

Official Docmana SDK — run document-analysis flows with a single call.

Downloads

2,044

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/sdk

Requires 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);

Fraud detection follows the same asynchronous Consumer Edge pattern. The high-level call starts the job, polls Consumer, and returns the normalized result:

const fraud = await docmana.detectFraud({
  file: "./claim.pdf",
  analystContext: "Travel claim; verify the invoice total.",
});

console.log(fraud.verdict, fraud.summary.highestSeverity);
console.log(fraud.summary, fraud.findings);

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 and detectFraud. 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() calls GET /v1/flows and returns { id, name, state }[] for the flows available to the Bearer token. Missing flow states are returned as "unknown".
  • listOrganizations() calls GET /v1/organizations and returns { id, name }[] for the organizations available to the Bearer token.
  • getFlowMetadata(flowId) calls GET /v1/flows/:flowId/metadata and returns { flowId, name?, jsonContextSchema, languages, minScore?, documents }.
  • getFlowDefinition(flowId) calls GET /v1/flows/:flowId/definition and returns the normalized flow definition { flowId, name?, languages, minScore?, nodes }.
  • listOrganizationDocuments({ prefix? }) calls GET /v1/organization-documents and returns { configured, documents }.
  • downloadOrganizationDocument(path) calls GET /v1/organization-documents/download?path=... and returns { bytes, filename, contentType? }.
  • downloadOrganizationDocuments(paths) downloads each requested path.
  • runFlowAsync(flowId, params, options?) posts multipart files to POST /v1/flows/:flowId/execute-async and returns { executionResultId, fileIds }.
  • runPartialFlow(flowId, params, options?) executes classification, extraction, or extraction with validation synchronously through Consumer Edge. It returns the standard DocmanaExecutionResult with partialExecution metadata.
  • runPartialFlowAsync(flowId, params, options?) starts the same partial execution asynchronously and returns { executionResultId, fileIds } for standard polling.
  • getStatus(executionResultId) calls GET /v1/executions/:executionResultId/status.
  • getResult(executionResultId, signal?, language?) calls GET /v1/executions/:executionResultId/result; its result includes the optional flowVersion identifying the exact flow version used by the execution. Processing steps can also include model and promptId provenance when Docmana reports them.
  • runFlow(flowId, params, options?) starts async execution, polls status, fetches the result, respects X-Poll-After and 429 Retry-After while polling, and throws DocmanaExecutionError if the result status is FAILED.
  • detectFraud(params, options?) starts fraud detection from either documentUrl plus filename, or a local file using the standard FileInput contract. It polls Consumer until COMPLETED or FAILED and returns the normalized verdict, aggregate summary and findings with job-local references. Each finding keeps its analysis category in layer. Internal preprocessing, layer telemetry, token counts, LLM calls, model usage, costs, persistence diagnostics, and the sensitive Fraud document payload are intentionally never exposed by Consumer or SDK.
  • detectFraudAsync(params, options?) starts fraud detection and immediately returns { jobId, status } without polling.
  • getFraudDetectionStatus(jobId, signal?) and getFraudDetectionResult(jobId, signal?) provide the lower-level polling workflow.

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.

Path inputs are streamed from disk instead of being loaded completely into memory. For multipart submissions only, the SDK retries one 503 Service Unavailable response carrying Retry-After with a fresh request body, respecting that delay up to 30 seconds. A downstream 503 without Retry-After is not retried. Payloads rejected with 413 Payload Too Large are never retried.

The SDK does not log or return documentUrl or uploaded file contents. Signed query parameters are sent only in the JSON request body to Consumer Edge. Fraud jobs are not organization-scoped; the legacy organization inputs are accepted but ignored for compatibility with SDK 0.20 callers.

URL and local-file inputs use the same methods:

const fromUrl = await docmana.detectFraudAsync({
  documentUrl: "https://downloads.example.com/claim.pdf?token=...",
  filename: "claim.pdf",
});

const fromFile = await docmana.detectFraudAsync({
  file: { path: "./claim.pdf" },
  analystContext: "Check the invoice total.",
});
const partial = await docmana.runPartialFlow("<flow-id>", {
  files: ["./contract.pdf"],
  executionScope: "extraction_and_validation",
  flowVersion: "published",
  classificationNodeNames: ["classificationContract"],
});

console.log(partial.documents, partial.partialExecution);
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 login

docmana 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:

  1. --access-token
  2. DOCMANA_ACCESS_TOKEN
  3. valid docmana.token.json
  4. OAuth refresh using CLI/config credentials
DOCMANA_API_URL="https://consumer.example.com/v1"
docmana flow list
DOCMANA_API_URL="https://consumer.example.com/v1"
docmana organization list
DOCMANA_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" \
  --context '{"contract":{"destination":"Canada"}}' \
  --draft \
  --json

Use --draft to run the current draft flow version. Use --context to pass business context as a JSON object. 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. | | DocmanaPayloadTooLargeError | 413 multipart payload exceeds a Consumer limit. | | 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. | | DocmanaFraudDetectionError | Fraud detection finished with status "FAILED". Carries .jobId, .error, and .result. | | DocmanaTimeoutError | Polling exceeded timeoutMs. | | DocmanaAbortError | The provided AbortSignal fired. | | DocmanaServiceUnavailableError | 503 Consumer upload capacity is unavailable after the bounded retry. Carries .retryAfterMs. |

Local development with yalc

In this library:

npm run dev

In the consuming project:

yalc add @docmana/sdk
npm install

For a one-shot publish to the local yalc store:

npm run yalc:publish

License

MIT