ReelQuoteAI Survey Tool
Filmed moving surveys, itemized inventories, firm quotes and bookings for moving companies.
¿Debería usar esto?
Calidad y seguridad
Hallazgos (6)
- LOWen list_home_profiles
- LOWen report_outcome
- LOWen quote_inventory
- LOWen create_upload_url
- LOWen list_webhook_deliveries
- LOWen replay_webhook_delivery
Basado en el análisis automatizado de las definiciones de herramientas y el cumplimiento del protocolo.
Costo de contexto
Este es el número aproximado de tokens que se consumen cada vez que las herramientas del servidor se cargan en el contexto de un modelo. Los recuentos más altos reducen la atención disponible para otras tareas.
Instalar
Instalación con un clic
Agrega esto a tu archivo `claude_desktop_config.json`:
{
"mcpServers": {
"survey-tool": {
"url": "https://widget.reelquoteai.com/mcp"
}
}
}Puntos de conexión remotos
https://widget.reelquoteai.com/mcpstreamable-httpQué puede hacer
Inventario de herramientas
Herramientas (32)
🟢get_session
Resolve the key to its mover (name, branding, config). Scope: quote.
Esquema de entrada
{
"type": "object",
"properties": {},
"additionalProperties": false
}🟢list_home_profiles
Home sizes for the ballpark step (2br_apt, 3br_house, …). Scope: quote.
Esquema de entrada
{
"type": "object",
"properties": {},
"additionalProperties": false
}⚪ballpark_estimate(dwelling, move_date, origin, destination, distance_miles)
Instant estimate range from home size + addresses. Free; never bookable. Scope: quote.
Esquema de entrada
{
"type": "object",
"properties": {
"dwelling": {
"type": "string",
"description": "A `dwelling` from GET /home-profiles",
"example": "2br_apt"
},
"move_date": {
"type": "string",
"format": "date",
"description": "Lets date-based pricing apply. Optional but strongly recommended."
},
"origin": {
"type": "object",
"properties": {
"address": {
"type": "string",
"description": "Free text as typed, or as resolved by /places/resolve"
},
"unit": {
"type": "string",
"description": "Apartment or suite. A number like 2104 lets the floor be inferred."
},
"place_id": {
"type": "string",
"description": "From /places/resolve, when address autocomplete was used"
},
"dwelling": {
"type": "string",
"description": "apartment, condo, townhouse, house or storage — free text is normalised"
}
},
"description": "One end of the move, as the customer gives it."
},
"destination": {
"type": "object",
"properties": {
"address": {
"type": "string",
"description": "Free text as typed, or as resolved by /places/resolve"
},
"unit": {
"type": "string",
"description": "Apartment or suite. A number like 2104 lets the floor be inferred."
},
"place_id": {
"type": "string",
"description": "From /places/resolve, when address autocomplete was used"
},
"dwelling": {
"type": "string",
"description": "apartment, condo, townhouse, house or storage — free text is normalised"
}
},
"description": "One end of the move, as the customer gives it."
},
"distance_miles": {
"type": "number",
"description": "Only used when no route can be found from the addresses"
}
},
"required": [
"dwelling"
],
"additionalProperties": false
}🟡create_job(idempotency_key, quote_kind, dwelling, move_date, origin, ...)
Start a job (the survey container). Contact details are NOT accepted here — funnel rule. Send `Idempotency-Key` so a retry cannot create a second job. Scope: jobs:write.
Esquema de entrada
{
"type": "object",
"properties": {
"idempotency_key": {
"type": "string",
"minLength": 1,
"maxLength": 255,
"pattern": "^[\\x21-\\x7E]+$",
"description": "Any unique string per intended job (a UUID, your order id). For 24 hours, the same key with the same body returns the ORIGINAL 201 body with `Idempotent-Replayed: true` and creates nothing; the same key with a different body is a 422 `idempotency_key_reused`; the same key while the first request is still running is a 409 `idempotency_in_progress` with `Retry-After: 1`. Scoped to your account and partitioned by the calling key's kind and mode. Omit it and the request is never deduplicated."
},
"quote_kind": {
"type": "string",
"enum": [
"ballpark",
"firm"
],
"default": "firm",
"description": "What the job is for. A firm job becomes bookable once filmed."
},
"dwelling": {
"type": "string",
"description": "What the customer says their home is (any wording; stored beside what the video measures)",
"example": "2br_apt"
},
"move_date": {
"type": "string",
"format": "date",
"description": "YYYY-MM-DD"
},
"origin": {
"type": "object",
"properties": {
"address": {
"type": "string",
"description": "Free text as typed, or as resolved by /places/resolve"
},
"unit": {
"type": "string",
"description": "Apartment or suite. A number like 2104 lets the floor be inferred."
},
"place_id": {
"type": "string",
"description": "From /places/resolve, when address autocomplete was used"
},
"dwelling": {
"type": "string",
"description": "apartment, condo, townhouse, house or storage — free text is normalised"
}
},
"description": "One end of the move, as the customer gives it."
},
"destination": {
"type": "object",
"properties": {
"address": {
"type": "string",
"description": "Free text as typed, or as resolved by /places/resolve"
},
"unit": {
"type": "string",
"description": "Apartment or suite. A number like 2104 lets the floor be inferred."
},
"place_id": {
"type": "string",
"description": "From /places/resolve, when address autocomplete was used"
},
"dwelling": {
"type": "string",
"description": "apartment, condo, townhouse, house or storage — free text is normalised"
}
},
"description": "One end of the move, as the customer gives it."
}
},
"additionalProperties": false
}🟢list_jobs(limit, status, cursor)
List jobs for the tenant, newest first. Secret key only. Paged: pass `limit` (1–100, default 25), optional `status`, and `cursor` from the previous page's `next_cursor` (null on the last page; a bad cursor is a 400). Scope: jobs:read.
Esquema de entrada
{
"type": "object",
"properties": {
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 25
},
"status": {
"type": "string",
"enum": [
"created",
"uploading",
"uploaded",
"queued",
"processing",
"complete",
"quoted",
"booked_requested",
"booked",
"declined",
"error"
],
"description": "Funnel order. **created** — The job exists. Nothing uploaded yet. **uploading** — An upload URL was issued; the video is on its way. **uploaded** — A chunked upload was assembled (a single PUT skips this state). **queued** — POST /submit accepted; waiting its turn to be analysed. **processing** — The walkthrough is being analysed. Poll GET /jobs/{id}; typically 1–4 minutes. **complete** — Inventory ready. The firm quote is computed on the first read. **quoted** — A firm, bookable quote exists (set by the first GET after complete). **booked_requested** — The customer asked to book; the mover has not answered yet. **booked** — The mover confirmed the booking. **declined** — The mover turned the request down. `decline_reason` may say why. **error** — Analysis failed. `error` carries a message; upload again or contact the mover."
},
"cursor": {
"type": "string",
"description": "Opaque; from the previous response's next_cursor."
}
},
"additionalProperties": false
}🟢get_job(job_id)
Full job state: status, inventory, quote, access, terms, move_date. Scope: jobs:read.
Esquema de entrada
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job's ULID"
}
},
"required": [
"job_id"
],
"additionalProperties": false
}🟡set_contact(job_id, email, name, consent, move_date)
Store customer email + optional name + move date. Sends NOTHING — required before upload-url. Scope: jobs:write.
Esquema de entrada
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"email": {
"type": "string"
},
"name": {
"type": "string"
},
"consent": {
"type": "boolean",
"description": "Must be true. Express consent to email about this quote."
},
"move_date": {
"type": [
"string",
"null"
],
"format": "date",
"description": "Null clears; absent leaves unchanged."
}
},
"required": [
"job_id",
"email",
"consent"
],
"additionalProperties": false
}🟡create_upload_url(job_id, content_type, size, consent)
Presigned upload for the walkthrough video. Requires stored contact email. Body: { content_type, consent, size }. Send the byte size: at or above 16 MB the response carries `multipart` (an upload_id, a part_size and a presigned PUT per part) instead of `upload`, and each part is retried on its own rather than restarting the whole video. Report each part's ETag to /upload-complete. Below the threshold, or if opening a multipart upload fails, the response is a single `upload`. Scope: jobs:write.
Esquema de entrada
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"content_type": {
"type": "string",
"description": "A video/* MIME type",
"example": "video/mp4"
},
"size": {
"type": "integer",
"description": "Byte size of the file. Decides single vs multipart."
},
"consent": {
"type": "boolean",
"description": "Must be true: the customer agrees the video will be analysed and shared with the mover."
}
},
"required": [
"job_id",
"content_type",
"consent"
],
"additionalProperties": false
}🟡complete_upload(job_id, upload_id, parts)
Assemble a chunked upload. Body: { upload_id, parts: [{ part_number, etag }] }. Only after every part has been PUT; then call /submit. Scope: jobs:write.
Esquema de entrada
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"upload_id": {
"type": "string",
"description": "From the multipart ticket"
},
"parts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"part_number": {
"type": "integer",
"description": "1-based"
},
"etag": {
"type": "string",
"description": "The ETag response header of that part's PUT"
}
}
},
"description": "Every part, in any order"
}
},
"required": [
"job_id",
"upload_id",
"parts"
],
"additionalProperties": false
}🟡submit_job(job_id)
Queue the uploaded video for analysis. Poll GET /jobs/{id} for status. Scope: jobs:write.
Esquema de entrada
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
}
},
"required": [
"job_id"
],
"additionalProperties": false
}🟡hand_off_video(job_id, url, content_type, consent)
Server-side handoff: give us a public https URL of the walkthrough instead of uploading it from a browser. Secret key only. We fetch it (up to 500 MB, streamed) and the job runs exactly as an upload would — poll GET /jobs/{id}. Needs the stored contact email and consent, like /upload-url. Replaces /upload-url, /upload-complete and /submit for a caller that already holds the file. A job whose fetch failed (`error`) may be handed a new URL. Counts toward the mover's daily analysis cap (429). Scope: jobs:write.
Esquema de entrada
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"url": {
"type": "string",
"description": "Public https URL that serves the video (a signed cloud-storage link is ideal). Private hosts and http are refused."
},
"content_type": {
"type": "string",
"description": "A video/* MIME type when you know it; otherwise the host's Content-Type must be video/*",
"example": "video/quicktime"
},
"consent": {
"type": "boolean",
"description": "Must be true: the customer agrees the video will be analysed and shared with the mover."
}
},
"required": [
"job_id",
"url",
"consent"
],
"additionalProperties": false
}⚪confirm_access(job_id, origin, destination, move_date)
Confirm floors / elevator / stairs / carry distance for both ends. Scope: jobs:write.
Esquema de entrada
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"origin": {
"type": "object",
"properties": {
"floor": {
"type": "integer",
"description": "0–199"
},
"elevator": {
"type": "boolean"
},
"stairs_flights": {
"type": "integer",
"description": "0–30"
},
"parking_to_door_ft": {
"type": "integer",
"description": "0–2000"
}
},
"description": "What a customer can actually answer about one end. Only these fields are writable; inferred values are kept."
},
"destination": {
"type": "object",
"properties": {
"floor": {
"type": "integer",
"description": "0–199"
},
"elevator": {
"type": "boolean"
},
"stairs_flights": {
"type": "integer",
"description": "0–30"
},
"parking_to_door_ft": {
"type": "integer",
"description": "0–2000"
}
},
"description": "What a customer can actually answer about one end. Only these fields are writable; inferred values are kept."
},
"move_date": {
"type": [
"string",
"null"
],
"format": "date",
"description": "YYYY-MM-DD"
}
},
"required": [
"job_id"
],
"additionalProperties": false
}🟡edit_items(job_id, items)
Edit inventory (quantity, exclude, add by item_key). Response carries the re-priced quote. Scope: jobs:write.
Esquema de entrada
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"items": {
"type": "array",
"items": {
"oneOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "An existing row's id"
},
"quantity": {
"type": "integer",
"description": "0 allowed"
},
"excluded": {
"type": "boolean",
"description": "true = staying. Rows are never deleted."
},
"notes": {
"type": "string",
"description": "Up to 500 chars"
}
},
"required": [
"id"
],
"description": "Edit a row"
},
{
"type": "object",
"properties": {
"item_key": {
"type": "string",
"description": "From GET /catalog"
},
"quantity": {
"type": "integer",
"description": "Default 1"
},
"room": {
"type": "string",
"description": "Default 'Added by customer'"
},
"notes": {
"type": "string"
}
},
"required": [
"item_key"
],
"description": "Add a row (source becomes customer_added)"
}
]
},
"description": "Non-empty. Any edit clears the customer's approval and the e-signature."
}
},
"required": [
"job_id",
"items"
],
"additionalProperties": false
}⚪approve_inventory(job_id, approved, notes)
Customer sign-off on the inventory, plus free-text notes. Scope: jobs:write.
Esquema de entrada
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"approved": {
"type": "boolean",
"description": "Default true. false withdraws the sign-off."
},
"notes": {
"type": "string",
"description": "Up to 2000 chars; absent leaves unchanged"
}
},
"required": [
"job_id"
],
"additionalProperties": false
}🟢acknowledge_gaps(job_id, acknowledged)
Record the customer's answer to the capture-gap prompts (inventory.gaps on GET /jobs/{id}). Body: { acknowledged: true } stamps "nothing is missing"; false clears it. The prompts themselves are recomputed from the current inventory on every read, so fixing a gap removes its prompt without this call. Scope: jobs:write.
Esquema de entrada
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"acknowledged": {
"type": "boolean",
"description": "Default true"
}
},
"required": [
"job_id"
],
"additionalProperties": false
}🟡send_resume_link(job_id, email, name, consent)
Email the customer their resume link. Only on explicit request. Scope: jobs:write.
Esquema de entrada
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"email": {
"type": "string"
},
"name": {
"type": "string"
},
"consent": {
"type": "boolean",
"description": "Must be true"
}
},
"required": [
"job_id",
"email",
"consent"
],
"additionalProperties": false
}⚪request_booking(job_id, name, email, phone, approve, ...)
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. Scope: jobs:write.
Esquema de entrada
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"name": {
"type": "string",
"description": "The submitted name is the signature on the approval."
},
"email": {
"type": "string"
},
"phone": {
"type": "string"
},
"approve": {
"type": "boolean",
"description": "Must be true: approves the inventory as complete and this quote. Cleared if the inventory is edited afterwards."
},
"accept_terms": {
"type": "boolean",
"description": "Required (true) when the job's `terms.terms` is non-null"
}
},
"required": [
"job_id",
"name",
"email",
"approve"
],
"additionalProperties": false
}⚪report_outcome(job_id, actual_hours, actual_total_cents, actual_crew, notes)
Report actuals (hours, final cents, crew). Secret key only. Scope: outcomes.
Esquema de entrada
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"actual_hours": {
"type": "number"
},
"actual_total_cents": {
"type": "integer",
"description": "What was actually billed, in integer cents. Currency travels beside it — never assume USD."
},
"actual_crew": {
"type": "integer"
},
"notes": {
"type": "string",
"description": "Up to 1000 chars"
}
},
"required": [
"job_id"
],
"additionalProperties": false
}🟢get_cube_sheet_csv(job_id)
Itemized cube sheet as CSV (Excel-ready): rooms, volumes, weights, exclusions. Secret key only. Scope: jobs:read.
Esquema de entrada
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
}
},
"required": [
"job_id"
],
"additionalProperties": false
}🟢get_date_prices(job_id)
This job's quote priced across a ~2-week window from the mover's date rules. flat=true means every day prices the same. Scope: jobs:read.
Esquema de entrada
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
}
},
"required": [
"job_id"
],
"additionalProperties": false
}🔴set_move_date(job_id, date)
Set or clear the move date; invalidates and recomputes the quote. Refused after a booking is requested. Scope: jobs:write.
Esquema de entrada
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"date": {
"type": [
"string",
"null"
],
"format": "date",
"description": "YYYY-MM-DD, or null to clear."
}
},
"required": [
"job_id"
],
"additionalProperties": false
}⚪quote_inventory(total_cuft, items, total_weight_lbs, distance_miles, move_date, ...)
Direct pricing for agents: items or total_cuft in, firm quote out. Secret key only. Scope: quote.
Esquema de entrada
{
"type": "object",
"properties": {
"total_cuft": {
"type": "number",
"description": "Either this…"
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"item_key": {
"type": "string"
},
"quantity": {
"type": "integer"
}
}
},
"description": "…or this, priced from the catalog"
},
"total_weight_lbs": {
"type": "number",
"description": "Optional with total_cuft; derived from items otherwise"
},
"distance_miles": {
"type": "number"
},
"move_date": {
"type": "string",
"format": "date",
"description": "YYYY-MM-DD"
},
"origin": {
"type": "object",
"properties": {
"address": {
"type": "string",
"description": "Free text as typed, or as resolved by /places/resolve"
},
"unit": {
"type": "string",
"description": "Apartment or suite. A number like 2104 lets the floor be inferred."
},
"place_id": {
"type": "string",
"description": "From /places/resolve, when address autocomplete was used"
},
"dwelling": {
"type": "string",
"description": "apartment, condo, townhouse, house or storage — free text is normalised"
}
},
"description": "One end of the move, as the customer gives it."
},
"destination": {
"type": "object",
"properties": {
"address": {
"type": "string",
"description": "Free text as typed, or as resolved by /places/resolve"
},
"unit": {
"type": "string",
"description": "Apartment or suite. A number like 2104 lets the floor be inferred."
},
"place_id": {
"type": "string",
"description": "From /places/resolve, when address autocomplete was used"
},
"dwelling": {
"type": "string",
"description": "apartment, condo, townhouse, house or storage — free text is normalised"
}
},
"description": "One end of the move, as the customer gives it."
},
"rate_card_id": {
"type": "string",
"description": "A specific card of this mover's; default the active one"
}
},
"additionalProperties": false
}🟢get_availability(from, days)
Which days the mover has marked off, before a job exists. Soft: a marked day can still be requested — the mover confirms and calls first — so steer to an open day when the customer has a choice. Scope: quote.
Esquema de entrada
{
"type": "object",
"properties": {
"from": {
"type": "string",
"format": "date",
"description": "First day, YYYY-MM-DD. Default tomorrow; earlier dates clamp to tomorrow."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 90,
"default": 30,
"description": "Window length; values above 90 are capped."
}
},
"additionalProperties": false
}🟢list_keys
List the account's API keys — live and test, active and revoked. Never a raw secret. Secret key with the `keys` scope. Scope: keys.
Esquema de entrada
{
"type": "object",
"properties": {},
"additionalProperties": false
}🟡create_key(kind, mode, scopes, label)
Mint a key. The raw secret is in THIS response only. A key can never mint one broader than itself; a test key mints only test keys; a live secret key may mint the sandbox pair. Scope: keys.
Esquema de entrada
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"enum": [
"publishable",
"secret"
],
"default": "secret"
},
"mode": {
"type": "string",
"enum": [
"live",
"test"
],
"description": "Default: the calling key's mode."
},
"scopes": {
"type": [
"array",
"null"
],
"items": {
"type": "string",
"enum": [
"quote",
"jobs:read",
"jobs:write",
"outcomes",
"keys"
]
},
"description": "null (default) = everything the kind allows"
},
"label": {
"type": "string",
"description": "Up to 80 chars, e.g. 'CRM sync'"
}
},
"additionalProperties": false
}🟡rotate_key(id, grace_hours)
Replace a key with its twin (same kind, mode, scopes, label). The old key keeps working until `replaced.expires_at` so a deploy can swap values without a gap. Needs the `keys` scope even to rotate itself — rotate a scoped key with a full key or from the dashboard. A key is rotated once: rotating it again is a 409 `key_already_rotated`, and during its grace the old key cannot mint, rotate, revoke or read deliveries (403 `key_rotated`). A test key cannot rotate a live key (403 `mode_mismatch`). Scope: keys.
Esquema de entrada
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Which key. Default: the key making this call."
},
"grace_hours": {
"type": "integer",
"minimum": 0,
"maximum": 72,
"default": 24,
"description": "0 revokes the old key immediately."
}
},
"additionalProperties": false
}⚪revoke_key(id)
Revoke a key immediately. The only live publishable key is refused (409) — rotate it instead. A test key cannot revoke a live key (403 `mode_mismatch`). Scope: keys.
Esquema de entrada
{
"type": "object",
"properties": {
"id": {
"type": "string"
}
},
"required": [
"id"
],
"additionalProperties": false
}🟢list_webhook_deliveries(webhook_id, job_id, limit, before)
The delivery log: every webhook POST for 30 days, newest first, with its outcome and the exact body sent. Secret key with the `keys` scope. A key reads the deliveries of its own mode: live keys the live log, test keys the sandbox log. Scope: keys.
Esquema de entrada
{
"type": "object",
"properties": {
"webhook_id": {
"type": "string",
"description": "One endpoint only"
},
"job_id": {
"type": "string",
"description": "Everything sent about one job"
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 25
},
"before": {
"type": "string",
"format": "date-time",
"description": "The previous page's next_before"
}
},
"additionalProperties": false
}🟡replay_webhook_delivery(id)
Re-send one delivery to its endpoint: the same body byte for byte (same envelope id, so your dedupe still works) under a fresh signature, with the usual retries. Returns the replay's own delivery record. Deliveries of the other mode are not found (404). Scope: keys.
Esquema de entrada
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "A delivery id from the log"
}
},
"required": [
"id"
],
"additionalProperties": false
}🟢search_catalog(q)
Search the cube-sheet catalog (?q=sofa). Scope: quote.
Esquema de entrada
{
"type": "object",
"properties": {
"q": {
"type": "string",
"minLength": 2,
"description": "Name, synonym or key fragment"
}
},
"required": [
"q"
],
"additionalProperties": false
}🟡suggest_addresses(input, session_token)
Address autocomplete (proxied; send session_token). Scope: quote.
Esquema de entrada
{
"type": "object",
"properties": {
"input": {
"type": "string",
"description": "What the customer has typed so far"
},
"session_token": {
"type": "string",
"description": "Any opaque string, kept for the whole typing session and the resolve call"
}
},
"additionalProperties": false
}⚪resolve_address(place_id, session_token)
Resolve a suggestion to a full address (same session_token). Scope: quote.
Esquema de entrada
{
"type": "object",
"properties": {
"place_id": {
"type": "string"
},
"session_token": {
"type": "string"
}
},
"additionalProperties": false
}Comunidad
Evidencia