Changelog
reserve no longer creates a reservation. It starts an attempt that the server keeps: each response carries an opaque attempt_token, and either the choices still needed (needs_input) or a summary ready to confirm (ready). reserve-answer sends the user's new answers on that token, and reserve-confirm is the only call that books. Nothing is booked or charged by reserve.
BREAKING for every caller of reserve. The bound transition runs from the 2026-07-20 contract, when reserve created the reservation in one call, to the 2026-10-21 release. (1) Flow: reserve starts an attempt and books nothing. It returns {status: needs_input, attempt_token, required_selections, settled_selections?} or {status: ready, attempt_token, summary}. Relay required_selections to the user and send only the new answers with reserve-answer {attempt_token, answers}; settled decisions are kept server side and must not be re-sent. On ready, show the summary and create the booking with reserve-confirm {attempt_token, payment?}, which returns the created reservation (reservation_ref, status CONFIRMED or AWAITING_SHOP_CONFIRMATION). reserve itself no longer returns reservation_ref, status, shop_ref, visit_date or person_count at the top level. (2) Request: reservation_state and selected_options are removed; the start takes {shop_ref, date, time, person}. person accepts 1 to 80 (it had no upper bound), the range CatchTable's own party picker offers; a value above 80 fails input validation (HTTP 400). Optional accepted_payment_methods (none, points, catch_pay; at least one) declares the confirmation methods the caller can complete: catch_pay is offered only when declared, and the ready summary's payment and cancellation facts (shop_name, pay_now_amount_krw, deposit_refund_policy, zero_deposit_cancellation_fee_policy, free_cancellation_until, attempt_expires_at, catch_pay_status) appear only for attempts that declare it. Optional use_point (KRW, 0 or at least 100) records the points the user chose, and reserve-confirm must send the same amount. (3) Response details: required_selections[].valid_options carry availability, default_quantity, min_quantity and max_quantity, and for add-ons unit_price_krw and age_verification_required; menu items that carry default_quantity or max_quantity may be answered together, one answer per item with its quantity. summary.time_is_next_day marks a slot CatchTable lists under date but shows as the next day; it is booked with the same date and time. (4) Errors since 2026-07-20: TABLE_SELECTION_REQUIRED, INVALID_RESERVATION_STATE, INVALID_RESERVATION_SELECTION and STALE_RESERVATION_STATE no longer exist (those situations are needs_input rounds); the rate-limit code is RATE_LIMITED (429) instead of 429; new SLOT_UNAVAILABLE (409, ask for another time), PERSON_OUT_OF_RANGE (409, the party is outside the sizes the shop takes online that day; details.context carries min_person_count and max_person_count when CatchTable states them), ONLINE_RESERVATION_UNAVAILABLE (409, no online reservation that day), UPSTREAM_REJECTED (409, CatchTable's own rules refused it; follow the error fix), UPSTREAM_RESULT_CODE_UNREGISTERED (502, do not retry the same request) and reauth_required (reconnect the CatchTable session). AUTH_REQUIRED, RESERVATION_SELECTION_REQUIRED, PAYMENT_REQUIRED and UPSTREAM_ERROR are unchanged.