Update an order
Orders rarely stay perfect after creation. Inventory changes, operations teams attach internal metadata after review, and approved quotes get revised after an invoice has already gone out. This guide shows how to use Update an order for the three update flows most teams hit in production without losing payment context or order history.
Prerequisites
- An existing order ID in a mutable state such as
preparingorrequires_payment - Familiarity with the Order lifecycle
- Access to the Update an order API reference
- A server-side secret key for authenticated API calls
How orders/update behaves
Reach for Update an order when you need to:
- Replace the order's full line item set before payment starts
- Refresh order-level
custom_datawithout disturbing payment state - Revise a sealed order and deliberately reseal it
The endpoint always returns the full order object. If the order is still open, Commerce applies the change directly. If the order is already sealed and your change affects economics or payment configuration, you must make the seal decision explicit with finalize. Metadata-only custom_data updates are different: Commerce can apply them without reopening or resealing by itself. The examples below use direct HTTPS calls so you can adopt the endpoint immediately from any stack.
Scenario 1: Replace line items before payment starts
This is the most common order update flow. A customer changes quantities, an item goes out of stock, or you need to add a manual fee before charging. Send the complete new line_items array—Commerce replaces the previous collection instead of merging it.
Replace line items
curl https://api.zebo.dev/orders/update \
-H "Authorization: Bearer $COMMERCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"order_id": "or_4Jv9wK2LmQ8rT6xP1nY5sC3dF7hB0uA",
"number": "ORDER-2025-041-REV1",
"line_items": [
{
"type": "product",
"product": {
"name": "Warehouse-picked sneaker",
"type": "physical",
"quantity": 2,
"price": { "currency": "ghs", "value": 9500 }
}
},
{
"type": "fee",
"fee": {
"label": "Delivery fee",
"amount": { "currency": "ghs", "value": 1500 }
}
}
]
}'
After the update:
- Commerce replaces the previous line items completely.
line_item_group.totalandpayment.amountare recalculated from the new items.- The order remains editable because you did not ask Commerce to seal it.
Scenario 2: Refresh order-level custom data
Many teams update orders after creation just to attach operational metadata: a review channel, approver identity, fulfillment lane, or internal note that should travel with the order. Send the full replacement custom_data object when you want to refresh those fields. This is a metadata-only mutation, so it does not require reopen or reseal by itself.
Update custom data
curl https://api.zebo.dev/orders/update \
-H "Authorization: Bearer $COMMERCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"order_id": "or_aR7mK4qP9dT2xW6nC1yV8sJ5bL0fH3u",
"custom_data": {
"channel": "ops_review",
"edited_by": "merx",
"priority": "high"
}
}'
After the update:
- Commerce replaces the top-level
custom_dataobject with the new one you provided. - The order's economic state stays unchanged: totals, line items, and payment amount are preserved.
- Because this is metadata-only, you can use it without reopening a sealed order or resealing it again.
Use this flow for internal annotations that should travel with the order record but must not change what the customer pays. Typical examples include review queues, fulfillment lanes, campaign attribution, and dashboard-only notes.
Scenario 3: Revise a sealed order and reseal it in one request
This is the high-friction case that matters most in production. You already finalized the order and maybe even sent the invoice, then a customer negotiates a change before payment completes. Send the revised fields together with finalize: true and Commerce will reopen the sealed order internally, apply the changes, and reseal it before returning the updated object.
Revise and reseal
curl https://api.zebo.dev/orders/update \
-H "Authorization: Bearer $COMMERCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"order_id": "or_zT5mQ8wL2pR7vK1xN4cD9sF6hB3uJ0y",
"number": "QUOTE-2025-009-REV2",
"finalize": true,
"line_items": [
{
"type": "product",
"product": {
"name": "Annual support retainer",
"type": "service",
"quantity": 1,
"price": { "currency": "ghs", "value": 120000 }
}
},
{
"type": "fee",
"fee": {
"label": "Onboarding workshop",
"amount": { "currency": "ghs", "value": 15000 }
}
}
]
}'
After the update:
- Commerce recalculates the order total and payment amount from the revised items.
sealed_atis refreshed because the order was sealed again.invoiceis regenerated so the latest hosted checkout and PDF reflect the revised commercial terms.
If you want the order to stay open for more manual edits, send the same mutation with finalize: false instead. Commerce will reopen the sealed order and clear the active invoice until you seal it again with Finalize an order or another Update an order call.
Related resources
- Update an order - Complete endpoint reference
- Create an order - Create orders that you may revise later
- Custom Data - Design metadata shapes for operational annotations
- Finalize an order - Seal the current order state without charging
- Pay for an order - Start payment after the latest revision is ready
- Order lifecycle - Understand which statuses still allow updates
- Order now, pay later - Build deferred payment flows around mutable orders