# Transition production order state (/docs/api-reference/production-orders/ProductionOrdersController_transition)

`POST /api/factories/{factoryId}/production-orders/{id}/transition`

Base URL: https://api.xentr.ai

Authentication: bearer token (`Authorization: Bearer <token>`).

## Parameters

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `factoryId` | path | yes | string | Factory ID |
| `id` | path | yes | string | Production Order ID |

## Request body

Required.

Content type: `application/json`

```json
{
  "type": "object",
  "properties": {
    "toState": {
      "type": "string",
      "description": "Target state",
      "enum": [
        "scheduled",
        "in_production",
        "paused",
        "completed",
        "cancelled"
      ],
      "example": "scheduled"
    },
    "reason": {
      "type": "string",
      "description": "Reason for state transition"
    },
    "reasonCode": {
      "type": "string",
      "description": "Structured reason code from the factory downtime-reason catalog"
    },
    "changedBy": {
      "type": "string",
      "description": "User who triggered the transition"
    },
    "expectedState": {
      "type": "string",
      "description": "E7.3 offline CAS — the state the client last saw. If it no longer matches the server, the transition is rejected (409) so a stale offline-queued action cannot clobber newer state."
    },
    "clientActionId": {
      "type": "string",
      "description": "Client-generated id for an offline-queued action (the /floor outbox sends it for traceability). Server-side replay dedup is not yet implemented — the expectedState CAS + the outbox idempotent-409 recheck already cover the lost-ack case."
    }
  },
  "required": [
    "toState"
  ]
}
```

## Responses

### 200: State transitioned successfully

### 400: Invalid state transition

### 404: Production order not found