Skip to content

Move a job — Resume, Retry, Cancel, Dismiss, or the producer’s report

PATCH
/v1/jobs/{id}/state
curl --request PATCH \
--url https://beta-api.plune.ai/v1/jobs/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/state \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "state": "paused", "reason": "Today’s AI quota is spent. It resumes by itself in 2 h.", "cursor": "failure:41" }'

A person: { action: resume } on a paused job (from its cursor — the same job, AC-03), { action: retry } on a failed one (from its cursor if it has one, else from the start; no new job — but not of a sync or an ingest: only running plune sync or plune ingest again retries one, so a browser session’s Retry of it is 403, #2027, #2085), { action: cancel } on a running, paused, action-needed or blocked one (it becomes cancelled, the reason «Cancelled by »; a blocked job has no other button), { action: dismiss } on a failed one (hidden from the list and the count; the second is the same 200, nothing written; audited as job/update, dismissed). The producer, from its API token only (a browser session is refused with 403 reports come from a producer token): { state, reason?, cursor? } — a stopped state says why; the state the job is already in, sent again with its reason and cursor or without them, is answered 200 with the job as it is and writes nothing. Any other move outside the table is 409 illegal_transition with the job’s state. Audited as job/state_change, the state and the reason before and after. A reader is refused with 403 read-only member.

id
required
string format: uuid
X-Plune-Project
string

Which of your projects to act in — an id from GET /v1/projects. Absent, your default project. A project you are not a member of answers 404, the same as one that does not exist. Ignored under a bearer token, which is bound to the project it was minted in.

Media type application/json
One of:
object
action
required
string
Allowed values: resume retry cancel dismiss
Examples

The producer: the AI quota is spent

{
"state": "paused",
"reason": "Today’s AI quota is spent. It resumes by itself in 2 h.",
"cursor": "failure:41"
}

The job, moved

Media type application/json

One long AI operation. Seven states: in_progress, action_needed (waits for a person — reason says for what), paused (the quota is spent; Resume, or it resumes by itself), blocked (an outside service failed — reason names it; no button), completed, failed (reason is the raw error; a person may Dismiss it), cancelled (a person’s Cancel — reason is «Cancelled by »; not an error, no button, listed for 24 hours like completed). progress is null while the operation does not know its size — never 0 / 0.

object
id
required
string format: uuid
kind
required

Which operation: ingest, sync, auto-analysis, and bin-empty — the Bin emptied by the server (#1388) — and runs-delete — runs deleted in the background (#1423) — today; open-ended.

string
>= 1 characters <= 64 characters
state
required
string
Allowed values: in_progress action_needed paused blocked completed failed cancelled
subjectId
required

What it works on — a run id, an import id.

string
nullable
subjectName
required

Its name as people know it — shown after the kind.

string
nullable <= 200 characters
progress
required

How far it got; null while the size is not known yet.

object
done
required
integer
total
required
integer
>= 1
reason
required

Why it stopped — only on action_needed, paused, blocked, failed, cancelled. A cancel reads «Cancelled by ».

string
nullable <= 2000 characters
cursor
required

The producer’s own token for where it resumes; null — a Retry starts from the beginning.

string
nullable <= 2000 characters
attempt
required

Which try this is: 1, and one more for each Retry. A producer sends it back in its reports (attempt), and a report of an earlier try is 409 stale_attempt.

integer
>= 1
createdAt
required
string format: date-time
startedAt
required
string format: date-time
nullable
finishedAt
required

Set on completed, failed and cancelled, and only there.

string format: date-time
nullable
runId

Only on a run-export job in GET /v1/jobs and in the answer of GET /v1/runs/{id}/export (202): the run the file is of, so «Export again» works from any browser. A completed export stays in the list 7 days (its file goes after 24 hours).

string
updatedAt
required
string format: date-time
Example
{
"id": "6f1c2b3a-0000-4000-8000-0000000000a9",
"kind": "ingest",
"state": "in_progress",
"subjectId": "6f1c2b3a-0000-4000-8000-0000000000d1",
"subjectName": "nightly-e2e",
"progress": {
"done": 600,
"total": 800
},
"reason": null,
"cursor": "result:600",
"attempt": 1,
"createdAt": "2026-09-16T10:02:00.000Z",
"startedAt": "2026-09-16T10:02:00.000Z",
"finishedAt": null,
"updatedAt": "2026-09-16T10:09:41.000Z"
}

The body is not JSON, or neither a button nor a report; a stopped state without its reason

Media type application/json
object
error
required
string
Example generated
{
"error": "example"
}

No / invalid token or session

A reader asked, or a browser session sent a report — reports come from the producer’s API token; a job the server runs itself (bin-empty, #1388) takes no report at all; Cancel, Retry and Resume of a TestRail import are the project owner’s (403 project owner only); a browser session’s Retry of a sync or an ingest starts nothing and is refused (403 a job of this kind is retried by running its producer again, not from here, #2027, #2085)

Media type application/json
object
error
required
string
Examples
{
"error": "read-only member"
}

No such job in this project

Media type application/json
object
error
required
string
Example generated
{
"error": "example"
}

The move is not in the table (illegal_transition; a report of cancelled too), or the report is of an earlier try (stale_attempt)

Media type application/json
object
error
required
string
code
required
string
Allowed values: illegal_transition job_finished stale_attempt subject_set
state
required

Where the job is now — a producer learns from it that a person cancelled.

string
Allowed values: in_progress action_needed paused blocked completed failed cancelled
attempt

Only with stale_attempt: the try the job is on.

integer
>= 1
Example
{
"error": "job '6f1c2b3a-0000-4000-8000-0000000000a9' is blocked: it cannot move to in_progress this way",
"code": "illegal_transition",
"state": "blocked"
}

Rate-limited: the per-route throttle, or the per-minute request limit of the plan an API token counts by (#1442; the body names the plan, #2092). Retry-After carries the seconds until the window rolls over.

Media type application/json
object
error
required
string
Example generated
{
"error": "example"
}
Retry-After
integer

Seconds until the window rolls over