SDKs & OpenAPI
An OpenAPI 3.1 spec for generating clients in any language, and a typed Node SDK. The spec and the REST API work today.
OpenAPI
The full spec lives at /openapi.json. Feed it to openapi-generator, Kiota, Speakeasy, or your tool of choice to generate a client in any language.
Node: @invoicejetai/node
Zero-dependency and fully typed. Runs on Node 18+, Bun, Deno, and edge runtimes (anything with fetch; webhook verification uses WebCrypto, falling back to node:crypto on Node 18).
npm install @invoicejetai/nodeimport { readFile } from "node:fs/promises";
import { InvoiceJet, InvoiceJetError } from "@invoicejetai/node";
const client = new InvoiceJet({ apiKey: process.env.INVOICEJET_API_KEY! });
// extract and wait
const invoice = await client.invoices.extract({
fileUrl: "https://example.com/invoices/june.pdf",
wait: true,
});
// or fire-and-poll
const queued = await client.invoices.extract({
file: await readFile("june.pdf"),
fileName: "june.pdf",
mimeType: "application/pdf",
});
const done = await client.waitForCompletion(queued.id); // complete, failed, or held
// every complete invoice ingested through the API (follows next_cursor)
for await (const inv of client.invoices.iterate({ status: "complete", source: "api", limit: 100 })) {
console.log(inv.id, inv.amount, inv.paid_at);
}
// export unpaid invoices as a QuickBooks bill-import CSV
const csv = await client.exports.create({ shape: "lines", format: "csv", paid: false });
// key + quota check
const me = await client.me();
// errors carry status, type, code, requestId, and extra fields in details
try {
await client.invoices.extract({ fileUrl: "https://example.com/july.pdf" });
} catch (e) {
if (e instanceof InvoiceJetError && e.code === "monthly_quota_reached") {
console.log("upgrade:", e.details.upgrade_url);
} else throw e;
}Timeouts, retries & errors
Each attempt times out after 60s, or 120s for file uploads and wait: true. Failed requests are retried up to twice with exponential backoff and full jitter. Retried failures are network errors, timeouts, and 408, 429, 500, 502, 503 and 504; other 4xx responses, 409 included, are not. A Retry-After header is honored, and a 429 without one waits for X-RateLimit-Reset. If the server asks for more than 60s, the SDK throws instead, with retryAfter set.
Only requests that are safe to repeat are retried: GETs, invoices.update, and invoices.extract. extract always sends an Idempotency-Key, yours or a generated one, and reuses it on every retry, so a retried create never duplicates an invoice. retryExtraction and requestApproval are never retried automatically. Files are buffered in memory (15 MB max), so retries resend them in full.
import { InvoiceJet, InvoiceJetError, InvoiceJetTimeoutError } from "@invoicejetai/node";
const client = new InvoiceJet({
apiKey: process.env.INVOICEJET_API_KEY!,
timeout: 60_000, // per attempt, ms; 0 = none
uploadTimeout: 120_000, // uploads and wait: true
maxRetries: 2, // 0 = no retries
});
// per-request overrides and cancellation, on every method
const page = await client.invoices.list(
{ paid: false },
{ timeout: 10_000, maxRetries: 5, signal: AbortSignal.timeout(30_000) },
);
try {
await client.invoices.extract({ fileUrl: "https://example.com/july.pdf", wait: true });
} catch (e) {
if (e instanceof InvoiceJetTimeoutError) {
// outcome unknown: the same key replays the original result, no duplicate
await client.invoices.extract({
fileUrl: "https://example.com/july.pdf",
idempotencyKey: e.idempotencyKey!,
});
} else if (e instanceof InvoiceJetError) {
console.error(e.status, e.code, e.message, e.requestId, e.retryAfter, e.attempts);
} else throw e; // your own AbortSignal rejects with its reason
}
console.log(client.lastRateLimit); // { limit, remaining, reset } from the last response| InvoiceJetError field | Meaning |
|---|---|
status | HTTP status; 0 when no response arrived. |
type / code / message | From the API error body. Connection failures: connection_error / connection_failed or timeout. |
details | Extra body fields, e.g. limit, used, upgrade_url on 402 monthly_quota_reached. |
requestId | The request id, when the API sends one. |
retryAfter | Seconds the API asked you to wait, or null. |
rateLimit | X-RateLimit-* values: limit, remaining, reset (Unix seconds). |
attempts | Attempts made, retries included. |
idempotencyKey | The key an extract was sent with; pass it back to retry safely. |
Subclasses: InvoiceJetConnectionError (no response) and InvoiceJetTimeoutError. The CLI takes --timeout <seconds> and --max-retries <n>; the MCP server reads INVOICEJET_TIMEOUT and INVOICEJET_MAX_RETRIES.
CLI & MCP
Built on the same SDK: the invoicejet CLI for scripts and pipelines, and the MCP server that gives Claude, Cursor, and any Model Context Protocol client invoice-extraction tools.
Base URL
https://nzsktgwehyzltgagloox.supabase.co/functions/v1/apiAll endpoints are versioned under /v1. Breaking changes ship as a new version prefix; /v1 stays stable.