Update Booking
Update an existing booking. Confirmed and PickedUp bookings can be updated, including ones that have already been paid; pass reconciliation_method to settle any price delta in the same call.
Bookings in cancelled, checked_in, returned, completed or no_show status cannot be updated. A no_show booking still takes a change of status on its own; see A booking marked as a no-show.
PATCH /v1/bookings/{id}
Authenticationโ
Requires a valid API key with bookings:write scope.
Path Parametersโ
| Parameter | Type | Description |
|---|---|---|
id | integer | Booking ID |
Request Bodyโ
All fields are optional. Only include fields you want to update.
| Field | Type | Description |
|---|---|---|
customer_id | integer | Change the customer |
customer | object | Edit the booking's customer: first_name, last_name, email, phone, phone_country_code. Other keys are ignored. Needs the customers:write scope (a phone app sign-in needs Edit customers), or the request returns 403. |
start_date | date | New start date (YYYY-MM-DD) |
end_date | date | New end date (YYYY-MM-DD), must be after start_date |
status | string | Change status: draft, confirmed, picked_up, checked_in, returned, completed or no_show. Setting a draft to confirmed works like Confirm Booking with both emails on. Any other change runs the same checks as the matching action, including the checklist gates, and returns 422 with code status_transition_failed when the move is not allowed. no_show works like Mark a No Show. To cancel, use Cancel Booking |
email_customer | boolean | With status: no_show only. Send the customer the We missed you email. Default true |
override | boolean | With a status change only. Skip a checklist that blocks it, as on the pickup and check-in actions. Needs the bookings.checklist_override permission |
override_reason | string | Why the checklist was skipped, up to 500 characters |
holds_dates | boolean | Drafts only. Turn the draft's hold on or off. Returns 409 with code HOLD_FAILED if the dates are no longer free. |
hold_until | string | Drafts only. Last day to hold the dates (YYYY-MM-DD), or null for no end date. |
source | string | Update source label |
discount | boolean | Enable/disable discount |
discount_type | string | percentage or flat |
discount_value | number | Discount amount |
security_deposit | boolean | Enable/disable security deposit |
security_deposit_amount | number | Deposit amount |
pickup_location_id | integer | Pickup location ID (see Locations). If the product prices by location, moving the booking re-prices its lines and totals at the new location. |
return_location_id | integer | Return location ID (see Locations). Does not affect pricing. |
add_bundles | array | Bundles to add (rental bookings only) |
remove_bundle_ids | array | Bundle line IDs to remove |
experiences | array | Edit experience lines already on the booking. Each item needs the line id (from Get Booking experiences[].id). Send start_time (local HH:MM, or null for the window's opening) to change when an open-window line starts on its day; the booking's start_date and end_date follow. A line moved to another day with schedule_id keeps its start time when the new day's window allows it |
attribution | object | Edit marketing attribution. Same shape as Create Booking โ Attribution Object. Send a key with an empty string to clear it; omit a key to leave it unchanged. |
reconciliation_method | string | Settle a price delta on a paid / partially-paid booking. One of: send_payment_link, refund_to_card, skip. See Reconciling a paid booking below |
delivery is not a field here. Changing where a booking goes re-prices the journey, which moves the subtotal, the tax and what is owed on a booking that may already be paid - so it needs its own endpoint rather than a field on the general update. Send delivery to this endpoint and it is refused with delivery_not_updatable (422) rather than accepted and ignored. Set the address on Create Booking.
Add Bundles Arrayโ
| Field | Type | Required | Description |
|---|---|---|---|
bundle_id | integer | Yes | Product bundle ID |
quantity | integer | No | Bundle quantity (default: 1) |
Request Exampleโ
curl -X PATCH "https://app.equipdash.com/api/v1/bookings/123" \
-H "Authorization: Bearer eqd_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"status": "confirmed",
"start_date": "2026-01-20",
"end_date": "2026-01-22"
}'
Responseโ
Returns 200 OK with the updated booking details.
{
"data": {
"id": 123,
"type": "rental",
"status": "confirmed",
"payment_status": "unpaid",
"source": "API",
"start_date": "2026-01-20T00:00:00+00:00",
"end_date": "2026-01-22T00:00:00+00:00",
"currency": "USD",
"pricing": {
"subtotal": 150.00,
"discount": {
"enabled": false,
"type": null,
"value": null,
"amount": 0
},
"promo_code": null,
"voucher": null,
"taxes": [],
"custom_charges": [],
"security_deposit": 0,
"total": 150.00
},
"pickup_location": {
"id": 1,
"name": "Main Store",
"address": "123 Beach Road, Bondi NSW 2026, AU"
},
"return_location": null,
"customer": {
"id": 456,
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com",
"phone": "+1234567890"
},
"products": [],
"experiences": [],
"bundles": [],
"payments": [],
"tags": [],
"notes": [],
"created_at": "2026-01-10T14:30:00+00:00",
"updated_at": "2026-01-15T09:00:00+00:00"
},
"meta": {
"request_id": "req_abc123",
"timestamp": "2026-01-15T09:00:00+00:00"
}
}
Confirm a Bookingโ
curl -X PATCH "https://app.equipdash.com/api/v1/bookings/123" \
-H "Authorization: Bearer eqd_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{"status": "confirmed"}'
Add Discountโ
curl -X PATCH "https://app.equipdash.com/api/v1/bookings/123" \
-H "Authorization: Bearer eqd_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"discount": true,
"discount_type": "flat",
"discount_value": 25
}'
Change Customerโ
curl -X PATCH "https://app.equipdash.com/api/v1/bookings/123" \
-H "Authorization: Bearer eqd_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{"customer_id": 789}'
Add Bundlesโ
Add product bundles to a rental booking:
curl -X PATCH "https://app.equipdash.com/api/v1/bookings/123" \
-H "Authorization: Bearer eqd_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"add_bundles": [
{"bundle_id": 5, "quantity": 1},
{"bundle_id": 8, "quantity": 2, "extras": [{"extra_id": 12, "quantity": 1}]}
]
}'
Each add_bundles entry accepts the same fields as bundles[] on Create booking, including extras[] and the selection keys (choices, durations, participants, schedules, variations, content_variations). Kit part picks use { "<bundle_item_id>": { "<template_item_id>": <variation_id> } }.
An extra charged once can only be added once. Sending a larger quantity for one returns a
field-named 422 under add_bundles.{i}.extras.{j}.quantity, and nothing is changed. A booking that
already carries two of one from before keeps them.
Remove Bundlesโ
Remove bundles by their line item IDs (found in the booking's bundles array):
curl -X PATCH "https://app.equipdash.com/api/v1/bookings/123" \
-H "Authorization: Bearer eqd_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"remove_bundle_ids": [45, 46]
}'
The remove_bundle_ids are the id values from the booking's bundles array (the bundle line item IDs), not the bundle's product ID.
Reconciling a paid bookingโ
When you update a booking that has already been paid (or partially paid) and the price changes, the response includes a reconciliation block under meta describing what action was taken. If you don't pass reconciliation_method, no payment action is taken โ the booking just reflects the new totals and the response asks you to settle the difference.
Methodsโ
| Method | When to use | Effect |
|---|---|---|
send_payment_link | New total > old total | Creates a payment link for the delta and emails it to the customer. The link is paid through the business's own gateway (Stripe, Paystack, Rapyd or Authorize.net). Booking flips to partial. Customer must have an email on file |
refund_to_card | New total < old total | Refunds the delta to the original payment method using the existing booking-level refund pipeline (Stripe or Paystack). |
skip | Either direction | No payment action. For a price increase the booking is marked partial so the imbalance shows up in the UI. Settle later via POST /bookings/{id}/payments or by sending a payment link manually |
Charge exampleโ
curl -X PATCH "https://app.equipdash.com/api/v1/bookings/123" \
-H "Authorization: Bearer eqd_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"add_bundles": [{"bundle_id": 8, "quantity": 1}],
"reconciliation_method": "send_payment_link"
}'
Response (excerpt):
{
"data": { "id": 123, "...": "..." },
"meta": {
"reconciliation": {
"old_total": 180.00,
"new_total": 245.00,
"delta": 65.00,
"action": "payment_link_sent",
"amount": 65.00
}
}
}
Refund exampleโ
curl -X PATCH "https://app.equipdash.com/api/v1/bookings/123" \
-H "Authorization: Bearer eqd_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"remove_bundle_ids": [45],
"reconciliation_method": "refund_to_card"
}'
The reconciliation.action will be refunded on success.
Validationโ
- Passing
send_payment_linkwhen the new total decreased returns422withcode: reconciliation_invalid. Userefund_to_card. - Passing
refund_to_cardwhen the new total increased returns422withcode: reconciliation_invalid. Usesend_payment_link. - A failed Stripe / Paystack refund returns
422withcode: reconciliation_failedโ the booking changes still persist; you can retry the refund separately.
A booking marked as a no-showโ
A booking whose status is no_show is locked like a completed one, with one exception: a request that sends only status (with email_customer, override or override_reason if you need them) still goes through.
status sent | Result |
|---|---|
confirmed | Undoes the no-show, the same as revert-no-show |
checked_in or picked_up | Records a late arrival and clears the no-show |
Any other field on a no_show booking returns 409 Conflict.
curl -X PATCH "https://app.equipdash.com/api/v1/bookings/123" \
-H "Authorization: Bearer eqd_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{"status": "checked_in"}'
Error Responsesโ
| Status | Code | Description |
|---|---|---|
| 401 | authentication_required | Missing or invalid API key |
| 403 | insufficient_scope | API key lacks bookings:write scope |
| 403 | forbidden | Booking belongs to another supplier |
| 404 | resource_not_found | Booking not found |
| 409 | conflict | Cannot update a cancelled, checked-in, returned, completed or no-show booking |
| 422 | status_transition_failed | The booking cannot move to that status. The message says why |
| 422 | validation_error | Invalid request data |
| 422 | reconciliation_invalid | reconciliation_method doesn't match the direction of the price change |
| 422 | reconciliation_failed | The refund or payment-link call failed downstream (booking changes still persist) |
Conflict Error Exampleโ
{
"error": {
"code": "conflict",
"message": "Cannot update a booking that is cancelled."
},
"meta": {
"request_id": "req_abc123"
}
}
Notesโ
- Cancelled, checked-in, returned, completed and no-show bookings cannot be updated. Use the Cancel Booking endpoint for cancellation.
- The customer must belong to your supplier account.
- Changing dates will automatically:
- Check availability for all products with the new dates
- Recalculate prices based on new rental duration
- Recalculate taxes and totals
- Return an error if any products are unavailable for the new dates
- Bundles can only be added to or removed from rental bookings.
- Prices are automatically recalculated when bundles are added or removed.
Bundle rate countsโ
For a bundle using prices by rate type, include rate_counts on each add_bundles[] entry. This maps offered rate category IDs to counts and replaces per-experience participant counts for that bundle. Keep the existing departure, choice, duration, and variation maps. The booking saves its rate labels, counts, and sold prices so later catalog changes do not reprice it.