Endpoints: 28,729MCP servers: 18,413Payout addresses: 2,071Paid calls: 1,533Letters: 13Defects: 1,322counted just now
teppi

Server definition

Hash
sha256:a91bb7f39c7bdb6a994e220c4cd1a4c83d0acccad9c5484efc1c5a2b26c1cb0a
What it is
What a remote MCP server returned when asked what it offers: 32 tools

The blob, as servednamed by its sha256

{ "instructions": "ReelQuoteAI US: video-based moving inventory and quoting for moving companies. Every tool is one endpoint of the public API (see the openapi resource); send the mover's API key as \"Authorization: Bearer <key>\" on this connection. Publishable keys (pk_) drive the customer funnel; secret keys (sk_) also read jobs, report outcomes and manage keys and webhooks. Test keys (pk_test_/sk_test_) run a sandbox: jobs complete instantly with a fixed inventory, nothing is billed or emailed. Funnel order: ballpark_estimate (free, no job) → create_job → set_contact → hand_off_video (a URL you hold) or create_upload_url…submit_job (a file) → get_job until status is complete/quoted → confirm_access, edit_items, set_move_date as needed → approve_inventory → request_booking. Money is always integer cents beside a currency. A failed call comes back with isError and the API's { error, message }.", "tools": [ { "description": "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.", "inputSchema": { "additionalProperties": false, "properties": { "acknowledged": { "description": "Default true", "type": "boolean" }, "job_id": { "description": "The job id (ULID)", "type": "string" } }, "required": [ "job_id" ], "type": "object" }, "name": "acknowledge_gaps", "outputSchema": null }, { "description": "Customer sign-off on the inventory, plus free-text notes. Scope: jobs:write.", "inputSchema": { "additionalProperties": false, "properties": { "approved": { "description": "Default true. false withdraws the sign-off.", "type": "boolean" }, "job_id": { "description": "The job id (ULID)", "type": "string" }, "notes": { "description": "Up to 2000 chars; absent leaves unchanged", "type": "string" } }, "required": [ "job_id" ], "type": "object" }, "name": "approve_inventory", "outputSchema": null }, { "description": "Instant estimate range from home size + addresses. Free; never bookable. Scope: quote.", "inputSchema": { "additionalProperties": false, "properties": { "destination": { "description": "One end of the move, as the customer gives it.", "properties": { "address": { "description": "Free text as typed, or as resolved by /places/resolve", "type": "string" }, "dwelling": { "description": "apartment, condo, townhouse, house or storage — free text is normalised", "type": "string" }, "place_id": { "description": "From /places/resolve, when address autocomplete was used", "type": "string" }, "unit": { "description": "Apartment or suite. A number like 2104 lets the floor be inferred.", "type": "string" } }, "type": "object" }, "distance_miles": { "description": "Only used when no route can be found from the addresses", "type": "number" }, "dwelling": { "description": "A `dwelling` from GET /home-profiles", "example": "2br_apt", "type": "string" }, "move_date": { "description": "Lets date-based pricing apply. Optional but strongly recommended.", "format": "date", "type": "string" }, "origin": { "description": "One end of the move, as the customer gives it.", "properties": { "address": { "description": "Free text as typed, or as resolved by /places/resolve", "type": "string" }, "dwelling": { "description": "apartment, condo, townhouse, house or storage — free text is normalised", "type": "string" }, "place_id": { "description": "From /places/resolve, when address autocomplete was used", "type": "string" }, "unit": { "description": "Apartment or suite. A number like 2104 lets the floor be inferred.", "type": "string" } }, "type": "object" } }, "required": [ "dwelling" ], "type": "object" }, "name": "ballpark_estimate", "outputSchema": null }, { "description": "Assemble a chunked upload. Body: { upload_id, parts: [{ part_number, etag }] }. Only after every part has been PUT; then call /submit. Scope: jobs:write.", "inputSchema": { "additionalProperties": false, "properties": { "job_id": { "description": "The job id (ULID)", "type": "string" }, "parts": { "description": "Every part, in any order", "items": { "properties": { "etag": { "description": "The ETag response header of that part's PUT", "type": "string" }, "part_number": { "description": "1-based", "type": "integer" } }, "type": "object" }, "type": "array" }, "upload_id": { "description": "From the multipart ticket", "type": "string" } }, "required": [ "job_id", "upload_id", "parts" ], "type": "object" }, "name": "complete_upload", "outputSchema": null }, { "description": "Confirm floors / elevator / stairs / carry distance for both ends. Scope: jobs:write.", "inputSchema": { "additionalProperties": false, "properties": { "destination": { "description": "What a customer can actually answer about one end. Only these fields are writable; inferred values are kept.", "properties": { "elevator": { "type": "boolean" }, "floor": { "description": "0–199", "type": "integer" }, "parking_to_door_ft": { "description": "0–2000", "type": "integer" }, "stairs_flights": { "description": "0–30", "type": "integer" } }, "type": "object" }, "job_id": { "description": "The job id (ULID)", "type": "string" }, "move_date": { "description": "YYYY-MM-DD", "format": "date", "type": [ "string", "null" ] }, "origin": { "description": "What a customer can actually answer about one end. Only these fields are writable; inferred values are kept.", "properties": { "elevator": { "type": "boolean" }, "floor": { "description": "0–199", "type": "integer" }, "parking_to_door_ft": { "description": "0–2000", "type": "integer" }, "stairs_flights": { "description": "0–30", "type": "integer" } }, "type": "object" } }, "required": [ "job_id" ], "type": "object" }, "name": "confirm_access", "outputSchema": null }, { "description": "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.", "inputSchema": { "additionalProperties": false, "properties": { "destination": { "description": "One end of the move, as the customer gives it.", "properties": { "address": { "description": "Free text as typed, or as resolved by /places/resolve", "type": "string" }, "dwelling": { "description": "apartment, condo, townhouse, house or storage — free text is normalised", "type": "string" }, "place_id": { "description": "From /places/resolve, when address autocomplete was used", "type": "string" }, "unit": { "description": "Apartment or suite. A number like 2104 lets the floor be inferred.", "type": "string" } }, "type": "object" }, "dwelling": { "description": "What the customer says their home is (any wording; stored beside what the video measures)", "example": "2br_apt", "type": "string" }, "idempotency_key": { "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.", "maxLength": 255, "minLength": 1, "pattern": "^[\\x21-\\x7E]+$", "type": "string" }, "move_date": { "description": "YYYY-MM-DD", "format": "date", "type": "string" }, "origin": { "description": "One end of the move, as the customer gives it.", "properties": { "address": { "description": "Free text as typed, or as resolved by /places/resolve", "type": "string" }, "dwelling": { "description": "apartment, condo, townhouse, house or storage — free text is normalised", "type": "string" }, "place_id": { "description": "From /places/resolve, when address autocomplete was used", "type": "string" }, "unit": { "description": "Apartment or suite. A number like 2104 lets the floor be inferred.", "type": "string" } }, "type": "object" }, "quote_kind": { "default": "firm", "description": "What the job is for. A firm job becomes bookable once filmed.", "enum": [ "ballpark", "firm" ], "type": "string" } }, "type": "object" }, "name": "create_job", "outputSchema": null }, { "description": "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.", "inputSchema": { "additionalProperties": false, "properties": { "kind": { "default": "secret", "enum": [ "publishable", "secret" ], "type": "string" }, "label": { "description": "Up to 80 chars, e.g. 'CRM sync'", "type": "string" }, "mode": { "description": "Default: the calling key's mode.", "enum": [ "live", "test" ], "type": "string" }, "scopes": { "description": "null (default) = everything the kind allows", "items": { "enum": [ "quote", "jobs:read", "jobs:write", "outcomes", "keys" ], "type": "string" }, "type": [ "array", "null" ] } }, "type": "object" }, "name": "create_key", "outputSchema": null }, { "description": "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.", "inputSchema": { "additionalProperties": false, "properties": { "consent": { "description": "Must be true: the customer agrees the video will be analysed and shared with the mover.", "type": "boolean" }, "content_type": { "description": "A video/* MIME type", "example": "video/mp4", "type": "string" }, "job_id": { "description": "The job id (ULID)", "type": "string" }, "size": { "description": "Byte size of the file. Decides single vs multipart.", "type": "integer" } }, "required": [ "job_id", "content_type", "consent" ], "type": "object" }, "name": "create_upload_url", "outputSchema": null }, { "description": "Edit inventory (quantity, exclude, add by item_key). Response carries the re-priced quote. Scope: jobs:write.", "inputSchema": { "additionalProperties": false, "properties": { "items": { "description": "Non-empty. Any edit clears the customer's approval and the e-signature.", "items": { "oneOf": [ { "description": "Edit a row", "properties": { "excluded": { "description": "true = staying. Rows are never deleted.", "type": "boolean" }, "id": { "description": "An existing row's id", "type": "string" }, "notes": { "description": "Up to 500 chars", "type": "string" }, "quantity": { "description": "0 allowed", "type": "integer" } }, "required": [ "id" ], "type": "object" }, { "description": "Add a row (source becomes customer_added)", "properties": { "item_key": { "description": "From GET /catalog", "type": "string" }, "notes": { "type": "string" }, "quantity": { "description": "Default 1", "type": "integer" }, "room": { "description": "Default 'Added by customer'", "type": "string" } }, "required": [ "item_key" ], "type": "object" } ] }, "type": "array" }, "job_id": { "description": "The job id (ULID)", "type": "string" } }, "required": [ "job_id", "items" ], "type": "object" }, "name": "edit_items", "outputSchema": null }, { "description": "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.", "inputSchema": { "additionalProperties": false, "properties": { "days": { "default": 30, "description": "Window length; values above 90 are capped.", "maximum": 90, "minimum": 1, "type": "integer" }, "from": { "description": "First day, YYYY-MM-DD. Default tomorrow; earlier dates clamp to tomorrow.", "format": "date", "type": "string" } }, "type": "object" }, "name": "get_availability", "outputSchema": null }, { "description": "Itemized cube sheet as CSV (Excel-ready): rooms, volumes, weights, exclusions. Secret key only. Scope: jobs:read.", "inputSchema": { "additionalProperties": false, "properties": { "job_id": { "description": "The job id (ULID)", "type": "string" } }, "required": [ "job_id" ], "type": "object" }, "name": "get_cube_sheet_csv", "outputSchema": null }, { "description": "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.", "inputSchema": { "additionalProperties": false, "properties": { "job_id": { "description": "The job id (ULID)", "type": "string" } }, "required": [ "job_id" ], "type": "object" }, "name": "get_date_prices", "outputSchema": null }, { "description": "Full job state: status, inventory, quote, access, terms, move_date. Scope: jobs:read.", "inputSchema": { "additionalProperties": false, "properties": { "job_id": { "description": "The job's ULID", "type": "string" } }, "required": [ "job_id" ], "type": "object" }, "name": "get_job", "outputSchema": null }, { "description": "Resolve the key to its mover (name, branding, config). Scope: quote.", "inputSchema": { "additionalProperties": false, "properties": {}, "type": "object" }, "name": "get_session", "outputSchema": null }, { "description": "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.", "inputSchema": { "additionalProperties": false, "properties": { "consent": { "description": "Must be true: the customer agrees the video will be analysed and shared with the mover.", "type": "boolean" }, "content_type": { "description": "A video/* MIME type when you know it; otherwise the host's Content-Type must be video/*", "example": "video/quicktime", "type": "string" }, "job_id": { "description": "The job id (ULID)", "type": "string" }, "url": { "description": "Public https URL that serves the video (a signed cloud-storage link is ideal). Private hosts and http are refused.", "type": "string" } }, "required": [ "job_id", "url", "consent" ], "type": "object" }, "name": "hand_off_video", "outputSchema": null }, { "description": "Home sizes for the ballpark step (2br_apt, 3br_house, …). Scope: quote.", "inputSchema": { "additionalProperties": false, "properties": {}, "type": "object" }, "name": "list_home_profiles", "outputSchema": null }, { "description": "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.", "inputSchema": { "additionalProperties": false, "properties": { "cursor": { "description": "Opaque; from the previous response's next_cursor.", "type": "string" }, "limit": { "default": 25, "maximum": 100, "minimum": 1, "type": "integer" }, "status": { "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.", "enum": [ "created", "uploading", "uploaded", "queued", "processing", "complete", "quoted", "booked_requested", "booked", "declined", "error" ], "type": "string" } }, "type": "object" }, "name": "list_jobs", "outputSchema": null }, { "description": "List the account's API keys — live and test, active and revoked. Never a raw secret. Secret key with the `keys` scope. Scope: keys.", "inputSchema": { "additionalProperties": false, "properties": {}, "type": "object" }, "name": "list_keys", "outputSchema": null }, { "description": "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.", "inputSchema": { "additionalProperties": false, "properties": { "before": { "description": "The previous page's next_before", "format": "date-time", "type": "string" }, "job_id": { "description": "Everything sent about one job", "type": "string" }, "limit": { "default": 25, "maximum": 100, "minimum": 1, "type": "integer" }, "webhook_id": { "description": "One endpoint only", "type": "string" } }, "type": "object" }, "name": "list_webhook_deliveries", "outputSchema": null }, { "description": "Direct pricing for agents: items or total_cuft in, firm quote out. Secret key only. Scope: quote.", "inputSchema": { "additionalProperties": false, "properties": { "destination": { "description": "One end of the move, as the customer gives it.", "properties": { "address": { "description": "Free text as typed, or as resolved by /places/resolve", "type": "string" }, "dwelling": { "description": "apartment, condo, townhouse, house or storage — free text is normalised", "type": "string" }, "place_id": { "description": "From /places/resolve, when address autocomplete was used", "type": "string" }, "unit": { "description": "Apartment or suite. A number like 2104 lets the floor be inferred.", "type": "string" } }, "type": "object" }, "distance_miles": { "type": "number" }, "items": { "description": "…or this, priced from the catalog", "items": { "properties": { "item_key": { "type": "string" }, "quantity": { "type": "integer" } }, "type": "object" }, "type": "array" }, "move_date": { "description": "YYYY-MM-DD", "format": "date", "type": "string" }, "origin": { "description": "One end of the move, as the customer gives it.", "properties": { "address": { "description": "Free text as typed, or as resolved by /places/resolve", "type": "string" }, "dwelling": { "description": "apartment, condo, townhouse, house or storage — free text is normalised", "type": "string" }, "place_id": { "description": "From /places/resolve, when address autocomplete was used", "type": "string" }, "unit": { "description": "Apartment or suite. A number like 2104 lets the floor be inferred.", "type": "string" } }, "type": "object" }, "rate_card_id": { "description": "A specific card of this mover's; default the active one", "type": "string" }, "total_cuft": { "description": "Either this…", "type": "number" }, "total_weight_lbs": { "description": "Optional with total_cuft; derived from items otherwise", "type": "number" } }, "type": "object" }, "name": "quote_inventory", "outputSchema": null }, { "description": "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.", "inputSchema": { "additionalProperties": false, "properties": { "id": { "description": "A delivery id from the log", "type": "string" } }, "required": [ "id" ], "type": "object" }, "name": "replay_webhook_delivery", "outputSchema": null }, { "description": "Report actuals (hours, final cents, crew). Secret key only. Scope: outcomes.", "inputSchema": { "additionalProperties": false, "properties": { "actual_crew": { "type": "integer" }, "actual_hours": { "type": "number" }, "actual_total_cents": { "description": "What was actually billed, in integer cents. Currency travels beside it — never assume USD.", "type": "integer" }, "job_id": { "description": "The job id (ULID)", "type": "string" }, "notes": { "description": "Up to 1000 chars", "type": "string" } }, "required": [ "job_id" ], "type": "object" }, "name": "report_outcome", "outputSchema": null }, { "description": "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.", "inputSchema": { "additionalProperties": false, "properties": { "accept_terms": { "description": "Required (true) when the job's `terms.terms` is non-null", "type": "boolean" }, "approve": { "description": "Must be true: approves the inventory as complete and this quote. Cleared if the inventory is edited afterwards.", "type": "boolean" }, "email": { "type": "string" }, "job_id": { "description": "The job id (ULID)", "type": "string" }, "name": { "description": "The submitted name is the signature on the approval.", "type": "string" }, "phone": { "type": "string" } }, "required": [ "job_id", "name", "email", "approve" ], "type": "object" }, "name": "request_booking", "outputSchema": null }, { "description": "Resolve a suggestion to a full address (same session_token). Scope: quote.", "inputSchema": { "additionalProperties": false, "properties": { "place_id": { "type": "string" }, "session_token": { "type": "string" } }, "type": "object" }, "name": "resolve_address", "outputSchema": null }, { "description": "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.", "inputSchema": { "additionalProperties": false, "properties": { "id": { "type": "string" } }, "required": [ "id" ], "type": "object" }, "name": "revoke_key", "outputSchema": null }, { "description": "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.", "inputSchema": { "additionalProperties": false, "properties": { "grace_hours": { "default": 24, "description": "0 revokes the old key immediately.", "maximum": 72, "minimum": 0, "type": "integer" }, "id": { "description": "Which key. Default: the key making this call.", "type": "string" } }, "type": "object" }, "name": "rotate_key", "outputSchema": null }, { "description": "Search the cube-sheet catalog (?q=sofa). Scope: quote.", "inputSchema": { "additionalProperties": false, "properties": { "q": { "description": "Name, synonym or key fragment", "minLength": 2, "type": "string" } }, "required": [ "q" ], "type": "object" }, "name": "search_catalog", "outputSchema": null }, { "description": "Email the customer their resume link. Only on explicit request. Scope: jobs:write.", "inputSchema": { "additionalProperties": false, "properties": { "consent": { "description": "Must be true", "type": "boolean" }, "email": { "type": "string" }, "job_id": { "description": "The job id (ULID)", "type": "string" }, "name": { "type": "string" } }, "required": [ "job_id", "email", "consent" ], "type": "object" }, "name": "send_resume_link", "outputSchema": null }, { "description": "Store customer email + optional name + move date. Sends NOTHING — required before upload-url. Scope: jobs:write.", "inputSchema": { "additionalProperties": false, "properties": { "consent": { "description": "Must be true. Express consent to email about this quote.", "type": "boolean" }, "email": { "type": "string" }, "job_id": { "description": "The job id (ULID)", "type": "string" }, "move_date": { "description": "Null clears; absent leaves unchanged.", "format": "date", "type": [ "string", "null" ] }, "name": { "type": "string" } }, "required": [ "job_id", "email", "consent" ], "type": "object" }, "name": "set_contact", "outputSchema": null }, { "description": "Set or clear the move date; invalidates and recomputes the quote. Refused after a booking is requested. Scope: jobs:write.", "inputSchema": { "additionalProperties": false, "properties": { "date": { "description": "YYYY-MM-DD, or null to clear.", "format": "date", "type": [ "string", "null" ] }, "job_id": { "description": "The job id (ULID)", "type": "string" } }, "required": [ "job_id" ], "type": "object" }, "name": "set_move_date", "outputSchema": null }, { "description": "Queue the uploaded video for analysis. Poll GET /jobs/{id} for status. Scope: jobs:write.", "inputSchema": { "additionalProperties": false, "properties": { "job_id": { "description": "The job id (ULID)", "type": "string" } }, "required": [ "job_id" ], "type": "object" }, "name": "submit_job", "outputSchema": null }, { "description": "Address autocomplete (proxied; send session_token). Scope: quote.", "inputSchema": { "additionalProperties": false, "properties": { "input": { "description": "What the customer has typed so far", "type": "string" }, "session_token": { "description": "Any opaque string, kept for the whole typing session and the resolve call", "type": "string" } }, "type": "object" }, "name": "suggest_addresses", "outputSchema": null } ] }
Verify it yourselfcurl -s https://api.teppi.xyz/v1/evidence/sha256:a91bb7f39c7bdb6a994e220c4cd1a4c83d0acccad9c5484efc1c5a2b26c1cb0a | sha256sum