Send print jobs from any web app, server, or script to printers connected through the PrintRelay Mac agent.
https://print.jiayun.aiv1 (prefix /api/public/v1)Every request must include a Bearer token in the Authorization header. Create an API key in Dashboard → API Keys. Keys look like prk_live_xxxxxxxx….
Authorization: Bearer prk_live_xxxxxxxxFor drop-in compatibility with tools written for PrintNode, HTTP Basic is also accepted (API key as username, empty password):
Authorization: Basic <base64(prk_live_xxxxxxxx:)>Scopes, rate limits & IP allowlist
read (list/get), print (create/cancel jobs), manage (everything). Default is read,print.429.403.All errors use this shape:
{
"error": {
"message": "Human-readable explanation",
"code": 401
}
}Validation errors (HTTP 400) include a details field with Zod-style field errors.
| HTTP | Meaning |
|---|---|
| 200 | OK |
| 201 | Created (new print job) |
| 400 | Validation failed |
| 401 | Missing, malformed, invalid, or revoked API key |
| 403 | Missing required scope, or IP not in allowlist |
| 404 | Not found, or not owned by you |
| 409 | Conflict (e.g. cancel a job that already finished) |
| 429 | Rate limit exceeded for this key |
| 500 | Internal server error |
All endpoints share the prefix https://print.jiayun.ai/api/public/v1.
/api/public/v1/whoamiVerify your API key and return the owning account. Use this as a health check.
curl https://print.jiayun.ai/api/public/v1/whoami \
-H "Authorization: Bearer prk_live_xxxxxxxx"Response 200
{
"id": "d9b1e6ea-7c21-4924-9727-5a12e2f0b826",
"displayName": "Jia Wei",
"company": "Jiayun"
}/api/public/v1/computersList Mac agents bound to your account. state is online when the agent's WebSocket is currently connected.
[
{
"id": "9be2a2fd-b3c2-4077-8605-309788806b41",
"name": "kgjs-MacBook-Pro",
"hostname": "kgjs-MacBook-Pro.local",
"os": "darwin",
"state": "online",
"client_version": "1.0.0",
"last_seen_at": "2026-05-29T12:40:11Z",
"created_at": "2026-05-29T11:02:33Z"
}
]/api/public/v1/printersList the printers exposed by all of your computers. Jobs sent to an offline printer are queued and delivered on reconnect.
curl https://print.jiayun.ai/api/public/v1/printers \
-H "Authorization: Bearer prk_live_xxxxxxxx"Response 200
[
{
"id": "88c9a3b3-7e92-4a53-a82a-c6651f925312",
"name": "HP_LaserJet_M109_M112",
"is_default": true,
"state": "online",
"capabilities": {},
"computer_id": "9be2a2fd-b3c2-4077-8605-309788806b41"
}
]/api/public/v1/printjobsSubmit a print job.
| Field | Type | Req. | Notes |
|---|---|---|---|
| printerId | uuid | yes | From GET /printers |
| title | string (1–200) | yes | Shown in dashboard |
| contentType | enum | yes | pdf_uri | pdf_base64 | raw_base64 | image_uri | image_base64 | url |
| content | string | yes | ≤ ~20 MB base64 |
| options | object | no | copies, paperSize, orientation, duplex, color, scale, tray, pageRanges, dpi |
| source | string (≤80) | no | Free-form tag (defaults to API key label) |
Pass Idempotency-Key: <your-id> as a header to safely retry without duplicate prints — same key returns the original job for 24h. Cancel a queued/sent job with DELETE /api/public/v1/printjobs/{id}.
Webhooks: configure endpoints in Dashboard → Webhooks to receive job.queued, job.sent, job.printing, job.done, job.error, job.cancelled, printer.online, printer.offline. Verify each request with X-PrintRelay-Signature: t=<ts>,v1=hmac_sha256(secret,"{ts}.{body}").
Sandbox testing: create a virtual printer in Dashboard → Printers to integrate against the full job lifecycle without installing the desktop agent.
Example — print a PDF by URL
curl -X POST https://print.jiayun.ai/api/public/v1/printjobs \
-H "Authorization: Bearer prk_live_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"printerId": "88c9a3b3-7e92-4a53-a82a-c6651f925312",
"title": "Invoice #1234",
"contentType": "pdf_uri",
"content": "https://example.com/files/invoice-1234.pdf",
"source": "billing"
}'Response 201
{
"id": "9f1c0a44-1f7b-4d2b-8d61-7c2a3a8c4c1e",
"title": "Invoice #1234",
"state": "sent", // "sent" if agent online, "queued" if offline
"created_at": "2026-05-29T12:40:00Z"
}Example — Node.js (in-memory PDF)
import fs from "node:fs";
const pdfBase64 = fs.readFileSync("/tmp/invoice.pdf").toString("base64");
await fetch("https://print.jiayun.ai/api/public/v1/printjobs", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PRINT_JIAYUN_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
printerId,
title: `Order ${orderId}`,
contentType: "pdf_base64",
content: pdfBase64,
source: "orders-service",
}),
});Example — Python
import base64, requests
with open("invoice.pdf", "rb") as f:
content = base64.b64encode(f.read()).decode()
r = requests.post(
"https://print.jiayun.ai/api/public/v1/printjobs",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"printerId": PRINTER_ID,
"title": "Invoice 1234",
"contentType": "pdf_base64",
"content": content,
"source": "billing",
},
)
r.raise_for_status()
print(r.json())Example — receipt printer (raw ESC/POS)
const raw = Buffer.from(escposBuffer).toString("base64");
await fetch("https://print.jiayun.ai/api/public/v1/printjobs", {
method: "POST",
headers: {
Authorization: `Bearer ${KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
printerId,
title: "Receipt #92",
contentType: "raw_base64",
content: raw,
}),
});/api/public/v1/printjobs?limit=50List recent jobs (most recent first). limit default 50, max 100.
curl "https://print.jiayun.ai/api/public/v1/printjobs?limit=20" \
-H "Authorization: Bearer prk_live_xxxxxxxx"Response 200
[
{
"id": "9f1c0a44-1f7b-4d2b-8d61-7c2a3a8c4c1e",
"title": "Invoice #1234",
"state": "done",
"source": "billing",
"content_type": "pdf_uri",
"created_at": "2026-05-29T12:40:00Z",
"printed_at": "2026-05-29T12:40:03Z",
"printer_id": "88c9a3b3-7e92-4a53-a82a-c6651f925312",
"error": null
}
]| State | Set by | Meaning |
|---|---|---|
| queued | server | Stored; waiting for the agent to come online |
| sent | server | Pushed over WebSocket to the agent |
| printing | agent | Handed to the OS print spooler |
| done | agent | Print completed; printed_at is set |
| error | agent | Failed; see error field |
| cancelled | agent | Cancelled before printing |
| contentType | content must be | Best for |
|---|---|---|
| pdf_uri | Public HTTPS URL to a PDF | PDFs hosted on your server / CDN |
| pdf_base64 | Base64-encoded PDF bytes | PDFs generated in memory; private docs |
| raw_base64 | Base64 raw printer bytes | Thermal receipts (ESC/POS), labels (ZPL/TSPL) |
Hard limit on content: ~20 MB of base64 text (≈ 15 MB binary). For pdf_uri, the URL must be reachable from the Mac agent's network — the agent fetches it, not the API server.
GET /whoami → expect HTTP 200.GET /printers; cache IDs or show a picker.POST /printjobs from your backend.GET /printjobs and show state to your users.GET /computers reports offline for long, alert someone.Migrating from PrintNode? Most code paths translate directly:
pdf_uri, pdf_base64, and raw_base64 have the same semantics.print.jiayun.ai vs api.printnode.com) and field naming (camelCase request, snake_case response above).Need help?
Contact us at support@jiayun.ai with the request ID from your 4xx/5xx response.