Read and write from a script
A small TypeScript script that searches for yesterday’s notes, reads them, and writes a digest to the pad. It
uses fetch only, so it runs on Node 20 or newer, Deno, Bun or a Worker. The token needs search, read and
write on the pad.
const BASE = "https://app.inseam.io";const TOKEN = process.env.INSEAM_TOKEN!;const headers = { Authorization: `Bearer ${TOKEN}` };
type Hit = { address: string; title: string; snippet?: string; meta: { mtime: string; mime: string } };
async function api<T>(path: string, init: RequestInit = {}): Promise<T> { const r = await fetch(BASE + path, { ...init, headers: { ...headers, ...(init.headers ?? {}) } }); if (!r.ok) { const body = await r.json().catch(() => ({})); throw new Error(`${init.method ?? "GET"} ${path}: ${r.status} ${JSON.stringify(body)}`); } return (r.headers.get("content-type") ?? "").includes("json") ? r.json() : (r.text() as unknown as T);}
// 1. Find notes changed in the last day.const since = new Date(Date.now() - 86_400_000).toISOString();const { hits } = await api<{ hits: Hit[] }>("/v1/search", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ q: "meeting OR todo OR decision", topK: 50, filter: { kind: "text/markdown" } }),});const recent = hits.filter((h) => h.meta.mtime >= since);
// 2. Read each one. Reads over 4 MiB are refused; notes are small.const sections: string[] = [];for (const hit of recent) { try { const text = await api<string>(`/v1/o/${encodeURIComponent(hit.address)}`); sections.push(`## ${hit.title}\n\n${hit.address}\n\n${text.slice(0, 2000)}`); } catch (e) { // A note on a sleeping Mac answers 503 steward-offline. Say so and move on. sections.push(`## ${hit.title}\n\n${hit.address}\n\n_${(e as Error).message}_`); }}
// 3. Write the digest to the pad. It is searchable when the call returns.const day = new Date().toISOString().slice(0, 10);const body = `# Digest for ${day}\n\n${recent.length} notes changed since ${since}.\n\n${sections.join("\n\n")}\n`;const { entry } = await api<{ entry: { address: string; etag: string } }>(`/v1/o/pad/digests/${day}.md`, { method: "PUT", headers: { "Content-Type": "text/markdown" }, body,});console.log(`wrote ${entry.address} (${entry.etag})`);INSEAM_TOKEN=ias_… node digest.ts- Addresses in URLs.
encodeURIComponent(address)works for a fullinseam://address. The short formpad/digests/2026-10-25.mdneeds no encoding for ordinary names. - Do not overwrite a newer version. Send
If-Match: {etag}from the last write; a412means someone else wrote in between, anddetails.etagis the current version. - Over the limit. A
402 over-limiton the write means the network is full. The bytes are stored and indexed as soon as there is room; see plans and billing. - Scheduling. A cron job on any machine works, with the token in its environment. The token can be limited to
the hosts it reads and to the
pad/digests/prefix when you create it.