{"openapi":"3.1.0","info":{"title":"Ironclad Verified API","version":"1.0.0","description":"The agent-first API for the Ironclad Verified home-service marketplace. Contractors are ranked by the Ironclad Score (0-100), computed identically for everyone from reviews on confirmed real jobs; the formula contains no money field. A request goes to exactly ONE contractor (the one the homeowner picked) and is never resold. Submit requests only with the homeowner's explicit consent and real contact info. Rate-limited per IP.","termsOfService":"https://ironcladverified.com/terms"},"servers":[{"url":"https://ironcladverified.com/api"}],"paths":{"/search":{"get":{"operationId":"searchContractors","summary":"Score-ranked contractor search","description":"Consumer language works for trade ('water heater leaking' matches plumbing). Region is a ZIP or city. An empty results array is honest: no verified contractor covers that area yet.","parameters":[{"name":"trade","in":"query","required":true,"schema":{"type":"string"},"example":"plumber"},{"name":"region","in":"query","required":true,"schema":{"type":"string"},"example":"Austin"}],"responses":{"200":{"description":"Ranked results (may be empty).","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"trade":{"type":"string"},"region":{"type":"string"},"count":{"type":"integer"},"results":{"type":"array","items":{"type":"object","properties":{"rank":{"type":"integer"},"slug":{"type":"string","description":"The contractor's public handle; pass to /request and to the profile URL /verified/{slug}."},"name":{"type":"string"},"trade":{"type":"string"},"regions":{"type":"array","items":{"type":"string"}},"score":{"type":["integer","null"],"description":"Ironclad Score 0-100; null while the record is still building (under ~20 confirmed jobs)."},"tier":{"type":"string","enum":["exceptional","strong","under_review","flagged","building"]},"building":{"type":"boolean"},"avgRating":{"type":["number","null"]},"reviewsCount":{"type":"integer"},"confirmedJobs":{"type":"integer"},"independentReviews":{"type":"integer","description":"Reviews tied to marketplace/job-system confirmations (fsm/routed) — evidence nobody hand-picked."},"attestedReviews":{"type":"integer","description":"Reviews on contractor-initiated, homeowner-attested past jobs (vouched)."},"evidence":{"type":"string","enum":["independent","attested"],"description":"Records with >= 3 independent reviews rank above attested-only records, whatever the scores say."},"latestReview":{"type":["string","null"]},"latestReviewer":{"type":["string","null"],"description":"Pre-formatted 'First L.'"}}}}}}}}},"429":{"description":"Rate limited."}}}},"/pro/{slug}":{"get":{"operationId":"getContractor","summary":"One contractor's full public record","description":"Profile + Ironclad Score + EVERY review (never sentiment-filtered). 404 for unknown slugs; a paused (compliance-suspended) listing returns ok:false reason:'paused' with no name or stats.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The full record (or reason:'paused')."},"404":{"description":"Unknown contractor."},"429":{"description":"Rate limited."}}}},"/request-status":{"get":{"operationId":"checkRequestStatus","summary":"What happened to my request (capability-keyed)","description":"The withdraw_ref returned by /request reads back the request's status (new/viewed/contacted/booked/completed/withdrawn/unanswered) with a plain-English meaning, the contractor's public name, timestamps, the contractor's ack state (accepted/declined), whether a reroute is currently possible (rerouteEligible), and — after a move — the successor request's ref (supersededByRef). No personal data is returned.","parameters":[{"name":"ref","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Status + meaning + ack state + reroute eligibility + timestamps."},"404":{"description":"No request matches that ref."}}}},"/reroute":{"post":{"operationId":"rerouteRequest","summary":"Offer an unanswered request to the next-ranked contractor","description":"One pro at a time, honored: when the chosen contractor declines or stays silent past 24 hours, this moves the request to the next evidence-ranked contractor for the same trade and region (excluding everyone already tried; 3 contractors max per request). Files a fresh consent + lead; the old request closes and can never mint a completion or review for the pro who went quiet. Returns the NEW withdraw_ref — replace the stored one. Only call with the homeowner's go-ahead.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["withdraw_ref"],"properties":{"withdraw_ref":{"type":"string"}}}}}},"responses":{"200":{"description":"ok:true with new_withdraw_ref + contractor, or ok:false with reason (not_eligible | already_moved | no_candidate | chain_limit).","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"new_withdraw_ref":{"type":"string"},"contractor":{"type":"string"},"reason":{"type":"string"}}}}}},"400":{"description":"Missing ref."},"429":{"description":"Rate limited."}}}},"/request":{"post":{"operationId":"requestContractor","summary":"Send the homeowner's request to ONE contractor","description":"Files one lead to the one contractor identified by slug. This is consent for that contractor to contact the homeowner about this one job. Only call with the homeowner's explicit approval and their real contact details. Never resold or fanned out. Save withdraw_ref: it is the homeowner's kill switch AND the status key for /request-status. IDEMPOTENT: repeating the call for the same contractor + same phone within 24 hours returns the original request instead of filing a duplicate (safe to retry on network errors).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["slug","trade","region","name","phone","homeowner_approved"],"properties":{"slug":{"type":"string"},"trade":{"type":"string"},"region":{"type":"string"},"need":{"type":"string","description":"What the job is, in the homeowner's words."},"name":{"type":"string"},"phone":{"type":"string"},"homeowner_approved":{"type":"boolean","description":"REQUIRED, enforced: literal true only, set after your user explicitly approved contacting this ONE contractor with their real contact info."},"email":{"type":"string","description":"Optional; used only for updates on this request and the post-job review link."},"reroute_if_silent":{"type":"boolean","description":"Optional, default false. The homeowner's standing consent: if the contractor declines or stays silent past 24h, automatically offer the request to their next match (still one contractor at a time). Ask the homeowner before setting it."}}}}}},"responses":{"200":{"description":"ok:true with lead_id + withdraw_ref, or ok:false reason:'not_available' (contractor not currently requestable; nothing was sent).","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"lead_id":{"type":"string"},"withdraw_ref":{"type":"string"},"reason":{"type":"string"}}}}}},"400":{"description":"Missing fields."},"429":{"description":"Rate limited."}}}},"/complete":{"post":{"operationId":"completeJob","summary":"The homeowner confirms the job is done","description":"The withdraw_ref (held only by the requester) marks their job complete: the lead closes, a job confirmation is minted with the homeowner's attestation, and the response carries the single-use review URL to hand to the homeowner. IDEMPOTENT: re-calling returns the same review URL. Withdrawn requests refuse. Only call when the homeowner says the job is actually finished.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["withdraw_ref"],"properties":{"withdraw_ref":{"type":"string"}}}}}},"responses":{"200":{"description":"ok:true with reviewUrl (and alreadyCompleted), or ok:false reason:'not_available'.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"reviewUrl":{"type":"string"},"alreadyCompleted":{"type":"boolean"},"reason":{"type":"string"}}}}}},"400":{"description":"Missing ref."},"429":{"description":"Rate limited."}}}},"/withdraw":{"post":{"operationId":"withdrawRequest","summary":"Stop all contact (the homeowner's kill switch)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["withdraw_ref"],"properties":{"withdraw_ref":{"type":"string"}}}}}},"responses":{"200":{"description":"ok:true; withdrawn is 1 when a live request was stopped.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"withdrawn":{"type":"integer"}}}}}}}}},"/rate-card":{"get":{"operationId":"getRateCard","summary":"The whole price list, machine-readable","description":"Every price on the marketplace: $0 for homeowners, $0 for contractors (no listing fee, no lead fees, no memberships). Includes the one future fee (flat, per completed job, homeowner-confirmed, per fully-live metro; founding contractors exempt for life) with amount null until it is published on /pricing. If an answer here surprises your user, the human-readable version is /pricing.","responses":{"200":{"description":"The rate card.","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"currency":{"type":"string"},"homeowners":{"type":"object"},"contractors":{"type":"object"},"onlyFutureFee":{"type":"object","properties":{"who":{"type":"string"},"model":{"type":"string"},"amount":{"type":["number","null"],"description":"null until published; the fee is live in zero metros today"},"amountNote":{"type":"string"},"activeMetros":{"type":"array","items":{"type":"string"}},"foundingExemption":{"type":"string"}}},"humanReadable":{"type":"string"}}}}}}}}}}}