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.11.0

Published

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

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

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() 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 }.
  • getStatus(executionResultId) calls GET /v1/executions/:executionResultId/status.
  • getResult(executionResultId, signal?, language?) calls GET /v1/executions/:executionResultId/result.
  • 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.

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 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" \
  --draft \
  --json

Use --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 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