Skip to content

Append an amendment to the booking's agreement

POST
/bookings/{booking_id}/amendments
curl --request POST \
--url https://quikstik-core.fly.dev/bookings/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/amendments \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "expected_version": 1, "amendment": { "kind": "start_time", "start_time": "example", "name": "example", "address": "example", "span_days": 1, "added": [ "example" ], "removed": [ "example" ], "add_on_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "quantity_delta": 1, "display_name": "example", "from": "example", "to": "example", "rider": "example", "net_delta_minor": 1, "reason": "example", "waiver_reason": "hardship" } }'

Typed amendments folding onto the agreement chain. expected_version must equal the agreement’s current version — a stale commit refuses 409 with stored_version and mutates nothing. The customer update notification enqueues in the same transaction (transactional outbox → the Rust confirmation rail).

Money-bearing kinds (party_delta, add_on_delta, price_override) are SERVER-priced: the request carries facts (names, an add-on id + delta, a net delta + reason), never a price the caller computed. Pricing comes from the governed rate calendar and add-on catalog; tax follows the merchant’s configured rate (fail-closed). price_override additionally requires the commerce.price_override permission (403 otherwise) and a non-empty reason. Party growth reserves departure capacity first (refused 409 capacity_exhausted mutates nothing). The owed floor (settled ≤ owed) refuses 422 owed_floor_use_refund_lane — un-owing collected money is the refund lane’s job, Rider REMOVAL money stays in the refund lane; removal here amends the roster only when no money would move.

cancel is TERMINAL and carries no money: the refund fraction is resolved server-side from the booking’s policy snapshot and the agreement’s own start time, and the ledger executes the split against the capture. The chain gains CancelledV1, the booking flips to cancelled, and the ledger event enqueues atomically. A cancelled chain accepts no further amendments (409 already_cancelled). Requires an idempotency key (400 without one) — a cancel moves money.

cancel may carry waiver_reason: a reason the booking’s policy lists in overrides.waiver_reasons bypasses the schedule for a FULL refund, recorded on the agreement chain as customer_waived:<reason>. Session-bearer only (403 waiver_requires_session for an operator key); a reason the policy does not list is 422 waiver_not_allowed. A customer-initiated cancel of an OTA-governed booking is 409 ota_governed — only a waiver cancels it.

booking_id
required
string format: uuid
Media typeapplication/json
object
expected_version
required
integer
>= 1
amendment
required
object
kind
required
string
Allowed values: start_time meeting_point span party_delta add_on_delta add_on_rider party_rename price_override cancel
start_time

RFC3339 UTC (kind=start_time)

string
name

Kind=meeting_point; null clears

string
nullable
address
string
nullable
span_days
integer
>= 1 <= 60
added

Kind=party_delta; rider display names joining. Server-priced.

Array<string>
removed

Kind=party_delta; riders leaving. REVERSAL-priced: each refunds their share of what the party actually pays (owed net minus add-on lines over party size) — the rate calendar is never consulted. Settled money comes back as a processor refund enqueued in the same transaction; unpaid balances simply shrink. Mixed added+removed refuses (422 mixed_party_delta); removing the whole party refuses (422 full_removal_is_cancel — use cancel).

Array<string>
add_on_id

Kind=add_on_delta

string format: uuid
quantity_delta

Kind=add_on_delta; nonzero. Positive units price from the governed catalog per day; negative units REVERSAL-price from the agreement line’s own paid rate (line_minor/quantity; whole-line removal refunds exactly line_minor), with the same same-transaction refund of settled money. Over-removal refuses (422 add_on_removal_exceeds_quantity).

integer
display_name

Kind=add_on_rider; the agreement line to re-bind

string
from

Kind=party_rename; current recorded name

string
to

Kind=party_rename; corrected name (bindings follow)

string
rider

Kind=add_on_rider; party member to bind, null = party-level

string
nullable
net_delta_minor

Kind=price_override (permission-gated)

integer format: int64
reason

Kind=price_override; required, non-empty

string
waiver_reason
string
Allowed values: hardship rain_rule goodwill flight_disruption

Amendment committed; the folded agreement facts.

Media typeapplication/json
object
version
required
integer
start_time
required
string
span_days
required
integer
meeting_point
object

Example generated

{
"version": 1,
"start_time": "example",
"span_days": 1,
"meeting_point": {}
}

Money kind requiring a permission the session lacks (price_override).

Unknown booking, or no agreement chain for it.

Stale expected_version (body carries stored_version — re-read and re-propose), or capacity_exhausted for party growth.

Invalid amendment body, unknown/unpriced add-on, or owed-floor refusal.

Backend unavailable.