First print from the browser
In local mode your web page talks directly to the agent on the same computer. Every request is signed by your backend, so a random website cannot print on your customers' printers.
You need an installed and activated agent, and Node 18.4 or later for the signing endpoint.
1. Allow your site
Dashboard → Developers → Allowed origins → Add, for example https://pos.example.com. An origin is a scheme, a host and a port, with no path.
While developing on http://localhost:3000, turn on Dev mode on the same page instead: it allows every http://localhost port.
2. Create a signing key
Dashboard → Developers → Signing keys → Create. The dashboard shows:
- the key ID (for example
sk_01J9…), - your organization ID (
org_…), - the private key, shown only once.
Store the private key in your server's secrets, for example in the environment variable JODAPRINT_SIGNING_KEY. Never put it in the web page. The agents receive the public key with their configuration within seconds.
3. Add the signing endpoint to your server
npm install @jodaprint/node
// server.ts (Express)
import express from "express";
import { createSigner, signRequestHandler } from "@jodaprint/node";
const signer = createSigner({
orgId: "org_123",
keyId: "sk_01J9ABC",
privateKey: process.env.JODAPRINT_SIGNING_KEY!,
});
const app = express();
app.post(
"/jodaprint/sign",
express.json(),
signRequestHandler(signer, {
// Required: decide who may print. Here, any logged-in user.
authorize: (req) => Boolean(req.session?.user),
}),
);
app.listen(3000);
The same handler works in Next.js route handlers, Bun, Deno and Hono:
// app/jodaprint/sign/route.ts
export const POST = signRequestHandler(signer, { authorize: async (req) => isLoggedIn(req) });
4. Print from the page
npm install @jodaprint/sdk
import { JodaPrint, httpSigner } from "@jodaprint/sdk";
const jp = new JodaPrint({ orgId: "org_123", signer: httpSigner("/jodaprint/sign") });
await jp.connect(); // finds the agent on wss://localhost:8182, 8283, 8384 or 8485
const printers = await jp.printers.list();
console.log(printers.map((p) => `${p.id} ${p.name}`));
const job = await jp.printAndWait({
type: "receipt",
data: jp.receipt().text("Bonjou!").bold().center().feed(3).cut(),
idempotencyKey: "order-10492",
});
console.log(job.state); // "printed"
With no printer, the job goes to the default printer. Name one with printer: { id: "prn_…" }, { alias: "receipt" } or { name: "EPSON TM-T20III" }.
Without a bundler, load the UMD build. It defines window.JodaPrint:
<script src="https://cdn.jsdelivr.net/npm/@jodaprint/sdk/dist/jodaprint.umd.min.js"></script>
<script>
const jp = new JodaPrint({ orgId: "org_123", signer: JodaPrint.httpSigner("/jodaprint/sign") });
</script>
What can go wrong
| Error | Meaning |
|---|---|
AGENT_NOT_FOUND | No agent answers. Install or start it; error.helpUrl points to the download page. |
AGENT_CERT_UNTRUSTED | The agent runs but the browser does not trust its certificate. See Troubleshooting. |
ORIGIN_NOT_ALLOWED | Add the page's origin (step 1). |
SIGNATURE_INVALID | Wrong key ID, organization or private key on the server. |
Every error has code, message and retryable. The full list is in Error codes.
Next
- Describe real receipts: Receipts.
- How signing works: Security and signing.
- Every SDK call: JavaScript SDK reference.