Reference
API Reference
REST endpoints for printers, print jobs, queues, history, materials, sites, and user provisioning, with authentication and request examples.
Use the REST API to build reporting dashboards and provision users. All paths below are
relative to your deployment’s /api/rest base URL, for example
https://print.example.com/api/rest. Send and receive JSON; requests with a body need
Content-Type: application/json. JSON request bodies are limited to 1 MiB; file uploads
use the separate slicer interface.
Jump to Printers, Jobs, Queue and history, Materials, Sites, or Users and roles.
| Endpoint | Required capability |
|---|---|
GET / | Any valid key |
GET /printers | printers:read |
GET /printers/:id | printers:read |
GET /jobs | jobs:read |
GET /jobs/:id | jobs:read |
GET /jobs/:id/events | jobs:read |
GET /queue/jobs | jobs:read |
GET /history | jobs:read |
GET /queue/estimation | queue:read |
GET /queue/estimation/summary | queue:read |
GET /materials | materials:read |
GET /materials/:id | materials:read |
GET /materials/types | materials:read |
GET /materials/brands | materials:read |
GET /materials/colors | materials:read |
GET /sites | sites:read |
GET /sites/:id | sites:read |
GET /users | users:read |
GET /users/:id | users:read |
POST /users | users:write* |
PUT /users/:id | users:write* |
POST /users/bulk | users:write* |
PUT /users/bulk | users:write* |
POST /users/:id/roles | users:write, roles:write |
DELETE /users/:id/roles | users:write, roles:write |
POST /users/bulk/roles | users:write, roles:write |
PUT /users/:id/id-number | users:write |
*Including roles in a user create/update request also requires roles:write, even for
an empty array or one item in a bulk request.
Authentication and access
Create a key in Admin → API Keys with the access your integration needs. See API Keys for profiles, expiration, and rotation. Every endpoint on this page requires a valid key. Send it over HTTPS in either header:
Authorization: Bearer <key>
X-API-Key: <key>
The examples use PLURAPRINT_API_KEY supplied by your secrets store or shell environment.
Replace the example hostname with your deployment’s hostname:
curl --fail-with-body \
--header "Authorization: Bearer $PLURAPRINT_API_KEY" \
'https://print.example.com/api/rest/printers?limit=25&isEnabled=true'
A key’s capabilities apply across the deployment. siteId filters results by physical
location; it does not limit what the key is authorized to read. Job access includes job
names and owner IDs. Responses omit printer connection secrets, storage URLs, and raw
telemetry. REST responses use Cache-Control: no-store.
| Capability | Allows |
|---|---|
printers:read | Printer lists and details, including configured material slots |
jobs:read | Job lists, details, lifecycle events, queue contents, and history |
queue:read | Queue completion estimates |
materials:read | Material variants, types, brands, and colors |
sites:read | Site lists and details |
users:read | User lists and details |
users:write | Create/update users and assign or clear an ID number |
roles:write | Role changes, together with users:write |
The Fleet reporting profile includes all five reporting capabilities. Queue reporting includes estimates only; use Read jobs and history for queue contents.
Discover endpoints
GET / · Any valid API key
Returns name, version, status, the endpoints directory, and authentication.
The directory lists available routes, including routes the key may not have permission to
call. Use the capability requirement documented for each endpoint.
Request and response conventions
Resource IDs for printers, jobs, materials, and sites are decimal integers from 1 to
2,147,483,647. User IDs are UUIDs. Response timestamps use ISO 8601. Nullable fields return
null when the value is unavailable or unassigned.
Pagination and search
Printer, job, event, material, and site lists accept these pagination parameters:
| Parameter | Default | Accepted value |
|---|---|---|
limit | 50 | Integer from 1 to 100 |
offset | 0 | Integer from 0 to 1000000 |
search | Omitted | Up to 100 characters after trimming; unavailable on job events |
Numeric values must be decimal integers. Boolean filters accept only true or false.
Unknown query parameters and invalid values return 400. A list responds with its named
collection, matching total, and pagination metadata. For example, an empty printer list:
{
"printers": [],
"total": 0,
"limit": 50,
"offset": 0,
"hasMore": false
}
For the next page, increase offset by limit. Stop when hasMore is false. Each request
reads the current data; jobs entering or leaving the queue can change later pages.
User lists retain their separate pagination defaults and response shape.
Errors and retries
| HTTP status | Meaning | Action |
|---|---|---|
400 | Invalid parameters, malformed body, or unknown role name | Correct the request |
401 | Missing, invalid, expired, revoked, or legacy key | Replace the key or fix the header |
403 | Missing capability, or ID number identification unavailable | Check key access and deployment configuration |
404 | Resource does not exist | Check the ID |
409 | Username, email, or ID number already assigned | Resolve the conflict |
413 | JSON request body exceeds 1 MiB | Reduce the request size |
415 | Unsupported request content type | Send Content-Type: application/json |
429 | Rate limit exceeded | Wait for Retry-After, then retry with backoff |
500 | Server could not complete the request | Retry reads with bounded backoff; investigate repeated failures |
Errors include an error string; authentication and capability errors also include a
message. For example, an invalid reporting query returns:
{ "error": "Invalid query parameters" }
The API’s shared rate limit is 300 requests per minute per IP address. Integrations behind the same network address share that budget. After an uncertain write result, read the affected user before retrying. Bulk endpoints can return 207 with successful and failed items; check every result as described under Bulk results.
Printers
Both endpoints require printers:read.
List printers
GET /printers
Accepts pagination and search, plus:
| Parameter | Filter |
|---|---|
siteId | Exact site ID |
status | offline, idle, ready, dispatching, printing, paused, or done |
model | Exact catalog model identifier, such as BAMBU_LAB_P1S; unknown identifiers return 400 |
isEnabled | true or false |
Search matches printer names and model identifiers without case sensitivity. Results sort
by printer index, then ID. Returns {printers, total, limit, offset, hasMore}.
Get a printer
GET /printers/:id
Returns a printer object, or 404 if it does not exist. No query parameters.
Printer object
List and detail responses use the same fields:
| Field | Type and meaning |
|---|---|
id, index, name | Resource ID, display-order integer, and printer name |
notes | String or null |
model, status, siteId | Model identifier, printer status, and site ID |
isEnabled | Whether the printer is enabled |
isDown | Whether a current downtime record exists, including pending downtime |
currentPrintJobId | Current job ID, or null |
progress | Current job percentage (0–100), or null without a current job |
timeRemaining | Current job’s remaining seconds, or null without a current job |
lastSeenAt | Last report timestamp, or null |
materials | Configured material slot assignments, ordered by extruder and slot |
createdAt, updatedAt | Record timestamps |
Each material assignment has slot, extruder, and material. Its material contains
id, name, type: {id, name}, brand: {id, name}, and nullable
color: {id, name, value}. Assignments may be manual or synchronized; they are the
configured loadout rather than a guarantee of what is physically installed.
For example, GET /printers/1 might return:
{
"id": 1,
"index": 0,
"name": "Workshop P1S",
"notes": null,
"model": "BAMBU_LAB_P1S",
"status": "idle",
"siteId": 1,
"isEnabled": true,
"isDown": false,
"progress": null,
"timeRemaining": null,
"currentPrintJobId": null,
"lastSeenAt": "2026-10-14T12:00:00.000Z",
"materials": [],
"createdAt": "2026-10-01T12:00:00.000Z",
"updatedAt": "2026-10-14T12:00:00.000Z"
}
Jobs
All three endpoints require jobs:read.
List jobs
GET /jobs
Lists jobs across all lifecycle states, newest creation first. Accepts pagination and search, plus the filters below. Search matches job names, without case sensitivity.
| Parameter | Filter |
|---|---|
siteId | Exact job site ID; excludes jobs without an assigned site |
printerId | Recorded printer assignment, current printer, or printer with an unresolved dispatch attempt |
userId | Job owner UUID |
status | One job status |
printerModel | Exact catalog model identifier, such as BAMBU_LAB_P1S; unknown identifiers return 400 |
createdAfter | Job created at or after this ISO 8601 timestamp |
createdBefore | Job created before this ISO 8601 timestamp |
Timestamps must include Z or a timezone offset. The lower bound is inclusive and the upper
bound is exclusive. createdBefore must be later than createdAfter when both are supplied;
equal or reversed bounds return 400. These filters describe job creation time, including
when used on history; they do not filter completion time.
curl --fail-with-body --get \
--header "Authorization: Bearer $PLURAPRINT_API_KEY" \
--data-urlencode 'createdAfter=2026-10-01T00:00:00Z' \
--data-urlencode 'createdBefore=2026-11-01T00:00:00Z' \
--data-urlencode 'siteId=1' \
'https://print.example.com/api/rest/jobs'
Returns {jobs, total, limit, offset, hasMore}. Creation time ties are resolved by job ID.
Get a job
GET /jobs/:id
Returns a job object, or 404 if it does not exist. No query parameters.
Job object
Job lists, details, queue contents, and history use the same fields:
| Field | Type and meaning |
|---|---|
id, name, status | Job ID, name, and lifecycle status |
siteId, userId | Site ID and owner UUID; each may be null |
printerModel | Target model identifier, or null before definition |
printerId, currentPrinterId | Recorded printer assignment and current printer ID; each may be null |
dispatchAttemptPrinterId, hasUnresolvedDispatch | Printer ID for an unresolved dispatch attempt (null if none), and whether such an attempt exists |
progress, timeRemaining | Stored progress percentage (0–100) and remaining seconds |
priority, deadline | Integer priority and optional deadline timestamp (null when unset) |
isReprint | Whether this job was created as a reprint |
approvedAt | Approval timestamp, or null |
estimatedDurationSeconds | Estimated print duration, or null when unknown |
quantities | Material estimates with explicit units, or null when unknown |
materialRequirements | Array of required materials, ordered by extruder and material index |
createdAt, updatedAt | Record timestamps |
Stored progress and remaining time may be zero before printing; use status to determine
the job’s lifecycle state. Each material requirement contains index, extruder,
type: {id, name}, nullable brand: {id, name}, and nullable color: {id, name, value}.
A null brand or color leaves that attribute unspecified. When hasUnresolvedDispatch is
true, printing may already have started without confirmation. Do not infer failure or
submit a duplicate job from missing confirmation.
quantities contains version: 1, total, and perMaterial. Quantity objects can contain
massG (grams), volumeMl (millilitres), and lengthMm (millimetres). Missing values mean
unknown, not zero. perMaterial also includes each material’s index. Optional derived
arrays name quantities calculated from another unit. For example:
{
"version": 1,
"total": { "massG": 24.5 },
"perMaterial": [{ "index": 0, "massG": 24.5 }]
}
A job response can look like this; IDs and names here are illustrative:
{
"id": 42,
"name": "Mounting bracket",
"status": "printing",
"siteId": 1,
"userId": "550e8400-e29b-41d4-a716-446655440000",
"printerModel": "BAMBU_LAB_P1S",
"printerId": 1,
"currentPrinterId": 1,
"dispatchAttemptPrinterId": null,
"hasUnresolvedDispatch": false,
"progress": 40,
"timeRemaining": 900,
"priority": 0,
"deadline": null,
"isReprint": false,
"approvedAt": "2026-10-14T11:40:00.000Z",
"estimatedDurationSeconds": 1500,
"quantities": {
"version": 1,
"total": { "massG": 24.5 },
"perMaterial": [{ "index": 0, "massG": 24.5 }]
},
"materialRequirements": [
{ "index": 0, "extruder": 0, "type": { "id": 1, "name": "PLA" }, "brand": null, "color": null }
],
"createdAt": "2026-10-14T11:30:00.000Z",
"updatedAt": "2026-10-14T12:00:00.000Z"
}
Get job events
GET /jobs/:id/events
Returns the job’s lifecycle event records as {events, total, limit, offset, hasMore}.
Accepts limit and offset; no search or job filters. Events sort oldest first, then by ID.
A missing job returns 404; an existing job with no events returns an empty list.
Each event contains id, printJobId, type, nullable userId, and createdAt.
Raw event details and user profiles are excluded.
Event types are uploaded, processing, awaiting_approval, approved, rejected,
queued, dispatching, started, progress, completed, failed, cancelled,
reprinted, prioritized, deprioritized, and revision_requested. Event types and job
statuses describe different things; for example, the started event accompanies printing.
Job statuses
| Group | Status values |
|---|---|
| Intake and preparation | uploaded, draft, pending_file, processing, pending_definition, awaiting_approval |
| Waiting and active | queued, dispatching, printing, paused |
| Terminal | completed, failed, rejected, cancelled, expired |
uploaded is a legacy intake state. See Submitting a Print Job
for the preparation and approval process.
Queue and history
List queue jobs
GET /queue/jobs · jobs:read
Lists every nonterminal job, including intake and preparation. Accepts the same pagination
and job filters as /jobs, but status must be nonterminal; a terminal status
returns 400. Returns {jobs, total, limit, offset, hasMore} with the same job shape as
/jobs.
Results place active jobs first, then queued jobs by priority and first-in-first-out order, then intake and preparation jobs. This is a listing order; it does not promise dispatch order or a per-job start time. Printer compatibility and policies still determine dispatch.
List history
GET /history · jobs:read
Lists completed, failed, rejected, cancelled, and expired jobs, newest creation
first. Accepts the same pagination and job filters as /jobs, but status
must be terminal; a nonterminal status returns 400. Returns
{jobs, total, limit, offset, hasMore}.
Each row is a job, rather than an individual print attempt or event.
Estimate queue completion
GET /queue/estimation · queue:read
Returns the estimated time to finish queued and active work across the deployment.
The only query parameter is refresh: true bypasses the 30-second estimate cache;
false or omission uses the cache when available. Other values return 400.
| Response field | Meaning |
|---|---|
totalSeconds, formattedTime | Estimated remaining completion horizon, in seconds and a display string |
hasUnknownWait | Some work has an unknown wait; totalSeconds is a lower bound |
queuedJobCount, activeJobCount, totalJobCount | Queued, active, and combined counts |
activePrinterCount | Printers included in the estimate |
calculatedAt | Calculation timestamp |
breakdown | Per-model estimates and counts |
printingJobCount | Deprecated alias for activeJobCount |
Each breakdown row includes printerModel, totalSeconds, formattedTime,
hasUnknownWait, queuedJobCount, activeJobCount, totalJobCount, printerCount, and
averageTimePerJob (rounded seconds). jobCount is a deprecated alias for totalJobCount.
Active work includes dispatching, printing, and paused jobs. Estimates depend on compatible
printers, availability, materials, and policy; they are not guaranteed finish times.
Get an estimate summary
GET /queue/estimation/summary · queue:read
Returns only totalSeconds, formattedTime, and hasUnknownWait, using the estimate cache.
No query parameters. For an empty queue with no uncertain work:
{ "totalSeconds": 0, "formattedTime": "0m", "hasUnknownWait": false }
Materials
All five endpoints require materials:read. Materials are the configured catalog of
filaments and resins. These endpoints do not report remaining spool mass or resin volume.
All material lists sort by name, then ID. Name searches are case-insensitive; matching tags
and alternate names uses exact values.
List material variants
GET /materials
Accepts pagination and search, plus optional form=filament or
form=resin. A variant combines a type, brand, and optional color. Search covers variant,
type, brand, and color names, their tags, and type alternate names. Returns
{materials, total, limit, offset, hasMore}.
Get a material variant
GET /materials/:id
Returns a material variant object, or 404 if it does not exist. No query parameters.
Material variant object
Variants contain id, name, tags, createdAt, updatedAt, and:
| Field | Contents |
|---|---|
type | id, name, form, alternateNames, tags, createdAt, updatedAt |
brand | id, name |
color | id, name, value, or null |
form is filament or resin. Color value is the catalog’s color value string.
An example GET /materials/1 response:
{
"id": 1,
"name": "Workshop PLA",
"tags": [],
"type": {
"id": 1,
"name": "PLA",
"form": "filament",
"alternateNames": [],
"tags": [],
"createdAt": "2026-10-01T12:00:00.000Z",
"updatedAt": "2026-10-01T12:00:00.000Z"
},
"brand": { "id": 1, "name": "Workshop materials" },
"color": null,
"createdAt": "2026-10-01T12:00:00.000Z",
"updatedAt": "2026-10-01T12:00:00.000Z"
}
List material types
GET /materials/types
Accepts pagination and search, plus optional form=filament or
form=resin. Search covers names, alternate names, and tags. Returns
{types, total, limit, offset, hasMore}. Each type contains id, name, form,
alternateNames, tags, createdAt, and updatedAt.
List material brands
GET /materials/brands
Accepts pagination and search; search covers names and tags.
Returns {brands, total, limit, offset, hasMore}. Each brand contains id, name,
tags, createdAt, and updatedAt.
List material colors
GET /materials/colors
Accepts pagination and search; search covers names and tags.
Returns {colors, total, limit, offset, hasMore}. Each color contains id, name,
value, tags, createdAt, and updatedAt.
Sites
Both endpoints require sites:read. Sites group printers and agents by location or
network; access remains deployment-wide.
List sites
GET /sites
Accepts pagination and search; search matches site names without
case sensitivity. Results sort by name, then ID. Returns
{sites, total, limit, offset, hasMore}.
Get a site
GET /sites/:id
Returns a site object, or 404 if it does not exist. No query parameters.
Site objects contain id, name, description, nullable disabledAt, createdAt, and
updatedAt. Disabled sites remain in the list. An example site response:
{
"id": 1,
"name": "Main lab",
"description": "Printers in the main lab",
"disabledAt": null,
"createdAt": "2026-10-01T12:00:00.000Z",
"updatedAt": "2026-10-01T12:00:00.000Z"
}
Users and roles
User responses contain id, username, displayName, nullable email, roles (role names),
createdAt, and updatedAt. User IDs are UUIDs; role inputs use existing role names,
including capitalization. Unknown role names return 400.
List users
GET /users · users:read
Accepts limit (default 100, maximum 1000), offset (default 0), and optional
search. Search matches display names, usernames, and email addresses without case
sensitivity. Results sort by display name. Returns {users, total, limit, offset}.
This endpoint does not include hasMore; compare offset + users.length with total.
Get a user
GET /users/:id · users:read
Returns a user object, or 404 if it does not exist.
Create a user
POST /users · users:write, plus roles:write when roles is present
Required body fields: username, displayName, email. Optional: roles (array of existing
role names). Returns 201 with the created user. An existing username or email returns
409. Creating a user does not set a password.
{
"username": "alex",
"displayName": "Alex Morgan",
"email": "alex@example.com"
}
Update a user
PUT /users/:id · users:write, plus roles:write when roles is present
The body accepts username, displayName, email, and roles; all fields are optional.
Omitted fields retain their values. roles replaces all role assignments; [] removes
all roles. Returns 200 with the updated user, 404 for a missing user, or 409 for a
username or email conflict.
{ "displayName": "Alex Rivera" }
Create users in bulk
POST /users/bulk · users:write, plus roles:write if any item includes roles
Body: { "users": [...] }, with 1–100 user objects using the same fields as
Create a user. Required fields and role names are checked before any users
are created. Returns 201 if every creation succeeds, otherwise 207 with
bulk results.
Update users in bulk
PUT /users/bulk · users:write, plus roles:write if any item includes roles
Body: { "users": [...] }, with 1–100 objects. Each requires id and accepts the optional
fields from Update a user. Role names are checked before updates begin.
Returns 200 if every update succeeds, otherwise 207 with bulk results.
{
"users": [
{ "id": "550e8400-e29b-41d4-a716-446655440000", "displayName": "Alex Rivera" }
]
}
Assign roles
POST /users/:id/roles · users:write and roles:write
Body: { "roles": ["Member"] }. Adds the named roles, preserving existing assignments and
avoiding duplicates. Returns 200 with the updated user; a missing user returns 404.
Use a role name configured in your deployment.
Remove roles
DELETE /users/:id/roles · users:write and roles:write
Send a JSON body: { "roles": ["Member"] }. Removes only those roles, preserving other
assignments. Returns 200 with the updated user; a missing user returns 404.
Assign roles in bulk
POST /users/bulk/roles · users:write and roles:write
Body: { "assignments": [...] }, with 1–100 objects containing userId and a roles
array. Adds roles as in Assign roles. Role names are checked before
assignments begin. Returns 200 if all succeed, otherwise 207 with
bulk results.
{
"assignments": [
{ "userId": "550e8400-e29b-41d4-a716-446655440000", "roles": ["Member"] }
]
}
Assign or clear an ID number
PUT /users/:id/id-number · users:write
Requires ID number identification to be enabled and configured. Body: idNumber as a
string of 1–32 digits, or null to clear it. Leading zeros are preserved. Returns
{ "success": true }; it does not return the number. A number assigned to another user
returns 409 and leaves the current assignment intact. An unavailable identification
method returns 403.
{ "idNumber": "00123456" }
{ "idNumber": null }
Bulk results
Bulk writes are processed item by item after initial validation. Successful changes remain
applied when another item fails. The response contains results and a summary with
total, succeeded, and failed. index is the zero-based position in the request array.
Treat 207 as a signal to inspect every result, including when all items failed.
| Operation | Successful result | Failed result |
|---|---|---|
| Create users | success, index, user | success: false, index, error |
| Update users | success, index, id, user | success: false, index, id, error |
| Assign roles | success, index, userId, roles | success: false, index, userId, error |
Bulk user objects contain id, username, displayName, nullable email, and roles;
they omit timestamps. Retry only failed items after resolving their errors. Replaying an
entire create batch would conflict with users already created.
Slicer uploads
Slicer uploads use the separate OctoPrint-compatible interface at
/api/ingest/octoprint, with /api/ingest/orca-3mf retained for existing slicer profiles.
API keys need files:upload, and the issuing person must still have upload permission.
This capability does not grant access to the reporting or user endpoints above.
GET /api/ingest/octoprint/api/version tests the connection.
POST /api/ingest/octoprint/api/files/local accepts one multipart file, up to 1 GB.
Uploads create work for preparation and approval; print=true does not start printing.
See Submitting a Print Job.