Filmed moving surveys, itemized inventories, firm quotes and bookings for moving companies.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Bearer <ReelQuoteAI API key>, issued to moving companies from their dashboard; a test key runs the sandbox.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
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.
The job id (ULID)
Default true
This tool isn't marked read-only, so it may change something on the server or in an account you connected.
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.
| Argument | Type | Description |
|---|---|---|
| job_id* | string | The job id (ULID) |
| acknowledged | boolean | Default true |
{
"type": "object",
"required": [
"job_id"
],
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"acknowledged": {
"type": "boolean",
"description": "Default true"
}
},
"additionalProperties": false
}Customer sign-off on the inventory, plus free-text notes. Scope: jobs:write.
| Argument | Type | Description |
|---|---|---|
| notes | string | Up to 2000 chars; absent leaves unchanged |
| job_id* | string | The job id (ULID) |
| approved | boolean | Default true. false withdraws the sign-off. |
{
"type": "object",
"required": [
"job_id"
],
"properties": {
"notes": {
"type": "string",
"description": "Up to 2000 chars; absent leaves unchanged"
},
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"approved": {
"type": "boolean",
"description": "Default true. false withdraws the sign-off."
}
},
"additionalProperties": false
}Instant estimate range from home size + addresses. Free; never bookable. Scope: quote.
| Argument | Type | Description |
|---|---|---|
| origin | object | One end of the move, as the customer gives it. |
| dwelling* | string | A `dwelling` from GET /home-profiles |
| move_date | string | Lets date-based pricing apply. Optional but strongly recommended. |
| destination | object | One end of the move, as the customer gives it. |
| distance_miles | number | Only used when no route can be found from the addresses |
{
"type": "object",
"required": [
"dwelling"
],
"properties": {
"origin": {
"type": "object",
"properties": {
"unit": {
"type": "string",
"description": "Apartment or suite. A number like 2104 lets the floor be inferred."
},
"address": {
"type": "string",
"description": "Free text as typed, or as resolved by /places/resolve"
},
"dwelling": {
"type": "string",
"description": "apartment, condo, townhouse, house or storage — free text is normalised"
},
"place_id": {
"type": "string",
"description": "From /places/resolve, when address autocomplete was used"
}
},
"description": "One end of the move, as the customer gives it."
},
"dwelling": {
"type": "string",
"example": "2br_apt",
"description": "A `dwelling` from GET /home-profiles"
},
"move_date": {
"type": "string",
"format": "date",
"description": "Lets date-based pricing apply. Optional but strongly recommended."
},
"destination": {
"type": "object",
"properties": {
"unit": {
"type": "string",
"description": "Apartment or suite. A number like 2104 lets the floor be inferred."
},
"address": {
"type": "string",
"description": "Free text as typed, or as resolved by /places/resolve"
},
"dwelling": {
"type": "string",
"description": "apartment, condo, townhouse, house or storage — free text is normalised"
},
"place_id": {
"type": "string",
"description": "From /places/resolve, when address autocomplete was used"
}
},
"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"
}
},
"additionalProperties": false
}Assemble a chunked upload. Body: { upload_id, parts: [{ part_number, etag }] }. Only after every part has been PUT; then call /submit. Scope: jobs:write.
| Argument | Type | Description |
|---|---|---|
| parts* | array | Every part, in any order |
| job_id* | string | The job id (ULID) |
| upload_id* | string | From the multipart ticket |
{
"type": "object",
"required": [
"job_id",
"upload_id",
"parts"
],
"properties": {
"parts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"etag": {
"type": "string",
"description": "The ETag response header of that part's PUT"
},
"part_number": {
"type": "integer",
"description": "1-based"
}
}
},
"description": "Every part, in any order"
},
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"upload_id": {
"type": "string",
"description": "From the multipart ticket"
}
},
"additionalProperties": false
}Confirm floors / elevator / stairs / carry distance for both ends. Scope: jobs:write.
| Argument | Type | Description |
|---|---|---|
| job_id* | string | The job id (ULID) |
| origin | object | What a customer can actually answer about one end. Only these fields are writable; inferred values are kept. |
| move_date | string | null | YYYY-MM-DD |
| destination | object | What a customer can actually answer about one end. Only these fields are writable; inferred values are kept. |
{
"type": "object",
"required": [
"job_id"
],
"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."
},
"move_date": {
"type": [
"string",
"null"
],
"format": "date",
"description": "YYYY-MM-DD"
},
"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."
}
},
"additionalProperties": false
}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.
| Argument | Type | Description |
|---|---|---|
| origin | object | One end of the move, as the customer gives it. |
| dwelling | string | What the customer says their home is (any wording; stored beside what the video measures) |
| move_date | string | YYYY-MM-DD |
| quote_kind | string | What the job is for. A firm job becomes bookable once filmed. |
| destination | object | One end of the move, as the customer gives it. |
| idempotency_key | string | 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. |
{
"type": "object",
"properties": {
"origin": {
"type": "object",
"properties": {
"unit": {
"type": "string",
"description": "Apartment or suite. A number like 2104 lets the floor be inferred."
},
"address": {
"type": "string",
"description": "Free text as typed, or as resolved by /places/resolve"
},
"dwelling": {
"type": "string",
"description": "apartment, condo, townhouse, house or storage — free text is normalised"
},
"place_id": {
"type": "string",
"description": "From /places/resolve, when address autocomplete was used"
}
},
"description": "One end of the move, as the customer gives it."
},
"dwelling": {
"type": "string",
"example": "2br_apt",
"description": "What the customer says their home is (any wording; stored beside what the video measures)"
},
"move_date": {
"type": "string",
"format": "date",
"description": "YYYY-MM-DD"
},
"quote_kind": {
"enum": [
"ballpark",
"firm"
],
"type": "string",
"default": "firm",
"description": "What the job is for. A firm job becomes bookable once filmed."
},
"destination": {
"type": "object",
"properties": {
"unit": {
"type": "string",
"description": "Apartment or suite. A number like 2104 lets the floor be inferred."
},
"address": {
"type": "string",
"description": "Free text as typed, or as resolved by /places/resolve"
},
"dwelling": {
"type": "string",
"description": "apartment, condo, townhouse, house or storage — free text is normalised"
},
"place_id": {
"type": "string",
"description": "From /places/resolve, when address autocomplete was used"
}
},
"description": "One end of the move, as the customer gives it."
},
"idempotency_key": {
"type": "string",
"pattern": "^[\\x21-\\x7E]+$",
"maxLength": 255,
"minLength": 1,
"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."
}
},
"additionalProperties": false
}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.
| Argument | Type | Description |
|---|---|---|
| kind | string | |
| mode | string | Default: the calling key's mode. |
| label | string | Up to 80 chars, e.g. 'CRM sync' |
| scopes | array | null | null (default) = everything the kind allows |
{
"type": "object",
"properties": {
"kind": {
"enum": [
"publishable",
"secret"
],
"type": "string",
"default": "secret"
},
"mode": {
"enum": [
"live",
"test"
],
"type": "string",
"description": "Default: the calling key's mode."
},
"label": {
"type": "string",
"description": "Up to 80 chars, e.g. 'CRM sync'"
},
"scopes": {
"type": [
"array",
"null"
],
"items": {
"enum": [
"quote",
"jobs:read",
"jobs:write",
"outcomes",
"keys"
],
"type": "string"
},
"description": "null (default) = everything the kind allows"
}
},
"additionalProperties": false
}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.
| Argument | Type | Description |
|---|---|---|
| size | integer | Byte size of the file. Decides single vs multipart. |
| job_id* | string | The job id (ULID) |
| consent* | boolean | Must be true: the customer agrees the video will be analysed and shared with the mover. |
| content_type* | string | A video/* MIME type |
{
"type": "object",
"required": [
"job_id",
"content_type",
"consent"
],
"properties": {
"size": {
"type": "integer",
"description": "Byte size of the file. Decides single vs multipart."
},
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"consent": {
"type": "boolean",
"description": "Must be true: the customer agrees the video will be analysed and shared with the mover."
},
"content_type": {
"type": "string",
"example": "video/mp4",
"description": "A video/* MIME type"
}
},
"additionalProperties": false
}Edit inventory (quantity, exclude, add by item_key). Response carries the re-priced quote. Scope: jobs:write.
| Argument | Type | Description |
|---|---|---|
| items* | array | Non-empty. Any edit clears the customer's approval and the e-signature. |
| job_id* | string | The job id (ULID) |
{
"type": "object",
"required": [
"job_id",
"items"
],
"properties": {
"items": {
"type": "array",
"items": {
"oneOf": [
{
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "An existing row's id"
},
"notes": {
"type": "string",
"description": "Up to 500 chars"
},
"excluded": {
"type": "boolean",
"description": "true = staying. Rows are never deleted."
},
"quantity": {
"type": "integer",
"description": "0 allowed"
}
},
"description": "Edit a row"
},
{
"type": "object",
"required": [
"item_key"
],
"properties": {
"room": {
"type": "string",
"description": "Default 'Added by customer'"
},
"notes": {
"type": "string"
},
"item_key": {
"type": "string",
"description": "From GET /catalog"
},
"quantity": {
"type": "integer",
"description": "Default 1"
}
},
"description": "Add a row (source becomes customer_added)"
}
]
},
"description": "Non-empty. Any edit clears the customer's approval and the e-signature."
},
"job_id": {
"type": "string",
"description": "The job id (ULID)"
}
},
"additionalProperties": false
}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.
| Argument | Type | Description |
|---|---|---|
| days | integer | Window length; values above 90 are capped. |
| from | string | First day, YYYY-MM-DD. Default tomorrow; earlier dates clamp to tomorrow. |
{
"type": "object",
"properties": {
"days": {
"type": "integer",
"default": 30,
"maximum": 90,
"minimum": 1,
"description": "Window length; values above 90 are capped."
},
"from": {
"type": "string",
"format": "date",
"description": "First day, YYYY-MM-DD. Default tomorrow; earlier dates clamp to tomorrow."
}
},
"additionalProperties": false
}Itemized cube sheet as CSV (Excel-ready): rooms, volumes, weights, exclusions. Secret key only. Scope: jobs:read.
| Argument | Type | Description |
|---|---|---|
| job_id* | string | The job id (ULID) |
{
"type": "object",
"required": [
"job_id"
],
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
}
},
"additionalProperties": false
}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.
| Argument | Type | Description |
|---|---|---|
| job_id* | string | The job id (ULID) |
{
"type": "object",
"required": [
"job_id"
],
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
}
},
"additionalProperties": false
}Full job state: status, inventory, quote, access, terms, move_date. Scope: jobs:read.
| Argument | Type | Description |
|---|---|---|
| job_id* | string | The job's ULID |
{
"type": "object",
"required": [
"job_id"
],
"properties": {
"job_id": {
"type": "string",
"description": "The job's ULID"
}
},
"additionalProperties": false
}Resolve the key to its mover (name, branding, config). Scope: quote.
No arguments.
{
"type": "object",
"properties": {},
"additionalProperties": false
}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.
| Argument | Type | Description |
|---|---|---|
| url* | string | Public https URL that serves the video (a signed cloud-storage link is ideal). Private hosts and http are refused. |
| job_id* | string | The job id (ULID) |
| consent* | boolean | Must be true: the customer agrees the video will be analysed and shared with the mover. |
| content_type | string | A video/* MIME type when you know it; otherwise the host's Content-Type must be video/* |
{
"type": "object",
"required": [
"job_id",
"url",
"consent"
],
"properties": {
"url": {
"type": "string",
"description": "Public https URL that serves the video (a signed cloud-storage link is ideal). Private hosts and http are refused."
},
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"consent": {
"type": "boolean",
"description": "Must be true: the customer agrees the video will be analysed and shared with the mover."
},
"content_type": {
"type": "string",
"example": "video/quicktime",
"description": "A video/* MIME type when you know it; otherwise the host's Content-Type must be video/*"
}
},
"additionalProperties": false
}Home sizes for the ballpark step (2br_apt, 3br_house, …). Scope: quote.
No arguments.
{
"type": "object",
"properties": {},
"additionalProperties": false
}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.
| Argument | Type | Description |
|---|---|---|
| limit | integer | |
| cursor | string | Opaque; from the previous response's next_cursor. |
| status | string | 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. |
{
"type": "object",
"properties": {
"limit": {
"type": "integer",
"default": 25,
"maximum": 100,
"minimum": 1
},
"cursor": {
"type": "string",
"description": "Opaque; from the previous response's next_cursor."
},
"status": {
"enum": [
"created",
"uploading",
"uploaded",
"queued",
"processing",
"complete",
"quoted",
"booked_requested",
"booked",
"declined",
"error"
],
"type": "string",
"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."
}
},
"additionalProperties": false
}List the account's API keys — live and test, active and revoked. Never a raw secret. Secret key with the `keys` scope. Scope: keys.
No arguments.
{
"type": "object",
"properties": {},
"additionalProperties": false
}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.
| Argument | Type | Description |
|---|---|---|
| limit | integer | |
| before | string | The previous page's next_before |
| job_id | string | Everything sent about one job |
| webhook_id | string | One endpoint only |
{
"type": "object",
"properties": {
"limit": {
"type": "integer",
"default": 25,
"maximum": 100,
"minimum": 1
},
"before": {
"type": "string",
"format": "date-time",
"description": "The previous page's next_before"
},
"job_id": {
"type": "string",
"description": "Everything sent about one job"
},
"webhook_id": {
"type": "string",
"description": "One endpoint only"
}
},
"additionalProperties": false
}Direct pricing for agents: items or total_cuft in, firm quote out. Secret key only. Scope: quote.
| Argument | Type | Description |
|---|---|---|
| items | array | …or this, priced from the catalog |
| origin | object | One end of the move, as the customer gives it. |
| move_date | string | YYYY-MM-DD |
| total_cuft | number | Either this… |
| destination | object | One end of the move, as the customer gives it. |
| rate_card_id | string | A specific card of this mover's; default the active one |
| distance_miles | number | |
| total_weight_lbs | number | Optional with total_cuft; derived from items otherwise |
{
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"item_key": {
"type": "string"
},
"quantity": {
"type": "integer"
}
}
},
"description": "…or this, priced from the catalog"
},
"origin": {
"type": "object",
"properties": {
"unit": {
"type": "string",
"description": "Apartment or suite. A number like 2104 lets the floor be inferred."
},
"address": {
"type": "string",
"description": "Free text as typed, or as resolved by /places/resolve"
},
"dwelling": {
"type": "string",
"description": "apartment, condo, townhouse, house or storage — free text is normalised"
},
"place_id": {
"type": "string",
"description": "From /places/resolve, when address autocomplete was used"
}
},
"description": "One end of the move, as the customer gives it."
},
"move_date": {
"type": "string",
"format": "date",
"description": "YYYY-MM-DD"
},
"total_cuft": {
"type": "number",
"description": "Either this…"
},
"destination": {
"type": "object",
"properties": {
"unit": {
"type": "string",
"description": "Apartment or suite. A number like 2104 lets the floor be inferred."
},
"address": {
"type": "string",
"description": "Free text as typed, or as resolved by /places/resolve"
},
"dwelling": {
"type": "string",
"description": "apartment, condo, townhouse, house or storage — free text is normalised"
},
"place_id": {
"type": "string",
"description": "From /places/resolve, when address autocomplete was used"
}
},
"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"
},
"distance_miles": {
"type": "number"
},
"total_weight_lbs": {
"type": "number",
"description": "Optional with total_cuft; derived from items otherwise"
}
},
"additionalProperties": false
}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.
| Argument | Type | Description |
|---|---|---|
| id* | string | A delivery id from the log |
{
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "A delivery id from the log"
}
},
"additionalProperties": false
}Report actuals (hours, final cents, crew). Secret key only. Scope: outcomes.
| Argument | Type | Description |
|---|---|---|
| notes | string | Up to 1000 chars |
| job_id* | string | The job id (ULID) |
| actual_crew | integer | |
| actual_hours | number | |
| actual_total_cents | integer | What was actually billed, in integer cents. Currency travels beside it — never assume USD. |
{
"type": "object",
"required": [
"job_id"
],
"properties": {
"notes": {
"type": "string",
"description": "Up to 1000 chars"
},
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"actual_crew": {
"type": "integer"
},
"actual_hours": {
"type": "number"
},
"actual_total_cents": {
"type": "integer",
"description": "What was actually billed, in integer cents. Currency travels beside it — never assume USD."
}
},
"additionalProperties": false
}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.
| Argument | Type | Description |
|---|---|---|
| name* | string | The submitted name is the signature on the approval. |
| email* | string | |
| phone | string | |
| job_id* | string | The job id (ULID) |
| approve* | boolean | 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 |
{
"type": "object",
"required": [
"job_id",
"name",
"email",
"approve"
],
"properties": {
"name": {
"type": "string",
"description": "The submitted name is the signature on the approval."
},
"email": {
"type": "string"
},
"phone": {
"type": "string"
},
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"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"
}
},
"additionalProperties": false
}Resolve a suggestion to a full address (same session_token). Scope: quote.
| Argument | Type | Description |
|---|---|---|
| place_id | string | |
| session_token | string |
{
"type": "object",
"properties": {
"place_id": {
"type": "string"
},
"session_token": {
"type": "string"
}
},
"additionalProperties": false
}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.
| Argument | Type | Description |
|---|---|---|
| id* | string |
{
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string"
}
},
"additionalProperties": false
}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.
| Argument | Type | Description |
|---|---|---|
| id | string | Which key. Default: the key making this call. |
| grace_hours | integer | 0 revokes the old key immediately. |
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Which key. Default: the key making this call."
},
"grace_hours": {
"type": "integer",
"default": 24,
"maximum": 72,
"minimum": 0,
"description": "0 revokes the old key immediately."
}
},
"additionalProperties": false
}Search the cube-sheet catalog (?q=sofa). Scope: quote.
| Argument | Type | Description |
|---|---|---|
| q* | string | Name, synonym or key fragment |
{
"type": "object",
"required": [
"q"
],
"properties": {
"q": {
"type": "string",
"minLength": 2,
"description": "Name, synonym or key fragment"
}
},
"additionalProperties": false
}Email the customer their resume link. Only on explicit request. Scope: jobs:write.
| Argument | Type | Description |
|---|---|---|
| name | string | |
| email* | string | |
| job_id* | string | The job id (ULID) |
| consent* | boolean | Must be true |
{
"type": "object",
"required": [
"job_id",
"email",
"consent"
],
"properties": {
"name": {
"type": "string"
},
"email": {
"type": "string"
},
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"consent": {
"type": "boolean",
"description": "Must be true"
}
},
"additionalProperties": false
}Store customer email + optional name + move date. Sends NOTHING — required before upload-url. Scope: jobs:write.
| Argument | Type | Description |
|---|---|---|
| name | string | |
| email* | string | |
| job_id* | string | The job id (ULID) |
| consent* | boolean | Must be true. Express consent to email about this quote. |
| move_date | string | null | Null clears; absent leaves unchanged. |
{
"type": "object",
"required": [
"job_id",
"email",
"consent"
],
"properties": {
"name": {
"type": "string"
},
"email": {
"type": "string"
},
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"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."
}
},
"additionalProperties": false
}Set or clear the move date; invalidates and recomputes the quote. Refused after a booking is requested. Scope: jobs:write.
| Argument | Type | Description |
|---|---|---|
| date | string | null | YYYY-MM-DD, or null to clear. |
| job_id* | string | The job id (ULID) |
{
"type": "object",
"required": [
"job_id"
],
"properties": {
"date": {
"type": [
"string",
"null"
],
"format": "date",
"description": "YYYY-MM-DD, or null to clear."
},
"job_id": {
"type": "string",
"description": "The job id (ULID)"
}
},
"additionalProperties": false
}Queue the uploaded video for analysis. Poll GET /jobs/{id} for status. Scope: jobs:write.
| Argument | Type | Description |
|---|---|---|
| job_id* | string | The job id (ULID) |
{
"type": "object",
"required": [
"job_id"
],
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
}
},
"additionalProperties": false
}Address autocomplete (proxied; send session_token). Scope: quote.
| Argument | Type | Description |
|---|---|---|
| input | string | What the customer has typed so far |
| session_token | string | Any opaque string, kept for the whole typing session and the resolve call |
{
"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
}