cypress.DOCS
Log inRegister
Cypress Docs
Developers

Errors

One error shape on both surfaces — what each status means, the stable codes to switch on, and whether to retry or fix.

TL;DRAI summary

This chapter explains the error messages you might see from Cypress, what they mean, and what to do next. You will learn how to identify different error types, such as problems with your request, permissions, or conflicts with a vehicle's status.

Dive deeper:

Every refusal on both surfaces (/api/v1 and /api/compatible/v1) has the same JSON body:

{
  "error": "This vehicle is in Planning, not in Sell. Updates, re-pushes, prices, listings, deletion and sold-elsewhere apply once its sale window hands it to Sell.",
  "code": "PLANNING_STOCK"
}
FieldAlways presentMeaning
erroryesA human-readable sentence for your logs. It names the first failing field on a 422. Don't parse it — the wording can improve between releases.
codeon refusals that have oneA stable machine code. Switch on this, not on error.
detailson 422, and on the codes that say soOn 422, the full validation result: fieldErrors (per field) and formErrors (whole payload). QUARTER_CLOSED names the vehicles it blocks and their quarter (vehicleIds, quarter); ODOMETER_ROLLBACK names the reading it conflicts with (recorded).

Bulk endpoints never fail the whole batch for one bad item. They answer 200 with one result per item, and each failed item carries its own status, code (when it has one) and errors[]:

{
  "items": [
    { "vin": "1FTFW1ET5DFC10312", "status": 409, "code": "PLANNING_STOCK", "errors": ["This vehicle is in Planning, not in Sell. …"] },
    { "vin": "2FTFW1ET5DFC10313", "status": 201, "id": "clx…", "href": "/api/v1/vehicles/clx…" },
    { "vin": "3FTFW1ET5DFC10314", "status": 403, "code": "PLANNING_NOT_ENABLED", "errors": ["…"] }
  ],
  "created": 1, "updated": 0, "conflicts": 1, "invalid": 0, "refused": 1
}

refused counts the items that answered 403 — planning stock on an organization without Planning, an embedded listing without the listings scope, or a planning item without planning:write. It's additive: existing counters (created/updated/conflicts/invalid) are unchanged, on both POST /api/v1/vehicles/bulk and POST /api/compatible/v1/units/bulk.

Status codes

StatusMeansTypical causesRetry?
400The body isn't JSONTruncated or non-JSON bodyNo — fix the request
401No valid keyMissing or malformed x-api-key; the key was revokedNo — check the key
403The key can't do thisORG_NOT_ACTIVE (organization not active), ACCOUNT_RULES (a feature off for your account, or one of its caps used), SCOPE_MISSING (key lacks the scope), PLANNING_NOT_ENABLED (planning capability, organization has no Planning), BUY_PLANNING_NOT_ENABLED (a Buy planning route, organization has no Buy planning), or AUTOMATIONS_NOT_ENABLED (an automations route, organization has no Automations)No — ask your admin or Cypress; see Your organization and key
404Not found for this keyWrong id; an id from another organization; a vehicle in Operation or Planning (hidden from the API without a planning:read key)No
409Conflicts with the current stateDuplicate VIN; VIN change on PUT; an open listing already exists; the listing is closed; a deal is in progress; the vehicle is sold; PLANNING_STOCK; LIFE_STAGE_READ_ONLY; a quarter that is over (QUARTER_CLOSED); the plan codes belowNo — read the resource and decide. Except PLAN_BUSY: retry after the Retry-After seconds
422The payload is invalidA missing required field, a wrong type, an out-of-range value, an unknown/lowercase/empty lifeStage, or CHANNEL_NOT_GRANTED (a channel your organization has no grant for)No — fix the field named in error
429Rate limitMore than 300 requests a minute on one keyYes — after the Retry-After seconds
5xxOur side—Yes — with backoff; writes are safe to repeat with ?mode=upsert

202 is not an error: it means the request was staged for Cypress review (sold-elsewhere, or a removal while a deal is in progress). The decision arrives later as a webhook — see De-listing.

Codes

CodeStatusWhenWhat to do
PLANNING_STOCK409The vehicle is in Operation or Planning (lifeStage OPERATION or PLANNING), the organization has Planning, and you asked for something only Sell stock can do: an update, a re-push by VIN, prices, a listing, deletion, sold-elsewhereNothing, usually. The vehicle appears in the API on its own when its sale window hands it to Sell. If you believe it should be selling now, ask the fleet's planner.
LIFE_STAGE_READ_ONLY409A PATCH/PUT sent a lifeStage on the other side of the Planning/Sell line (e.g. OPERATION for a vehicle in Sell)Drop lifeStage from writes, or send back the value you read. A vehicle moves between Planning and Sell only through its plan.
ORG_NOT_ACTIVE403Your organization's status isn't ACTIVENothing you can fix from the API. Ask Cypress. GET /api/v1/organization still answers 200 and names this on every capability.
ACCOUNT_RULES403Your account's rules refuse it (account on GET /api/v1/organization): a feature that is off for your organization (account.features: the team, apps, API keys, pricing rules, marketing campaigns), one of its caps used (account.limits against account.used: valuations and cars listed in total, team seats, campaign emails this month), or an individual's email not confirmed yet. Since 1.9.0.Nothing from the API. Confirm the email, or ask Cypress: it sets the rules for each kind of account and for your organization.
PLANNING_NOT_ENABLED403A capability that needs Planning (planning stock, planning:read/planning:write) on an organization that doesn't have Planning switched on — including a vehicle in Operation or Planning when the organization has no PlanningAsk Cypress to turn on Planning for your organization.
BUY_PLANNING_NOT_ENABLED403A Buy planning route (/api/v1/buy-planning/*, scopes buy-planning:read/buy-planning:write) on an organization that doesn't have Buy planning switched on. A switch of its own: Planning being on does not open it. Since 1.4.0.Ask Cypress to turn on Buy planning for your organization.
AUTOMATIONS_NOT_ENABLED403An automations route (/api/v1/automations, scopes automations:read/automations:write) on an organization that doesn't have Automations switched on. Asked before the scopes. Since 1.5.0.Ask Cypress to turn on Automations for your organization.
SCOPE_MISSING403The key doesn't hold the scope this endpoint needsAsk your organization's admin to add the scope on the Integrations page — the key doesn't need to be rotated.
CHANNEL_NOT_GRANTED422A listing/offering create or update named a sales channel (MARKETPLACE, STOREFRONT, DIRECT_LINK) your organization has no grant forDrop the channel, or ask Cypress for the grant.

Offers, listings, external ids and retries (1.5.0)

The offer and listing commands answer the same codes to every caller: an integration, a connected app or an agent. A decision that is refused writes nothing.

CodeStatusWhenWhat to do
EXTERNAL_ID_TAKEN409An externalIds entry (source and id) already names another record of the same kind in your organization: a vehicle, listing, contact, business, deal or task. taken names it. On a bulk push, only that item is refusedLook the id up (?externalId=<source>:<id> on the list) and fix the mapping on your side. See Your own ids.
PEOPLE_ONLY403Only a person may do this, and nothing is written. From any caller of the API: accepting or countering an offer below the floor, and a campaign audience of pasted addresses (email-list). From an agent or an API key: emailing a contact one to one. From an agent: setting the floor (on a listing it changes or creates); cancelling a listing (taking a car off market: an agent pauses it instead); approving a quarter, Sell or Buy (including POST /api/v1/buy-planning/drafts/apply with approve: true); confirming that a closing's money arrived; activating or sending a campaign; switching an automation back on; reading or setting notification preferencesLeave it to a person in the app. An agent on Cypress Operate is given the link where a person decides.
BELOW_FLOOR403A price or a decision would sit below the floor (a buy-now under the floor; a delegated decision on a below-floor offer)Choose a value at or above the floor, or leave it to the seller's Fleet Manager.
POLICY_DENIED403A rule or a delegation switch refuses this caller (for example, the organization has not delegated offer decisions)Ask the organization's admin.
CONFLICT409The record is not in a state that allows it: an offer that is no longer open, a price change under a live offer or saleRead the record again and decide.
NOT_FOUND404No such offer or listing for this keyCheck the id.
INVALID422The request is not valid for this command (for example, an Idempotency-Key longer than 200 characters)Fix the request.
IDEMPOTENCY_KEY_REUSED422The Idempotency-Key was already used with a different requestUse a new key for a new request.
IDEMPOTENCY_IN_PROGRESS409The first request with this Idempotency-Key is still runningRetry shortly with the same key: you get its answer.
KEY_ENVIRONMENT403The key is used outside its environment: a test key (cyp_test_) on a live organization, or a live key (cyp_live_) on a sandbox; or a sandbox's own act asked of a live organization (POST /api/v1/sandbox/reset). Nothing is read or writtenUse the organization's own kind of key. See Live and test keys.
MASS_REMOVAL409A full-inventory push (POST /api/v1/vehicles/bulk?removeMissing=true) would take more than a fifth of your active vehicles (and more than five) off Cypress, as a truncated feed would. activeInventory and missing say how many. Nothing is written, the items included (since 1.8.0)Check the feed. When your inventory is larger than one push, name every VIN you hold in inventoryVins on the last page; when the removal is meant, send allowMassRemoval=true. See Pushing inventory.

Planning codes (1.3.0)

From the Planning API. QUARTER_CLOSED is the one code added since (1.4.0):

CodeStatusWhenWhat to do
PLAN_BUSY409Another write took the plan's next version at the same moment. Nothing was written. Carries Retry-After: 2. On a planning push the vehicle is still written — the code rides on its 201 (200 on an upsert)Retry the same request after the header's seconds (a push: bench it with POST /api/v1/planning/moves, or re-push with ?mode=upsert).
IN_SELL409A planning-stock-only route — odometer readings, departure — on a vehicle that is in SellUse the Sell route: PATCH for mileage, /disposition for a sale elsewhere.
RETIRED409A planning-stock-only route — odometer readings, departure — on a vehicle that has already left the fleet (archived)Nothing — its record is closed.
ODOMETER_ROLLBACK409The reading runs the odometer series backwards; details.recorded is the reading it conflicts withCheck the figure. For a replaced or corrected odometer, send force: true.
NO_PLAN—Not a refusal: on a planning push's 201 (200 on an upsert), the vehicle was written in OPERATION but there is no live plan to bench it inBench it later with POST /api/v1/planning/moves (to: BENCH).
PREVIEW_STALE409A cohort confirm whose preview expired (24 hours) or whose cohort changed since. Since 1.5.0 also a schedule apply whose proposal expired or whose plan changed, and a Sell quarter approval whose digest is not the live plan'sPreview (or read the quarter) again, then send the new digest.
KIND_CHANGE409PATCH /api/v1/planning/goals/{id} that sends params of another kind: a goal keeps its kind (units, cash, shape, renewal). Since 1.5.0.Add a new goal of that kind, and remove the old one if you no longer want it.
WINDOW_CLOSED409A move into, or a hand-off (preview or confirm) from, a sale window that has closed in a quarter still in progress. A window of a quarter that is over answers QUARTER_CLOSED instead: the quarter is asked firstUse an open window — GET /api/v1/planning/plan (state, and closed since 1.4.0).
WINDOW_GONE404A move named a window the latest plan version no longer has. An id from an earlier version is accepted — it is followed by its lineage — so this means the window itself is gone (or the id is unknown)Re-read the plan (GET /api/v1/planning/plan) and pick an open window.
SELLING_BEGUN409A demotion of a vehicle that is listed or beyondDelist it first, or ask for an archive review.
RESOLVED409Resolving or editing a blocker that is already resolvedNothing — a resolved blocker is history.
QUARTER_PASSED422Parking until a quarter that has already startedPick a coming quarter.
QUARTER_CLOSED409The request would change a quarter that is over — its last day has passed, UTC. A window counts in the quarter that holds its 8th day. Refused: a move into one of its windows; a move to OPERATION, or a park, of a vehicle scheduled in one; a track change of a planned vehicle (not yet handed to Sell) in one; the hand-off preview or confirm of one of its windows. The request is refused whole and nothing was written. details.vehicleIds lists the vehicles it blocks, details.quarter (2026-Q3) their quarter when it is oneNothing to retry — the quarter does not reopen. Use a window of a quarter in progress (GET /api/v1/planning/plan: closed: false), or send the request again without the vehicles named in details.vehicleIds. See Closed quarters. Since 1.4.0.

QUARTER_PASSED and QUARTER_CLOSED are different codes: QUARTER_PASSED (422) is parking until a quarter that has already started; QUARTER_CLOSED (409) is acting on a quarter that is over.

Buy planning codes (1.4.0)

From the Planning API (Buy planning). QUARTER_CLOSED is the same code as above, here for the dealer's own plan (it carries no details), and BUY_PLANNING_NOT_ENABLED is in the table above. The approval of a quarter has its own three codes, and two more guard the removal of a target and of a purchase:

CodeStatusWhenWhat to do
QUARTER_CLOSED409The goal of a quarter that is over (PUT /quarters/{periodKey}), or a target needed in one: adding it, pausing, reopening, closing, editing or removing it, or an edit that would move one into such a quarter. Nothing was written. A purchase is never refused with it — it is a factNothing to retry — the quarter does not reopen. Add the target to a week of a quarter in progress.
PLAN_CHANGED409POST /quarters/{periodKey}/approve with a digest that is not the live plan's: the plan moved since you read the quarter. "The plan changed. Refresh and approve again."Read the quarter again and approve the new digest.
PERIOD_CLOSED409POST /quarters/{periodKey}/approve on a quarter that is over, Buy or (since 1.5.0) Sell. (The approval's own code; the goal of such a quarter answers QUARTER_CLOSED.)Nothing — a quarter that is over cannot be approved.
NOTHING_TO_APPROVE409POST /quarters/{periodKey}/approve on a quarter with no goal, no budget and no target: "Set a goal or add a target first."Set a goal or add a target, then approve.
HAS_PURCHASES409DELETE /targets/{id} on a target that has purchasesClose it instead (PATCH with { "status": "CLOSED" }); its history stays.
FROM_CLOSING409DELETE /purchases/{id} on a purchase that came from a Cypress closingNothing — it follows its closing. Only a purchase you recorded can be removed.

Another organization's target or purchase is 404, as one that does not exist. A body that doesn't validate is 422 with the same error and details as everywhere else.

Subscribing to the BUY.* webhooks (POST or PUT on /api/v1/webhooks/subscriptions) answers 403 SCOPE_MISSING when the key lacks buy-planning:read, 403 BUY_PLANNING_NOT_ENABLED when the organization has no Buy planning, and 422 when the subscription would use the MANHEIM names ("BUY.* events are available only on the Cypress dialect"). See Webhooks.

A bulk planning move (moves, moves/track, moves/park, moves/unpark) doesn't refuse a vehicle it can't move — it lists it in skipped with a reason, including an unknown id or another organization's (not in the plan), and moves the rest. It answers 409 only when every vehicle was skipped.

One exception: a closed quarter. A request that would change a vehicle in a window of a quarter that is over is refused whole with 409 QUARTER_CLOSED — nothing is applied, not even to the vehicles that could have changed — and details.vehicleIds lists exactly the vehicles it blocks, so you can send the request again without them:

{
  "error": "This quarter is closed. A closed quarter is history, so its plan no longer changes.",
  "code": "QUARTER_CLOSED",
  "details": { "vehicleIds": ["veh_a…", "veh_c…"], "quarter": "2026-Q3" }
}

details.quarter is present when those vehicles' windows all count in the same quarter. error is one fixed sentence: switch on code.

Echoing lifeStage is safe

lifeStage is read-only. Sending back the value you read — or any value on the same side of the line, such as POST_SALE for a vehicle in Sell — is accepted and ignored, so a client that copies lifeStage from a read into its next PATCH or PUT keeps working.

Changed in 1.4.0

API 1.4.0 added Buy planning over the API (new scopes, routes, codes and webhooks; the scope strings and everything that already existed are unchanged), and changed one thing for existing keys that this page is about: a request that changes a quarter that is over now answers 409 QUARTER_CLOSED. (The other change is a delivery, not an error: OFFERS.ACCEPTED is now also sent when a buyer accepts a counter. See Webhooks.) A move into a window of such a quarter, a move to OPERATION or a park of a vehicle scheduled in one, a track change of a planned vehicle in one, and the hand-off of one of its windows used to be accepted, except that a move into a window, or a hand-off, answered 409 WINDOW_CLOSED when the window itself had closed (which is now answered only for a window that has closed in a quarter still in progress). A hand-off preview of such a window answers QUARTER_CLOSED too, instead of a digest the confirm would refuse. A vehicle already in Sell can still change track, and blockers and demotions are not refused: see Closed quarters.

Before 1.1.0

Refusals carried only error (and details on 422). The code field and PLANNING_STOCK/LIFE_STAGE_READ_ONLY arrived in API 1.1.0; ORG_NOT_ACTIVE, PLANNING_NOT_ENABLED, SCOPE_MISSING and CHANNEL_NOT_GRANTED arrived in 1.2.0, alongside GET /api/v1/organization (see Your organization and key); the planning codes arrived in 1.3.0 with the Planning API, and QUARTER_CLOSED in 1.4.0.

Some older statuses changed. API 1.2.0 changed planning stock at an organization without Planning from 409 PLANNING_STOCK to 403 PLANNING_NOT_ENABLED, and changed a vehicles-only key pushing a vehicle/unit with an embedded listing/offering from creating it (with the listing silently dropped) to 403 SCOPE_MISSING. API 1.3.0 changed:

  • A PATCH /api/v1/vehicles/{id} on planning stock, at an organization with Planning, from a key without planning:write, now answers 403 SCOPE_MISSING; 1.2.0 answered 409 PLANNING_STOCK. (A body naming prices, listing or images still answers 409 PLANNING_STOCK; without Planning it is still 403 PLANNING_NOT_ENABLED.)
  • A POST /api/compatible/v1/units/bulk item whose embedded offering names an ungranted channel used to create the unit without the offering and answer 201; it now answers that item 422 CHANNEL_NOT_GRANTED and creates nothing.

API 1.3.0 also gives existing keys one new capability: GET/PUT /api/v1/vehicles/{id}/ownership needs only the vehicles scope for a vehicle in Sell, so a key with the default scopes can read and replace the purchase & financing record of its organization's Sell vehicles. And webhooks about planning stock — VEHICLES.* for a vehicle in Operation or Planning, and every PLAN.* event — reach only a Cypress-dialect subscription that lists them, so an unfiltered or MANHEIM subscription receives none of them (see Webhooks). Every other older status and message is unchanged.