ja
予約›Book, schedule and confirm a job in one call

Book, schedule and confirm a job in one call

Creates the job, quotes it (job_duration_minutes, or the job type's default_duration_minutes) and confirms it at scheduled_at on the customer's behalf — the dashboard's "book for a caller" in a single request, built for voice agents and automation platforms that cannot run create → quote → slots → confirm. Customer: send `customer_id`, or `customer` (+ `address`) to match-or-create by phone/email — a caller who already exists is matched, never duplicated. Find a caller first with listCustomers?phone=. Every input is validated BEFORE anything is written (time and business timezone, future start, a duration or a job-type default, non-negative mobilization/demobilization minutes, active job type, phone format); those failures are ordinary 4xx and create nothing. ⚠️ A `technician_id` the engine cannot honour at that time is refused BEFORE anything is written: `409 JOB_REQUEST_TECH_INFEASIBLE`, no job, no customer, no webhook. Change the technician or the time and send again with a NEW Idempotency-Key: like every 4xx here the 409 is cached against its key for 24h, so the same key replays it (same body) or answers 422 IDEMPOTENCY_KEY_REUSE (changed body). This check is BEST-EFFORT — if the scheduling engine cannot be reached it is skipped and the call proceeds, so a pinned request can still come back as the 200 + `refusal` below. Handle both. ⚠️ ONE carve-out: a `p0` whose pinned technician is merely BUSY is NOT refused here. The job is created and the call answers 200 + `refusal` carrying JOB_REQUEST_P0_REQUIRES_DISPLACEMENT, because the remedy for that code is the emergency flow and listEmergencyCandidates / previewEmergencyReschedule / commitEmergencyReschedule all require `emergency_job_id` — a 409 carries no job id. Read `job_id` off the 200 and run the emergency flow; do not re-book. Every other doomed pin on a `p0` (out of area, missing skills, not a lead) still answers 409. ⚠️ A SECOND carve-out, narrower: for a `p0`, a PERSONAL-CALENDAR overlap on the pinned technician is not refused here either — naming them is treated as having already phoned them — and the call answers 200 + `confirmed: true`. Nothing on this response discloses it; the only record is the business's activity feed (`job_calendar_override`). `calendar_busy`/`calendar_conflict` therefore never appear in the 409 reason vocabulary below when the pin is a `p0` naming a technician explicitly (this endpoint never books a crew, so the exclusion is unconditional here). ⚠️ Otherwise, once the job is created it is never discarded, and the call answers 200 even if scheduling then fails: `confirmed: false` with `refusal` (stage + the exact error_code/data the quote or confirm endpoint would have returned, e.g. JOB_REQUEST_NO_TECHNICIAN_AVAILABLE with blockers). The job is then quoted and waiting in the coordinator's queue — tell the caller the office will confirm a time. Retry with the SAME Idempotency-Key to replay the result; a new key books a second job. P0 dispatch: send `emergency` with `priority: "p0"`. `dispatch: "now"` plans the displacement cascade BEFORE anything is written — a refusal is a 409 creating nothing — then books, quotes and commits it. `dispatch: "later"` parks a quoted P0 in the tray and ignores the other fields. ⚠️ `emergency.expected_move_ids` is REQUIRED with `dispatch: "now"` and has three meanings: omitted is refused, `[]` means "my preview showed no displacement" and commits a free lane, and a named set is the fence. Get the ids from previewEmergencyReschedule. ⚠️ A refusal AFTER the job is created answers 200 with `confirmed: false` and `refusal.stage: "dispatch"`. Read `job_id`, re-preview with previewEmergencyReschedule and commit with commitEmergencyReschedule. Never re-book.

引数

Idempotency-Keystringheader任意
Makes retries safe: a repeat send with the same key returns the original result instead of booking again
▾addressobjectbody任意
Service address. REQUIRED with `customer`. OPTIONAL with `customer_id`: when sent it is used for THIS JOB and the stored customer address is only the fallback when it is omitted — it is never written back to the customer record. Missing coordinates are geocoded.
citystringbody任意
countrystringbody任意
formattedstringbody任意
latitudenumberbody任意
linestringbody任意
line2stringbody任意
longitudenumberbody任意
postal_codestringbody任意
statestringbody任意
▾customerobjectbody任意
A caller who may or may not exist yet: matched by phone/email against the business's customers (no duplicate is created for a known caller), created otherwise. Send this OR `customer_id`.
emailstringbody任意
Email. Phone or email is required.
full_namestringbody任意
Customer's full name. Required with `customer`.
phonestringbody任意
Phone in E.164 with the leading + (e.g. +16135550142); separators are stripped. A bare national number is refused with PHONE_INVALID. Phone or email is required.
sms_opt_inbooleanbody任意
Record SMS consent ONLY if the caller explicitly agreed to texts (a voice agent must ask). Without it the customer gets email, never SMS.
customer_idstringbody任意
UUID of an existing customer. Send this OR `customer`, not both.
demobilization_minutesintegerbody任意
Demobilization (teardown) minutes after the work, min 0 — same rules as mobilization_minutes.
descriptionstringbody任意
Free-text description of the work. Optional; max 2000 chars.
▾emergencyobjectbody任意
P0 dispatch. Present only with priority p0 (or a business whose default priority is p0). See the endpoint description.
after_hours_overridebooleanbody任意
Place this P0 outside working hours / approved time-off — you have phoned the technician and they agreed.
dispatchenumbody任意
"now" commits the cascade in this call; "later" parks a quoted P0 in the tray and ignores every other field here.
displacement_modeenumbody任意
Fate of displaced jobs: reschedule (default) or reassign. Genuinely optional, unlike mode — omitted means reschedule. Must match the preview.
expected_move_idsarray<string>body任意
The displaced job IDs your preview returned. REQUIRED with dispatch "now". Send [] when the preview showed no displacement — an omitted key is refused, an empty array commits a free lane.
modeenumbody任意
Cascade mode: overtime = displaced jobs stay same-day; next_day = overflow rolls to the next working day. REQUIRED with dispatch "now" (omitted is a 400 EMERGENCY_RESCHEDULE_INVALID_INPUT, data.field = "emergency.mode"); ignored with "later". Must match the preview.
job_duration_minutesintegerbody任意
Hands-on work minutes. Optional when the job type has a default_duration_minutes.
job_type_idstringbody任意
UUID of the job type: it classifies the job and supplies default_duration_minutes when job_duration_minutes is omitted. Optional: omitted, the business's DEFAULT job type (is_default, the seeded "General", 60 min + 15 + 15 unless the business changed it) is used.
mobilization_minutesintegerbody任意
Mobilization (setup/travel-prep) minutes before the work, min 0 — same meaning as on quoteJobRequest. Optional: omitted, the job type's default_mobilization_minutes applies when job_duration_minutes is ALSO omitted, and 0 when you sent a duration (an explicit duration is never mixed with the type's buffers). A value you send always wins. Counted when checking whether a pinned technician_id is free.
priorityenumbody任意
Scheduling priority p0–p3. Optional; omitted = the business default.
scheduled_atstringbody任意
Start time — a BUSINESS-LOCAL wall clock, same rule as confirmJobRequest: canonical `2026-09-30T10:00:00`, seconds optional, a space may replace the T; an offset is accepted only when it agrees with the business timezone. Must be in the future.
skill_idsarray<string>body任意
UUIDs of skills the work needs. Optional; up to 20.
technician_idstringbody任意
Force a specific lead technician (feasibility still enforced). Omit to let Crisphive pick.
POST/v1/job-requests/book-and-confirm
curl -X POST "https://api.crisphive.com/v1/job-requests/book-and-confirm" \
  -H "Authorization: Bearer chsk_test_4eC8xQ9mZ2pL7Ka0rT" \
  -H "Content-Type: application/json" \
  -d '{
  "address": {
    "city": "San Francisco",
    "country": "US",
    "formatted": "123 Market Street, San Francisco, CA 94103",
    "latitude": 1,
    "line": "123 Market Street",
    "line2": "Suite 200",
    "longitude": 1,
    "postal_code": "94103",
    "state": "CA"
  },
  "customer": {
    "email": "jane.doe@example.com",
    "full_name": "Marie Tremblay",
    "phone": "+16135550142",
    "sms_opt_in": true
  },
  "customer_id": "9b2f6c3e-1a4d-4f0a-8f2e-7c5d1b3a9e01",
  "demobilization_minutes": 15,
  "description": "Please call on arrival.",
  "emergency": {
    "after_hours_override": true,
    "dispatch": "now",
    "displacement_mode": "reschedule",
    "expected_move_ids": [
      "9b2f6c3e-1a4d-4f0a-8f2e-7c5d1b3a9e01"
    ],
    "mode": "overtime"
  },
  "job_duration_minutes": 90,
  "job_type_id": "9b2f6c3e-1a4d-4f0a-8f2e-7c5d1b3a9e01",
  "mobilization_minutes": 15,
  "priority": "p0",
  "scheduled_at": "2026-09-30T10:00:00",
  "skill_ids": [
    "9b2f6c3e-1a4d-4f0a-8f2e-7c5d1b3a9e01"
  ],
  "technician_id": "9b2f6c3e-1a4d-4f0a-8f2e-7c5d1b3a9e01"
}'
リクエストボディ
{
"address": {
"city": ,
"country": ,
"formatted": ,
"latitude": ,
"line": ,
"line2": ,
"longitude": ,
"postal_code": ,
"state":
},
"customer": {
"email": ,
"full_name": ,
"phone": ,
"sms_opt_in":
},
"customer_id": ,
"demobilization_minutes": ,
"description": ,
"emergency": {
"after_hours_override": ,
"dispatch": ,
"displacement_mode": ,
"expected_move_ids": [
],
"mode":
},
"job_duration_minutes": ,
"job_type_id": ,
"mobilization_minutes": ,
"priority": ,
"scheduled_at": ,
"skill_ids": [
],
"technician_id":
}

レスポンス