Developers
inklet Portal SDK
A server-side TypeScript client for the paper on your walls. Hand it text, a link, an image, or a PDF — Inklet does the layout, picks the room, and renders for the panel.
v0.1.0 · Node 20+ · ESM & CommonJS
import { Inklet } from "@inklethq/sdk";
const inklet = new Inklet({ pat: process.env.INKLET_PAT! });
await inklet.push.auto({
title: "Daily brief",
intent: "Make the key update easy to scan",
assets: [
inklet.assets.text("Revenue is up 12% week over week."),
inklet.assets.link("https://example.com/report"),
],
});Push
Three ways to put something on a wall.
They differ in how much of the decision you keep. All three take the same assets, return the same result, and can be replayed safely with an idempotency key.
You have something worth showing and no opinion about where.
Inklet reads the assets, chooses the displays that can render them, and does the typesetting. Intent is a sentence of direction, not a template — it steers the layout without describing it.
const result = await inklet.push.auto({
idempotencyKey: "daily-brief-2026-08-15",
title: "Daily brief",
intent: "Make the key update easy to scan",
assets: [
inklet.assets.text("Revenue is up 12% week over week."),
inklet.assets.link("https://example.com/report"),
],
});You know the room. Inklet still sets the type.
One display, named by id. The asset pipeline is unchanged — text, links, images, PDFs all still get summarised and laid out — but the routing decision stays yours.
await inklet.push.manual({
displayId: "display_123",
assets: [
inklet.assets.image({
data: await readFile("chart.png"),
filename: "chart.png",
contentType: "image/png",
}),
inklet.assets.text("This week's trend"),
],
});You already made the picture.
Exactly one PNG or JPEG, to exactly one display, rendered as sent. Inklet scales it to the panel — your source does not have to arrive at 800×480.
await inklet.push.hardcode({
displayId: "display_123",
image: inklet.assets.image({
data: await readFile("poster.jpg"),
filename: "poster.jpg",
contentType: "image/jpeg",
}),
});Lifecycle
A push is a request, not a render.
The call returns as soon as Inklet has your assets. Summarising, routing, and rendering happen after — and a display only shows the result once it wakes and confirms it. Poll the Content if you need to know it landed.
Processing stages
awaiting_upload · fetching_links · summarizing · routing · creating_presentations
const result = await inklet.push.auto({ assets });
// A successful push commonly returns a `processing` Content
// with no Presentation IDs yet. Poll until it settles.
let content = await inklet.contents.retrieve(result.contentId);
while (content.state === "processing") {
await new Promise((resolve) => setTimeout(resolve, 1000));
content = await inklet.contents.retrieve(content.id);
}
if (content.state === "failed") {
console.error(content.processing.error);
}Reference
Four resources, fully typed.
inklet.displays
The panels bound to your account, and what each can render.
- list()Paginated, newest first
- retrieve(id)Battery, firmware, tags, capabilities
- listQueue(id)What is waiting, over a time range
- current(id)The confirmed Presentation, or null
inklet.contents
The lower-level lifecycle, when push.* hides too much.
- create(input, key)Returns upload tickets
- retrieve(id)State, stage, warnings, errors
- list()Filter by mode and state
- confirm(id)Close uploads and start processing
inklet.presentations
A rendered frame for one display, in one format.
- retrieve(id)png · raw2 · raw4
inklet.assets
Validated locally, before anything leaves the process.
- text(string)Plain prose
- link(url)Fetched and summarised by Inklet
- image({ data })png · jpeg · gif · webp · svg
- file({ data })pdf · txt · md · json
Guardrails
A key that reaches your walls deserves care.
Server-only, by construction
Constructing the client where a document exists throws before a request is made. A personal access token cannot end up in a browser bundle by accident.
Uploads never carry the token
Binary assets go straight to temporary storage URLs. The token is sent only to Inklet endpoints, and requests refuse absolute URLs and cross-origin redirects.
Safe to replay
Every push takes an idempotency key. Omit it and the SDK generates one, then hands it back — so your retry is the same call, not a second one.
Errors you can act on
Every error extends InkletError and keeps the backend code, HTTP status, request ID, and structured details. Credentials are redacted from messages.
import { InkletError, RateLimitError } from "@inklethq/sdk";
try {
await inklet.displays.list();
} catch (error) {
if (error instanceof RateLimitError) {
// Retry on your own schedule.
} else if (error instanceof InkletError) {
console.error(error.code, error.status, error.requestId);
}
}Or keep it off the cloud entirely
The service address defaults to dev.iminklet.com while the SDK is in developer preview. Point baseUrl at a Compute Hub instead and the same code runs without anything leaving your network.
10 MiB
per binary asset
50
assets per push
3
output formats
Start with a token.
The SDK is in developer preview. The surface is small on purpose and stable enough to build on; breaking changes are announced in the changelog.
Create a token
Personal access tokens are issued in the Portal dashboard and scoped to your displays.
Install the package
npm install @inklethq/sdk — Node 20 or newer, ESM or CommonJS, types included.
Push something
One call puts words on a wall. Everything else is refinement.