Layers
Partner APIAPI referenceJobs

POST /v1/jobs/:jobId/cancel

Best-effort cancel on a running job. Some stages refuse cancellation; the response tells you which.

View as Markdown
POST/v1/jobs/{jobId}/cancel
Phase 1stable
Auth
Bearer
Scope
jobs:cancel

Requests cancellation for a running job. Cancellation is cooperative, not forceful: an in-flight platform API call may complete first, and some stages refuse cancellation because rolling them back would leave partial external state, such as a half-uploaded asset or a PR already opened against your repo.

The response tells you whether the signal landed. It does not tell you the job is already canceled - keep polling GET /v1/jobs/:jobId until status flips to canceled or a terminal state.

Path
  • jobId
    string (job_<ULID>)required
    The jobId returned from the originating 202 response.

Example request

curl -X POST https://api.layers.com/v1/jobs/job_01HXA1NHKJZXPV8R7Q6WSM5BCD/cancel \
  -H "Authorization: Bearer lp_..."
const res = await fetch(
  `https://api.layers.com/v1/jobs/${jobId}/cancel`,
  { method: "POST", headers: { Authorization: `Bearer ${apiKey}` } },
);
const { accepted, reason } = await res.json();
import httpx

r = httpx.post(
    f"https://api.layers.com/v1/jobs/{job_id}/cancel",
    headers={"Authorization": f"Bearer {api_key}"},
)
payload = r.json()

Response

202Cancel signal accepted. Poll the job to see status flip to canceled.
{
  "jobId": "job_01HXA1NHKJZXPV8R7Q6WSM5BCD",
  "accepted": true
}

reason is only present on the 200 "already terminal" shape below. On a fresh 202 accept, it's omitted.

200Already terminal - nothing to cancel. Flat rejection shape with the terminal reason.
{
  "jobId": "job_01HXA1NHKJZXPV8R7Q6WSM5BCD",
  "accepted": false,
  "reason": "ALREADY_COMPLETED"
}
409Current stage refuses cancellation. Canonical CONFLICT envelope with details.subcode = JOB_CANCEL_UNAVAILABLE.
{
  "error": {
    "code": "CONFLICT",
    "message": "Job cannot be canceled during this stage.",
    "requestId": "req_01HXA1NHZ4KYE8GP9Q2WX3BCDE",
    "details": {
      "subcode": "JOB_CANCEL_UNAVAILABLE",
      "jobId": "job_01HXA1NHKJZXPV8R7Q6WSM5BCD",
      "stage": "opening_pr"
    }
  }
}

Branch on error.details.subcode === "JOB_CANCEL_UNAVAILABLE" and read details.stage for the current non-cancelable stage.

404Unknown jobId in your organization.

Non-cancelable stages

Some stages would leave external state in an inconsistent place if interrupted. The API refuses cancellation during them and returns STAGE_NOT_CANCELABLE with the current stage.

KindStages that refuse cancel
project_ingest_githubopening_pr, finalizing
project_ingest_websitepersisting
content_generatefinalizing
content_regeneratefinalizing
content_clone_from_postfinalizing
influencer_createpersisting
appstore_ingestpersisting
marketing_bootstrapproject_create, ingest_website, sdk_app_create, layer_provision, influencer_create, first_content, finalizing
ad_optimizer_runapplying_actions, finalizing
project_keywords_refreshfinalizing

marketing_bootstrap currently refuses cancel at every stage. Bootstrap is a fan-out orchestrator and cancellation mid-flight could leak half-built projects (project row exists, no influencer, etc.); this is a v1 limitation while bootstrap stages don't yet have rollback semantics. Partners should treat bootstrap as an atomic operation — if it fails, retry with the same idempotency key (which short-circuits to the prior bootstrapJobId) once the underlying issue is fixed.

Everything else is fair game. If the job is still in an early cancellable stage, the cancel request is usually accepted.

Notes

  • accepted: true means the cancel signal was delivered, not that the job is already canceled. Poll GET /v1/jobs/:jobId for the terminal flip.
  • Canceled jobs are sticky - once status: "canceled", they never revert.
  • Cancel is best-effort. If a platform API call is already in flight, it will finish before the job exits.
  • Canceling a content_generate job before finalizing reverses any reserved credits. Canceling after does not - the content exists.

See also

On this page