{"openapi":"3.1.0","info":{"title":"Upcycle Hub Agent API","version":"0.1.0","description":"Public agent surface for Upcycle Hub (Sacramento region reuse/junk-removal). Read tools (check_service_area, get_quote_estimate, find_donation_centers) need no auth. Write tools require end-user OAuth today and are marked x-status: coming_soon for agent-scoped use pending PR #146. See /api/agent/capabilities for the live, machine-checkable status of every door and capability.","contact":{"url":"https://myupcyclehub.com/connect-your-agent"}},"servers":[{"url":"https://myupcyclehub.com"}],"tags":[{"name":"agent","description":"Agent discovery and capabilities"},{"name":"mcp","description":"Model Context Protocol tool surface"},{"name":"jobs","description":"Submitting a job with no auth required — pending_deploy, not live yet (PR #149); submit via the website at /quote until then"}],"paths":{"/api/agent/capabilities":{"get":{"tags":["agent"],"operationId":"getAgentCapabilities","summary":"Get Upcycle Hub's current agent-facing capabilities (honest, live-checked statuses)","responses":{"200":{"description":"Capabilities payload","content":{"application/json":{"schema":{"type":"object"},"example":{"provider":{"name":"Upcycle Hub","type":"private_for_profit","url":"https://myupcyclehub.com","mission":"We work to keep usable goods out of the landfill through resale, reuse, and upcycling."},"service_area":{"region":"Sacramento region, CA","primary_cities":["Sacramento","Elk Grove","Folsom","Roseville","Davis","Rancho Cordova","Citrus Heights","Rocklin"],"note":"Sacramento region only — does not include Manteca or Stockton (a different market, not served).","check_tool":"check_service_area"},"doors":[{"id":"junk_removal","name":"Junk Removal","url":"/junk-removal","status":"website_only","description":"Pickup and haul for furniture, appliances, electronics, yard waste, and general junk. Items with reuse value are routed to donation or recycling before disposal. Submit today via the website at /quote (a human reviews it) — there is no agent-callable endpoint yet: POST /api/quote/guest is pending_deploy (PR #149, not merged)."},{"id":"cleanout","name":"Cleanouts","url":"/cleanouts","status":"website_only","description":"Whole-space cleanouts — garage, storage unit, rental turnover — submitted as a single job. Submit today via the website at /quote (a human reviews it) — there is no agent-callable endpoint yet: POST /api/quote/guest is pending_deploy (PR #149, not merged)."},{"id":"estate","name":"Estate Sales","url":"/estate-sales","status":"website_only","description":"Estate and household clear-out jobs submitted as a single job. A human confirms scope before anything is scheduled. Submit today via the website at /quote (a human reviews it) — there is no agent-callable endpoint yet: POST /api/quote/guest is pending_deploy (PR #149, not merged)."},{"id":"donation","name":"Donations","url":"/donations","status":"website_only","description":"Donate reusable goods to Upcycle Hub, a private for-profit company, for resale and reuse — not a tax-deductible charitable donation. For a tax-deductible drop-off, use the find_donation_centers tool to locate a nonprofit instead. Submit today via the website at /quote (a human reviews it) — there is no agent-callable endpoint yet: POST /api/quote/guest is pending_deploy (PR #149, not merged)."}],"exits":{"whole_lot_buyout":{"status":"coming_soon"},"consignment":{"status":"coming_soon"}},"agent_access":{"auth":{"status":"coming_soon","detail":"Agent-scoped identities, keys, and an audit trail land with PR #146 (not merged; its migration is not applied). Until then, the 5 write-capable MCP tools (schedule_pickup, list_my_quotes, track_job, get_diversion_report, cancel_quote) require end-user OAuth 2.1 (mcp:write) — there is no distinct agent identity yet."},"autonomy":"human_confirms_every_job","autonomy_detail":"A human at Upcycle Hub confirms every job — junk_removal, cleanout, estate, or donation — before it is scheduled; nothing is dispatched automatically. An agent can check service area and get an estimate today. Submitting a job is not yet agent-callable — POST /api/quote/guest is pending_deploy (PR #149, not merged) — until then an agent can only point a user at the website (/quote) to submit.","payments":{"status":"not_available"},"public_tools":["check_service_area","get_quote_estimate","find_donation_centers"],"auth_required_tools":["schedule_pickup","list_my_quotes","track_job","get_diversion_report","cancel_quote"],"pending_tools":[{"name":"submit_quote_request","status":"pending_deploy","description":"Submit a junk_removal, cleanout, estate, or donation job with no auth — the MCP-tool form of POST /api/quote/guest, plus an optional idempotency_key. Ships with PR #149 (a sibling lane); not registered in this branch and not available until that PR deploys.","tracking_pr":149}]},"endpoints":{"mcp_manifest":"https://myupcyclehub.com/api/mcp/manifest","mcp_tools":"https://myupcyclehub.com/api/mcp/tools","mcp_invoke":"https://myupcyclehub.com/api/mcp/invoke","mcp_health":"https://myupcyclehub.com/api/mcp/health","mcp_streamable_http":"https://myupcyclehub.com/api/mcp","guest_quote":"https://myupcyclehub.com/api/quote/guest","capabilities":"https://myupcyclehub.com/api/agent/capabilities","openapi":"https://myupcyclehub.com/openapi.json","llms_txt":"https://myupcyclehub.com/llms.txt","llms_full_txt":"https://myupcyclehub.com/llms-full.txt","well_known_mcp":"https://myupcyclehub.com/.well-known/mcp.json","well_known_agent":"https://myupcyclehub.com/.well-known/agent.json","well_known_ai_plugin":"https://myupcyclehub.com/.well-known/ai-plugin.json","connect_your_agent":"https://myupcyclehub.com/connect-your-agent","agent_terms":"https://myupcyclehub.com/agent-terms"},"contact":{"email":"support@myupcyclehub.com"},"terms_url":"https://myupcyclehub.com/agent-terms","version":"0.1.0","updated_at":"2026-11-01T01:17:54.986Z"}}}}}}},"/api/mcp":{"post":{"tags":["mcp"],"operationId":"mcpStreamableHttp","summary":"Spec-standard MCP \"Streamable HTTP\" endpoint (JSON-RPC 2.0, protocol 2025-06-18)","description":"The spec-standard MCP transport, for any MCP-compliant client (Claude, MCP Inspector, etc.) — separate from the bespoke /api/mcp/{manifest,tools,invoke,health} REST envelope below, kept for backward compatibility. Exposes only the 3 public read tools (check_service_area, get_quote_estimate, find_donation_centers) as an allowlist; no auth-required or write tools are reachable here. Body is a JSON-RPC 2.0 envelope per the MCP spec.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"JSON-RPC 2.0 request per the MCP Streamable HTTP spec."}}}},"responses":{"200":{"description":"JSON-RPC 2.0 response","content":{"application/json":{"schema":{"type":"object"}}}},"405":{"description":"GET is not implemented — this transport only responds to POST"}}}},"/api/mcp/manifest":{"get":{"tags":["mcp"],"operationId":"getMcpManifest","summary":"MCP app manifest (tool list, schemas, auth config)","responses":{"200":{"description":"Manifest","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/mcp/tools":{"get":{"tags":["mcp"],"operationId":"listMcpTools","summary":"List MCP tool definitions","responses":{"200":{"description":"Tool list","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/mcp/health":{"get":{"tags":["mcp"],"operationId":"getMcpHealth","summary":"MCP server health check","responses":{"200":{"description":"Health status","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/mcp/invoke":{"post":{"tags":["mcp"],"operationId":"invokeMcpTool","summary":"Invoke one MCP tool by name","description":"Single endpoint for every MCP tool. Body is {\"tool\": \"<name>\", \"input\": {...}}; the schema below is a discriminated union keyed on \"tool\". Public tools (check_service_area, get_quote_estimate, find_donation_centers) need no Authorization header. The other 5 tools require a Bearer OAuth token today (end-user mcp:write scope) — marked x-status: coming_soon for agent-scoped credentials pending PR #146. NOTE ON security BELOW: this operation lists [{}, oauth2] because the anonymous alternative applies ONLY to tools whose request schema has \"x-requires-auth\": false — check that field per tool (see components.schemas.*Request), not this operation-level list, before assuming a call needs no token. The live route rejects any x-requires-auth: true tool called without one (401 AUTH_REQUIRED).","security":[{},{"oauth2":["mcp:read","mcp:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/check_service_areaRequest"},{"$ref":"#/components/schemas/get_quote_estimateRequest"},{"$ref":"#/components/schemas/find_donation_centersRequest"},{"$ref":"#/components/schemas/schedule_pickupRequest"},{"$ref":"#/components/schemas/list_my_quotesRequest"},{"$ref":"#/components/schemas/track_jobRequest"},{"$ref":"#/components/schemas/get_diversion_reportRequest"},{"$ref":"#/components/schemas/cancel_quoteRequest"},{"$ref":"#/components/schemas/submit_quote_requestRequest"}],"discriminator":{"propertyName":"tool","mapping":{"check_service_area":"#/components/schemas/check_service_areaRequest","get_quote_estimate":"#/components/schemas/get_quote_estimateRequest","find_donation_centers":"#/components/schemas/find_donation_centersRequest","schedule_pickup":"#/components/schemas/schedule_pickupRequest","list_my_quotes":"#/components/schemas/list_my_quotesRequest","track_job":"#/components/schemas/track_jobRequest","get_diversion_report":"#/components/schemas/get_diversion_reportRequest","cancel_quote":"#/components/schemas/cancel_quoteRequest","submit_quote_request":"#/components/schemas/submit_quote_requestRequest"}}}}}},"responses":{"200":{"description":"Tool result (success or error envelope)","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/check_service_areaResponse"},{"$ref":"#/components/schemas/get_quote_estimateResponse"},{"$ref":"#/components/schemas/find_donation_centersResponse"},{"$ref":"#/components/schemas/schedule_pickupResponse"},{"$ref":"#/components/schemas/list_my_quotesResponse"},{"$ref":"#/components/schemas/track_jobResponse"},{"$ref":"#/components/schemas/get_diversion_reportResponse"},{"$ref":"#/components/schemas/cancel_quoteResponse"},{"$ref":"#/components/schemas/submit_quote_requestResponse"}]}}}},"401":{"description":"AUTH_REQUIRED — missing/invalid token for an auth-required tool"},"429":{"description":"RATE_LIMITED"}}}},"/api/quote/guest":{"post":{"tags":["jobs"],"operationId":"submitGuestQuote","summary":"Hand off a job with no auth required (junk_removal, cleanout, donation, or estate) — NOT YET DEPLOYED","description":"NOT DEPLOYED YET. This route does not exist in production as of this PR — it ships with PR #149 (x-status: pending_deploy below) and will 404 until that PR merges. Once live, it will be the no-auth path for an agent to submit a job on a user's behalf; a human at Upcycle Hub confirms every job before it is scheduled. Same contract will also be reachable as the MCP tool submit_quote_request (see POST /api/mcp/invoke), same pending status. Until then, submit through the website at /quote instead — see /api/agent/capabilities for live status.","x-status":"pending_deploy","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["door","name","email","zip_code","city","description","source"],"properties":{"door":{"type":"string","enum":["junk_removal","cleanout","donation","estate"]},"name":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"zip_code":{"type":"string"},"city":{"type":"string"},"address":{"type":"string"},"description":{"type":"string"},"items":{"type":"array","items":{"type":"string"}},"photos":{"type":"array","items":{"type":"string","format":"uri"}},"requested_date":{"type":"string","format":"date"},"is_asap":{"type":"boolean"},"source":{"type":"string","description":"Caller identifier, e.g. an agent/app name"}},"additionalProperties":false}}}},"responses":{"201":{"description":"Job accepted","content":{"application/json":{"schema":{"type":"object","required":["id","status","next"],"properties":{"id":{"type":"string"},"status":{"type":"string"},"next":{"type":"string"}}}}}},"400":{"description":"Validation error"}}}}},"components":{"securitySchemes":{"oauth2":{"type":"oauth2","description":"End-user OAuth 2.1. mcp:write is required for the 5 auth-required tools today; there is no separate agent-scoped credential yet (coming_soon, PR #146).","flows":{"authorizationCode":{"authorizationUrl":"https://myupcyclehub.com/api/oauth/authorize","tokenUrl":"https://myupcyclehub.com/api/oauth/token","scopes":{"mcp:read":"View your quotes, jobs, and account information","mcp:write":"Create quotes, schedule pickups, and manage your account"}}}}},"schemas":{"check_service_areaRequest":{"type":"object","required":["tool","input"],"properties":{"tool":{"const":"check_service_area"},"input":{"type":"object","required":["zip_code"],"properties":{"zip_code":{"type":"string","pattern":"^[0-9]{5}$","description":"US ZIP code (5 digits)"}},"additionalProperties":false}},"description":"Check if a ZIP code is within our junk removal service area. Returns availability, city name, and estimated response time.","x-requires-auth":false},"get_quote_estimateRequest":{"type":"object","required":["tool","input"],"properties":{"tool":{"const":"get_quote_estimate"},"input":{"type":"object","required":["item_description","zip_code"],"properties":{"item_description":{"type":"string","maxLength":500,"description":"Description of items to remove (e.g., \"old couch, broken TV, yard waste\")"},"zip_code":{"type":"string","pattern":"^[0-9]{5}$","description":"Service location ZIP code"},"include_diversion_estimate":{"type":"boolean","default":true,"description":"Include estimated donation/recycling breakdown"}},"additionalProperties":false}},"description":"Get a preliminary price estimate for junk removal based on item description and location. Does not require an account. For exact pricing, schedule a pickup.","x-requires-auth":false},"find_donation_centersRequest":{"type":"object","required":["tool","input"],"properties":{"tool":{"const":"find_donation_centers"},"input":{"type":"object","required":["zip_code"],"properties":{"zip_code":{"type":"string","pattern":"^[0-9]{5}$","description":"ZIP code to search near"},"item_category":{"type":"string","enum":["furniture","electronics","clothing","appliances","all"],"default":"all","description":"Filter by type of items you want to donate"},"radius_miles":{"type":"integer","minimum":1,"maximum":50,"default":10,"description":"Search radius in miles (applied only to geocoded locations)"}},"additionalProperties":false}},"description":"Find nearby donation centers and thrift/nonprofit locations that accept specific types of items, from our live disposal directory. Returns center details, hours, and what they accept.","x-requires-auth":false},"schedule_pickupRequest":{"type":"object","required":["tool","input"],"properties":{"tool":{"const":"schedule_pickup"},"input":{"type":"object","required":["item_description","address","preferred_date"],"properties":{"item_description":{"type":"string","minLength":10,"maxLength":1000,"description":"Detailed description of items to be removed"},"address":{"type":"object","required":["line1","city","state","zip_code"],"properties":{"line1":{"type":"string","minLength":5,"maxLength":200,"description":"Street address"},"city":{"type":"string","minLength":2,"maxLength":100},"state":{"type":"string","pattern":"^[A-Z]{2}$","description":"Two-letter state code (e.g., CA)"},"zip_code":{"type":"string","pattern":"^[0-9]{5}$","description":"Five-digit ZIP code"}},"additionalProperties":false},"preferred_date":{"type":"string","format":"date","description":"Preferred pickup date (YYYY-MM-DD)"},"time_preference":{"type":"string","enum":["morning","afternoon","evening","flexible"],"description":"Preferred time of day"},"special_instructions":{"type":"string","maxLength":500,"description":"Any special access or handling instructions"}},"additionalProperties":false}},"description":"Create a junk removal pickup request. Requires authentication. This is for physical goods removal service only - no digital purchases.","x-requires-auth":true,"x-status":"coming_soon"},"list_my_quotesRequest":{"type":"object","required":["tool","input"],"properties":{"tool":{"const":"list_my_quotes"},"input":{"type":"object","properties":{"status_filter":{"type":"string","enum":["all","pending","quoted","accepted","completed"],"description":"Filter quotes by status"},"limit":{"type":"integer","minimum":1,"maximum":20,"description":"Maximum number of quotes to return (default: 10)"}},"additionalProperties":false}},"description":"List your junk removal quote requests and their current status. Requires authentication.","x-requires-auth":true,"x-status":"coming_soon"},"track_jobRequest":{"type":"object","required":["tool","input"],"properties":{"tool":{"const":"track_job"},"input":{"type":"object","required":["job_id"],"properties":{"job_id":{"type":"string","minLength":1,"description":"The job ID to track"}},"additionalProperties":false}},"description":"Track the status of a scheduled junk removal job. Requires authentication.","x-requires-auth":true,"x-status":"coming_soon"},"get_diversion_reportRequest":{"type":"object","required":["tool","input"],"properties":{"tool":{"const":"get_diversion_report"},"input":{"type":"object","properties":{"job_id":{"type":"string","description":"Specific job ID to get report for. If not provided, returns aggregate report."},"date_range":{"type":"object","properties":{"start_date":{"type":"string","format":"date","description":"Start date for report period (YYYY-MM-DD)"},"end_date":{"type":"string","format":"date","description":"End date for report period (YYYY-MM-DD)"}},"required":["start_date","end_date"],"additionalProperties":false}},"additionalProperties":false}},"description":"Get your environmental impact and diversion report showing how your items were donated, recycled, or disposed. Requires authentication.","x-requires-auth":true,"x-status":"coming_soon"},"cancel_quoteRequest":{"type":"object","required":["tool","input"],"properties":{"tool":{"const":"cancel_quote"},"input":{"type":"object","required":["quote_id"],"properties":{"quote_id":{"type":"string","minLength":1,"description":"The quote ID to cancel"},"cancellation_reason":{"type":"string","maxLength":500,"description":"Optional reason for cancellation"}},"additionalProperties":false}},"description":"Cancel a pending quote request. Cannot cancel jobs that are already scheduled or in progress. Requires authentication.","x-requires-auth":true,"x-status":"coming_soon"},"submit_quote_requestRequest":{"type":"object","required":["tool","input"],"properties":{"tool":{"const":"submit_quote_request"},"input":{"type":"object","required":["door","name","email","zip_code","city","description","source"],"properties":{"door":{"type":"string","enum":["junk_removal","cleanout","donation","estate"]},"name":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"zip_code":{"type":"string"},"city":{"type":"string"},"address":{"type":"string"},"description":{"type":"string"},"items":{"type":"array","items":{"type":"string"}},"photos":{"type":"array","items":{"type":"string","format":"uri"}},"requested_date":{"type":"string","format":"date"},"is_asap":{"type":"boolean"},"source":{"type":"string","description":"Caller identifier, e.g. an agent/app name"},"idempotency_key":{"type":"string","description":"Optional — safely retry the same submission without creating a duplicate job."}},"additionalProperties":false}},"description":"Submit a junk_removal, cleanout, estate, or donation job with no auth — the MCP-tool form of POST /api/quote/guest, plus an optional idempotency_key. Ships with PR #149 (a sibling lane); not registered in this branch and not available until that PR deploys.","x-requires-auth":false,"x-status":"pending_deploy"},"check_service_areaResponse":{"type":"object","required":["success","metadata"],"properties":{"success":{"type":"boolean"},"data":{"type":"object","required":["in_service_area","message"],"properties":{"in_service_area":{"type":"boolean"},"city":{"type":"string"},"state":{"type":"string"},"estimated_response_time":{"type":"string"},"providers_available":{"type":"integer"},"message":{"type":"string"}}},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}},"metadata":{"type":"object","properties":{"timestamp":{"type":"string","format":"date-time"},"toolName":{"type":"string"},"executionTimeMs":{"type":"number"}}}}},"get_quote_estimateResponse":{"type":"object","required":["success","metadata"],"properties":{"success":{"type":"boolean"},"data":{"type":"object","required":["price_range","estimated_volume_cubic_feet","service_available","message","next_action"],"properties":{"price_range":{"type":"object","required":["low","high"],"properties":{"low":{"type":"number"},"high":{"type":"number"}}},"estimated_volume_cubic_feet":{"type":"number"},"service_available":{"type":"boolean"},"diversion_breakdown":{"type":"object","properties":{"donation_eligible":{"type":"array","items":{"type":"string"}},"recyclable":{"type":"array","items":{"type":"string"}},"disposal_required":{"type":"array","items":{"type":"string"}}}},"message":{"type":"string"},"next_action":{"type":"string","enum":["schedule_pickup","contact_support"]}}},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}},"metadata":{"type":"object","properties":{"timestamp":{"type":"string","format":"date-time"},"toolName":{"type":"string"},"executionTimeMs":{"type":"number"}}}}},"find_donation_centersResponse":{"type":"object","required":["success","metadata"],"properties":{"success":{"type":"boolean"},"data":{"type":"object","required":["centers","total","message"],"properties":{"centers":{"type":"array","items":{"type":"object","required":["name","address","distance_miles","accepts","hours","provides_tax_receipt"],"properties":{"name":{"type":"string"},"address":{"type":"string"},"distance_miles":{"type":["number","null"]},"accepts":{"type":"array","items":{"type":"string"}},"hours":{"type":["string","null"]},"provides_tax_receipt":{"type":"boolean"},"phone":{"type":["string","null"]},"website":{"type":["string","null"]},"notes":{"type":["string","null"]}}}},"total":{"type":"integer"},"message":{"type":"string"}}},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}},"metadata":{"type":"object","properties":{"timestamp":{"type":"string","format":"date-time"},"toolName":{"type":"string"},"executionTimeMs":{"type":"number"}}}}},"schedule_pickupResponse":{"type":"object","required":["success","metadata"],"properties":{"success":{"type":"boolean"},"data":{"type":"object","description":"Shape not yet published for this tool."},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}},"metadata":{"type":"object","properties":{"timestamp":{"type":"string","format":"date-time"},"toolName":{"type":"string"},"executionTimeMs":{"type":"number"}}}},"x-status":"coming_soon"},"list_my_quotesResponse":{"type":"object","required":["success","metadata"],"properties":{"success":{"type":"boolean"},"data":{"type":"object","description":"Shape not yet published for this tool."},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}},"metadata":{"type":"object","properties":{"timestamp":{"type":"string","format":"date-time"},"toolName":{"type":"string"},"executionTimeMs":{"type":"number"}}}},"x-status":"coming_soon"},"track_jobResponse":{"type":"object","required":["success","metadata"],"properties":{"success":{"type":"boolean"},"data":{"type":"object","description":"Shape not yet published for this tool."},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}},"metadata":{"type":"object","properties":{"timestamp":{"type":"string","format":"date-time"},"toolName":{"type":"string"},"executionTimeMs":{"type":"number"}}}},"x-status":"coming_soon"},"get_diversion_reportResponse":{"type":"object","required":["success","metadata"],"properties":{"success":{"type":"boolean"},"data":{"type":"object","description":"Shape not yet published for this tool."},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}},"metadata":{"type":"object","properties":{"timestamp":{"type":"string","format":"date-time"},"toolName":{"type":"string"},"executionTimeMs":{"type":"number"}}}},"x-status":"coming_soon"},"cancel_quoteResponse":{"type":"object","required":["success","metadata"],"properties":{"success":{"type":"boolean"},"data":{"type":"object","description":"Shape not yet published for this tool."},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}},"metadata":{"type":"object","properties":{"timestamp":{"type":"string","format":"date-time"},"toolName":{"type":"string"},"executionTimeMs":{"type":"number"}}}},"x-status":"coming_soon"},"submit_quote_requestResponse":{"type":"object","required":["success","metadata"],"properties":{"success":{"type":"boolean"},"data":{"type":"object","required":["id","status","next"],"properties":{"id":{"type":"string"},"status":{"type":"string"},"next":{"type":"string"}}},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}},"metadata":{"type":"object","properties":{"timestamp":{"type":"string","format":"date-time"},"toolName":{"type":"string"},"executionTimeMs":{"type":"number"}}}},"x-status":"pending_deploy"}}}}