Appearance
Job management
A job (JobState) is one production order on the shop floor: the counterpart of a JDF job ticket. The records around it all point back at it by jobId:
text
JobState
├── ComponentState the printed parts (cover, text block, insert …)
├── OperationState the production steps (printing, folding, binding …)
└── MilestoneState dates you track (proof sent, plates ready …)Operations and milestones can also belong to a specific component through componentId. Related jobs can be collected in a job group (JobGroupState). Components are covered in Components and media, and orders and estimates that create jobs in Orders and commerce.
Job status
JobState.status is a JobStatus. The values are:
| Value | Meaning |
|---|---|
DRAFT | Job created but not yet confirmed; the default for new jobs |
RFQ | Awaiting cost estimation |
CONFIRMED | Order confirmed, ready for production |
PREPRESS | Artwork preparation and plate-making |
PRESS | Currently printing |
POSTPRESS | Finishing operations (binding, cutting, etc.) |
PACKING | Packing for shipment |
SHIPPED | Dispatched to customer |
COMPLETED | Delivered and closed |
WAITING | On hold pending external action |
CANCELLED | Job cancelled |
EXCEPTION | Requires attention due to error |
The status is checked against this set when you save a job.
You change the status with updateJob (below). There is no separate transition mutation.
Create a job
name and quantity are required. Set externalId to the job's reference in your own system so you can find it again.
graphql
mutation CreateJob {
createJob(
input: {
name: "Spring catalogue 2027"
quantity: 5000
externalId: "MIS-2026-0042"
jobNumber: "J-10042"
dueAt: "2026-10-15T16:00:00Z"
priority: 5
tags: ["catalogue", "repeat-customer"]
billingCustomerId: "cus_01M0R2496SFANSZFKWPYW17V0J"
description: "A4 portrait, 48 pages plus cover"
}
) {
job {
id
jobNumber
status
}
errors {
path
message
}
}
}A new job starts as DRAFT unless you pass status. priority defaults to 0; a higher number sorts first when you sort by PRIORITY descending. billingCustomerId and deliveryCustomerId link the job to customers.
Read a job
graphql
query JobDetails {
getJob(id: "job_01M0R249JMETNBM1Z7466MXGZD") {
id
jobNumber
name
status
quantity
dueAt
scheduledStartAt
actualStartAt
actualEndAt
billingCustomer {
id
name
}
components {
id
name
kind
quantity
}
operations {
id
name
kind
status
sortOrder
}
milestones {
id
name
dueAt
isCompleted
}
}
}Use getJobBy(externalId:) to look a job up by your own reference.
List and search jobs
listJobs takes the filters from JobFilterInput and the sort fields from JobSortField. See Core patterns for the operators.
graphql
query OpenJobsDueSoon {
listJobs(
first: 50
filter: {
status: { notIn: ["COMPLETED", "SHIPPED", "CANCELLED"] }
dueAt: { lt: "2026-10-01T00:00:00Z" }
}
sort: [{ field: DUE_AT, direction: ASC }]
) {
totalCount
edges {
node {
id
jobNumber
name
status
dueAt
}
}
pageInfo {
hasNextPage
endCursor
}
}
}Update a job
updateJob changes only the fields you pass:
graphql
mutation StartPress {
updateJob(id: "job_01M0R249JMETNBM1Z7466MXGZD", input: { status: PRESS }) {
job {
id
status
updatedAt
}
errors {
path
message
}
}
}upsertJob takes the whole job (name and quantity are required) and updates the job whose id you pass, or creates one when you leave id out. It suits a sync that always sends the full record. Fields you leave out keep their current value.
To delete a job, call deleteJob(input: { id }); it returns success and errors.
Operations
An operation (OperationState) is one production step. jobId, name and kind (OperationKind) are required. sortOrder orders the steps, expectedDuration is the planned duration in seconds, and requestedWorkCenterId asks for a specific work center.
graphql
mutation AddPrintingStep {
createOperation(
input: {
jobId: "job_01M0R249JMETNBM1Z7466MXGZD"
componentId: "cmp_01M0R249NQEQGR8E91ZHP09KP0"
name: "Print text block"
kind: DIGITAL_PRINTING
sortOrder: 10
productionQuantity: 5000
remarks: "Use the matte stock"
}
) {
operation {
id
status
}
errors {
path
message
}
}
}Operations move through OperationStatus: PENDING, AVAILABLE, SETUP, RUNNING, CLEANUP, COMPLETED, FAILED, STOPPED, CANCELLED and RESTARTING. Change the status with transitionOperation. A transition that is not allowed from the current status comes back in errors.
graphql
mutation StartOperation {
transitionOperation(input: { id: "opr_01M0R24B2Q7XK5W3N8D4F6H9TJ", status: RUNNING }) {
operation {
id
status
actualStartAt
}
errors {
path
message
}
}
}List a job's operations with listOperations:
graphql
query JobOperations {
listOperations(
filter: { jobId: { eq: "job_01M0R249JMETNBM1Z7466MXGZD" } }
sort: [{ field: SORT_ORDER, direction: ASC }]
) {
edges {
node {
id
name
kind
status
requestedWorkCenter {
name
}
}
}
}
}Milestones
Milestones (MilestoneState) are dated checkpoints on a job, or on one of its components.
graphql
mutation AddMilestone {
createMilestone(
input: {
jobId: "job_01M0R249JMETNBM1Z7466MXGZD"
name: "Proof approved"
dueAt: "2026-10-05T12:00:00Z"
sortOrder: 1
}
) {
milestone {
id
isCompleted
}
errors {
path
message
}
}
}Mark one done with completeMilestone(input: { id }), or cancel it with cancelMilestone(input: { id, reason }). uncompleteMilestone and uncancelMilestone undo those.
Job groups
A job group (JobGroupState) collects related jobs, for example all jobs of one order. Create the group, then set jobGroupId on each job with createJob or updateJob:
graphql
mutation CreateGroup {
createJobGroup(input: { name: "Autumn campaign", tags: ["campaign"] }) {
jobGroup {
id
}
errors {
path
message
}
}
}graphql
query GroupJobs {
getJobGroup(id: "jgr_01M0R24C8V5N2K7T4Q9W3X6B1D") {
name
jobs {
id
name
status
}
}
}removeJobFromJobGroup(input: { jobId }) takes a job out of its group. addJobToJobGroup is different: it creates a new job from the group's product template, so it only works on groups that have one.
Following changes
To keep another system in step, poll the jobs you track, for example with listJobs filtered by status or externalId, and compare updatedAt. The API also has GraphQL subscriptions (dataChangeEvents, statusEvents); see GraphQL tooling before you rely on them from an integration.