handwriteGo to Handwrite ↗
← Back to all docs

SDKs · API v1 · Developer documentation

Build with the Handwrite SDKs.

Typed clients for every endpoint a Handwrite token can call, with retries, pagination and webhook checks built in. Pick a language: every example on this page switches with it.

Node SDK

0.1.0 · pre-releasePackage handwrites.ioRequires Node 20API v1

The reference site at handwrite.gitlab.io/sdk/node/ is being published from the repository and may not be live yet; the repository README documents the same API.

01 / INSTALL

Install

These SDKs are pre-releases that aren’t on package registries yet. The commands work once a release is published; until then, build from the repository.

Node · npm
npm install handwrites.io
02 / QUICKSTART

Quickstart

Create a client with an API key from Account → Developer, list your documents and create one.

TypeScript
const hw = new Handwrite({ accountType: "regular", token: process.env.HANDWRITE_TOKEN! });

const me = await hw.User.get();
console.log(`Signed in as ${me.principal}`);

const page = await hw.Documents.list({ limit: 10 });
for (const doc of page.documents) {
  if (!("deleted" in doc)) console.log(doc.id, doc.title);
}

const doc = await hw.Documents.create({ title: "Field notes", content: "The heron came back." });
console.log(`Created ${doc.id} at revision ${doc.revision}`);
03 / ACCOUNT TYPES

Create a client

The account type decides which resources the client has, checked by the compiler where the language allows.

TypeScript · Regular
const hw = new Handwrite({ accountType: "regular", token: process.env.HANDWRITE_TOKEN! });
// Every /api/v1 resource for one person: hw.Documents, hw.Audio, hw.Webhooks…
04 / ERRORS

Handle errors

Every non-2xx answer is a typed error with the HTTP status, a stable code and the request ID to quote to support.

TypeScript
try {
  await hw.Documents.update(id, { title: "Field notes, week 2" }, { ifMatch: revision });
} catch (error) {
  if (error instanceof HandwriteError) {
    if (error.code === "revision_mismatch") {
      // Someone else saved first: fetch the document again and retry.
    } else if (error.code === "insufficient_scope") {
      // The token lacks documents:write.
    }
    console.error(`${error.status} ${error.code}: ${error.message} (request ${error.requestId})`);
  }
  throw error;
}
// 429 and 5xx answers are retried for you (maxRetries, default 2), honouring Retry-After.
05 / PAGINATION

Paginate

Lists are cursor-paginated. Pass the cursor back yourself, or let the iterator follow it.

TypeScript
// One page at a time: pass the cursor back until it is null.
let cursor: string | undefined;
do {
  const page = await hw.Documents.list({ limit: 100, ...(cursor && { cursor }) });
  for (const doc of page.documents) console.log(doc.id);
  cursor = page.cursor ?? undefined;
} while (cursor);

// Or let the SDK follow the cursor for you.
for await (const doc of hw.Documents.iterate()) {
  if (!("deleted" in doc)) console.log(doc.title);
}
06 / WEBHOOKS

Verify webhooks

Check the Handwrite-Signature header against the raw body before you parse it. Signatures older than five minutes are rejected.

TypeScript
const secret = process.env.HANDWRITE_WEBHOOK_SECRET!; // whsec_…, shown once when the webhook is created

createServer((req, res) => {
  const chunks: Buffer[] = [];
  req.on("data", (chunk: Buffer) => chunks.push(chunk));
  req.on("end", () => {
    const rawBody = Buffer.concat(chunks).toString("utf8"); // verify before JSON.parse
    const signature = req.headers["handwrite-signature"];
    if (!verifyWebhook(secret, typeof signature === "string" ? signature : null, rawBody)) {
      res.writeHead(400).end();
      return;
    }
    const event = JSON.parse(rawBody) as WebhookEvent;
    console.log(event.type, event.data.document.id);
    res.writeHead(204).end();
  });
}).listen(8080);

versions

API v1 and its SDKs

Every SDK targets API v1. This table is generated from each SDK repository’s version files when the docs are built.

API v1 against each SDK
APISDKPackageVersionMinimum runtimeChanges
v1Nodehandwrites.io0.1.0 · pre-releaseNode 20Releases
v1Pythonhandwrites.io0.1.0 · pre-releasePython 3.10Changelog
v1Gogitlab.com/handwrite/sdk/go0.1.0 · pre-releaseGo 1.22Changelog
v1Rusthandwrites0.1.0 · pre-releaseRust 1.83Changelog
v1Javaio.handwrites:handwrites0.1.0 · pre-releaseJava 17Changelog
v1Kotlinio.handwrites:handwrites-kotlin0.1.0 · pre-releaseKotlin/JVM 17Changelog
v1C#Handwrites.IO0.1.0 · pre-release.NET 8 · .NET Standard 2.0Changelog
v1C++handwrites0.1.0 · pre-releaseC++20 · CMake 3.21Changelog
v1Clibhandwrites0.1.0 · pre-releaseC11 · CMake 3.21Changelog

Prefer raw HTTP?

Each edition documents every endpoint with request and response examples: Regular accounts, Teams and Classrooms. You can also try the API in the playground.