Skip to main content

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​

ErrorMeaning
AGENT_NOT_FOUNDNo agent answers. Install or start it; error.helpUrl points to the download page.
AGENT_CERT_UNTRUSTEDThe agent runs but the browser does not trust its certificate. See Troubleshooting.
ORIGIN_NOT_ALLOWEDAdd the page's origin (step 1).
SIGNATURE_INVALIDWrong key ID, organization or private key on the server.

Every error has code, message and retryable. The full list is in Error codes.

Next​