{"openapi":"3.0.3","info":{"title":"SecondAppraisal Agent Intake API","version":"1.0.0","description":"Let an AI agent submit its owner's vehicle total-loss situation to SecondAppraisal — from a bare phone number up to VIN, odometer, loss date, the insurer's offer, and the insurer's valuation report.\n\nWhat happens after you POST: we create a consultation on our team's board immediately, send the customer a welcome email/text with a one-tap confirm link (unless you set `consent.notify_customer: false` and relay `customer_message` yourself), and a person on our team calls the customer.\n\nContact policy, stated to the customer verbatim: \"We will attempt to call you to confirm that you want our free appraisal consultation and to confirm this request came from you or your representative/agent.\" We never place automated or AI calls for that verification.\n\nMinimum input: one of `customer.email` / `customer.phone`, plus your `agent` self-identification and a `consent.attestation`. Everything else is optional and improves the `preliminary` market read we return.\n\nIdempotency: send an `Idempotency-Key` header; a replay returns the original receipt with `idempotent_replay: true`.\n\nRegistered agents (free): POST /register with an operator email and acceptance of the Agent API Agreement, enter the emailed code at /register/verify, and send the returned `sa_agent_` key as `Authorization: Bearer …` on every call. Registration unlocks the preliminary on every submission, `consent.mode: warm_handoff` (priority callback), 6× rate limits, and signed webhooks.","contact":{"name":"SecondAppraisal","url":"https://secondappraisal.com/for-agents"},"termsOfService":"https://secondappraisal.com/terms"},"servers":[{"url":"https://secondappraisal.com"}],"tags":[{"name":"intakes","description":"Submit and follow a customer's intake."},{"name":"registration","description":"Optional: register your agent for a free API key (Phase B)."},{"name":"account","description":"Registered agents: your record, key rotation, webhook."}],"paths":{"/api/agent/v1/intakes":{"post":{"tags":["intakes"],"operationId":"submitTotalLossIntake","summary":"Submit a vehicle owner's total-loss situation","description":"Minimum: one of customer.phone / customer.email, your `agent` self-identification, and a `consent.attestation`. Returns a receipt with the customer's confirmation link, a relayable `customer_message`, the contact policy, and an optional preliminary market read. PRICING (#5132): the consultation is free. The receipt's `pricing` block lists the two paid services in cents — Appraisal Research (list $199; paid up front as a hold captured at delivery; no guarantee) and Appraisal Research + Negotiation (list $497; $472 with the $25 discount granted to customers agents submit; a card HOLD captured only after the settlement improves by at least $1,000, otherwise nothing is charged). The $25 applies to representation only — research stays $199 and the $25 carries to an upgrade — and is one discount per customer from any source. Agents may quote `pricing` verbatim. Payment is never taken through this API: the `checkout` block gives the customer-side pre-authorization pages once the customer has confirmed; the customer accepts our terms and pre-authorizes their own card there.","security":[{},{"agentKey":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Any unique string per logical submission. Replays return the original receipt."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentIntakeRequest"}}}},"responses":{"200":{"description":"Idempotent replay (same key, same body), a neutral `duplicate` answer (this contact already came through recently — no link, no poll token; the first submission's receipt is the customer's path in), or a neutral `not_eligible` answer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentIntakeReceipt"}}}},"201":{"description":"Created. `poll_token` is returned ONLY here — store it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentIntakeReceipt"}}}},"400":{"description":"Validation failed. `issues` names each field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}},"404":{"description":"The program is not accepting submissions right now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}},"409":{"description":"`Idempotency-Key` reused with a different body. Send a new key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}},"429":{"description":"Rate limited. Honour `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}}}}},"/api/agent/v1/intakes/{intake_id}":{"get":{"tags":["intakes"],"operationId":"getIntakeStatus","summary":"Follow an intake","security":[{"pollToken":[]}],"parameters":[{"name":"intake_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Current receipt (no `poll_token`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentIntakeReceipt"}}}},"401":{"description":"Missing or wrong poll token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}},"404":{"description":"Unknown intake.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}}}}},"/api/agent/v1/intakes/{intake_id}/documents":{"post":{"tags":["intakes"],"operationId":"uploadValuationReport","summary":"Attach the insurer's valuation report (PDF)","description":"multipart/form-data with a single `file` field (PDF, up to 15 MB). We parse it synchronously (vehicle, mileage, offer, insurer, loss date) and refresh the preliminary. Allow up to 120 seconds.","security":[{"pollToken":[]}],"parameters":[{"name":"intake_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"200":{"description":"Updated receipt.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentIntakeReceipt"}}}},"400":{"description":"Not a PDF, too large, or unreadable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}},"401":{"description":"Missing or wrong poll token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}},"404":{"description":"Unknown intake.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}},"409":{"description":"The intake has no consultation to attach to yet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}}}}},"/api/agent/v1/agreement":{"get":{"tags":["registration"],"operationId":"getAgentAgreement","summary":"The current Agent API Agreement (version, hash, markdown)","responses":{"200":{"description":"Read `version` and send it back as `agreement.version` on registration.","content":{"application/json":{"schema":{"type":"object","required":["version","hash","url","markdown"],"properties":{"version":{"type":"string"},"hash":{"type":"string"},"url":{"type":"string"},"markdown":{"type":"string"}}}}}}}}},"/api/agent/v1/register":{"post":{"tags":["registration"],"operationId":"registerAgent","summary":"Register an agent (operator email verification)","description":"Creates a pending registration and emails the operator a 6-digit code (30-minute lifetime). No key is issued until POST /register/verify. Limited to 5 calls/minute per IP and 3 pending registrations per operator email.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentRegistrationRequest"}}}},"responses":{"202":{"description":"Pending; code emailed.","content":{"application/json":{"schema":{"type":"object","required":["registration_id","status","operator_email_masked","code_expires_at","next_step","agreement"],"properties":{"registration_id":{"type":"string"},"status":{"type":"string","enum":["pending_verification"]},"operator_email_masked":{"type":"string"},"code_expires_at":{"type":"string","format":"date-time"},"next_step":{"type":"string"},"email_delivery_error":{"type":"string"},"agreement":{"type":"object","properties":{"version":{"type":"string"},"url":{"type":"string"}}}}}}}},"400":{"description":"Validation failed; see `issues`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}},"404":{"description":"Registration is not open.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}},"409":{"description":"Wrong agreement version; the current one is in `current_version`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}},"429":{"description":"Rate limited, or too many pending registrations for that email.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}}}}},"/api/agent/v1/register/verify":{"post":{"tags":["registration"],"operationId":"verifyAgentRegistration","summary":"Enter the emailed code; receive the API key (once)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentVerifyRequest"}}}},"responses":{"201":{"description":"Active. Store `api_key`; it is never shown again (rotate instead).","content":{"application/json":{"schema":{"type":"object","required":["principal_id","status","api_key","key_prefix","agreement","docs_url"],"properties":{"principal_id":{"type":"string"},"status":{"type":"string","enum":["active"]},"api_key":{"type":"string","description":"`sa_agent_` + 32 chars. Send as `Authorization: Bearer <api_key>`."},"key_prefix":{"type":"string"},"agreement":{"type":"object","properties":{"version":{"type":"string"},"hash":{"type":"string"}}},"docs_url":{"type":"string"}}}}}},"400":{"description":"Wrong code (`attempts_left` in the body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}},"404":{"description":"Unknown registration id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}},"409":{"description":"Already verified.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}},"410":{"description":"Code expired; register again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}},"429":{"description":"Too many wrong codes; register again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}}}}},"/api/agent/v1/me":{"get":{"tags":["account"],"operationId":"getAgentMe","summary":"Your registration: status, agreement, webhook, key prefixes, limits","security":[{"agentKey":[]}],"responses":{"200":{"description":"Your record. Never includes a key or the webhook secret.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Missing, unknown, revoked, suspended, or unverified key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}}}}},"/api/agent/v1/me/rotate-key":{"post":{"tags":["account"],"operationId":"rotateAgentKey","summary":"Mint a new key and revoke every other one","security":[{"agentKey":[]}],"responses":{"201":{"description":"The new key, shown once.","content":{"application/json":{"schema":{"type":"object","required":["api_key","key_prefix"],"properties":{"api_key":{"type":"string"},"key_prefix":{"type":"string"},"revoked_previous":{"type":"boolean"}}}}}},"401":{"description":"Missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}}}}},"/api/agent/v1/me/webhook":{"put":{"tags":["account"],"operationId":"setAgentWebhook","summary":"Set the webhook URL (https); returns the signing secret once","description":"Events: intake.received, intake.welcome_sent, intake.contact_attempted, intake.customer_confirmed, intake.consultation_active, intake.declined, intake.expired. Deliveries carry status only — never customer contact details — and are signed `X-SecondAppraisal-Signature: t=<unix>,v1=<hex hmac-sha256(secret, \"<t>.<body>\")>`.","security":[{"agentKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentWebhookRequest"}}}},"responses":{"200":{"description":"Set. Store `secret`; it is never shown again.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Not an https URL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}},"401":{"description":"Missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}}}},"delete":{"tags":["account"],"operationId":"clearAgentWebhook","summary":"Remove the webhook","security":[{"agentKey":[]}],"responses":{"200":{"description":"Cleared."},"401":{"description":"Missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentApiError"}}}}}}}},"components":{"securitySchemes":{"pollToken":{"type":"http","scheme":"bearer","description":"The `poll_token` from the 201 response — the only credential an anonymous caller has for the read/upload routes."},"agentKey":{"type":"http","scheme":"bearer","description":"A registered agent's `sa_agent_…` API key (POST /register → /register/verify). Optional on POST /intakes, where it unlocks the preliminary, warm_handoff, and 6× limits; required on the account routes."}},"schemas":{"AgentIntakeRequest":{"type":"object","properties":{"agent":{"type":"object","properties":{"name":{"allOf":[{"type":"string","maxLength":120},{"type":"string","minLength":1,"maxLength":120}],"description":"Human-readable name of the agent or product submitting on the customer's behalf (e.g. \"OpenClaw\", \"Acme Claims Copilot\"). Shown to the customer."},"platform":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{0,59}$","description":"Platform slug for attribution. Known values: openclaw, chatgpt, claude, gemini, copilot, perplexity, custom. Any lowercase slug is accepted."},"version":{"type":"string","minLength":1,"maxLength":60,"description":"Agent / integration version string."},"operator_url":{"type":"string","format":"uri","maxLength":300,"description":"Homepage or documentation URL of the agent operator."}},"required":["name","platform"],"additionalProperties":false,"description":"Who is calling. Required — an intake without self-identification is refused."},"customer":{"type":"object","properties":{"first_name":{"allOf":[{"type":"string","maxLength":80},{"type":"string","minLength":1,"maxLength":80}]},"last_name":{"allOf":[{"type":"string","maxLength":80},{"type":"string","minLength":1,"maxLength":80}]},"email":{"type":"string","format":"email","maxLength":254,"description":"Customer email address."},"phone":{"allOf":[{"type":"string"},{"type":"string","pattern":"^[2-9]\\d{9}$"}],"description":"US mobile or landline number. Punctuation and a leading +1 are tolerated."}},"additionalProperties":false,"description":"The vehicle owner. At least one of email / phone is required; everything else is optional."},"vehicle":{"type":"object","properties":{"vin":{"allOf":[{"type":"string"},{"type":"string","minLength":11,"maxLength":17}],"description":"Vehicle Identification Number. A full 17-character VIN lets us decode year/make/model/trim."},"plate":{"type":"string","minLength":1,"maxLength":12,"description":"License plate. Requires plate_state to resolve a VIN."},"plate_state":{"type":"string","minLength":2,"maxLength":2,"description":"Two-letter US state code, e.g. TX."},"year":{"type":"integer","minimum":1900,"maximum":2100},"make":{"allOf":[{"type":"string","maxLength":60},{"type":"string","minLength":1,"maxLength":60}]},"model":{"allOf":[{"type":"string","maxLength":80},{"type":"string","minLength":1,"maxLength":80}]},"trim":{"allOf":[{"type":"string","maxLength":80},{"type":"string","minLength":1,"maxLength":80}]},"odometer":{"type":"integer","minimum":0,"maximum":2000000,"description":"Odometer reading in miles at the time of loss."}},"additionalProperties":false,"description":"The vehicle. A VIN (or plate + plate_state) is best; year/make/model works too."},"loss":{"type":"object","properties":{"date_of_loss":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Calendar date, YYYY-MM-DD."},"loss_state":{"type":"string","minLength":2,"maxLength":2,"description":"State where the loss occurred / the policy is written."},"zip":{"type":"string","pattern":"^\\d{5}(-\\d{4})?$","description":"US ZIP code (5 or 5+4). Used to search comparable vehicles near the customer."},"insurance_path":{"type":"string","enum":["own","at-fault","unknown","none"],"description":"Whose insurance is handling it: own (customer's carrier), at-fault (the other driver's carrier), unknown, or none (no insurance claim — valuation only)."}},"additionalProperties":false,"description":"When and where the loss happened."},"offer":{"type":"object","properties":{"insurer":{"allOf":[{"type":"string","maxLength":120},{"type":"string","minLength":1,"maxLength":120}],"description":"Insurance company handling the total loss."},"claim_number":{"type":"string","minLength":1,"maxLength":60},"acv_offer_cents":{"type":"integer","minimum":0,"maximum":100000000,"description":"Insurer's Actual Cash Value offer in cents. `acv_offer` (dollars) is also accepted."},"acv_offer":{"type":"number","minimum":0,"maximum":1000000,"description":"Insurer's Actual Cash Value offer in dollars. Converted to cents server-side."}},"additionalProperties":false,"description":"The insurer's position so far, if any."},"consent":{"type":"object","properties":{"mode":{"type":"string","enum":["customer_directed","warm_handoff"],"default":"customer_directed","description":"customer_directed: the customer asked their agent to submit this. warm_handoff: a registered agent attests the customer asked to be contacted now (priority callback; requires an API key)."},"attestation":{"type":"string","minLength":20,"maxLength":1000,"description":"The agent's attestation, in its own words, that the customer asked for this submission and knows SecondAppraisal will email/text and call to confirm. Stored verbatim on the intake."},"attestor_name":{"allOf":[{"type":"string","maxLength":120},{"type":"string","minLength":1,"maxLength":120}],"description":"Who is attesting (the agent, or the person operating it)."},"notify_customer":{"type":"boolean","default":true,"description":"true (default): we send the customer our welcome email/text with the confirm link. false: you relay `customer_message` and `customer_confirmation_url` yourself and we send nothing until they confirm or we call."}},"required":["attestation"],"additionalProperties":false,"description":"Consent basis. Attestation wording version 2026-09-16.1."},"external_ref":{"type":"string","minLength":1,"maxLength":120,"description":"Your reference id; echoed in every response."}},"required":["agent","customer","consent"],"additionalProperties":false,"description":"Submit a vehicle owner's total-loss situation on their behalf."},"AgentIntakeReceipt":{"type":"object","properties":{"intake_id":{"type":"string"},"status":{"type":"string","enum":["received","welcome_sent","contact_attempted","customer_confirmed","consultation_active","declined","not_eligible","duplicate","expired"]},"idempotent_replay":{"type":"boolean"},"external_ref":{"type":"string","nullable":true},"customer_confirmation_url":{"type":"string","nullable":true,"description":"Give this to the customer. One tap confirms they want our help and records their consent."},"customer_message":{"type":"string","nullable":true,"description":"Ready-to-relay text for the customer. Includes the confirm link and the verification-call notice."},"contact_policy":{"type":"object","properties":{"will_email":{"type":"boolean"},"will_text":{"type":"boolean"},"will_call":{"type":"boolean","description":"true on every accepted intake: a person on our team calls to verify. false only on not_eligible."},"call_purpose":{"type":"string","enum":["confirm the request came from the customer or their representative/agent and that they want the free appraisal consultation"]},"automated_calls":{"type":"boolean","enum":[false],"description":"We never place automated or AI calls for this verification."}},"required":["will_email","will_text","will_call","call_purpose","automated_calls"],"additionalProperties":false,"description":"Exactly what SecondAppraisal will do with the contact details you provided."},"agent_tier":{"type":"string","enum":["anonymous","registered"],"description":"registered when the call carried a valid sa_agent_ API key (see POST /register)."},"pricing":{"type":"object","properties":{"currency":{"type":"string","enum":["USD"]},"research":{"type":"object","properties":{"list_cents":{"type":"integer"},"your_price_cents":{"type":"integer"}},"required":["list_cents","your_price_cents"],"additionalProperties":false,"description":"Appraisal Research alone: a written valuation the customer can use themselves. Paid up front (a hold captured at delivery); no guarantee."},"representation":{"type":"object","properties":{"list_cents":{"type":"integer"},"your_price_cents":{"type":"integer"}},"required":["list_cents","your_price_cents"],"additionalProperties":false,"description":"Appraisal Research + Negotiation: we act as the customer's appointed appraiser. A card HOLD is placed; it is captured only after the settlement improves by at least $1,000 (the guarantee) — otherwise nothing is charged."},"discount":{"type":"object","properties":{"applied":{"type":"boolean"},"amount_cents":{"type":"integer"},"applies_to":{"type":"string","enum":["representation"]},"note":{"type":"string"}},"required":["applied","amount_cents","applies_to","note"],"additionalProperties":false,"description":"The one $25 discount. Applies to representation only; research stays at list price and the $25 carries to an upgrade."}},"required":["currency","research","representation","discount"],"additionalProperties":false,"description":"What the customer would pay, in cents. Agents may quote these figures verbatim. Payment is always taken from the customer, on our site, with their own card and their own click — never through this API."},"checkout":{"type":"object","properties":{"model":{"type":"string","enum":["pre-authorization"]},"research_url":{"type":"string","nullable":true},"representation_url":{"type":"string","nullable":true},"note":{"type":"string"}},"required":["model","research_url","representation_url","note"],"additionalProperties":false,"description":"Where the CUSTOMER pre-authorizes, once they have confirmed and our free consultation is delivered. Null until then — hand the customer the confirmation link first."},"priority_callback":{"type":"boolean","description":"true when consent.mode = warm_handoff was honoured: a priority callback is queued for a person on our team."},"preliminary":{"type":"object","properties":{"vehicle_label":{"type":"string"},"coverage":{"type":"string","enum":["good","thin","none"],"description":"How much comparable inventory we found."},"sample_size":{"type":"integer"},"market":{"type":"object","properties":{"low":{"type":"integer","nullable":true},"mid":{"type":"integer","nullable":true},"high":{"type":"integer","nullable":true}},"required":["low","mid","high"],"additionalProperties":false,"description":"Comparable listing prices in whole dollars (10th percentile, median, 90th percentile)."},"adjusted_mid":{"type":"integer","nullable":true,"description":"Median adjusted for odometer when provided."},"adjusted_range":{"type":"object","properties":{"low":{"type":"integer"},"high":{"type":"integer"}},"required":["low","high"],"additionalProperties":false,"nullable":true},"gap_vs_offer_cents":{"type":"integer","nullable":true,"description":"adjusted_mid minus the insurer's offer, in cents. Positive means the offer looks low."},"snapshot_id":{"type":"string","nullable":true},"disclaimer":{"type":"string"}},"required":["vehicle_label","coverage","sample_size","market","adjusted_mid","adjusted_range","gap_vs_offer_cents","snapshot_id","disclaimer"],"additionalProperties":false,"description":"An early market read, when we had enough vehicle + location detail to search. Not an appraisal.","nullable":true},"missing_fields":{"type":"array","items":{"type":"string"},"description":"Fields that would sharpen the preliminary or speed the consultation (e.g. vehicle.vin, loss.zip)."},"poll_url":{"type":"string","description":"GET here with `Authorization: Bearer <poll_token>` to follow status."},"poll_token":{"type":"string","description":"Returned once, on creation. Store it; it is the only credential for poll_url without an API key."},"docs_url":{"type":"string"}},"required":["intake_id","status","external_ref","customer_confirmation_url","customer_message","contact_policy","agent_tier","pricing","checkout","priority_callback","preliminary","missing_fields","poll_url","docs_url"],"additionalProperties":false},"AgentApiError":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["invalid_request","not_found","unauthorized","rate_limited","program_disabled","idempotency_conflict","internal"]},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"],"additionalProperties":false},"description":"Field-level validation problems (400 only)."}},"required":["error"],"additionalProperties":false},"AgentRegistrationRequest":{"type":"object","properties":{"agent":{"type":"object","properties":{"name":{"type":"string","minLength":2,"maxLength":80,"description":"Product / agent name, e.g. OpenClaw."},"platform":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{1,39}$","description":"Platform slug — the same value you send as agent.platform on intakes."},"website":{"type":"string","format":"uri","maxLength":200},"description":{"type":"string","maxLength":600,"description":"What your agent does and who uses it."}},"required":["name","platform"],"additionalProperties":false},"operator":{"type":"object","properties":{"name":{"type":"string","minLength":2,"maxLength":120},"email":{"type":"string","format":"email","maxLength":200,"description":"A working mailbox; we email a 6-digit code here."}},"required":["name","email"],"additionalProperties":false},"agreement":{"type":"object","properties":{"accepted":{"type":"boolean","enum":[true],"description":"You accept the Agent API Agreement at /for-agents/agreement."},"version":{"type":"string","description":"Current version: 2026-09-17."}},"required":["accepted","version"],"additionalProperties":false}},"required":["agent","operator","agreement"],"additionalProperties":false},"AgentVerifyRequest":{"type":"object","properties":{"registration_id":{"type":"string","minLength":1},"code":{"type":"string","pattern":"^\\d{6}$"}},"required":["registration_id","code"],"additionalProperties":false},"AgentWebhookRequest":{"type":"object","properties":{"url":{"type":"string","format":"uri","maxLength":500}},"required":["url"],"additionalProperties":false}}}}