@subnoto/api-client
v2.12.3
Published
TypeScript SDK for Subnoto Public API
Downloads
3,289
Readme
Subnoto TypeScript SDK
TypeScript client for the Subnoto Public API with Oak tunnel encryption and HTTP signature authentication.
Table of contents
- Installation
- Usage
- Configuration
- Upload Helper
- Full example: upload and send
- Error handling and tunnel retry
- Documentation
- Development
Installation
Prerequisites: Node.js. The upload examples below use Node's fs module and are intended for Node.js.
pnpm add @subnoto/api-clientUsage
import { SubnotoClient } from "@subnoto/api-client";
const client = new SubnotoClient({
accessKey: process.env.API_ACCESS_KEY,
secretKey: process.env.API_SECRET_KEY
});
// Use openapi-fetch syntax
const { data, error } = await client.POST("/public/workspace/list", { body: {} });
const whoami = await client.POST("/public/utils/whoami", { body: {} });
const contacts = await client.POST("/public/contact/list", {
body: { workspaceUuid: "..." }
});Configuration
Pass credentials via the config object; environment variables (e.g. SUBNOTO_ACCESS_KEY, SUBNOTO_SECRET_KEY)
are recommended for local development and CI.
| Option | Type | Required | Description |
| ----------- | ------ | -------- | ---------------------------- |
| accessKey | string | Yes | API access key |
| secretKey | string | Yes | API secret key, hex-encoded |
Upload Helper
import { SubnotoClient } from "@subnoto/api-client";
import { readFileSync } from "fs";
const client = new SubnotoClient({
accessKey: process.env.API_ACCESS_KEY,
secretKey: process.env.API_SECRET_KEY
});
// Read your PDF file
const fileBuffer = readFileSync("path/to/document.pdf");
// Upload document - handles encryption and S3 upload automatically
const { envelopeUuid, documentUuid } = await client.uploadDocument({
workspaceUuid: "your-workspace-uuid",
fileBuffer: fileBuffer,
envelopeTitle: "My Document"
});
console.log("Document uploaded:", envelopeUuid, documentUuid);Full example: upload and send
The uploadDocumentAndSend helper runs the full flow in one call: upload the main document, optionally update
envelope options, add recipients, add attachments, add blocks (signatures, text, etc.), then send.
import { SubnotoClient } from "@subnoto/api-client";
import { readFileSync } from "fs";
const client = new SubnotoClient({
accessKey: process.env.API_ACCESS_KEY!,
secretKey: process.env.API_SECRET_KEY!
});
const workspaceUuid = "your-workspace-uuid";
const mainPdf = readFileSync("path/to/contract.pdf");
const appendixPdf = readFileSync("path/to/appendix.pdf");
const { envelopeUuid, documentUuid } = await client.uploadDocumentAndSend({
workspaceUuid,
fileBuffer: mainPdf,
envelopeTitle: "Contract with appendix",
recipients: [
{
type: "manual",
email: "[email protected]",
firstname: "Jane",
lastname: "Doe"
}
],
options: {
title: "Sales contract",
tags: ["contract", "2024"],
expirationPeriod: 30,
documents: [{ title: "Main contract", mustReadAllPages: true }]
},
attachments: [{ documentTitle: "Appendix A", fileBuffer: appendixPdf }],
blocks: [
{
page: "1",
x: 100,
y: 200,
type: "signature",
recipientEmail: "[email protected]"
},
{
page: "1",
x: 100,
y: 250,
type: "text",
text: "Signed by signer"
}
]
});
console.log("Envelope sent:", envelopeUuid, documentUuid);Steps run in order: upload → update envelope options (if options) → add recipients → add attachments (if
attachments) → add blocks (if blocks) → send. If any step fails, a SubnotoError is thrown.
Error handling and tunnel retry
The SDK follows the Subnoto error reference.
See that page for all error codes.
On tunnel 401 errors (TUNNEL_SESSION_NOT_FOUND or TUNNEL_ERROR), the client automatically invalidates the
tunnel session and retries the request up to 3 times with a new session.
You do not need to manage tunnel lifecycle when using SubnotoClient.
For custom handling you can use the exported helpers and codes:
import { SubnotoClient, isTunnelError, SubnotoError } from "@subnoto/api-client";
const { data, error } = await client.POST("/public/utils/whoami", { body: {} });
if (error) {
if (isTunnelError(error)) {
// SDK already retried (up to 3 times); this is the result after retries
}
throw SubnotoError.fromApiError(error);
}Use getErrorCode(error) if you need the code without throwing.
The thrown SubnotoError has .message, .code, and .statusCode.
Documentation
The API documentation is available here.
Development
To regenerate types from the OpenAPI spec (contributors):
bazel run //libs/api-client-ts:update_generated