Routerra LogoTeams
Browse articles· Plan lifecycle & statuses
For ownersUpdated 2026-09-08 · 7 min read

Plan lifecycle & statuses

Every status a Routerra Teams plan can hold, the API calls that move it between them, and the transitions the API refuses — the plan state machine.

A plan is built in two steps. Dispatch computes the routes — only your office sees them. Approve sends those routes to drivers. Everything else in the lifecycle follows from that split: editing stops on a plan that already has routes throws it back to a state where the routes are stale, and it needs a dispatch and an approve again before drivers see the change.

If you drive Routerra from your own systems, this is the state machine you are integrating against. Statuses are also what your polling loop keys off after a 202.

How a plan moves

dispatchsolver okapproverevokesolver errordispatch againedit stopsdispatchedit stopsall stops unroutableDELETEDELETECREATEDDISPATCHING_IN_PROGRESSDISPATCHEDAPPROVEDHAS_CHANGESDISPATCH_FAILEDCANCELLED
  • CREATED goes to DISPATCHING_IN_PROGRESS on dispatch.
  • DISPATCHING_IN_PROGRESS goes to DISPATCHED on solver ok.
  • DISPATCHED goes to APPROVED on approve.
  • APPROVED goes to DISPATCHED on revoke.
  • DISPATCHING_IN_PROGRESS goes to DISPATCH_FAILED on solver error.
  • DISPATCH_FAILED goes to DISPATCHING_IN_PROGRESS on dispatch again.
  • DISPATCHED goes to HAS_CHANGES on edit stops.
  • HAS_CHANGES goes to DISPATCHING_IN_PROGRESS on dispatch.
  • APPROVED goes to HAS_CHANGES on edit stops.
  • CREATED goes to DISPATCH_FAILED on all stops unroutable.
  • DISPATCHED goes to CANCELLED on DELETE.
  • APPROVED goes to CANCELLED on DELETE.
Plan statuses and the calls that move between them

The teal path is the happy one: create, dispatch, approve. Everything else is a detour off it.

What each status means

  • CREATED — the plan has stops but no routes. Plans land here when you create them with dispatch=false, when they are copies made through the duplicate endpoint, and when not a single address could be resolved.
  • DISPATCHING_IN_PROGRESS — the solver is working, usually for 5 to 30 seconds. This is the status you poll out of.
  • DISPATCHED — routes exist and belong to the office. Drivers see nothing yet.
  • HAS_CHANGES — someone edited stops on a plan that already had routes, so the routes no longer match the stops. Dispatch again, then approve again.
  • APPROVED — the routes are with the drivers.
  • DISPATCH_FAILED — the solver returned no routes. The reason is in dispatchFailureReason, and the optimization token is handed back rather than burned.
  • CANCELLED — the plan was soft-cancelled. Routes are pulled, stops and delivery photos are kept.
Routes existDrivers have themListed by GET /plans
CREATEDNoNoYes
DISPATCHING_IN_PROGRESSNoNoYes
DISPATCH_FAILEDNoNoYes
DISPATCHEDYesNoYes
HAS_CHANGESYesPartialYes
APPROVEDYesYesYes
CANCELLEDNoNoNo
What each status means for the office and for the drivers

HAS_CHANGES is the partial row because it depends on where the plan came from. If it had been approved, the drivers are still holding the routes you sent them; your edits only reach them after the next dispatch and approve. If it had only been dispatched, nothing was ever sent.

Cancelled plans drop out of GET /plans until you ask for them with status=CANCELLED. GET /plans/{planId} keeps returning them, with status: "CANCELLED" — a cancelled plan is not a 404.

The two-step dispatch loop

  1. T+0

    Create or dispatch

    POST /optimize or POST /plans/{planId}/dispatch answers 202 and the plan turns DISPATCHING_IN_PROGRESS.

  2. 5-30 s

    Poll for the result

    GET /plans/{planId} until the status leaves DISPATCHING_IN_PROGRESS — DISPATCHED on success, DISPATCH_FAILED otherwise.

  3. When you are ready

    Approve

    POST /plans/{planId}/approve moves DISPATCHED to APPROVED and the routes appear in the driver app.

  4. Mid-shift

    Revoke, edit, repeat

    Revoke pulls the routes back to DISPATCHED; editing stops sets HAS_CHANGES. Either way, dispatch and approve again.

One pass through the loop

approve only accepts a plan in DISPATCHED. From anything else you get 409 plan_state_transition — and for a plan in HAS_CHANGES that is the API telling you to dispatch first.

Reading the status over the API

GET /plans/{planId} is the one call that tells you where a plan stands. routes is only present once optimization has produced something — in DISPATCHED, APPROVED, HAS_CHANGES and DISPATCH_FAILED. For a plan in CREATED, the stops are in unassignedStops and unassignedSummary instead.

Response · dispatch accepted

{
"planId": 315,
"status": "DISPATCHING_IN_PROGRESS",
"driverIds": [91, 93],
"revokedBeforeDispatch": true,
"deduplicated": false
}

Response · poll, view=concise

{
"id": 315,
"name": "Friday deliveries",
"date": "2026-09-05",
"status": "DISPATCHED",
"hasUnoptimizedChanges": false,
"stopsCount": 3,
"routesCount": 2
// DISPATCH_FAILED adds "dispatchFailureReason"
}

hasUnoptimizedChanges is the same fact as the HAS_CHANGES status seen from the field level: it flags a plan whose stops have moved on from its routes.

Cancelling a plan

DELETE/api/v1/external/plans/{planId}dispatch200

Soft-cancels the plan. The routes come down and drivers stop seeing it immediately, but the plan, its stops and its proof-of-delivery photos are all kept and the status becomes CANCELLED. It also frees the concurrent-optimization slot if the plan is stuck in DISPATCHING_IN_PROGRESS.

{
"planId": 315,
"name": "Friday deliveries",
"statusBeforeCancellation": "APPROVED",
"cancelled": true,
"status": "CANCELLED",
"restorable": true
}

Statuses you will only see on dashboard plans

File-import statuses — IMPORT_IN_PROGRESS and the IMPORT_FAILED* family — only ever appear on plans created from the dashboard, never on plans your integration creates. They matter to an integration in one way: a plan sitting in IMPORT_IN_PROGRESS refuses to be cancelled.

For the same statuses as they appear to dispatchers in the web app, see Statuses reference. For the office-side version of the two-step loop, see Send routes to drivers.

Was this article helpful?

Still stuck?

Write to us — a human reads every message.

Contact support