TRAYHOP DEVELOPER GUIDE

Print from a web app
to an approved local printer.

TrayHop connects browser applications to label and document printers through a workstation agent at http://127.0.0.1:18181. The operator approves an exact website origin and exact printer IDs before the application can pair.

Private alpha: the browser SDK is public for pilot integration, while the workstation app and production licensing are still controlled. Current integrations must retain their existing print provider as a fallback until physical acceptance is complete.

01

QUICKSTART

Prepare, persist, then submit once.

  1. Install and start TrayHop on the workstation in preview mode.
  2. In local setup, approve the application's exact HTTPS origin and selected printers.
  3. Generate a five-minute, single-use pairing code and let the operator enter it in the application.
  4. Save an application intent and unique job key before reading or sending the PDF.
  5. Submit once. Reconcile an interrupted response with read-only lookup.

The alpha SDK keeps its pairing credential in memory. A reload requires a new pairing code; never put a credential in a URL, source file, analytics event, or log.

JavaScript · ESMsafe intent flow
import {
  LocalPrintClient,
  PrintIntentCoordinator,
  IndexedDbPrintIntentStore,
  TrayHopError
} from "https://trayhop.com/sdk/0.19/trayhop.js";

const client = new LocalPrintClient();
await client.pair(codeEnteredByOperator);

const printing = new PrintIntentCoordinator(
  client,
  new IndexedDbPrintIntentStore(
    "my-app-trayhop-intents-v1"
  )
);

const intentId = crypto.randomUUID();
await printing.prepare({
  intentId,
  idempotencyKey: crypto.randomUUID(),
  printerId: selectedPrinter.id,
  options: {
    widthMm: 101.6,
    heightMm: 50.8,
    copies: 1,
    duplex: "none"
  }
});

try {
  const { job } = await printing.submit(
    intentId,
    pdfBlob
  );
  renderJobState(job);
} catch (error) {
  if (error instanceof TrayHopError &&
      error.submissionMayHaveOccurred) {
    showCheckStatus(intentId);
  } else {
    showKnownFailure(error);
  }
}
02

INTEGRATION CONTRACT

Keep these boundaries intact.

Browser to loopback

Call the local agent from browser code. A hosted server cannot reach the workstation's loopback address. Current Chrome and Edge may ask for local-network access.

Exact origin

The browser Origin must exactly match the origin approved by the workstation operator. Production origins use HTTPS.

Exact printer grants

Discovery returns only approved printers. Do not accept an arbitrary printer name from a URL or untrusted document.

PDF dimensions

Send physical width and height in millimeters. If the PDF already repeats pages for copies, send copies: 1.

Durable job key

Generate and retain a unique 12–80 character key before submission. Reusing it with different content or settings is a conflict.

Acknowledgement limits

accepted means the operating system acknowledged the queue request. It is not proof that paper or a label physically printed.

Job states and required application behavior
StateMeaningApplication behavior
pendingReserved; preflight is running.Wait or query the same job.
dispatchingNative preparation or submission is underway.Wait; never create a replacement automatically.
acceptedThe OS acknowledged submission.Show accepted; verify physical output when required.
previewedA preview PDF was saved.No physical print occurred.
rejectedA known failure occurred before native submission.Fix it; a deliberate retry uses a new intent and key.
unknownSubmission could not be confirmed.Check queue and output before any deliberate new job.
03

READ-ONLY RECOVERY

A missing response is a state, not retry permission.

If the page reloads or a response is interrupted, restore the saved intent ID and call recover. That operation looks up the original key across retained history and never sends the PDF.

A null lookup is not proof that nothing printed. Access may have changed, a request may still be arriving, or the application may be connected to a different workstation or printing mode.

Recoveryread only
const { intent, job } =
  await printing.recover(savedIntentId);

if (!job || [
  "pending",
  "dispatching",
  "unknown"
].includes(job.state)) {
  pauseNewSubmission(intent);
  askOperatorToReconcile();
}
04

MACHINE-READABLE RESOURCES

Give your coding agent the canonical files.

05

COMMON QUESTIONS

What builders need to know.

What is TrayHop?

TrayHop is an independent local printing connector for approved web applications. It exposes a browser SDK and loopback API for PDFs, labels, barcodes, receipts, and documents.

Is TrayHop a QZ Tray fork?

No. TrayHop is an independent implementation and includes no QZ source. During pilot integration, applications should retain their existing QZ or browser-print path as a fallback.

Does TrayHop install printer drivers?

No. It uses printers already configured by the operating system. Driver installation and printer administration remain outside TrayHop.

Can a server call TrayHop?

A remote server cannot reach a user's loopback agent. The browser calls TrayHop locally after the workstation operator approves that website origin.

Can it silently retry a failed job?

No. A failure can arrive after native submission began. TrayHop retains the original key and provides read-only recovery so the application and operator can reconcile without blind duplicates.