---
title: "Request the booking. Idempotent per job; requires approve:true (types-name-as-signature approval of the inventory + quote, stored with a quote snapshot) and accept_terms when the mover has terms."
method: POST
path: "/jobs/{id}/book"
---

# Request the booking. Idempotent per job; requires approve:true (types-name-as-signature approval of the inventory + quote, stored with a quote snapshot) and accept_terms when the mover has terms.

`POST /jobs/{id}/book`

## Request

- Base URL: `https://widget.reelquoteai.com/api/v1`
- URL: `https://widget.reelquoteai.com/api/v1/jobs/{id}/book`
- Auth: HTTP bearer

## Request body

- object
  - `name` string, required: The submitted name is the signature on the approval.
  - `email` string, required
  - `phone` string
  - `approve` boolean, required: Must be true: approves the inventory as complete and this quote. Cleared if the inventory is edited afterwards.
  - `accept_terms` boolean: Required (true) when the job's `terms.terms` is non-null

## Response `200`

Requested — or, on a repeat, the existing request with already_requested:true and nothing re-sent

- object
  - `job_id` string
  - `status` 'booked_requested' | 'booked'
  - `quote` Quote: A priced quote. bookable=false means a ballpark range; only bookable quotes can be booked. The key kind decides the view. A publishable key — it sits in the mover's page source — receives the customer view: each line's label, amount, hours, crew and miles, the overage rate, the assumptions and the inputs. A secret key also receives the rate behind each line, applied_rules, subtotal_cents, minimum_charge_cents, rate_card_id, rate_card_name and engine_version.
    - `kind` 'ballpark' | 'firm', required
    - `bookable` boolean, required: true only for a firm quote built from a filmed inventory. Gate any booking action on this flag, never on kind.
    - `currency` string, required: ISO 4217
    - `units` string: Measurement system for volumes and distances
    - `crew` integer, required: Movers on the job
    - `labour_hours` number, required: Billable hours at the home(s)
    - `overage` object: Disclosed on every quote: what an hour past the estimate costs.
      - `label` string
      - `hourly_cents` integer: Rate for time beyond the estimate, in integer cents. Currency travels beside it — never assume USD.
      - `same_as_hourly` boolean
    - `buckets` object, required: The price, line by line.
      - `labour` Bucket: One line of the price: a label and an amount. Labour lines also carry hours and crew; truck legs carry miles.
        - `label` string: Plain-language line name
        - `cents` integer: This line, in integer cents. Currency travels beside it — never assume USD.
      - `move_leg_time` Bucket: One line of the price: a label and an amount. Labour lines also carry hours and crew; truck legs carry miles.
        - `label` string: Plain-language line name
        - `cents` integer: This line, in integer cents. Currency travels beside it — never assume USD.
      - `deadhead_leg_time` Bucket: One line of the price: a label and an amount. Labour lines also carry hours and crew; truck legs carry miles.
        - `label` string: Plain-language line name
        - `cents` integer: This line, in integer cents. Currency travels beside it — never assume USD.
      - `truck_move_leg` Bucket: One line of the price: a label and an amount. Labour lines also carry hours and crew; truck legs carry miles.
        - `label` string: Plain-language line name
        - `cents` integer: This line, in integer cents. Currency travels beside it — never assume USD.
      - `truck_deadhead_leg` Bucket: One line of the price: a label and an amount. Labour lines also carry hours and crew; truck legs carry miles.
        - `label` string: Plain-language line name
        - `cents` integer: This line, in integer cents. Currency travels beside it — never assume USD.
      - `travel_flat` Bucket: One line of the price: a label and an amount. Labour lines also carry hours and crew; truck legs carry miles.
        - `label` string: Plain-language line name
        - `cents` integer: This line, in integer cents. Currency travels beside it — never assume USD.
      - `access_origin` Bucket: One line of the price: a label and an amount. Labour lines also carry hours and crew; truck legs carry miles.
        - `label` string: Plain-language line name
        - `cents` integer: This line, in integer cents. Currency travels beside it — never assume USD.
      - `access_destination` Bucket: One line of the price: a label and an amount. Labour lines also carry hours and crew; truck legs carry miles.
        - `label` string: Plain-language line name
        - `cents` integer: This line, in integer cents. Currency travels beside it — never assume USD.
    - `applied_rules` object[]: Date and seasonal rules that fired, in order. Empty when none did.
      - `rule` string: Rule name from the mover's rate card, e.g. weekend, month_end
      - `mult` number, nullable: Multiplier, when the rule is one
      - `add_cents` integer, nullable: Flat addition, when the rule is one, in integer cents. Currency travels beside it — never assume USD.
      - `delta_cents` integer: What the rule changed the total by, in integer cents. Currency travels beside it — never assume USD.
    - `subtotal_cents` integer: Before date rules and the minimum charge, in integer cents. Currency travels beside it — never assume USD.
    - `minimum_charge_cents` integer: The mover's floor, in integer cents. Currency travels beside it — never assume USD.
    - `minimum_charge_applied` boolean: true when the floor lifted the total
    - `total_cents` integer, nullable, required: The price. null on a ballpark — use low_cents / high_cents, in integer cents. Currency travels beside it — never assume USD.
    - `low_cents` integer: Ballpark only: low end of the range, in integer cents. Currency travels beside it — never assume USD.
    - `high_cents` integer: Ballpark only: high end of the range, in integer cents. Currency travels beside it — never assume USD.
    - `inputs` object: What the price was computed from — pinned on the job, so a later rate-card change can never reprice history. A ballpark adds the home size (dwelling) and its volume range (cuft_low, cuft_high).
      - `cuft` number: Volume priced
      - `weight_lbs` integer
      - `distance_miles` number, nullable
      - `duration_minutes` number, nullable: Routed drive time, when a route was found
      - `move_date` string, date, nullable: YYYY-MM-DD
    - `assumptions` string[], required: Plain-language caveats. Show these to the human.
    - `rate_card_id` string: The mover's rate card this was priced from
    - `rate_card_name` string
    - `engine_version` string: Pricing engine version
  - `already_requested` boolean: Only present (true) on a repeat call

## Other responses

- `400`: `invalid_request` — A field is missing, malformed or not allowed in this state. `message` says which. `secret_key_in_url` — A secret key was sent as `?k=`. Secret keys go in the Authorization header only.
- `401`: `unauthorized` — No key, an unknown key, or a revoked key.
- `404`: `not_found` — No such job for this key, or no such endpoint.
- `409`: `last_publishable_key` — The only live publishable key cannot be revoked — the widget stops without one. Rotate it instead. `key_already_rotated` — That key was already rotated. A key is rotated once; its replacement is the one to use and to rotate next. `webhook_paused` — That webhook endpoint is paused; turn it back on before replaying a delivery to it. `video_in_flight` — A video is already being fetched for this job (POST /jobs/{id}/video). Poll GET /jobs/{id}. `video_already_supplied` — The job already has a video and has moved past upload. Start a new job for a different walkthrough. `no_rate_card` — The mover has no active rate card, so nothing can be priced yet. `ballpark_not_bookable` — Only a firm quote from a filmed walkthrough can be booked. `already_requested` — The job is already booked or awaiting the mover, so the move date can no longer change here. Contact the mover. `idempotency_in_progress` — A request with the same Idempotency-Key is still being processed. Obey `Retry-After` (1 s) and retry with the same key.
- `429`: `rate_limited` — Per-key limit (60/min publishable, 300/min secret), the mover's daily ballpark or video-analysis cap, or a visitor's daily catalog-search cap. Obey `Retry-After`. Carries `Retry-After` in seconds.

---

[API](https://skmtc.dev/reelquoteai/apis/reelquoteai-us-api.md) · [All operations](https://skmtc.dev/reelquoteai/apis/reelquoteai-us-api/llms.txt) · [OpenAPI document](https://skmtc.dev/reelquoteai/apis/reelquoteai-us-api/revisions/56c304752192?raw)
