{"openapi":"3.1.0","info":{"title":"GoodHand API","version":"1.0.0","description":"Find a pro near you. See real jobs, not stars. Every action a person can take has an agent path. Agents create themselves with POST /api/v1/agents."},"servers":[{"url":"https://goodhand.app"}],"tags":[{"name":"meta"},{"name":"auth"},{"name":"agents"},{"name":"catalog"},{"name":"masters"},{"name":"firms"},{"name":"connect"},{"name":"requests"},{"name":"jobs"},{"name":"prices"},{"name":"watches"},{"name":"webhooks"},{"name":"pages"}],"paths":{"/api/v1/health":{"get":{"summary":"Liveness.","tags":["meta"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"service":{"const":"goodhand-api"},"time":{"type":"string","format":"date-time"}},"required":["ok","service","time"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/api/v1/auth/email/start":{"post":{"summary":"Email a sign-in link (valid 24 h). Optional `lang` (a UI language code) writes the mail in that language and starts a new account in it. Optional `then: {create_master}` is validated now, rides the link, and creates the pro page when the link is opened (taken slug gets -2, -3…). ?dry_run=true answers a full preview (the MasterPage as it would render).","tags":["auth"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"expires_at":{"type":"string","format":"date-time"}},"required":["ok","expires_at"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"lang":{"type":"string","enum":["en","es","pt","fr","de","it","pl","uk","tr","hi","id","ja","zh","ar","ca","fa","sq","hy","nl","sv","az","bs","bn","bg","ms","dz","be","el","cs","da","am","et","fi","fo","ka","hr","ht","hu","he","is","ky","km","ko","kk","lo","si","lt","lb","lv","ro","sr","mk","my","mn","tl","mt","dv","ny","no","ne","ur","ru","rw","sl","sk","so","th","tg","tk","to","sw","uz","vi","bi","sm","zu"]},"then":{"type":"object","properties":{"create_master":{"type":"object","properties":{"slug":{"type":"string","pattern":"^[a-z0-9-]{3,40}$"},"display_name":{"type":"string","minLength":1,"maxLength":80},"trades":{"type":"array","items":{"type":"string"}},"languages":{"type":"array","items":{"type":"string","minLength":2,"maxLength":3}},"city_id":{"type":"integer"},"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180},"service_radius_km":{"type":"integer","minimum":1,"maximum":100},"hours":{"type":"object","properties":{"showPrices":{"type":"boolean"}},"additionalProperties":{}}},"required":["slug","display_name","trades","languages","city_id","lat","lng"]}},"required":["create_master"]}},"required":["email"]}}}}}},"/api/v1/auth/email/complete":{"post":{"summary":"Complete sign-in with the token from the link. Returns then_result when the link carried a draft: master_created, already_master, or null.","tags":["auth"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"account":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["person","agent"]},"email":{"anyOf":[{"type":"string","format":"email"},{"type":"null"}]},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"ui_lang":{"type":"string","enum":["en","es","pt","fr","de","it","pl","uk","tr","hi","id","ja","zh","ar","ca","fa","sq","hy","nl","sv","az","bs","bn","bg","ms","dz","be","el","cs","da","am","et","fi","fo","ka","hr","ht","hu","he","is","ky","km","ko","kk","lo","si","lt","lb","lv","ro","sr","mk","my","mn","tl","mt","dv","ny","no","ne","ur","ru","rw","sl","sk","so","th","tg","tk","to","sw","uz","vi","bi","sm","zu"]},"anonymous":{"type":"boolean"},"master_slug":{"anyOf":[{"type":"string"},{"type":"null"}]},"scopes":{"type":"array","items":{"type":"string","enum":["read","problems:read","requests:write","book","master:read","master:write","offers:write","webhooks:write"]}},"acting_for":{"anyOf":[{"type":"object","properties":{"master_slug":{"type":"string"},"display_name":{"type":"string"},"role":{"const":"assistant"}},"required":["master_slug","display_name","role"]},{"type":"null"}]}},"required":["id","kind","email","display_name","ui_lang","anonymous","master_slug","scopes"]},"then_result":{"anyOf":[{},{"type":"null"}]}},"required":["account","then_result"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":10}},"required":["token"]}}}}}},"/api/v1/auth/signout":{"post":{"summary":"End the session.","tags":["auth"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"session":[]}],"x-scope":"session"}},"/api/v1/me":{"get":{"summary":"The signed-in account. An agent bound as a pro's assistant also gets master_slug and acting_for {master_slug, display_name, role: assistant}.","tags":["auth"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["person","agent"]},"email":{"anyOf":[{"type":"string","format":"email"},{"type":"null"}]},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"ui_lang":{"type":"string","enum":["en","es","pt","fr","de","it","pl","uk","tr","hi","id","ja","zh","ar","ca","fa","sq","hy","nl","sv","az","bs","bn","bg","ms","dz","be","el","cs","da","am","et","fi","fo","ka","hr","ht","hu","he","is","ky","km","ko","kk","lo","si","lt","lb","lv","ro","sr","mk","my","mn","tl","mt","dv","ny","no","ne","ur","ru","rw","sl","sk","so","th","tg","tk","to","sw","uz","vi","bi","sm","zu"]},"anonymous":{"type":"boolean"},"master_slug":{"anyOf":[{"type":"string"},{"type":"null"}]},"scopes":{"type":"array","items":{"type":"string","enum":["read","problems:read","requests:write","book","master:read","master:write","offers:write","webhooks:write"]}},"acting_for":{"anyOf":[{"type":"object","properties":{"master_slug":{"type":"string"},"display_name":{"type":"string"},"role":{"const":"assistant"}},"required":["master_slug","display_name","role"]},{"type":"null"}]}},"required":["id","kind","email","display_name","ui_lang","anonymous","master_slug","scopes"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"any"},"patch":{"summary":"Change display name or UI language.","tags":["auth"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["person","agent"]},"email":{"anyOf":[{"type":"string","format":"email"},{"type":"null"}]},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"ui_lang":{"type":"string","enum":["en","es","pt","fr","de","it","pl","uk","tr","hi","id","ja","zh","ar","ca","fa","sq","hy","nl","sv","az","bs","bn","bg","ms","dz","be","el","cs","da","am","et","fi","fo","ka","hr","ht","hu","he","is","ky","km","ko","kk","lo","si","lt","lb","lv","ro","sr","mk","my","mn","tl","mt","dv","ny","no","ne","ur","ru","rw","sl","sk","so","th","tg","tk","to","sw","uz","vi","bi","sm","zu"]},"anonymous":{"type":"boolean"},"master_slug":{"anyOf":[{"type":"string"},{"type":"null"}]},"scopes":{"type":"array","items":{"type":"string","enum":["read","problems:read","requests:write","book","master:read","master:write","offers:write","webhooks:write"]}},"acting_for":{"anyOf":[{"type":"object","properties":{"master_slug":{"type":"string"},"display_name":{"type":"string"},"role":{"const":"assistant"}},"required":["master_slug","display_name","role"]},{"type":"null"}]}},"required":["id","kind","email","display_name","ui_lang","anonymous","master_slug","scopes"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"display_name":{"type":"string","minLength":1,"maxLength":80},"ui_lang":{"type":"string","enum":["en","es","pt","fr","de","it","pl","uk","tr","hi","id","ja","zh","ar","ca","fa","sq","hy","nl","sv","az","bs","bn","bg","ms","dz","be","el","cs","da","am","et","fi","fo","ka","hr","ht","hu","he","is","ky","km","ko","kk","lo","si","lt","lb","lv","ro","sr","mk","my","mn","tl","mt","dv","ny","no","ne","ur","ru","rw","sl","sk","so","th","tg","tk","to","sw","uz","vi","bi","sm","zu"]}}}}}},"security":[{"session":[]}],"x-scope":"session"},"delete":{"summary":"Delete YOUR OWN account: a person (session or full-scope key), an anonymous device account, or an agent (its own key). Body {\"confirm\":\"delete\"}; ?dry_run=true returns the report and changes nothing. Keys, sessions and grants go; the row stays as an empty shell. Repeating it after the key is gone answers 401.","tags":["auth"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"confirm":{"const":"delete"}},"required":["confirm"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"any"}},"/api/v1/me/anonymous":{"post":{"summary":"Create an anonymous device account (browsers; sets the gh_session cookie).","tags":["auth"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"account":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["person","agent"]},"email":{"anyOf":[{"type":"string","format":"email"},{"type":"null"}]},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"ui_lang":{"type":"string","enum":["en","es","pt","fr","de","it","pl","uk","tr","hi","id","ja","zh","ar","ca","fa","sq","hy","nl","sv","az","bs","bn","bg","ms","dz","be","el","cs","da","am","et","fi","fo","ka","hr","ht","hu","he","is","ky","km","ko","kk","lo","si","lt","lb","lv","ro","sr","mk","my","mn","tl","mt","dv","ny","no","ne","ur","ru","rw","sl","sk","so","th","tg","tk","to","sw","uz","vi","bi","sm","zu"]},"anonymous":{"type":"boolean"},"master_slug":{"anyOf":[{"type":"string"},{"type":"null"}]},"scopes":{"type":"array","items":{"type":"string","enum":["read","problems:read","requests:write","book","master:read","master:write","offers:write","webhooks:write"]}},"acting_for":{"anyOf":[{"type":"object","properties":{"master_slug":{"type":"string"},"display_name":{"type":"string"},"role":{"const":"assistant"}},"required":["master_slug","display_name","role"]},{"type":"null"}]}},"required":["id","kind","email","display_name","ui_lang","anonymous","master_slug","scopes"]}},"required":["account"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/api/v1/agents":{"post":{"summary":"An agent creates itself: no person needed. Returns api_key and claim_code once.","tags":["agents"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"account_id":{"type":"string","format":"uuid"},"api_key":{"type":"string"},"claim_code":{"type":"string"}},"required":["account_id","api_key","claim_code"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"description":{"type":"string","minLength":1,"maxLength":500},"name":{"type":"string","minLength":1,"maxLength":80}},"required":["description"]}}}}}},"/api/v1/agents/claim":{"post":{"summary":"A person claims an agent with its claim code.","tags":["agents"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"agent_description":{"anyOf":[{"type":"string"},{"type":"null"}]},"created_at":{"type":"string","format":"date-time"}},"required":["id","display_name","agent_description","created_at"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"claim_code":{"type":"string","minLength":1}},"required":["claim_code"]}}}},"security":[{"session":[]}],"x-scope":"session"}},"/api/v1/me/agents/{id}":{"delete":{"summary":"A person deletes an agent they claimed (same report and body as DELETE /me; ?dry_run=true first; repeating answers already_deleted).","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"confirm":{"const":"delete"}},"required":["confirm"]}}}},"security":[{"session":[]}],"x-scope":"session"}},"/api/v1/me/agents/{id}/grants":{"get":{"summary":"Grants a person has given an agent.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"agent_id":{"type":"string","format":"uuid"},"scopes":{"type":"array","items":{"type":"string","enum":["read","problems:read","requests:write","book","master:read","master:write","offers:write","webhooks:write"]}},"limits":{"type":"object","additionalProperties":{}},"created_at":{"type":"string","format":"date-time"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["id","agent_id","scopes","limits","created_at","revoked_at"]}}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"session":[]}],"x-scope":"session"},"post":{"summary":"Grant an agent scopes within limits.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"agent_id":{"type":"string","format":"uuid"},"scopes":{"type":"array","items":{"type":"string","enum":["read","problems:read","requests:write","book","master:read","master:write","offers:write","webhooks:write"]}},"limits":{"type":"object","additionalProperties":{}},"created_at":{"type":"string","format":"date-time"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["id","agent_id","scopes","limits","created_at","revoked_at"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"scopes":{"type":"array","items":{"type":"string","enum":["read","problems:read","requests:write","book","master:read","master:write","offers:write","webhooks:write"]}},"limits":{"type":"object","additionalProperties":{}}},"required":["scopes"]}}}},"security":[{"session":[]}],"x-scope":"session"},"delete":{"summary":"Revoke every grant an agent has.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"session":[]}],"x-scope":"session"}},"/api/v1/me/agents/{id}/grants/{grant_id}":{"delete":{"summary":"Revoke a grant.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"grant_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"session":[]}],"x-scope":"session"}},"/api/v1/me/grant-requests":{"post":{"summary":"An AGENT asks its person for scopes: returns an approve_url (and emails it to person_email; opening it signs the person in) valid 24 hours. One tap on /grant/{token} approves or declines; approving also claims an unclaimed agent. Poll GET /me/grant-requests/{id} or listen for the grant.decided webhook. 10 per hour, 5 emails per day; the same pending scopes refresh one request. Any agent key, even unclaimed.","tags":["agents"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","approved","declined","expired"]},"scopes":{"type":"array","items":{"type":"string","enum":["read","problems:read","requests:write","book","master:read","master:write","offers:write","webhooks:write"]}},"reason":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"decided_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"grant_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"emailed_to":{"anyOf":[{"type":"string"},{"type":"null"}]},"create_master":{"anyOf":[{"type":"object","properties":{"slug":{"type":"string"},"display_name":{"type":"string"}},"required":["slug","display_name"]},{"type":"null"}]},"page_slug":{"anyOf":[{"type":"string"},{"type":"null"}]},"approve_url":{"anyOf":[{"type":"string"},{"type":"null"}]},"next":{"type":"string"}},"required":["id","status","scopes","reason","created_at","expires_at","decided_at","grant_id","emailed_to","create_master","page_slug","approve_url","next"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"scopes":{"type":"array","items":{"type":"string","enum":["read","problems:read","requests:write","book","master:read","master:write","offers:write","webhooks:write"]}},"reason":{"type":"string","minLength":3,"maxLength":500,"description":"Why you need them, in a sentence the person can judge."},"person_email":{"type":"string","format":"email"},"create_master":{"type":"object","properties":{"slug":{"type":"string","pattern":"^[a-z0-9-]{3,40}$"},"display_name":{"type":"string","minLength":1,"maxLength":80},"trades":{"type":"array","items":{"type":"string"}},"languages":{"type":"array","items":{"type":"string","minLength":2,"maxLength":3}},"city_id":{"type":"integer"},"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180},"service_radius_km":{"type":"integer","minimum":1,"maximum":100},"hours":{"type":"object","properties":{"showPrices":{"type":"boolean"}},"additionalProperties":{}}},"required":["slug","display_name","trades","languages","city_id","lat","lng"]}},"required":["scopes","reason"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"any"},"get":{"summary":"Your grant requests, newest first.","tags":["agents"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","approved","declined","expired"]},"scopes":{"type":"array","items":{"type":"string","enum":["read","problems:read","requests:write","book","master:read","master:write","offers:write","webhooks:write"]}},"reason":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"decided_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"grant_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"emailed_to":{"anyOf":[{"type":"string"},{"type":"null"}]},"create_master":{"anyOf":[{"type":"object","properties":{"slug":{"type":"string"},"display_name":{"type":"string"}},"required":["slug","display_name"]},{"type":"null"}]},"page_slug":{"anyOf":[{"type":"string"},{"type":"null"}]},"approve_url":{"anyOf":[{"type":"string"},{"type":"null"}]},"next":{"type":"string"}},"required":["id","status","scopes","reason","created_at","expires_at","decided_at","grant_id","emailed_to","create_master","page_slug","approve_url","next"]}}},"required":["results"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"any"}},"/api/v1/me/grant-requests/{id}":{"get":{"summary":"Where a grant request stands: pending, approved (grant_id), declined, expired. `next` says what to do.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","approved","declined","expired"]},"scopes":{"type":"array","items":{"type":"string","enum":["read","problems:read","requests:write","book","master:read","master:write","offers:write","webhooks:write"]}},"reason":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"decided_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"grant_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"emailed_to":{"anyOf":[{"type":"string"},{"type":"null"}]},"create_master":{"anyOf":[{"type":"object","properties":{"slug":{"type":"string"},"display_name":{"type":"string"}},"required":["slug","display_name"]},{"type":"null"}]},"page_slug":{"anyOf":[{"type":"string"},{"type":"null"}]},"approve_url":{"anyOf":[{"type":"string"},{"type":"null"}]},"next":{"type":"string"}},"required":["id","status","scopes","reason","created_at","expires_at","decided_at","grant_id","emailed_to","create_master","page_slug","approve_url","next"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"any"}},"/api/v1/me/grant-requests/{id}/restart":{"post":{"summary":"Restart a pending or expired request in one step: a new approve_url and another 24 hours (optional body person_email re-emails it).","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","approved","declined","expired"]},"scopes":{"type":"array","items":{"type":"string","enum":["read","problems:read","requests:write","book","master:read","master:write","offers:write","webhooks:write"]}},"reason":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"decided_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"grant_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"emailed_to":{"anyOf":[{"type":"string"},{"type":"null"}]},"create_master":{"anyOf":[{"type":"object","properties":{"slug":{"type":"string"},"display_name":{"type":"string"}},"required":["slug","display_name"]},{"type":"null"}]},"page_slug":{"anyOf":[{"type":"string"},{"type":"null"}]},"approve_url":{"anyOf":[{"type":"string"},{"type":"null"}]},"next":{"type":"string"}},"required":["id","status","scopes","reason","created_at","expires_at","decided_at","grant_id","emailed_to","create_master","page_slug","approve_url","next"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"person_email":{"type":"string","format":"email"}}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"any"}},"/api/v1/grant-requests/by-token/{token}":{"get":{"summary":"What an approve link asks (agent, scopes in plain words, reason), and whether the signed-in person may answer. The token is the secret; no sign-in needed to read.","tags":["agents"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","approved","declined","expired"]},"reason":{"type":"string"},"expires_at":{"type":"string","format":"date-time"},"agent":{"type":"object","properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"claimed":{"type":"boolean"}},"required":["name","description","claimed"]},"scopes":{"type":"array","items":{"type":"object","properties":{"scope":{"type":"string","enum":["read","problems:read","requests:write","book","master:read","master:write","offers:write","webhooks:write"]},"description":{"type":"string"}},"required":["scope","description"]}},"will_create_page":{"anyOf":[{"type":"object","properties":{"display_name":{"type":"string"},"slug":{"type":"string"},"trades":{"type":"array","items":{"type":"string"}}},"required":["display_name","slug","trades"]},{"type":"null"}]},"you":{"type":"object","properties":{"signed_in":{"type":"boolean"},"can_decide":{"type":"boolean"},"why_not":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["signed_in","can_decide","why_not"]}},"required":["id","status","reason","expires_at","agent","scopes","will_create_page","you"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/api/v1/grant-requests/by-token/{token}/decide":{"post":{"summary":"A signed-in person approves or declines: body {\"decision\":\"approve\"|\"decline\"} (approved and declined are accepted too). Approve creates the grant (and claims an unclaimed agent). Repeating the same answer is a no-op. People only: a person's session or a full-scope key.","tags":["agents"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["pending","approved","declined","expired"]},"grant_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"agent_claimed":{"type":"boolean"},"assistant_bound":{"type":"boolean"},"page":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["master_created","already_master"]},"slug":{"type":"string"}},"required":["kind","slug"]},{"type":"null"}]}},"required":["status","grant_id","agent_claimed","page"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"decision":{"type":"string","enum":["approve","decline"]}},"required":["decision"]}}}},"security":[{"session":[]}],"x-scope":"session"}},"/api/v1/me/keys":{"get":{"summary":"Your API keys.","tags":["agents"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string","enum":["read","problems:read","requests:write","book","master:read","master:write","offers:write","webhooks:write"]}},"label":{"anyOf":[{"type":"string"},{"type":"null"}]},"created_at":{"type":"string","format":"date-time"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["id","prefix","scopes","label","created_at","last_used_at","revoked_at"]}}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"any"},"post":{"summary":"Create an API key.","tags":["agents"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string","enum":["read","problems:read","requests:write","book","master:read","master:write","offers:write","webhooks:write"]}},"label":{"anyOf":[{"type":"string"},{"type":"null"}]},"created_at":{"type":"string","format":"date-time"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["id","prefix","scopes","label","created_at","last_used_at","revoked_at"]},"secret":{"type":"string"}},"required":["key","secret"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string","maxLength":80},"scopes":{"type":"array","items":{"type":"string","enum":["read","problems:read","requests:write","book","master:read","master:write","offers:write","webhooks:write"]}}},"required":["scopes"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"any"}},"/api/v1/me/keys/{id}":{"delete":{"summary":"Revoke an API key.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"any"}},"/api/v1/devices":{"post":{"summary":"Register a phone's signing key (ECDSA P-256 public JWK).","tags":["auth"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"device_id":{"type":"string","format":"uuid"}},"required":["device_id"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"public_key_jwk":{"type":"object","properties":{"kty":{"const":"EC"},"crv":{"const":"P-256"},"x":{"type":"string"},"y":{"type":"string"}},"required":["kty","crv","x","y"]}},"required":["public_key_jwk"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"any"}},"/api/v1/trades":{"get":{"summary":"All trades with names in every UI language.","tags":["catalog"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"names":{"type":"object","additionalProperties":{"type":"string"}},"icon":{"anyOf":[{"type":"string"},{"type":"null"}]},"sort":{"type":"integer"}},"required":["slug","names","icon","sort"]}}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/api/v1/job-types":{"get":{"summary":"Job types; q matches names and synonyms in lang.","tags":["catalog"],"parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string"}},{"name":"lang","in":"query","required":false,"schema":{"type":"string","enum":["en","es","pt","fr","de","it","pl","uk","tr","hi","id","ja","zh","ar","ca","fa","sq","hy","nl","sv","az","bs","bn","bg","ms","dz","be","el","cs","da","am","et","fi","fo","ka","hr","ht","hu","he","is","ky","km","ko","kk","lo","si","lt","lb","lv","ro","sr","mk","my","mn","tl","mt","dv","ny","no","ne","ur","ru","rw","sl","sk","so","th","tg","tk","to","sw","uz","vi","bi","sm","zu"]}},{"name":"trade","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"trade":{"type":"string"},"parent_id":{"anyOf":[{"type":"integer"},{"type":"null"}]},"slug":{"type":"string"},"names":{"type":"object","additionalProperties":{"type":"string"}},"synonyms":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"price_unit":{"type":"string"}},"required":["id","trade","parent_id","slug","names","synonyms","price_unit"]}}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/api/v1/cities":{"get":{"summary":"Cities. No arguments: the launch city (Lviv) first, then open ones, then the biggest. ?near=lat,lng nearest first, ?country=ua, ?status=active|locked (active = open, requests can be sent; locked = not open yet). A bare array by default; ?envelope=1 answers {results, status_meanings, launch_cities, next}.","tags":["catalog"],"parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["locked","active"]}},{"name":"envelope","in":"query","required":false,"schema":{"type":"string","enum":["1","true"]}},{"name":"near","in":"query","required":false,"schema":{"anyOf":[{"const":"viewer"},{"type":"string","pattern":"^-?\\d+(\\.\\d+)?,-?\\d+(\\.\\d+)?$"}]}},{"name":"country","in":"query","required":false,"schema":{"type":"string","minLength":2,"maxLength":2}},{"name":"ids","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d+(,\\d+){0,49}$"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"country":{"type":"string","minLength":2,"maxLength":2},"slug":{"type":"string"},"names":{"type":"object","additionalProperties":{"type":"string"}},"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180},"population":{"type":"integer"},"timezone":{"type":"string"},"currency":{"type":"string","minLength":3,"maxLength":3},"languages":{"type":"array","items":{"type":"string","minLength":2,"maxLength":3}},"status":{"type":"string","enum":["locked","active"]},"radius_km":{"type":"integer"}},"required":["id","country","slug","names","lat","lng","population","timezone","currency","languages","status","radius_km"]}},{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"country":{"type":"string","minLength":2,"maxLength":2},"slug":{"type":"string"},"names":{"type":"object","additionalProperties":{"type":"string"}},"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180},"population":{"type":"integer"},"timezone":{"type":"string"},"currency":{"type":"string","minLength":3,"maxLength":3},"languages":{"type":"array","items":{"type":"string","minLength":2,"maxLength":3}},"status":{"type":"string","enum":["locked","active"]},"radius_km":{"type":"integer"},"launch":{"type":"boolean"}},"required":["id","country","slug","names","lat","lng","population","timezone","currency","languages","status","radius_km","launch"]}},"status_meanings":{"type":"object","additionalProperties":{"type":"string"}},"launch_cities":{"type":"array","items":{"type":"string"}},"next":{"type":"string"}},"required":["results","status_meanings","launch_cities","next"]}]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/api/v1/cities/{cc}/{slug}":{"get":{"summary":"One city with pro and trade counts.","tags":["catalog"],"parameters":[{"name":"cc","in":"path","required":true,"schema":{"type":"string"}},{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"country":{"type":"string","minLength":2,"maxLength":2},"slug":{"type":"string"},"names":{"type":"object","additionalProperties":{"type":"string"}},"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180},"population":{"type":"integer"},"timezone":{"type":"string"},"currency":{"type":"string","minLength":3,"maxLength":3},"languages":{"type":"array","items":{"type":"string","minLength":2,"maxLength":3}},"status":{"type":"string","enum":["locked","active"]},"radius_km":{"type":"integer"},"country_names":{"type":"object","additionalProperties":{"type":"string"}},"masters":{"type":"integer"},"trades":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"masters":{"type":"integer"}},"required":["slug","masters"]}}},"required":["id","country","slug","names","lat","lng","population","timezone","currency","languages","status","radius_km","country_names","masters","trades"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/api/v1/fault-codes":{"get":{"summary":"Every fault code we cover (filter ?brand= ?appliance= ?lang=): meaning, severity, hazard, whether Try first is withheld pending human review. With ?code=F28 (brand optional; dots, dashes, spaces and case ignored) it returns every matching entry across brands as full entries with labelled fields. Add ?envelope=1 to get { results, coverage, next }: coverage lists the brands the catalog has for that appliance type and says codes of other brands are not in it yet (so one result is not the complete answer).","tags":["catalog"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"brand_slug":{"type":"string"},"brand":{"type":"string"},"appliance":{"type":"string"},"code":{"type":"string"},"meaning":{"anyOf":[{"type":"string"},{"type":"null"}]},"severity":{"type":"string","enum":["low","medium","stop-now"]},"hazard":{"type":"string","enum":["gas","electrical","water","none"]},"try_first_withheld":{"type":"boolean"},"has_source":{"type":"boolean"}},"required":["brand_slug","brand","appliance","code","meaning","severity","hazard","try_first_withheld","has_source"]}},{"type":"array","items":{"type":"object","properties":{"brand_slug":{"type":"string"},"brand":{"type":"string"},"appliance":{"type":"string"},"code":{"type":"string"},"job_type":{"type":"object","properties":{"id":{"type":"integer"},"trade":{"type":"string"},"parent_id":{"anyOf":[{"type":"integer"},{"type":"null"}]},"slug":{"type":"string"},"names":{"type":"object","additionalProperties":{"type":"string"}},"synonyms":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"price_unit":{"type":"string"}},"required":["id","trade","parent_id","slug","names","synonyms","price_unit"]},"meaning":{"anyOf":[{"type":"string"},{"type":"null"}]},"meanings":{"type":"object","additionalProperties":{"type":"string"}},"fields":{"type":"object","properties":{"meaning":{"type":"string"},"severity":{"type":"string","enum":["low","medium","stop-now"]},"try_first":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"do_not":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"call_pro_if":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"source":{"anyOf":[{"type":"object","properties":{"url":{"type":"string","format":"uri"},"title":{"type":"string"},"publisher":{"type":"string"}},"required":["url","title","publisher"]},{"type":"null"}]},"withheld":{"anyOf":[{"type":"object","properties":{"fields":{"type":"array","items":{"type":"string","enum":["try_first","call_pro_if"]}},"reason":{"type":"string","enum":["gas_unreviewed","electrical_unreviewed"]},"why":{"type":"string"}},"required":["fields","reason","why"]},{"type":"null"}]},"safety":{"type":"object","properties":{"hazard":{"type":"string","enum":["gas","electrical","water","none"]},"reviewed":{"type":"boolean"},"try_first_withheld":{"type":"boolean"}},"required":["hazard","reviewed","try_first_withheld"]}},"required":["meaning","severity","try_first","do_not","call_pro_if","source","withheld","safety"]}},"required":["brand_slug","brand","appliance","code","job_type","meaning","meanings","fields"]}}]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/api/v1/fault-codes/{brand}/{code}":{"get":{"summary":"What an appliance fault code means, and the job type that fixes it, as labelled fields (meaning, severity, try_first, do_not, call_pro_if, source). Gas and electrical codes without a human review withhold try_first.","tags":["catalog"],"parameters":[{"name":"brand","in":"path","required":true,"schema":{"type":"string"}},{"name":"code","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"brand_slug":{"type":"string"},"brand":{"type":"string"},"appliance":{"type":"string"},"code":{"type":"string"},"job_type":{"type":"object","properties":{"id":{"type":"integer"},"trade":{"type":"string"},"parent_id":{"anyOf":[{"type":"integer"},{"type":"null"}]},"slug":{"type":"string"},"names":{"type":"object","additionalProperties":{"type":"string"}},"synonyms":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"price_unit":{"type":"string"}},"required":["id","trade","parent_id","slug","names","synonyms","price_unit"]},"meaning":{"anyOf":[{"type":"string"},{"type":"null"}]},"meanings":{"type":"object","additionalProperties":{"type":"string"}},"fields":{"type":"object","properties":{"meaning":{"type":"string"},"severity":{"type":"string","enum":["low","medium","stop-now"]},"try_first":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"do_not":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"call_pro_if":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"source":{"anyOf":[{"type":"object","properties":{"url":{"type":"string","format":"uri"},"title":{"type":"string"},"publisher":{"type":"string"}},"required":["url","title","publisher"]},{"type":"null"}]},"withheld":{"anyOf":[{"type":"object","properties":{"fields":{"type":"array","items":{"type":"string","enum":["try_first","call_pro_if"]}},"reason":{"type":"string","enum":["gas_unreviewed","electrical_unreviewed"]},"why":{"type":"string"}},"required":["fields","reason","why"]},{"type":"null"}]},"safety":{"type":"object","properties":{"hazard":{"type":"string","enum":["gas","electrical","water","none"]},"reviewed":{"type":"boolean"},"try_first_withheld":{"type":"boolean"}},"required":["hazard","reviewed","try_first_withheld"]}},"required":["meaning","severity","try_first","do_not","call_pro_if","source","withheld","safety"]}},"required":["brand_slug","brand","appliance","code","job_type","meaning","meanings","fields"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/api/v1/problems":{"post":{"summary":"Read a problem (photo or text) into a job type.","tags":["catalog"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"job_type":{"anyOf":[{"type":"object","properties":{"id":{"type":"integer"},"trade":{"type":"string"},"parent_id":{"anyOf":[{"type":"integer"},{"type":"null"}]},"slug":{"type":"string"},"names":{"type":"object","additionalProperties":{"type":"string"}},"synonyms":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"price_unit":{"type":"string"}},"required":["id","trade","parent_id","slug","names","synonyms","price_unit"]},{"type":"null"}]},"confidence":{"type":"number","minimum":0,"maximum":1},"alternatives":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"trade":{"type":"string"},"parent_id":{"anyOf":[{"type":"integer"},{"type":"null"}]},"slug":{"type":"string"},"names":{"type":"object","additionalProperties":{"type":"string"}},"synonyms":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"price_unit":{"type":"string"}},"required":["id","trade","parent_id","slug","names","synonyms","price_unit"]}},"brand":{"anyOf":[{"type":"string"},{"type":"null"}]},"code":{"anyOf":[{"type":"string"},{"type":"null"}]},"notes":{"anyOf":[{"type":"string"},{"type":"null"}]},"reader_unavailable":{"type":"boolean"},"fault_codes":{"type":"array","items":{"type":"object","properties":{"brand_slug":{"type":"string"},"brand":{"type":"string"},"appliance":{"type":"string"},"code":{"type":"string"},"job_type":{"type":"object","properties":{"id":{"type":"integer"},"trade":{"type":"string"},"parent_id":{"anyOf":[{"type":"integer"},{"type":"null"}]},"slug":{"type":"string"},"names":{"type":"object","additionalProperties":{"type":"string"}},"synonyms":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"price_unit":{"type":"string"}},"required":["id","trade","parent_id","slug","names","synonyms","price_unit"]},"meaning":{"anyOf":[{"type":"string"},{"type":"null"}]},"meanings":{"type":"object","additionalProperties":{"type":"string"}},"fields":{"type":"object","properties":{"meaning":{"type":"string"},"severity":{"type":"string","enum":["low","medium","stop-now"]},"try_first":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"do_not":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"call_pro_if":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"source":{"anyOf":[{"type":"object","properties":{"url":{"type":"string","format":"uri"},"title":{"type":"string"},"publisher":{"type":"string"}},"required":["url","title","publisher"]},{"type":"null"}]},"withheld":{"anyOf":[{"type":"object","properties":{"fields":{"type":"array","items":{"type":"string","enum":["try_first","call_pro_if"]}},"reason":{"type":"string","enum":["gas_unreviewed","electrical_unreviewed"]},"why":{"type":"string"}},"required":["fields","reason","why"]},{"type":"null"}]},"safety":{"type":"object","properties":{"hazard":{"type":"string","enum":["gas","electrical","water","none"]},"reviewed":{"type":"boolean"},"try_first_withheld":{"type":"boolean"}},"required":["hazard","reviewed","try_first_withheld"]}},"required":["meaning","severity","try_first","do_not","call_pro_if","source","withheld","safety"]}},"required":["brand_slug","brand","appliance","code","job_type","meaning","meanings","fields"]}},"gas_emergency":{"anyOf":[{"type":"object","properties":{"country":{"type":"string"},"number":{"type":"string"},"tel":{"type":"string"},"source":{"type":"string"}},"required":["country","number","tel","source"]},{"type":"null"}]}},"required":["job_type","confidence","alternatives"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"photo_key":{"type":"string"},"text":{"type":"string","maxLength":2000},"lang":{"type":"string","enum":["en","es","pt","fr","de","it","pl","uk","tr","hi","id","ja","zh","ar","ca","fa","sq","hy","nl","sv","az","bs","bn","bg","ms","dz","be","el","cs","da","am","et","fi","fo","ka","hr","ht","hu","he","is","ky","km","ko","kk","lo","si","lt","lb","lv","ro","sr","mk","my","mn","tl","mt","dv","ny","no","ne","ur","ru","rw","sl","sk","so","th","tg","tk","to","sw","uz","vi","bi","sm","zu"]},"city_id":{"type":"integer"}},"required":["lang"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"problems:read"}},"/api/v1/uploads":{"post":{"summary":"Presigned PUT for a photo.","tags":["catalog"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"upload_key":{"type":"string"},"put_url":{"type":"string"},"headers":{"type":"object","additionalProperties":{"type":"string"}},"expires_in":{"type":"integer"}},"required":["upload_key","put_url","expires_in"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["problem","before","after"]}},"required":["kind"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"any"}},"/api/v1/masters":{"get":{"summary":"Pros ranked by confirmed jobs; every score input is returned so you can re-rank. When nobody matches, `empty` {reason: no_pros_yet|filters|radius, nearest_km, tried, next[]} says why and offers only steps that would change the answer (widen the radius, drop filters, watch, invite).","tags":["masters"],"parameters":[{"name":"job_type","in":"query","required":false,"schema":{"type":"string"}},{"name":"trade","in":"query","required":false,"schema":{"type":"string"}},{"name":"lat","in":"query","required":false,"schema":{"type":"number","minimum":-90,"maximum":90}},{"name":"lng","in":"query","required":false,"schema":{"type":"number","minimum":-180,"maximum":180}},{"name":"city","in":"query","required":false,"schema":{"type":"string"}},{"name":"radius_km","in":"query","required":false,"schema":{"type":"number","exclusiveMinimum":0,"maximum":200}},{"name":"speaks","in":"query","required":false,"schema":{"type":"string"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","minLength":1}},{"name":"free_today","in":"query","required":false,"schema":{"type":"string","enum":["1","true"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"display_name":{"type":"string"},"trades":{"type":"array","items":{"type":"string"}},"languages":{"type":"array","items":{"type":"string","minLength":2,"maxLength":3}},"city_id":{"type":"integer"},"area_cell":{"type":"string"},"standing":{"type":"object","properties":{"confirmed":{"type":"integer"},"held":{"type":"integer"},"checked":{"type":"integer"},"failed":{"type":"integer"},"no_shows":{"type":"integer"},"distinct_customers":{"type":"integer"},"median_reply_min":{"anyOf":[{"type":"number"},{"type":"null"}]},"reply_rate":{"anyOf":[{"type":"number","minimum":0,"maximum":1},{"type":"null"}]}},"required":["confirmed","held","checked","failed","no_shows","distinct_customers","median_reply_min","reply_rate"]},"match":{"type":"object","properties":{"job_type_id":{"anyOf":[{"type":"integer"},{"type":"null"}]},"jobs_nearby":{"type":"integer"},"rungs":{"type":"object","properties":{"claimed":{"type":"integer"},"photo":{"type":"integer"},"on_site":{"type":"integer"},"confirmed":{"type":"integer"},"held":{"type":"integer"}},"required":["claimed","photo","on_site","confirmed","held"]}},"required":["job_type_id","jobs_nearby","rungs"]},"score":{"type":"number"},"score_inputs":{"type":"object","properties":{"jobs_of_type":{"type":"object","properties":{"claimed":{"type":"integer"},"photo":{"type":"integer"},"on_site":{"type":"integer"},"confirmed":{"type":"integer"},"held":{"type":"integer"}},"required":["claimed","photo","on_site","confirmed","held"]},"jobs_of_trade":{"type":"object","properties":{"claimed":{"type":"integer"},"photo":{"type":"integer"},"on_site":{"type":"integer"},"confirmed":{"type":"integer"},"held":{"type":"integer"}},"required":["claimed","photo","on_site","confirmed","held"]},"failed_holds":{"type":"integer"},"no_shows":{"type":"integer"},"reply_rate":{"anyOf":[{"type":"number"},{"type":"null"}]},"distance_km":{"anyOf":[{"type":"number"},{"type":"null"}]},"city_prior":{"type":"number"}},"required":["jobs_of_type","jobs_of_trade","failed_holds","no_shows","reply_rate","distance_km","city_prior"]},"price":{"anyOf":[{"type":"object","properties":{"currency":{"type":"string","minLength":3,"maxLength":3},"n":{"type":"integer"},"p25":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"median":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"p75":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]}},"required":["currency","n","p25","median","p75"]},{"type":"null"}]},"since":{"type":"string","format":"date-time"},"free_today":{"anyOf":[{"type":"object","properties":{"from":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$"},"until":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$"}},"required":["from","until"]},{"type":"null"}]}},"required":["id","slug","display_name","trades","languages","city_id","area_cell","standing","match","score","score_inputs","price","since","free_today"]}},"next_cursor":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}]},"origin":{"anyOf":[{"type":"string","enum":["point","city_centre"]},{"type":"null"}]},"empty":{"type":"object","properties":{"reason":{"type":"string","enum":["no_pros_yet","filters","radius"]},"nearest_km":{"anyOf":[{"type":"number"},{"type":"null"}]},"tried":{"type":"object","properties":{"radius_km":{"anyOf":[{"type":"number"},{"type":"null"}]},"filters":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]}}},"required":["radius_km","filters"]},"next":{"type":"array","items":{"type":"object","properties":{"action":{"type":"string"},"method":{"type":"string","enum":["GET","POST","DELETE"]},"path":{"type":"string"},"query":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"}]}},"body":{"type":"object","additionalProperties":{}},"why":{"type":"string"}},"required":["action","method","path"]}}},"required":["reason","nearest_km","tried","next"]}},"required":["results","next_cursor","origin"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/api/v1/masters/slug-available":{"get":{"summary":"Is this page address free? {slug, available, suggestion}. Rate-limited per IP.","tags":["masters"],"parameters":[{"name":"slug","in":"query","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]{3,40}$"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"slug":{"type":"string"},"available":{"type":"boolean"},"suggestion":{"type":"string"}},"required":["slug","available","suggestion"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/api/v1/masters/{slug}":{"get":{"summary":"One pro's public page data.","tags":["masters"],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"display_name":{"type":"string"},"trades":{"type":"array","items":{"type":"string"}},"languages":{"type":"array","items":{"type":"string","minLength":2,"maxLength":3}},"city_id":{"type":"integer"},"area_cell":{"type":"string"},"standing":{"type":"object","properties":{"confirmed":{"type":"integer"},"held":{"type":"integer"},"checked":{"type":"integer"},"failed":{"type":"integer"},"no_shows":{"type":"integer"},"distinct_customers":{"type":"integer"},"median_reply_min":{"anyOf":[{"type":"number"},{"type":"null"}]},"reply_rate":{"anyOf":[{"type":"number","minimum":0,"maximum":1},{"type":"null"}]}},"required":["confirmed","held","checked","failed","no_shows","distinct_customers","median_reply_min","reply_rate"]},"match":{"type":"object","properties":{"job_type_id":{"anyOf":[{"type":"integer"},{"type":"null"}]},"jobs_nearby":{"type":"integer"},"rungs":{"type":"object","properties":{"claimed":{"type":"integer"},"photo":{"type":"integer"},"on_site":{"type":"integer"},"confirmed":{"type":"integer"},"held":{"type":"integer"}},"required":["claimed","photo","on_site","confirmed","held"]}},"required":["job_type_id","jobs_nearby","rungs"]},"score":{"type":"number"},"score_inputs":{"type":"object","properties":{"jobs_of_type":{"type":"object","properties":{"claimed":{"type":"integer"},"photo":{"type":"integer"},"on_site":{"type":"integer"},"confirmed":{"type":"integer"},"held":{"type":"integer"}},"required":["claimed","photo","on_site","confirmed","held"]},"jobs_of_trade":{"type":"object","properties":{"claimed":{"type":"integer"},"photo":{"type":"integer"},"on_site":{"type":"integer"},"confirmed":{"type":"integer"},"held":{"type":"integer"}},"required":["claimed","photo","on_site","confirmed","held"]},"failed_holds":{"type":"integer"},"no_shows":{"type":"integer"},"reply_rate":{"anyOf":[{"type":"number"},{"type":"null"}]},"distance_km":{"anyOf":[{"type":"number"},{"type":"null"}]},"city_prior":{"type":"number"}},"required":["jobs_of_type","jobs_of_trade","failed_holds","no_shows","reply_rate","distance_km","city_prior"]},"price":{"anyOf":[{"type":"object","properties":{"currency":{"type":"string","minLength":3,"maxLength":3},"n":{"type":"integer"},"p25":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"median":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"p75":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]}},"required":["currency","n","p25","median","p75"]},{"type":"null"}]},"since":{"type":"string","format":"date-time"},"free_today":{"anyOf":[{"type":"object","properties":{"from":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$"},"until":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$"}},"required":["from","until"]},{"type":"null"}]},"firm":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"}},"required":["id","name"]},{"type":"null"}]},"status":{"type":"string","enum":["active","paused","hidden"]},"disputed_imports":{"type":"integer","exclusiveMinimum":0}},"required":["id","slug","display_name","trades","languages","city_id","area_cell","standing","match","score","score_inputs","price","since","free_today","firm","status"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/api/v1/masters/{slug}/jobs":{"get":{"summary":"A pro's public jobs: type, area, month, rung (price only if they opted in).","tags":["masters"],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"job_type_id":{"type":"integer"},"area":{"type":"string"},"month":{"type":"string","pattern":"^\\d{4}-\\d{2}$"},"rung":{"type":"string","enum":["claimed","photo","on_site","confirmed","held"]},"price_minor":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"currency":{"type":"string","minLength":3,"maxLength":3}},"required":["id","job_type_id","area","month","rung","price_minor","currency"]}},"next_cursor":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}]}},"required":["results","next_cursor"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/api/v1/me/master":{"post":{"summary":"Become a pro. ?dry_run=true answers would.page (the public page as GET /masters/{slug} serves it) and would.url (the address it would reserve).","tags":["masters"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"slug":{"type":"string"},"display_name":{"type":"string"},"trades":{"type":"array","items":{"type":"string"}},"languages":{"type":"array","items":{"type":"string","minLength":2,"maxLength":3}},"city_id":{"type":"integer"},"service_radius_km":{"type":"integer"},"hours":{"type":"object","properties":{"showPrices":{"type":"boolean"}},"additionalProperties":{}},"away":{"type":"array","items":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"]}},"status":{"type":"string","enum":["active","paused","hidden"]},"assistant_agent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"assistant_limits":{"type":"object","properties":{"priceFloorMinor":{"type":"integer","minimum":0},"jobTypes":{"type":"array","items":{"type":"string"}},"hours":{"type":"object","additionalProperties":{"type":"array","items":{"type":"array","prefixItems":[{"type":"string"},{"type":"string"}],"minItems":2,"maxItems":2}}}}},"firm_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"city":{"type":"object","properties":{"id":{"type":"integer"},"country":{"type":"string","minLength":2,"maxLength":2},"slug":{"type":"string"},"names":{"type":"object","additionalProperties":{"type":"string"}},"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180},"population":{"type":"integer"},"timezone":{"type":"string"},"currency":{"type":"string","minLength":3,"maxLength":3},"languages":{"type":"array","items":{"type":"string","minLength":2,"maxLength":3}},"status":{"type":"string","enum":["locked","active"]},"radius_km":{"type":"integer"}},"required":["id","country","slug","names","lat","lng","population","timezone","currency","languages","status","radius_km"]},"acting_for":{"anyOf":[{"type":"object","properties":{"master_slug":{"type":"string"},"display_name":{"type":"string"},"role":{"const":"assistant"}},"required":["master_slug","display_name","role"]},{"type":"null"}]}},"required":["slug","display_name","trades","languages","city_id","service_radius_km","hours","away","status","assistant_agent_id","assistant_limits","firm_id","city"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"slug":{"type":"string","pattern":"^[a-z0-9-]{3,40}$"},"display_name":{"type":"string","minLength":1,"maxLength":80},"trades":{"type":"array","items":{"type":"string"}},"languages":{"type":"array","items":{"type":"string","minLength":2,"maxLength":3}},"city_id":{"type":"integer"},"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180},"service_radius_km":{"type":"integer","minimum":1,"maximum":100},"hours":{"type":"object","properties":{"showPrices":{"type":"boolean"}},"additionalProperties":{}}},"required":["slug","display_name","trades","languages","city_id","lat","lng"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:write"},"patch":{"summary":"Change your pro profile, hours, away dates, assistant.","tags":["masters"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"slug":{"type":"string"},"display_name":{"type":"string"},"trades":{"type":"array","items":{"type":"string"}},"languages":{"type":"array","items":{"type":"string","minLength":2,"maxLength":3}},"city_id":{"type":"integer"},"service_radius_km":{"type":"integer"},"hours":{"type":"object","properties":{"showPrices":{"type":"boolean"}},"additionalProperties":{}},"away":{"type":"array","items":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"]}},"status":{"type":"string","enum":["active","paused","hidden"]},"assistant_agent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"assistant_limits":{"type":"object","properties":{"priceFloorMinor":{"type":"integer","minimum":0},"jobTypes":{"type":"array","items":{"type":"string"}},"hours":{"type":"object","additionalProperties":{"type":"array","items":{"type":"array","prefixItems":[{"type":"string"},{"type":"string"}],"minItems":2,"maxItems":2}}}}},"firm_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"city":{"type":"object","properties":{"id":{"type":"integer"},"country":{"type":"string","minLength":2,"maxLength":2},"slug":{"type":"string"},"names":{"type":"object","additionalProperties":{"type":"string"}},"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180},"population":{"type":"integer"},"timezone":{"type":"string"},"currency":{"type":"string","minLength":3,"maxLength":3},"languages":{"type":"array","items":{"type":"string","minLength":2,"maxLength":3}},"status":{"type":"string","enum":["locked","active"]},"radius_km":{"type":"integer"}},"required":["id","country","slug","names","lat","lng","population","timezone","currency","languages","status","radius_km"]},"acting_for":{"anyOf":[{"type":"object","properties":{"master_slug":{"type":"string"},"display_name":{"type":"string"},"role":{"const":"assistant"}},"required":["master_slug","display_name","role"]},{"type":"null"}]}},"required":["slug","display_name","trades","languages","city_id","service_radius_km","hours","away","status","assistant_agent_id","assistant_limits","firm_id","city"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"display_name":{"type":"string","minLength":1,"maxLength":80},"trades":{"type":"array","items":{"type":"string"}},"languages":{"type":"array","items":{"type":"string","minLength":2,"maxLength":3}},"service_radius_km":{"type":"integer","minimum":1,"maximum":100},"hours":{"type":"object","properties":{"showPrices":{"type":"boolean"}},"additionalProperties":{}},"away":{"type":"array","items":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"]}},"status":{"type":"string","enum":["active","paused","hidden"]},"assistant_agent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"assistant_limits":{"type":"object","properties":{"priceFloorMinor":{"type":"integer","minimum":0},"jobTypes":{"type":"array","items":{"type":"string"}},"hours":{"type":"object","additionalProperties":{"type":"array","items":{"type":"array","prefixItems":[{"type":"string"},{"type":"string"}],"minItems":2,"maxItems":2}}}}}}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:write"},"get":{"summary":"Your pro profile. A bound assistant gets the pro's profile with acting_for {master_slug, display_name, role}.","tags":["masters"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"slug":{"type":"string"},"display_name":{"type":"string"},"trades":{"type":"array","items":{"type":"string"}},"languages":{"type":"array","items":{"type":"string","minLength":2,"maxLength":3}},"city_id":{"type":"integer"},"service_radius_km":{"type":"integer"},"hours":{"type":"object","properties":{"showPrices":{"type":"boolean"}},"additionalProperties":{}},"away":{"type":"array","items":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"]}},"status":{"type":"string","enum":["active","paused","hidden"]},"assistant_agent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"assistant_limits":{"type":"object","properties":{"priceFloorMinor":{"type":"integer","minimum":0},"jobTypes":{"type":"array","items":{"type":"string"}},"hours":{"type":"object","additionalProperties":{"type":"array","items":{"type":"array","prefixItems":[{"type":"string"},{"type":"string"}],"minItems":2,"maxItems":2}}}}},"firm_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"city":{"type":"object","properties":{"id":{"type":"integer"},"country":{"type":"string","minLength":2,"maxLength":2},"slug":{"type":"string"},"names":{"type":"object","additionalProperties":{"type":"string"}},"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180},"population":{"type":"integer"},"timezone":{"type":"string"},"currency":{"type":"string","minLength":3,"maxLength":3},"languages":{"type":"array","items":{"type":"string","minLength":2,"maxLength":3}},"status":{"type":"string","enum":["locked","active"]},"radius_km":{"type":"integer"}},"required":["id","country","slug","names","lat","lng","population","timezone","currency","languages","status","radius_km"]},"acting_for":{"anyOf":[{"type":"object","properties":{"master_slug":{"type":"string"},"display_name":{"type":"string"},"role":{"const":"assistant"}},"required":["master_slug","display_name","role"]},{"type":"null"}]}},"required":["slug","display_name","trades","languages","city_id","service_radius_km","hours","away","status","assistant_agent_id","assistant_limits","firm_id","city"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:read"}},"/api/v1/me/master/standing":{"get":{"summary":"Your standing, derived from jobs.","tags":["masters"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"standing":{"type":"object","properties":{"confirmed":{"type":"integer"},"held":{"type":"integer"},"checked":{"type":"integer"},"failed":{"type":"integer"},"no_shows":{"type":"integer"},"distinct_customers":{"type":"integer"},"median_reply_min":{"anyOf":[{"type":"number"},{"type":"null"}]},"reply_rate":{"anyOf":[{"type":"number","minimum":0,"maximum":1},{"type":"null"}]}},"required":["confirmed","held","checked","failed","no_shows","distinct_customers","median_reply_min","reply_rate"]},"held_rate_text":{"type":"string"},"reply_median_min":{"anyOf":[{"type":"number"},{"type":"null"}]}},"required":["standing","held_rate_text","reply_median_min"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:read"}},"/api/v1/me/master/earnings":{"get":{"summary":"Earnings by period, json or csv.","tags":["masters"],"parameters":[{"name":"period","in":"query","required":false,"schema":{"type":"string","enum":["week","month","year"]}},{"name":"format","in":"query","required":false,"schema":{"type":"string","enum":["json","csv"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"period":{"type":"string"},"currency":{"type":"string","minLength":3,"maxLength":3},"total_minor":{"type":"integer","minimum":0},"jobs":{"type":"integer"},"by_trade":{"type":"array","items":{"type":"object","properties":{"trade":{"type":"string"},"total_minor":{"type":"integer","minimum":0},"jobs":{"type":"integer"}},"required":["trade","total_minor","jobs"]}},"by_day":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"total_minor":{"type":"integer","minimum":0}},"required":["date","total_minor"]}}},"required":["period","currency","total_minor","jobs","by_trade","by_day"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:read"}},"/api/v1/me/master/clients/import":{"post":{"summary":"Import past jobs; each client gets an emailed confirm link. ?dry_run=true returns would.messages[] {to, name, lang, subject, text}: the exact mail each client will read (in ONE language: each row's optional `lang` code, else the pro's UI language); nothing is sent. At most 200 new rows per pro in any rolling 24 hours (over that: 429 rate_limited with `next` giving the time to retry; the dry run returns would.accepted, would.remaining_today, would.over_cap, would.can_import_again_at). The pro, or the agent bound as their assistant with master:write, may call it (dry run and real); the mails go out in the pro's name.","tags":["masters"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"created":{"type":"integer"},"job_ids":{"type":"array","items":{"type":"string","format":"uuid"}}},"required":["created","job_ids"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1},"phone_or_email":{"type":"string","minLength":3},"job_type_id":{"type":"integer"},"month":{"type":"string","pattern":"^\\d{4}-\\d{2}$"},"price_minor":{"type":"integer","minimum":0},"lang":{"type":"string","minLength":2,"maxLength":12}},"required":["name","phone_or_email","job_type_id","month","price_minor"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:write"}},"/api/v1/me/master/assistant":{"put":{"summary":"Bind an agent you have claimed as the pro's assistant, within limits.","tags":["masters"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"agent_id":{"type":"string","format":"uuid"},"limits":{"type":"object","properties":{"priceFloorMinor":{"type":"integer","minimum":0},"jobTypes":{"type":"array","items":{"type":"string"}},"hours":{"type":"object","additionalProperties":{"type":"array","items":{"type":"array","prefixItems":[{"type":"string"},{"type":"string"}],"minItems":2,"maxItems":2}}}}}},"required":["agent_id"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:write"},"delete":{"summary":"Unbind the assistant and clear its limits.","tags":["masters"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:write"}},"/api/v1/me/master/jobs":{"get":{"summary":"The pro's booked jobs (schedule, Today); filter by date and status. Includes the address for the pro's own jobs.","tags":["masters"],"parameters":[{"name":"date","in":"query","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["claimed","photo","on_site","confirmed","held","open","cancelled","no_show"]}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:read"}},"/api/v1/me/master/clients/{job_id}/resend":{"post":{"summary":"Re-email an imported client's confirm link (one step restart).","tags":["masters"],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:write"}},"/api/v1/me/master/firm":{"post":{"summary":"Start a firm; the caller becomes its owner.","tags":["firms"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"201":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80}},"required":["name"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:write"}},"/api/v1/me/firm":{"get":{"summary":"Your firm, your role and its members.","tags":["firms"],"parameters":[],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:read"}},"/api/v1/me/firm/members":{"post":{"summary":"Add a pro to your firm (owner or dispatcher). Same as /api/v1/me/master/firm/members.","tags":["firms"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"201":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"master_slug":{"type":"string"},"role":{"type":"string","enum":["dispatcher","member"]}},"required":["master_slug"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:write"}},"/api/v1/me/master/firm/members":{"post":{"summary":"Alias of POST /api/v1/me/firm/members.","tags":["firms"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"201":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"master_slug":{"type":"string"},"role":{"type":"string","enum":["dispatcher","member"]}},"required":["master_slug"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:write"}},"/api/v1/me/firm/members/{master_id}":{"delete":{"summary":"Remove a member (owner or dispatcher; anyone may remove themself; the owner stays).","tags":["firms"],"parameters":[{"name":"master_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:write"}},"/api/v1/requests/{id}/assign":{"post":{"summary":"A firm's owner or dispatcher routes a request to another member.","tags":["firms"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"201":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"master_id":{"type":"string","format":"uuid"}},"required":["master_id"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:write"}},"/api/v1/me/master/connect":{"get":{"summary":"Connected Google / Microsoft accounts, which providers are configured, and features. Starting or finishing a connection is a person in a browser; an agent cannot.","tags":["connect"],"parameters":[],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:read"}},"/api/v1/me/master/connect/{provider}/start":{"post":{"summary":"Start an OAuth grant (google or microsoft) for contacts and/or mail. People only: an agent gets 403.","tags":["connect"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"features":{"type":"array","items":{"type":"string","enum":["contacts","mail_read"]}}}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:write"}},"/api/v1/me/master/connect/{provider}/callback":{"get":{"summary":"The provider's redirect target; completes the grant in the person's browser and redirects to /work/connections.","tags":["connect"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:write"}},"/api/v1/me/master/connect/{provider}":{"delete":{"summary":"Disconnect: tokens are deleted and syncing stops.","tags":["connect"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:write"}},"/api/v1/me/master/connect/contacts/parse":{"post":{"summary":"Parse a .vcf or .csv contacts export (json {content, format, country} or multipart file). Stores nothing. Each contact has phones[] and emails[] apart; phone_or_email is the first email when there is one (confirm links are emailed), else the first phone.","tags":["connect"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string"},"format":{"type":"string","enum":["vcf","csv"]},"country":{"type":"string","minLength":2,"maxLength":2}},"required":["content"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:write"}},"/api/v1/me/master/connect/{provider}/contacts":{"get":{"summary":"Read contacts live from a connected account (needs the contacts permission). Stores nothing.","tags":["connect"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:write"}},"/api/v1/me/master/connect/{provider}/scan-past-clients":{"post":{"summary":"Suggest past clients from the connected mailbox since a date. Stores nothing.","tags":["connect"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"since":{"type":"string"}}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:write"}},"/api/v1/me/master/connect/import":{"post":{"summary":"Import the ticked contacts as past clients; same effect as the clients import, each client gets an emailed confirm link.","tags":["connect"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"phone_or_email":{"type":"string"},"job_type_id":{"type":"integer"},"month":{"type":"string"},"price_minor":{"type":"integer"}},"required":["name","phone_or_email","job_type_id","month"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:write"}},"/api/v1/me/master/inbox-address":{"get":{"summary":"The pro's private forwarding address: forwarded mail becomes a request.","tags":["connect"],"parameters":[],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:read"}},"/api/v1/me/master/inbox-address/rotate":{"post":{"summary":"Retire the forwarding address and issue a new one.","tags":["connect"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:write"}},"/api/v1/requests":{"post":{"summary":"Ask up to 3 pros about a job: master_ids, or nearest (1 to 3) to ask the nearest N who do job_type_id (none in reach: the real send is 404 not_found with a next; a dry run is 200 with would.asked [], would.reason no_pro_in_reach, would.pro_sees and next steps). The address stays encrypted until a pro is booked. With ?dry_run=true it validates and answers `would` even without requests:write, plus needs {scope, scopes, why, how}.","tags":["requests"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"job_type_id":{"anyOf":[{"type":"integer"},{"type":"null"}]},"city_id":{"type":"integer"},"cell":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"photo_url":{"anyOf":[{"type":"string"},{"type":"null"}]},"photo_read":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}]},"status":{"type":"string","enum":["open","booked","closed","expired"]},"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"targets":{"type":"array","items":{"type":"object","properties":{"master_slug":{"type":"string"},"master_name":{"type":"string"},"seen_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"declined_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["master_slug","master_name","seen_at","declined_at"]}},"offers":{"type":"array","items":{}}},"required":["id","job_type_id","city_id","cell","description","photo_url","photo_read","status","created_at","expires_at","targets","offers"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"job_type_id":{"type":"integer"},"city_id":{"type":"integer"},"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180},"description":{"type":"string","maxLength":2000},"photo_key":{"type":"string"},"master_ids":{"type":"array","items":{"type":"string","format":"uuid"}},"nearest":{"type":"integer","minimum":1,"maximum":3},"address":{"type":"string","minLength":3,"maxLength":500},"on_behalf_of":{"type":"string","format":"uuid"}},"required":["city_id","lat","lng","address"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"requests:write"}},"/api/v1/me/requests":{"get":{"summary":"Your requests.","tags":["requests"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"job_type_id":{"anyOf":[{"type":"integer"},{"type":"null"}]},"city_id":{"type":"integer"},"cell":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"photo_url":{"anyOf":[{"type":"string"},{"type":"null"}]},"photo_read":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}]},"status":{"type":"string","enum":["open","booked","closed","expired"]},"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"targets":{"type":"array","items":{"type":"object","properties":{"master_slug":{"type":"string"},"master_name":{"type":"string"},"seen_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"declined_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["master_slug","master_name","seen_at","declined_at"]}},"offers":{"type":"array","items":{}}},"required":["id","job_type_id","city_id","cell","description","photo_url","photo_read","status","created_at","expires_at","targets","offers"]}}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"requests:write"}},"/api/v1/me/inbox":{"get":{"summary":"A pro's incoming requests.","tags":["requests"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"request_id":{"type":"string","format":"uuid"},"job_type_id":{"anyOf":[{"type":"integer"},{"type":"null"}]},"city_id":{"type":"integer"},"cell":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"photo_url":{"anyOf":[{"type":"string"},{"type":"null"}]},"sent_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"reply_due_at":{"type":"string","format":"date-time"},"my_offer":{"anyOf":[{},{"type":"null"}]},"travel":{"anyOf":[{"type":"object","properties":{"km":{"type":"number","minimum":0},"minutes":{"type":"integer","minimum":0}},"required":["km","minutes"]},{"type":"null"}]}},"required":["request_id","job_type_id","city_id","cell","description","photo_url","sent_at","expires_at","reply_due_at","my_offer","travel"]}}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"master:read"}},"/api/v1/requests/{id}/offers":{"post":{"summary":"Send an offer (pro or their assistant, within limits).","tags":["requests"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"request_id":{"type":"string","format":"uuid"},"master_slug":{"type":"string"},"master_name":{"type":"string"},"slot_from":{"type":"string","format":"date-time"},"slot_to":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"price_min_minor":{"type":"integer","minimum":0},"price_max_minor":{"type":"integer","minimum":0},"currency":{"type":"string","minLength":3,"maxLength":3},"note":{"anyOf":[{"type":"string"},{"type":"null"}]},"status":{"type":"string","enum":["sent","accepted","declined","withdrawn","expired"]},"created_at":{"type":"string","format":"date-time"}},"required":["id","request_id","master_slug","master_name","slot_from","slot_to","price_min_minor","price_max_minor","currency","note","status","created_at"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"slot_from":{"type":"string","format":"date-time"},"slot_to":{"type":"string","format":"date-time"},"price_min_minor":{"type":"integer","minimum":0},"price_max_minor":{"type":"integer","minimum":0},"note":{"type":"string","maxLength":1000}},"required":["slot_from","price_min_minor","price_max_minor"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"offers:write"}},"/api/v1/requests/{id}/decline":{"post":{"summary":"A pro (or their assistant) can't take a request: the customer sees it, a sent offer is withdrawn. Repeats answer the same.","tags":["requests"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"request_id":{"type":"string","format":"uuid"},"declined_at":{"type":"string","format":"date-time"},"withdrew_offer_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"all_declined":{"type":"boolean"}},"required":["request_id","declined_at","withdrew_offer_id","all_declined"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","enum":["busy","too_far","not_my_trade","other"]},"note":{"type":"string","maxLength":300}}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"offers:write"}},"/api/v1/offers/{id}/accept":{"post":{"summary":"Accept an offer; creates the job.","tags":["requests"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"job_id":{"type":"string","format":"uuid"},"offer":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"request_id":{"type":"string","format":"uuid"},"master_slug":{"type":"string"},"master_name":{"type":"string"},"slot_from":{"type":"string","format":"date-time"},"slot_to":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"price_min_minor":{"type":"integer","minimum":0},"price_max_minor":{"type":"integer","minimum":0},"currency":{"type":"string","minLength":3,"maxLength":3},"note":{"anyOf":[{"type":"string"},{"type":"null"}]},"status":{"type":"string","enum":["sent","accepted","declined","withdrawn","expired"]},"created_at":{"type":"string","format":"date-time"}},"required":["id","request_id","master_slug","master_name","slot_from","slot_to","price_min_minor","price_max_minor","currency","note","status","created_at"]}},"required":["job_id","offer"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"book"}},"/api/v1/offers/{id}/decline":{"post":{"summary":"Decline an offer.","tags":["requests"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"book"}},"/api/v1/offers/{id}/withdraw":{"post":{"summary":"The pro (or assistant) takes back an offer that has not been accepted.","tags":["requests"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"offers:write"}},"/api/v1/jobs/{id}":{"get":{"summary":"A job with its full proof trail (parties only).","tags":["jobs"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"request_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"master_slug":{"type":"string"},"master_name":{"type":"string"},"job_type_id":{"type":"integer"},"city_id":{"type":"integer"},"cell":{"type":"string"},"source":{"type":"string","enum":["app","imported"]},"rung":{"type":"string","enum":["claimed","photo","on_site","confirmed","held"]},"scheduled_for":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"finished_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"price_minor_master":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"price_minor_customer":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"currency":{"type":"string","minLength":3,"maxLength":3},"confirmed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"hold_due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"held":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"no_show":{"type":"boolean"},"cancelled_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"disputed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"self_confirm_suspected":{"type":"boolean"},"self_confirm_note":{"anyOf":[{"type":"string"},{"type":"null"}]},"address":{"anyOf":[{"type":"string"},{"type":"null"}]},"signature_svg":{"anyOf":[{"type":"string"},{"type":"null"}]},"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"kind":{"type":"string"},"actor_kind":{"anyOf":[{"type":"string"},{"type":"null"}]},"data":{"type":"object","additionalProperties":{}},"at":{"type":"string","format":"date-time"}},"required":["id","kind","actor_kind","data","at"]}},"customer_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"travel":{"anyOf":[{"type":"object","properties":{"km":{"type":"number","minimum":0},"minutes":{"type":"integer","minimum":0}},"required":["km","minutes"]},{"type":"null"}]}},"required":["id","request_id","master_slug","master_name","job_type_id","city_id","cell","source","rung","scheduled_for","started_at","finished_at","price_minor_master","price_minor_customer","currency","confirmed_at","hold_due_at","held","no_show","cancelled_at","address","signature_svg","events","customer_name","travel"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"any"}},"/api/v1/me/home":{"get":{"summary":"A customer's jobs by date.","tags":["jobs"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"request_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"master_slug":{"type":"string"},"master_name":{"type":"string"},"job_type_id":{"type":"integer"},"city_id":{"type":"integer"},"cell":{"type":"string"},"source":{"type":"string","enum":["app","imported"]},"rung":{"type":"string","enum":["claimed","photo","on_site","confirmed","held"]},"scheduled_for":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"finished_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"price_minor_master":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"price_minor_customer":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"currency":{"type":"string","minLength":3,"maxLength":3},"confirmed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"hold_due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"held":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"no_show":{"type":"boolean"},"cancelled_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"disputed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"self_confirm_suspected":{"type":"boolean"},"self_confirm_note":{"anyOf":[{"type":"string"},{"type":"null"}]},"address":{"anyOf":[{"type":"string"},{"type":"null"}]},"signature_svg":{"anyOf":[{"type":"string"},{"type":"null"}]},"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"kind":{"type":"string"},"actor_kind":{"anyOf":[{"type":"string"},{"type":"null"}]},"data":{"type":"object","additionalProperties":{}},"at":{"type":"string","format":"date-time"}},"required":["id","kind","actor_kind","data","at"]}},"customer_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"travel":{"anyOf":[{"type":"object","properties":{"km":{"type":"number","minimum":0},"minutes":{"type":"integer","minimum":0}},"required":["km","minutes"]},{"type":"null"}]}},"required":["id","request_id","master_slug","master_name","job_type_id","city_id","cell","source","rung","scheduled_for","started_at","finished_at","price_minor_master","price_minor_customer","currency","confirmed_at","hold_due_at","held","no_show","cancelled_at","address","signature_svg","events","customer_name","travel"]}},"next_cursor":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}]}},"required":["results","next_cursor"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"any"}},"/api/v1/jobs/{id}/door-code":{"post":{"summary":"Customer's rotating door code (phone only).","tags":["jobs"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","minLength":6,"maxLength":6},"qr":{"type":"string"},"expires_at":{"type":"string","format":"date-time"}},"required":["code","qr","expires_at"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180}},"required":["lat","lng"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"phone","x-phone-only":"An agent gets 403 agents_cannot_confirm: a person's phone must do this."}},"/api/v1/jobs/{id}/start":{"post":{"summary":"The pro scans the door code on site (phone only).","tags":["jobs"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"job":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"request_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"master_slug":{"type":"string"},"master_name":{"type":"string"},"job_type_id":{"type":"integer"},"city_id":{"type":"integer"},"cell":{"type":"string"},"source":{"type":"string","enum":["app","imported"]},"rung":{"type":"string","enum":["claimed","photo","on_site","confirmed","held"]},"scheduled_for":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"finished_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"price_minor_master":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"price_minor_customer":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"currency":{"type":"string","minLength":3,"maxLength":3},"confirmed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"hold_due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"held":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"no_show":{"type":"boolean"},"cancelled_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"disputed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"self_confirm_suspected":{"type":"boolean"},"self_confirm_note":{"anyOf":[{"type":"string"},{"type":"null"}]},"address":{"anyOf":[{"type":"string"},{"type":"null"}]},"signature_svg":{"anyOf":[{"type":"string"},{"type":"null"}]},"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"kind":{"type":"string"},"actor_kind":{"anyOf":[{"type":"string"},{"type":"null"}]},"data":{"type":"object","additionalProperties":{}},"at":{"type":"string","format":"date-time"}},"required":["id","kind","actor_kind","data","at"]}},"customer_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"travel":{"anyOf":[{"type":"object","properties":{"km":{"type":"number","minimum":0},"minutes":{"type":"integer","minimum":0}},"required":["km","minutes"]},{"type":"null"}]}},"required":["id","request_id","master_slug","master_name","job_type_id","city_id","cell","source","rung","scheduled_for","started_at","finished_at","price_minor_master","price_minor_customer","currency","confirmed_at","hold_due_at","held","no_show","cancelled_at","address","signature_svg","events","customer_name","travel"]},"distance_m":{"type":"integer"}},"required":["job","distance_m"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","minLength":6,"maxLength":6},"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180}},"required":["code","lat","lng"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"phone","x-phone-only":"An agent gets 403 agents_cannot_confirm: a person's phone must do this."}},"/api/v1/jobs/{id}/photos":{"post":{"summary":"Attach a signed before/after photo.","tags":["jobs"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"job":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"request_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"master_slug":{"type":"string"},"master_name":{"type":"string"},"job_type_id":{"type":"integer"},"city_id":{"type":"integer"},"cell":{"type":"string"},"source":{"type":"string","enum":["app","imported"]},"rung":{"type":"string","enum":["claimed","photo","on_site","confirmed","held"]},"scheduled_for":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"finished_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"price_minor_master":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"price_minor_customer":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"currency":{"type":"string","minLength":3,"maxLength":3},"confirmed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"hold_due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"held":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"no_show":{"type":"boolean"},"cancelled_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"disputed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"self_confirm_suspected":{"type":"boolean"},"self_confirm_note":{"anyOf":[{"type":"string"},{"type":"null"}]},"address":{"anyOf":[{"type":"string"},{"type":"null"}]},"signature_svg":{"anyOf":[{"type":"string"},{"type":"null"}]},"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"kind":{"type":"string"},"actor_kind":{"anyOf":[{"type":"string"},{"type":"null"}]},"data":{"type":"object","additionalProperties":{}},"at":{"type":"string","format":"date-time"}},"required":["id","kind","actor_kind","data","at"]}},"customer_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"travel":{"anyOf":[{"type":"object","properties":{"km":{"type":"number","minimum":0},"minutes":{"type":"integer","minimum":0}},"required":["km","minutes"]},{"type":"null"}]}},"required":["id","request_id","master_slug","master_name","job_type_id","city_id","cell","source","rung","scheduled_for","started_at","finished_at","price_minor_master","price_minor_customer","currency","confirmed_at","hold_due_at","held","no_show","cancelled_at","address","signature_svg","events","customer_name","travel"]}},"required":["job"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["before","after"]},"upload_key":{"type":"string","minLength":1}},"required":["kind","upload_key"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"any"}},"/api/v1/jobs/{id}/finish":{"post":{"summary":"Finish on site (phone only).","tags":["jobs"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"job":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"request_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"master_slug":{"type":"string"},"master_name":{"type":"string"},"job_type_id":{"type":"integer"},"city_id":{"type":"integer"},"cell":{"type":"string"},"source":{"type":"string","enum":["app","imported"]},"rung":{"type":"string","enum":["claimed","photo","on_site","confirmed","held"]},"scheduled_for":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"finished_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"price_minor_master":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"price_minor_customer":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"currency":{"type":"string","minLength":3,"maxLength":3},"confirmed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"hold_due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"held":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"no_show":{"type":"boolean"},"cancelled_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"disputed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"self_confirm_suspected":{"type":"boolean"},"self_confirm_note":{"anyOf":[{"type":"string"},{"type":"null"}]},"address":{"anyOf":[{"type":"string"},{"type":"null"}]},"signature_svg":{"anyOf":[{"type":"string"},{"type":"null"}]},"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"kind":{"type":"string"},"actor_kind":{"anyOf":[{"type":"string"},{"type":"null"}]},"data":{"type":"object","additionalProperties":{}},"at":{"type":"string","format":"date-time"}},"required":["id","kind","actor_kind","data","at"]}},"customer_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"travel":{"anyOf":[{"type":"object","properties":{"km":{"type":"number","minimum":0},"minutes":{"type":"integer","minimum":0}},"required":["km","minutes"]},{"type":"null"}]}},"required":["id","request_id","master_slug","master_name","job_type_id","city_id","cell","source","rung","scheduled_for","started_at","finished_at","price_minor_master","price_minor_customer","currency","confirmed_at","hold_due_at","held","no_show","cancelled_at","address","signature_svg","events","customer_name","travel"]}},"required":["job"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180},"price_minor_master":{"type":"integer","minimum":0}},"required":["lat","lng","price_minor_master"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"phone","x-phone-only":"An agent gets 403 agents_cannot_confirm: a person's phone must do this."}},"/api/v1/jobs/{id}/confirm":{"post":{"summary":"Customer confirms and enters what they paid (phone only).","tags":["jobs"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"job":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"request_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"master_slug":{"type":"string"},"master_name":{"type":"string"},"job_type_id":{"type":"integer"},"city_id":{"type":"integer"},"cell":{"type":"string"},"source":{"type":"string","enum":["app","imported"]},"rung":{"type":"string","enum":["claimed","photo","on_site","confirmed","held"]},"scheduled_for":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"finished_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"price_minor_master":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"price_minor_customer":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"currency":{"type":"string","minLength":3,"maxLength":3},"confirmed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"hold_due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"held":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"no_show":{"type":"boolean"},"cancelled_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"disputed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"self_confirm_suspected":{"type":"boolean"},"self_confirm_note":{"anyOf":[{"type":"string"},{"type":"null"}]},"address":{"anyOf":[{"type":"string"},{"type":"null"}]},"signature_svg":{"anyOf":[{"type":"string"},{"type":"null"}]},"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"kind":{"type":"string"},"actor_kind":{"anyOf":[{"type":"string"},{"type":"null"}]},"data":{"type":"object","additionalProperties":{}},"at":{"type":"string","format":"date-time"}},"required":["id","kind","actor_kind","data","at"]}},"customer_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"travel":{"anyOf":[{"type":"object","properties":{"km":{"type":"number","minimum":0},"minutes":{"type":"integer","minimum":0}},"required":["km","minutes"]},{"type":"null"}]}},"required":["id","request_id","master_slug","master_name","job_type_id","city_id","cell","source","rung","scheduled_for","started_at","finished_at","price_minor_master","price_minor_customer","currency","confirmed_at","hold_due_at","held","no_show","cancelled_at","address","signature_svg","events","customer_name","travel"]}},"required":["job"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"price_minor_customer":{"type":"integer","minimum":0},"signature_svg":{"type":"string","maxLength":20000}},"required":["price_minor_customer"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"phone","x-phone-only":"An agent gets 403 agents_cannot_confirm: a person's phone must do this."}},"/api/v1/jobs/{id}/hold":{"post":{"summary":"Customer answers the day-30 question (phone only).","tags":["jobs"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"job":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"request_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"master_slug":{"type":"string"},"master_name":{"type":"string"},"job_type_id":{"type":"integer"},"city_id":{"type":"integer"},"cell":{"type":"string"},"source":{"type":"string","enum":["app","imported"]},"rung":{"type":"string","enum":["claimed","photo","on_site","confirmed","held"]},"scheduled_for":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"finished_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"price_minor_master":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"price_minor_customer":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"currency":{"type":"string","minLength":3,"maxLength":3},"confirmed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"hold_due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"held":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"no_show":{"type":"boolean"},"cancelled_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"disputed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"self_confirm_suspected":{"type":"boolean"},"self_confirm_note":{"anyOf":[{"type":"string"},{"type":"null"}]},"address":{"anyOf":[{"type":"string"},{"type":"null"}]},"signature_svg":{"anyOf":[{"type":"string"},{"type":"null"}]},"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"kind":{"type":"string"},"actor_kind":{"anyOf":[{"type":"string"},{"type":"null"}]},"data":{"type":"object","additionalProperties":{}},"at":{"type":"string","format":"date-time"}},"required":["id","kind","actor_kind","data","at"]}},"customer_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"travel":{"anyOf":[{"type":"object","properties":{"km":{"type":"number","minimum":0},"minutes":{"type":"integer","minimum":0}},"required":["km","minutes"]},{"type":"null"}]}},"required":["id","request_id","master_slug","master_name","job_type_id","city_id","cell","source","rung","scheduled_for","started_at","finished_at","price_minor_master","price_minor_customer","currency","confirmed_at","hold_due_at","held","no_show","cancelled_at","address","signature_svg","events","customer_name","travel"]}},"required":["job"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"working":{"type":"boolean"}},"required":["working"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"phone","x-phone-only":"An agent gets 403 agents_cannot_confirm: a person's phone must do this."}},"/api/v1/jobs/{id}/no-show":{"post":{"summary":"Report a no-show.","tags":["jobs"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"job":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"request_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"master_slug":{"type":"string"},"master_name":{"type":"string"},"job_type_id":{"type":"integer"},"city_id":{"type":"integer"},"cell":{"type":"string"},"source":{"type":"string","enum":["app","imported"]},"rung":{"type":"string","enum":["claimed","photo","on_site","confirmed","held"]},"scheduled_for":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"finished_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"price_minor_master":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"price_minor_customer":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"currency":{"type":"string","minLength":3,"maxLength":3},"confirmed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"hold_due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"held":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"no_show":{"type":"boolean"},"cancelled_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"disputed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"self_confirm_suspected":{"type":"boolean"},"self_confirm_note":{"anyOf":[{"type":"string"},{"type":"null"}]},"address":{"anyOf":[{"type":"string"},{"type":"null"}]},"signature_svg":{"anyOf":[{"type":"string"},{"type":"null"}]},"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"kind":{"type":"string"},"actor_kind":{"anyOf":[{"type":"string"},{"type":"null"}]},"data":{"type":"object","additionalProperties":{}},"at":{"type":"string","format":"date-time"}},"required":["id","kind","actor_kind","data","at"]}},"customer_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"travel":{"anyOf":[{"type":"object","properties":{"km":{"type":"number","minimum":0},"minutes":{"type":"integer","minimum":0}},"required":["km","minutes"]},{"type":"null"}]}},"required":["id","request_id","master_slug","master_name","job_type_id","city_id","cell","source","rung","scheduled_for","started_at","finished_at","price_minor_master","price_minor_customer","currency","confirmed_at","hold_due_at","held","no_show","cancelled_at","address","signature_svg","events","customer_name","travel"]}},"required":["job"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"any"}},"/api/v1/jobs/{id}/cancel":{"post":{"summary":"Cancel a job.","tags":["jobs"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"job":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"request_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"master_slug":{"type":"string"},"master_name":{"type":"string"},"job_type_id":{"type":"integer"},"city_id":{"type":"integer"},"cell":{"type":"string"},"source":{"type":"string","enum":["app","imported"]},"rung":{"type":"string","enum":["claimed","photo","on_site","confirmed","held"]},"scheduled_for":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"finished_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"price_minor_master":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"price_minor_customer":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"currency":{"type":"string","minLength":3,"maxLength":3},"confirmed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"hold_due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"held":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"no_show":{"type":"boolean"},"cancelled_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"disputed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"self_confirm_suspected":{"type":"boolean"},"self_confirm_note":{"anyOf":[{"type":"string"},{"type":"null"}]},"address":{"anyOf":[{"type":"string"},{"type":"null"}]},"signature_svg":{"anyOf":[{"type":"string"},{"type":"null"}]},"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"kind":{"type":"string"},"actor_kind":{"anyOf":[{"type":"string"},{"type":"null"}]},"data":{"type":"object","additionalProperties":{}},"at":{"type":"string","format":"date-time"}},"required":["id","kind","actor_kind","data","at"]}},"customer_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"travel":{"anyOf":[{"type":"object","properties":{"km":{"type":"number","minimum":0},"minutes":{"type":"integer","minimum":0}},"required":["km","minutes"]},{"type":"null"}]}},"required":["id","request_id","master_slug","master_name","job_type_id","city_id","cell","source","rung","scheduled_for","started_at","finished_at","price_minor_master","price_minor_customer","currency","confirmed_at","hold_due_at","held","no_show","cancelled_at","address","signature_svg","events","customer_name","travel"]}},"required":["job"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"any"}},"/api/v1/prices":{"get":{"summary":"Price range for a job type in a city: p25, median, p75 and n, in the city's currency. Nulls below n=5, then `empty` {reason, n, needed, next[]} says so.","tags":["prices"],"parameters":[{"name":"job_type","in":"query","required":true,"schema":{"type":"string"}},{"name":"city","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"currency":{"type":"string","minLength":3,"maxLength":3},"n":{"type":"integer"},"p25":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"median":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"p75":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}]},"empty":{"type":"object","properties":{"reason":{"const":"not_enough_confirmed_jobs"},"n":{"type":"integer"},"needed":{"type":"integer"},"next":{"type":"array","items":{"type":"object","properties":{"action":{"type":"string"},"method":{"type":"string","enum":["GET","POST","DELETE"]},"path":{"type":"string"},"query":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"}]}},"body":{"type":"object","additionalProperties":{}},"why":{"type":"string"}},"required":["action","method","path"]}}},"required":["reason","n","needed","next"]}},"required":["currency","n","p25","median","p75"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/api/v1/watches":{"get":{"summary":"Your \"tell me when a pro joins near a place\" watches.","tags":["watches"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"job_type_id":{"anyOf":[{"type":"integer"},{"type":"null"}]},"cell":{"type":"string"},"radius_km":{"type":"integer"},"channel":{"type":"string","enum":["email","webhook"]},"created_at":{"type":"string","format":"date-time"},"matched_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"matched_master_slug":{"anyOf":[{"type":"string"},{"type":"null"}]},"waiting_for_email":{"type":"boolean"},"centre":{"type":"object","properties":{"lat":{"type":"number"},"lng":{"type":"number"}},"required":["lat","lng"]},"place":{"anyOf":[{"type":"object","properties":{"city_id":{"type":"integer"},"name":{"type":"string"},"country":{"type":"string"}},"required":["city_id","name","country"]},{"type":"null"}]}},"required":["id","job_type_id","cell","radius_km","channel","created_at","matched_at","matched_master_slug","waiting_for_email"]}},"limit":{"type":"integer"}},"required":["results","limit"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"read"},"post":{"summary":"Watch a place: ONE email (or a watch.matched webhook event) when a new listed pro who does the job type joins within radius_km. The place is kept as an H3 cell only. A device-only account must send `email`; a sign-in link goes there and the watch starts once it is confirmed. Same watch twice returns the existing one. No scope needed: any signed-in actor, an unclaimed agent too (it uses channel webhook; with none registered it polls GET /watches for matched_at). At most 20 open watches and 30 new ones per hour per account. ?dry_run=true supported.","tags":["watches"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"watch":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"job_type_id":{"anyOf":[{"type":"integer"},{"type":"null"}]},"cell":{"type":"string"},"radius_km":{"type":"integer"},"channel":{"type":"string","enum":["email","webhook"]},"created_at":{"type":"string","format":"date-time"},"matched_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"matched_master_slug":{"anyOf":[{"type":"string"},{"type":"null"}]},"waiting_for_email":{"type":"boolean"},"centre":{"type":"object","properties":{"lat":{"type":"number"},"lng":{"type":"number"}},"required":["lat","lng"]},"place":{"anyOf":[{"type":"object","properties":{"city_id":{"type":"integer"},"name":{"type":"string"},"country":{"type":"string"}},"required":["city_id","name","country"]},{"type":"null"}]}},"required":["id","job_type_id","cell","radius_km","channel","created_at","matched_at","matched_master_slug","waiting_for_email"]},"existing":{"type":"boolean"},"confirm_email_sent_to":{"anyOf":[{"type":"string"},{"type":"null"}]},"note":{"type":"string"}},"required":["watch","existing","confirm_email_sent_to"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"job_type_id":{"type":"integer"},"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180},"radius_km":{"type":"integer","minimum":1,"maximum":200},"channel":{"type":"string","enum":["email","webhook"]},"email":{"type":"string","format":"email","maxLength":254}},"required":["lat","lng"]}}}}}},"/api/v1/watches/{id}":{"delete":{"summary":"Stop a watch. No scope needed (your own watches only).","tags":["watches"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/api/v1/me/webhooks":{"get":{"summary":"Your webhooks.","tags":["webhooks"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string","enum":["request.new","request.declined","offer.received","offer.accepted","job.on_site","job.photo","job.finished","job.confirmed","job.hold_due","job.held","job.hold_failed","job.no_show","job.disputed","watch.matched","grant.decided"]}},"created_at":{"type":"string","format":"date-time"},"disabled_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["id","url","events","created_at","disabled_at"]}}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"webhooks:write"},"post":{"summary":"Create a webhook; the secret is shown once. Deliveries are signed with HMAC-SHA256 in X-GoodHand-Signature.","tags":["webhooks"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"webhook":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string","enum":["request.new","request.declined","offer.received","offer.accepted","job.on_site","job.photo","job.finished","job.confirmed","job.hold_due","job.held","job.hold_failed","job.no_show","job.disputed","watch.matched","grant.decided"]}},"created_at":{"type":"string","format":"date-time"},"disabled_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["id","url","events","created_at","disabled_at"]},"secret":{"type":"string"}},"required":["webhook","secret"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string","enum":["request.new","request.declined","offer.received","offer.accepted","job.on_site","job.photo","job.finished","job.confirmed","job.hold_due","job.held","job.hold_failed","job.no_show","job.disputed","watch.matched","grant.decided"]}}},"required":["url","events"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"webhooks:write"}},"/api/v1/me/webhooks/{id}":{"delete":{"summary":"Delete a webhook.","tags":["webhooks"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"webhooks:write"}},"/api/v1/me/notifications":{"get":{"summary":"Your notifications.","tags":["webhooks"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"kind":{"type":"string"},"text":{"type":"string"},"data":{"type":"object","additionalProperties":{}},"created_at":{"type":"string","format":"date-time"},"read_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["id","kind","text","data","created_at","read_at"]}},"next_cursor":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}]},"unread":{"type":"integer"}},"required":["results","next_cursor","unread"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"any"}},"/api/v1/me/push":{"post":{"summary":"Register a web push subscription.","tags":["webhooks"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"endpoint":{"type":"string","format":"uri"},"keys":{"type":"object","properties":{"p256dh":{"type":"string"},"auth":{"type":"string"}},"required":["p256dh","auth"]}},"required":["endpoint","keys"]}}}},"security":[{"session":[]}],"x-scope":"session"},"delete":{"summary":"Remove a web push subscription.","tags":["webhooks"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"session":[]}],"x-scope":"session"}},"/api/v1/me/agents":{"get":{"summary":"Agents you have claimed.","tags":["agents"],"parameters":[],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"session":[]}],"x-scope":"session"}},"/api/v1/me/devices":{"get":{"summary":"Your registered devices.","tags":["auth"],"parameters":[],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"any"}},"/api/v1/me/webhooks/{id}/test":{"post":{"summary":"Send a signed test delivery.","tags":["webhooks"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"webhooks:write"}},"/api/v1/me/webhooks/{id}/deliveries":{"get":{"summary":"Recent deliveries of a webhook.","tags":["webhooks"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"webhooks:write"}},"/api/v1/me/notifications/read":{"post":{"summary":"Mark notifications read (ids or all).","tags":["webhooks"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"integer"}},"all":{"type":"boolean"}}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"any"}},"/api/v1/me/notifications/prefs":{"get":{"summary":"Notification preferences.","tags":["webhooks"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"boolean"}},"required":["email"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"any"},"put":{"summary":"Change notification preferences.","tags":["webhooks"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"boolean"}},"required":["email"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"boolean"}},"required":["email"]}}}},"security":[{"bearer":[]},{"session":[]}],"x-scope":"any"}},"/api/v1/push/key":{"get":{"summary":"The VAPID public key for web push.","tags":["webhooks"],"parameters":[],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/api/v1/imports/confirm/{token}":{"get":{"summary":"What a client is asked to confirm from an emailed import link.","tags":["jobs"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}}},"post":{"summary":"A client confirms an imported job from their emailed link. Only the client's own confirmation moves it to confirmed; one that arrives from the pro's own network answers state recorded with counted false and does not count. A disputed job (or one already confirmed) answers a structured conflict.","tags":["jobs"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/api/v1/imports/confirm/{token}/dispute":{"post":{"summary":"A client says an imported job was not theirs (\"I never had this job\"), from the same emailed link, no sign-in. Body {reason?}. The job never counts, the link is spent, the pro hears (job.disputed). Repeating answers the same {state: disputed}; after a confirmation it answers a conflict. GET on the link returns state: open | confirmed | disputed | cancelled | expired.","tags":["jobs"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK"},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/api/v1/masters/{id}/reports":{"post":{"summary":"Report a pro (id or slug): reason fake_jobs | not_who_they_say | unsafe_work | no_show_or_rude | other, optional text (max 1000) and contact_email. No sign-in needed; ?dry_run=true; rate limited. Never public; the pro is never told who reported. MCP: report_master.","tags":["masters"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"Validate and return what would happen, including what the pro would see."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"received":{"const":true},"report_id":{"type":"string","format":"uuid"}},"required":["received","report_id"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","enum":["fake_jobs","not_who_they_say","unsafe_work","no_show_or_rude","other"]},"text":{"type":"string","maxLength":1000},"contact_email":{"type":"string","format":"email","maxLength":254}},"required":["reason"]}}}}}},"/{lang}/{cc}/{city}/":{"get":{"summary":"SEO page (HTML); with Accept: application/json, the JSON twin (PageEnvelope). Same for /{lang}/, /{lang}/{cc}/, /{lang}/{cc}/{city}/{trade}/, .../speaks-{l}/, /{lang}/{cc}/{city}/prices/{job-type}/, /{lang}/{cc}/fix/, /{lang}/{cc}/fix/{brand}/, /{lang}/{cc}/fix/{brand}/{code}/, /{lang}/{cc}/m/{master}/. The old /{lang}/fix/... and /{lang}/m/{slug}/ answer 301 to these.","tags":["pages"],"parameters":[{"name":"lang","in":"path","required":true,"schema":{"type":"string"}},{"name":"cc","in":"path","required":true,"schema":{"type":"string"}},{"name":"city","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["home","country","city","city_trade","city_trade_speaks","city_prices","city_masters","fault_code","fault_hub","fault_brand","master","how_it_works","costs","privacy","terms","about"]},"lang":{"type":"string","enum":["en","es","pt","fr","de","it","pl","uk","tr","hi","id","ja","zh","ar","ca","fa","sq","hy","nl","sv","az","bs","bn","bg","ms","dz","be","el","cs","da","am","et","fi","fo","ka","hr","ht","hu","he","is","ky","km","ko","kk","lo","si","lt","lb","lv","ro","sr","mk","my","mn","tl","mt","dv","ny","no","ne","ur","ru","rw","sl","sk","so","th","tg","tk","to","sw","uz","vi","bi","sm","zu"]},"path":{"type":"string"},"canonical":{"type":"string"},"indexable":{"type":"boolean"},"noindex_reasons":{"type":"array","items":{"type":"string"}},"title":{"type":"string"},"description":{"type":"string"},"og_image":{"type":"string"},"alternates":{"type":"array","items":{"type":"object","properties":{"lang":{"type":"string"},"path":{"type":"string"}},"required":["lang","path"]}},"languages":{"type":"array","items":{"type":"object","properties":{"lang":{"type":"string"},"path":{"type":"string"}},"required":["lang","path"]}},"breadcrumbs":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"path":{"type":"string"}},"required":["name","path"]}},"data":{"type":"object","additionalProperties":{}}},"required":["kind","lang","path","canonical","indexable","noindex_reasons","title","description","alternates","languages","breadcrumbs","data"]}}}},"default":{"description":"Error: { error: { code, message, next? } }, where next says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/sitemap.xml":{"get":{"summary":"Sitemap index; per-country sitemaps hold only indexable pages.","tags":["pages"],"responses":{"200":{"description":"XML"}}}},"/llms.txt":{"get":{"summary":"What GoodHand is and how an agent uses it.","tags":["pages"],"responses":{"200":{"description":"Text"}}}},"/.well-known/mcp.json":{"get":{"summary":"MCP server manifest.","tags":["pages"],"responses":{"200":{"description":"JSON"}}}}},"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"API key (gh_...) from POST /api/v1/agents or /api/v1/me/keys."},"session":{"type":"apiKey","in":"cookie","name":"gh_session"}},"schemas":{"ApiError":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["agents_cannot_confirm","sign_in_required","scope_missing","not_found","validation","rate_limited","idempotency_conflict","forbidden","conflict","expired","too_far","self_confirm","internal"]},"message":{"type":"string"},"next":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["code","message"]}},"required":["error"]}}}}