Skip to content

Approve a change order

Finance module

POST/change-orders/{id}/approvechangeOrders.approve

Records an approval decision on the change order. Its requester cannot price and approve it alone, and an unpriced change order cannot be approved at zero. Spend authority and separation of duties are enforced in the approval RPC; a refusal returns 422 and names the rule. Requires `Idempotency-Key`; a replay returns the original response. A rule that refuses the request returns 422 with `refusal` and `reason`.

  • Requires the capability finance.change_order.approve.
  • OAuth scopes: finance.
  • Send an Idempotency-Key header. A replay with the same key returns the original response.
  • Error statuses: 400, 401, 404, 409, 422, 429. Errors are RFC 9457 problem details.

Path and query parameters

changeOrders.approve: Path and query parameters
FieldTypeRequiredDescription
idstring (uuid)YesIdentifier of the change order.

Response 200: ChangeOrder

changeOrders.approve: ChangeOrder
FieldTypeRequiredDescription
idstring (uuid)YesIdentifier of the change order.
org_idstring (uuid)YesIdentifier of the owning organization, derived from the parent row and never accepted from the caller.
created_atstring (date-time)YesWhen the row was created.
created_bystring (uuid) | nullYesIdentifier of the person who created the row; null when a seed, platform or system process created it.
updated_atstring (date-time)YesWhen the row was last updated.
updated_bystring (uuid) | nullYesIdentifier of the person who last updated the row; null when a seed, platform or system process updated it.
project_idstring (uuid)YesIdentifier of the project.
titlestringYesChange order title.Constraints: length 1 to 300
reasonstring | nullYesWhat changes and why.Constraints: length 1 to 20000
requested_bystring (uuid) | nullYesIdentifier of the requester.
change_order_state"Proposed" | "Priced" | "Approved" | "Rejected" | "Canceled"YesChange order state.
totalMoneyTotalYesA money aggregate that never coerces unpriced inputs to zero.
budget_idstring (uuid)YesIdentifier of the budget the change order revises.
change_order_numberintegerYesChange order number within the project.Constraints: range 1 to any
order_type_enum_idstring | nullYesCanon change order type.Constraints: pattern ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$
priced_bystring (uuid) | nullYesIdentifier of the person who priced the change order.
schedule_impact_daysinteger | nullYesDays the change moves the schedule; negative when it pulls it in, zero for none.Constraints: range -3650 to any
client_impactstring | nullYesEffect of the change on the client, as stated.Constraints: length 1 to 500
approved_atstring (date-time) | nullYesWhen the change order moved to Approved, from its transition ledger.
approved_bystring (uuid) | nullYesIdentifier of the person who moved the change order to Approved.
{
  "id": "0192f1e2-7c3a-7b4d-9e8f-1a2b3c4d5e6f",
  "org_id": "0192f1e2-7c3a-7b4d-9e8f-1a2b3c4d5e6f",
  "created_at": "2026-10-08T14:30:00Z",
  "created_by": "0192f1e2-7c3a-7b4d-9e8f-1a2b3c4d5e6f",
  "updated_at": "2026-10-08T14:30:00Z",
  "updated_by": "0192f1e2-7c3a-7b4d-9e8f-1a2b3c4d5e6f",
  "project_id": "0192f1e2-7c3a-7b4d-9e8f-1a2b3c4d5e6f",
  "title": "Add Second Bar",
  "reason": "Client added a second bar at the north lawn.",
  "requested_by": "0192f1e2-7c3a-7b4d-9e8f-1a2b3c4d5e6f",
  "change_order_state": "Proposed",
  "total": {
    "total_minor": 1250000,
    "currency": "USD",
    "unpriced_count": 2
  },
  "budget_id": "0192f1e2-7c3a-7b4d-9e8f-1a2b3c4d5e6f",
  "change_order_number": 3,
  "order_type_enum_id": "ENM-092",
  "priced_by": "0192f1e2-7c3a-7b4d-9e8f-1a2b3c4d5e6f",
  "schedule_impact_days": 2,
  "client_impact": "Adds a second stage to the run of show.",
  "approved_at": "2026-10-08T14:30:00Z",
  "approved_by": "0192f1e2-7c3a-7b4d-9e8f-1a2b3c4d5e6f"
}

Code samples

curl -X POST "$XOS_BASE_URL/change-orders/0192f1e2-7c3a-7b4d-9e8f-1a2b3c4d5e6f/approve" \
  -H "Authorization: Bearer $XOS_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
const { data } = await xos.changeOrders.approve("0192f1e2-7c3a-7b4d-9e8f-1a2b3c4d5e6f");
response = xos.change_orders.approve("0192f1e2-7c3a-7b4d-9e8f-1a2b3c4d5e6f")
xos change-orders approve 0192f1e2-7c3a-7b4d-9e8f-1a2b3c4d5e6f