Skip to main content
Glama

confirmJobRequest

Confirm a booking on behalf of the customer

Fires the customer-actor confirm_booking action from the BUSINESS surface (audited as business_on_behalf). Two uses: (1) LIVE — staff confirm a slot for a customer who booked by phone; (2) SANDBOX — the customer magic-token surface is live-only (a sandbox job's link can never reach a real customer), so this is the ONLY way to drive a sandbox test job past booking (book → quote → confirm → assign → complete). Body carries the customer-chosen scheduled_at (business-local naive datetime). DECISION TABLE — every 409 this endpoint returns, and the correct NEXT STEP (branch on error_code, never on the HTTP status): • JOB_REQUEST_STAGE_CONFLICT — the job changed since you read it (NOTE: every FAILED confirm attempt also bumps status_version by design). Next: re-GET the job, retry with the fresh status_version. • JOB_REQUEST_ACTION_NOT_PENDING — the job is no longer at the confirm step (usually: already confirmed). Next: re-GET and show current status; do not retry. • JOB_REQUEST_NO_TECHNICIAN_AVAILABLE — the TIME is infeasible for everyone (outside working hours / the customer window, or nobody qualifies). Best-effort data breakdown: considered (roster size checked), blocked_by (histogram of blockers[0].kind → count, only kinds that actually blocked someone), truncated (roster larger than the check covered). Next: pick another time via booking-windows / time-segments. NOT an emergency case — displacement cannot conjure capacity. • JOB_REQUEST_TECH_INFEASIBLE — the FORCED technician cannot take the job then; data.reason says why (blockers[0].kind): outside_service_area | missing_required_skills | not_lead_tier | no_working_day | on_time_off | off_shift | calendar_busy | calendar_conflict | visit_too_long | occupied (they are already working then — data.earliest_feasible_at is when they are free) | cannot_arrive_in_time (see data.earliest_feasible_at, RFC3339 UTC — the first same-day time they CAN be on site → offer it) | not_available_today (diagnosis unavailable). data.blockers[] names EVERY cause, most-structural first; data.technician carries {id, name}, name best-effort (empty only when the lookup itself could not resolve) — EXCEPT on not_lead_tier, which carries a flat data.technician_id and no technician object. A UI that reads only reason still works. Next: keep the tech and reschedule to earliest_feasible_at+, OR keep the time and drop technician_id (auto-pick) / choose another tech from time-segments. NOT an emergency case. • JOB_REQUEST_P0_REQUIRES_DISPLACEMENT — the ONLY code that routes to the EMERGENCY flow: the job is P0, the tech qualifies, but the lane is genuinely occupied. Next: POST emergency/candidates → preview → commit (the commit auto-confirms). Caveat: if the occupying jobs are themselves P0 the preview will reject with EMERGENCY_RESCHEDULE_SLOT_OCCUPIED (P0 never displaces P0) — then pick another tech/time.

Tool call template

Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "id": "<id>"
    },
    "name": "confirmJobRequest"
  }
}

Input Schema

NameRequiredDescriptionDefault
idYesJob request ID
scheduled_atNoChosen start time — business-local wall clock (the business_time.datetime value from the time-segments picker), converted to UTC against the job's business timezone. Seconds may be omitted and a space may replace the T. An offset is accepted only when it agrees with the business timezone; a disagreeing one is refused with JOB_REQUEST_INVALID_INPUT naming what it means locally.
technician_idNoTechnicianID (BUSINESS confirm only — ignored on the customer surface): force-assign the job to this technician instead of the ranked auto-pick. Ranking is bypassed; feasibility (hours/time-off/geo/skills), the TierLead rule and the double-booking guard still apply — an infeasible forced tech rejects the confirm (P0 gets the displacement hint). ⚠️ One exception: for a SINGLE-PERSON p0 job, a PERSONAL-CALENDAR overlap on this exact technician no longer rejects — naming them here is treated as having already phoned them, so the confirm succeeds and the override is recorded on the business's activity feed (this response has no warnings array to carry it). A multi-member crew still rejects on it.
status_versionNoOptimistic-lock fence: the status_version from your last read. Omitted/0 = fence on the row's current version (no race protection).
idempotency_keyNoUnique key making retries safe: a repeat send with the same key replays the original response (header Idempotent-Replayed: true) instead of re-running the operation. Reusing a key with a different body returns 422 IDEMPOTENCY_KEY_REUSE. (sent as the Idempotency-Key header)
after_hours_overrideNoAfterHoursOverride (BUSINESS confirm only): schedule this P0 outside the technician's working hours or approved time-off — you have phoned them and they agreed. Requires priority p0, a single-person job, and technician_id. Double-booking, service area, required skills and the lead-tier rule still reject. Deliberately NOT a binding-tag rule: gin evaluates tags before the handler, which would make the three AFTER_HOURS codes unreachable.
arrival_window_minutesNoArrivalWindowMinutes = width (phút) của arrival-window ô khách bấm ở slot-picker (chính là time_slot_step_minutes, mặc định 30). Persist để post-confirm detail render lại đúng window. Optional; bounds ([5, 240], khớp slot-picker step) validate ở usecase — single authority, một error code (JOB_REQUEST_INVALID_INPUT).

Output Schema

NameRequiredDescriptionDefault
dataNoThe response payload. Omitted on error unless the error carries structured data.
errorsNoField-level validation details; present only for VALIDATION_ERROR responses.
messageNoHuman-readable message ("Success" or an error description), localized via the X-Locale header.
error_codeNo0 on success; a stable string error code on failure (e.g. CUSTOMER_NOT_FOUND).

Schema Changelog

Changes observed during successful MCP inspections.

  1. First observed

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only cover safety hints (readOnly=false, destructive=false, openWorld=true), while the description adds substantial behavior: the audit trail ('business_on_behalf'), the status_version bump side effect on failed attempts, per-code error semantics with `data` payload shapes, and the single-person P0 personal-calendar exception. This is far richer than what the annotations provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long, but it is front-loaded with purpose and organized under a labeled DECISION TABLE with bulleted codes, so every section earns its place for a mutation endpoint with many failure modes. Density is high but structure keeps it navigable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists so return values need not be explained, and the description covers purpose, mode selection, argument semantics, and exhaustive error recovery. Nothing an agent needs to correctly invoke and recover this tool is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the param descriptions are themselves extremely detailed, so the schema carries most of the burden (baseline 3). The description still adds meaning beyond the schema, notably that the body carries a business-local naive scheduled_at and that failed confirms bump status_version by design.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Confirm a booking on behalf of the customer') and precisely scopes the two supported modes (LIVE staff confirm, SANDBOX test-drive past booking), making the tool's role unmistakable. This functional scoping distinguishes it from the bookAndConfirmJobRequest sibling even without naming it.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit when-to-use context (the two uses) and an exhaustive DECISION TABLE mapping every 409 error_code to the correct next step, explicitly naming alternative routes such as booking-windows/time-segments and emergency/candidates→preview→commit. It even instructs the agent to branch on error_code rather than HTTP status, which is exactly the routing guidance an agent needs.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.