{"openapi":"3.1.0","info":{"title":"Hodos Public API","version":"1.4.0","description":"Hodos is a roofing CRM. This API lets another program, including an AI agent, read a company's contacts, jobs, pipelines, team, tasks, insurance claims, files and the key owner's notifications, and add tasks, notes (tagging teammates), tags and files.\n\nSign in: an admin creates a key in Hodos under Settings > API access, picks its scopes and expiry, and copies the secret once. Send it on every call as `Authorization: Bearer <key>`. A key only sees its own company, never more than the admin who made it, and stops working when it expires, is revoked, or that admin loses access.\n\nEvery response is JSON with `data` and a `request_id`. Errors return `error.code` and `error.message`. Lists return up to `limit` records and a `page.next_cursor`; pass it back as `cursor` for the next page, and stop when it is null. Each key can make 120 calls a minute and each company 1,200; creating, changing or deleting tasks is capped at 30 a minute per key. Past a limit the API answers 429 with a `Retry-After` header.\n\nThis API cannot text, email or call customers, take payments, or sign documents.\n\nChat apps such as ChatGPT and Claude should use the Hodos connector at https://hodos.strutq.com/api/mcp instead, where each person signs in as themselves."},"servers":[{"url":"https://hodos.strutq.com/api/v1","description":"Production"},{"url":"https://staging-hodos.strutq.com/api/v1","description":"Staging"}],"tags":[{"name":"Account"},{"name":"Contacts"},{"name":"Jobs"},{"name":"Team"},{"name":"Tasks"},{"name":"Claims"},{"name":"Files"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"hodos_live"}},"schemas":{"ApiError":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}},"ErrorResponse":{"type":"object","required":["error","request_id"],"properties":{"error":{"$ref":"#/components/schemas/ApiError"},"request_id":{"type":"string","format":"uuid"}}},"Page":{"type":"object","required":["next_cursor"],"properties":{"next_cursor":{"type":["string","null"]}}},"SuccessResponse":{"type":"object","required":["data","request_id"],"properties":{"data":{},"request_id":{"type":"string","format":"uuid"}}},"DetailResponse":{"type":"object","required":["data","request_id"],"properties":{"data":{"type":"object"},"request_id":{"type":"string","format":"uuid"}}},"ListResponse":{"type":"object","required":["data","page","request_id"],"properties":{"data":{"type":"array","items":{}},"page":{"$ref":"#/components/schemas/Page"},"request_id":{"type":"string","format":"uuid"}}},"Me":{"type":"object","required":["key_id","display_prefix","org_id","scopes","expires_at"],"properties":{"key_id":{"type":"string","format":"uuid","description":"This key's id."},"display_prefix":{"type":"string","description":"The visible start of the key, to tell keys apart."},"org_id":{"type":"string","format":"uuid","description":"The company this key belongs to."},"scopes":{"type":"array","items":{"type":"string"},"description":"Scopes this key can use right now."},"expires_at":{"type":["string","null"],"format":"date-time"}}},"MeResponse":{"type":"object","required":["data","request_id"],"properties":{"data":{"$ref":"#/components/schemas/Me"},"request_id":{"type":"string","format":"uuid"}}},"Contact":{"type":"object","description":"A homeowner or other customer.","required":["id","tags","display_name","phone","email","address","city","state","zip","created_at","updated_at"],"properties":{"tags":{"type":"array","items":{"type":"string"},"description":"Labels on the contact. Change them with the contact tags call."},"id":{"type":"string","format":"uuid"},"display_name":{"type":"string","description":"Full name as shown in Hodos."},"phone":{"type":["string","null"]},"email":{"type":["string","null"]},"address":{"type":["string","null"],"description":"Street address."},"city":{"type":["string","null"]},"state":{"type":["string","null"]},"zip":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"ContactResponse":{"type":"object","required":["data","request_id"],"properties":{"data":{"$ref":"#/components/schemas/Contact"},"request_id":{"type":"string","format":"uuid"}}},"ContactListResponse":{"type":"object","required":["data","page","request_id"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Contact"}},"page":{"$ref":"#/components/schemas/Page"},"request_id":{"type":"string","format":"uuid"}}},"Job":{"type":"object","description":"A roofing job for one contact, sitting in one stage of a pipeline.","required":["id","tags","name","contact_id","pipeline_id","stage_id","address","city","state","zip","priority","created_at","updated_at"],"properties":{"tags":{"type":"array","items":{"type":"string"},"description":"Labels on the job. Change them with the job tags call."},"id":{"type":"string","format":"uuid"},"name":{"type":"string","description":"Job name, usually the customer and address."},"contact_id":{"type":"string","format":"uuid","description":"The job's main contact."},"pipeline_id":{"type":"string","format":"uuid","description":"The pipeline (workflow) the job is in."},"stage_id":{"type":"string","format":"uuid","description":"The pipeline stage the job is in now."},"address":{"type":["string","null"],"description":"Job site street address."},"city":{"type":["string","null"]},"state":{"type":["string","null"]},"zip":{"type":["string","null"]},"priority":{"type":"string","enum":["low","normal","high","urgent"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"JobResponse":{"type":"object","required":["data","request_id"],"properties":{"data":{"$ref":"#/components/schemas/Job"},"request_id":{"type":"string","format":"uuid"}}},"JobListResponse":{"type":"object","required":["data","page","request_id"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Job"}},"page":{"$ref":"#/components/schemas/Page"},"request_id":{"type":"string","format":"uuid"}}},"Stage":{"type":"object","required":["id","label","key","group","position"],"properties":{"id":{"type":"string","format":"uuid","description":"Matches a job's `stage_id`."},"label":{"type":"string","description":"The stage name as the board shows it."},"key":{"type":"string"},"group":{"type":"string","description":"Which part of the job's life the stage belongs to, such as lead or production."},"position":{"type":"integer","description":"Order on the board, lowest first."}}},"Pipeline":{"type":"object","required":["id","name","stages"],"properties":{"id":{"type":"string","format":"uuid","description":"Matches a job's `pipeline_id`."},"name":{"type":"string"},"stages":{"type":"array","items":{"$ref":"#/components/schemas/Stage"}}}},"PipelineListResponse":{"type":"object","required":["data","request_id"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Pipeline"}},"request_id":{"type":"string","format":"uuid"}}},"Teammate":{"type":"object","required":["id","name","role"],"properties":{"id":{"type":"string","format":"uuid","description":"Hodos user id, as in `assignee_id` and `author_user_id`."},"name":{"type":["string","null"]},"role":{"type":"string","description":"admin or rep."}}},"TeamListResponse":{"type":"object","required":["data","request_id"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Teammate"}},"request_id":{"type":"string","format":"uuid"}}},"Task":{"type":"object","description":"A to-do for the team, optionally tied to a job or contact.","required":["id","title","description","priority","due_date","assignee_id","job_id","contact_id","completed_at","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"description":{"type":["string","null"]},"priority":{"type":"string","enum":["low","normal","high","urgent"]},"due_date":{"type":["string","null"],"format":"date"},"assignee_id":{"type":["string","null"],"format":"uuid","description":"The main person assigned."},"job_id":{"type":["string","null"],"format":"uuid"},"contact_id":{"type":["string","null"],"format":"uuid"},"completed_at":{"type":["string","null"],"format":"date-time","description":"Set when the task is done; null while open."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"TaskResponse":{"type":"object","required":["data","request_id"],"properties":{"data":{"$ref":"#/components/schemas/Task"},"request_id":{"type":"string","format":"uuid"}}},"TaskListResponse":{"type":"object","required":["data","page","request_id"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Task"}},"page":{"$ref":"#/components/schemas/Page"},"request_id":{"type":"string","format":"uuid"}}},"CreatedTask":{"allOf":[{"$ref":"#/components/schemas/Task"},{"type":"object","required":["assignee_ids"],"properties":{"assignee_ids":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Everyone assigned to the task."}}}]},"TaskCreatedResponse":{"type":"object","required":["data","request_id"],"properties":{"data":{"$ref":"#/components/schemas/CreatedTask"},"request_id":{"type":"string","format":"uuid"}}},"ClaimSummary":{"type":"object","required":["id","job_id","claim_number","carrier","scope_status","filed_on","approved_on","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"job_id":{"type":"string","format":"uuid"},"claim_number":{"type":["string","null"]},"carrier":{"type":["string","null"]},"scope_status":{"type":"string"},"filed_on":{"type":["string","null"],"format":"date"},"approved_on":{"type":["string","null"],"format":"date"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"ClaimJob":{"type":"object","required":["id","name","contact_id","address","city","state","zip"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"contact_id":{"type":["string","null"],"format":"uuid"},"address":{"type":["string","null"]},"city":{"type":["string","null"]},"state":{"type":["string","null"]},"zip":{"type":["string","null"]}}},"ClaimDetail":{"allOf":[{"$ref":"#/components/schemas/ClaimSummary"},{"type":"object","required":["job"],"properties":{"job":{"$ref":"#/components/schemas/ClaimJob"}}}]},"ClaimResponse":{"type":"object","required":["data","request_id"],"properties":{"data":{"$ref":"#/components/schemas/ClaimDetail"},"request_id":{"type":"string","format":"uuid"}}},"ClaimListResponse":{"type":"object","required":["data","page","request_id"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ClaimSummary"}},"page":{"$ref":"#/components/schemas/Page"},"request_id":{"type":"string","format":"uuid"}}},"Note":{"type":"object","required":["id","body","external_id","author_user_id","created_at"],"properties":{"in_reply_to":{"type":"string","format":"uuid","description":"The note this one answers, when it is a reply."},"mention_user_ids":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Teammates tagged in the note, when it was added with the job notes call."},"id":{"type":"string","format":"uuid"},"body":{"type":["string","null"]},"external_id":{"type":["string","null"]},"author_user_id":{"type":["string","null"],"format":"uuid"},"created_at":{"type":"string","format":"date-time"}}},"NoteResponse":{"type":"object","required":["data","request_id"],"properties":{"data":{"$ref":"#/components/schemas/Note"},"request_id":{"type":"string","format":"uuid"}}},"NoteListResponse":{"type":"object","required":["data","page","request_id"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Note"}},"page":{"$ref":"#/components/schemas/Page"},"request_id":{"type":"string","format":"uuid"}}},"File":{"type":"object","required":["id","name","kind","folder","mime_type","size_bytes","uploaded_by","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"kind":{"type":"string"},"folder":{"type":["string","null"]},"mime_type":{"type":["string","null"]},"size_bytes":{"type":"integer"},"uploaded_by":{"type":["string","null"],"format":"uuid"},"created_at":{"type":"string","format":"date-time"}}},"FileListResponse":{"type":"object","required":["data","page","request_id"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/File"}},"page":{"$ref":"#/components/schemas/Page"},"request_id":{"type":"string","format":"uuid"}}},"UploadMetadata":{"type":"object","additionalProperties":false,"required":["hodosUploadIntentId","attachmentId","declaredContentSha256","digestSource"],"properties":{"hodosUploadIntentId":{"type":"string","format":"uuid"},"attachmentId":{"type":"string","format":"uuid"},"declaredContentSha256":{"type":"string","pattern":"^[0-9a-f]{64}$"},"digestSource":{"type":"string","const":"client-declared-sha256-v1"}}},"Upload":{"type":"object","required":["status","intent_id","attachment_id","expires_at","mime_type","size_bytes"],"properties":{"status":{"type":"string","enum":["authorized","registered"]},"intent_id":{"type":"string","format":"uuid"},"attachment_id":{"type":"string","format":"uuid"},"expires_at":{"type":"string","format":"date-time"},"mime_type":{"type":"string","enum":["image/jpeg","image/png","image/webp","application/pdf","application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document"]},"size_bytes":{"type":"integer","minimum":1,"maximum":26214400},"bucket":{"type":"string","const":"job-files"},"upload_path":{"type":"string"},"upload_token":{"type":"string"},"upload_url":{"type":"string","format":"uri"},"upload_metadata":{"$ref":"#/components/schemas/UploadMetadata"}}},"UploadResponse":{"type":"object","required":["data","request_id"],"properties":{"data":{"$ref":"#/components/schemas/Upload"},"request_id":{"type":"string","format":"uuid"}}},"Finalize":{"type":"object","required":["status","intent_id","attachment_id"],"properties":{"status":{"type":"string","enum":["awaiting_upload","registered"]},"intent_id":{"type":"string","format":"uuid"},"attachment_id":{"type":"string","format":"uuid"}}},"FinalizeResponse":{"type":"object","required":["data","request_id"],"properties":{"data":{"$ref":"#/components/schemas/Finalize"},"request_id":{"type":"string","format":"uuid"}}},"TaskUpdateInput":{"type":"object","additionalProperties":false,"required":["completed"],"properties":{"completed":{"type":"boolean","description":"`true` marks the task done, `false` reopens it."}}},"TaskArchived":{"type":"object","required":["id","archived"],"properties":{"id":{"type":"string","format":"uuid"},"archived":{"type":"boolean","const":true}}},"TaskArchivedResponse":{"type":"object","required":["data","request_id"],"properties":{"data":{"$ref":"#/components/schemas/TaskArchived"},"request_id":{"type":"string","format":"uuid"}}},"TaskCreateInput":{"type":"object","additionalProperties":false,"required":["title"],"properties":{"title":{"type":"string","minLength":1,"maxLength":200},"description":{"type":["string","null"],"maxLength":4000},"priority":{"type":"string","enum":["low","normal","high","urgent"],"default":"normal"},"due_date":{"type":["string","null"],"format":"date","description":"YYYY-MM-DD."},"job_id":{"type":["string","null"],"format":"uuid","description":"Tie the task to this job. Also needs `jobs.read`."},"contact_id":{"type":["string","null"],"format":"uuid","description":"Tie the task to this contact. Also needs `contacts.read`."},"assignee_ids":{"type":"array","maxItems":50,"items":{"type":"string","format":"uuid"},"description":"Hodos user ids to assign, up to 50. Each must be an active teammate."}}},"NoteCreateInput":{"type":"object","additionalProperties":false,"required":["body"],"properties":{"in_reply_to":{"type":"string","format":"uuid","description":"Reply to this note. It must be a note on the same job. It shows under that note in Hodos and alerts the person who wrote it."},"body":{"type":"string","minLength":1,"maxLength":10000},"external_id":{"type":"string","minLength":1,"maxLength":128}}},"FileUploadInput":{"type":"object","additionalProperties":false,"required":["request_id","attachment_id","name","kind","mime_type","size_bytes","sha256"],"properties":{"request_id":{"type":"string","format":"uuid"},"attachment_id":{"type":"string","format":"uuid"},"name":{"type":"string","maxLength":255},"kind":{"type":"string","enum":["photo","scope","report","other"]},"mime_type":{"type":"string","enum":["image/jpeg","image/png","image/webp","application/pdf","application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document"]},"size_bytes":{"type":"integer","minimum":1,"maximum":26214400},"sha256":{"type":"string","pattern":"^[0-9a-f]{64}$"}}},"JobNoteCreateInput":{"type":"object","additionalProperties":false,"required":["body"],"properties":{"in_reply_to":{"type":"string","format":"uuid","description":"Reply to this note. It must be a note on the same job. It shows under that note in Hodos and alerts the person who wrote it."},"body":{"type":"string","minLength":1,"maxLength":10000},"external_id":{"type":"string","minLength":1,"maxLength":128,"description":"Your own id for the note, to avoid adding it twice."},"mention_user_ids":{"type":"array","maxItems":20,"items":{"type":"string","format":"uuid"},"description":"Teammates to tag, from the team list. Each must be an active teammate."}}},"TagsInput":{"type":"object","additionalProperties":false,"description":"Send `set` on its own, or `add` and/or `remove` (at least one), not both kinds.","properties":{"set":{"type":"array","maxItems":50,"items":{"type":"string","minLength":1,"maxLength":40}},"add":{"type":"array","maxItems":50,"items":{"type":"string","minLength":1,"maxLength":40}},"remove":{"type":"array","maxItems":50,"items":{"type":"string","minLength":1,"maxLength":40}}},"oneOf":[{"required":["set"],"not":{"anyOf":[{"required":["add"]},{"required":["remove"]}]}},{"anyOf":[{"required":["add"]},{"required":["remove"]}],"not":{"required":["set"]}}]},"Tags":{"type":"object","required":["id","tags"],"properties":{"id":{"type":"string","format":"uuid"},"tags":{"type":"array","items":{"type":"string"}}}},"TagsResponse":{"type":"object","required":["data","request_id"],"properties":{"data":{"$ref":"#/components/schemas/Tags"},"request_id":{"type":"string","format":"uuid"}}},"Notification":{"type":"object","required":["id","event_key","title","body","href","record_type","record_id","created_at","read"],"properties":{"id":{"type":"string","format":"uuid"},"event_key":{"type":"string","description":"What kind of alert it is, such as note_mention."},"title":{"type":"string"},"body":{"type":["string","null"]},"href":{"type":["string","null"],"description":"A Hodos path to open."},"record_type":{"type":["string","null"]},"record_id":{"type":["string","null"],"format":"uuid"},"created_at":{"type":"string","format":"date-time"},"read":{"type":"boolean"}}},"NotificationListResponse":{"type":"object","required":["data","page","request_id"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Notification"}},"page":{"$ref":"#/components/schemas/Page"},"request_id":{"type":"string","format":"uuid"}}},"Read":{"type":"object","required":["id","read"],"properties":{"id":{"type":"string","format":"uuid"},"read":{"type":"boolean"}}},"ReadResponse":{"type":"object","required":["data","request_id"],"properties":{"data":{"$ref":"#/components/schemas/Read"},"request_id":{"type":"string","format":"uuid"}}},"ReadAll":{"type":"object","required":["read_through"],"properties":{"read_through":{"type":["string","null"],"format":"date-time","description":"The cutoff before which everything is read. Null when this key can only see some alerts: the ones it can see are marked read, but no overall cutoff is set."}}},"ReadAllResponse":{"type":"object","required":["data","request_id"],"properties":{"data":{"$ref":"#/components/schemas/ReadAll"},"request_id":{"type":"string","format":"uuid"}}},"BulkItemError":{"type":"object","required":["code"],"properties":{"code":{"type":"string","description":"Why this one file failed, such as invalid_request or not_found."},"message":{"type":"string"}}},"BulkUploadInput":{"type":"object","additionalProperties":false,"required":["files"],"properties":{"files":{"type":"array","minItems":1,"maxItems":20,"items":{"$ref":"#/components/schemas/FileUploadInput"}}}},"BulkUploadItem":{"type":"object","required":["request_id","attachment_id","status"],"properties":{"request_id":{"type":["string","null"],"description":"Null when the item did not carry a usable id."},"attachment_id":{"type":["string","null"],"description":"Null when the item did not carry a usable id."},"status":{"type":"integer","description":"201 reserved, 200 already reserved or registered, or an error status."},"upload":{"$ref":"#/components/schemas/Upload"},"error":{"$ref":"#/components/schemas/BulkItemError"}}},"BulkUploadResponse":{"type":"object","required":["data","request_id"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BulkUploadItem"}},"request_id":{"type":"string","format":"uuid"}}},"BulkFinalizeInput":{"type":"object","additionalProperties":false,"required":["intent_ids"],"properties":{"intent_ids":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"string","format":"uuid"}}}},"BulkFinalizeItem":{"type":"object","required":["intent_id","status"],"properties":{"intent_id":{"type":"string","format":"uuid"},"status":{"type":"integer","description":"200 registered, 202 bytes not there yet, or an error status."},"result":{"$ref":"#/components/schemas/Finalize"},"error":{"$ref":"#/components/schemas/BulkItemError"}}},"BulkFinalizeResponse":{"type":"object","required":["data","request_id"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BulkFinalizeItem"}},"request_id":{"type":"string","format":"uuid"}}}}},"paths":{"/me":{"get":{"operationId":"me","tags":["Account"],"summary":"Check the API key","description":"Returns the key's id, its company (org_id), the scopes it can use right now, and when it expires. Call this first to confirm a key works. Needs no scope.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/contacts":{"get":{"operationId":"contacts_list","tags":["Contacts"],"summary":"List contacts","description":"Lists the company's contacts (homeowners and other customers), newest first. Archived contacts are left out. Page through with `cursor`. Needs `contacts.read`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"How many records to return, 1 to 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","required":false,"description":"The `page.next_cursor` from the previous page. Leave out for the first page.","schema":{"type":"string","maxLength":2048}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactListResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/contacts/{id}":{"get":{"operationId":"contacts_read","tags":["Contacts"],"summary":"Get a contact","description":"Returns one contact by id. Needs `contacts.read`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The record's Hodos id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/contacts/{id}/tags":{"patch":{"operationId":"contacts_tags_update","tags":["Contacts"],"summary":"Change a contact's tags","description":"Changes the tags on a contact. Send `set` to replace them all, or `add` and/or `remove` to change some. Tags are 1 to 40 characters, up to 50 per contact, and matching ignores capitals. Repeating the same call changes nothing more. Needs `contacts.read` and `contacts.update`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The record's Hodos id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagsInput"}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagsResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/jobs":{"get":{"operationId":"jobs_list","tags":["Jobs"],"summary":"List jobs","description":"Lists the company's jobs, newest first. Archived jobs are left out. Each job names its contact, its pipeline and the stage it sits in. Page through with `cursor`. Needs `jobs.read`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"How many records to return, 1 to 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","required":false,"description":"The `page.next_cursor` from the previous page. Leave out for the first page.","schema":{"type":"string","maxLength":2048}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobListResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/jobs/{id}":{"get":{"operationId":"jobs_read","tags":["Jobs"],"summary":"Get a job","description":"Returns one job by id. Needs `jobs.read`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The record's Hodos id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/jobs/{id}/tags":{"patch":{"operationId":"jobs_tags_update","tags":["Jobs"],"summary":"Change a job's tags","description":"Changes the tags on a job. Send `set` to replace them all, or `add` and/or `remove` to change some. Tags are 1 to 40 characters, up to 50 per job, and matching ignores capitals. Repeating the same call changes nothing more. Needs `jobs.read` and `jobs.update`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The record's Hodos id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagsInput"}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagsResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/jobs/{id}/notes":{"post":{"operationId":"job_notes_create","tags":["Jobs"],"summary":"Add a note to a job and tag teammates","description":"Adds a plain-text note to the job's timeline. The team sees it; the customer does not. List teammates in `mention_user_ids` (from the team list, up to 20) to tag them: each gets a Hodos alert and, unless they turned it off, an email, the same as a tag typed in the app. Send a unique `Idempotency-Key`. Needs `jobs.read`, `notes.read` and `notes.create`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The record's Hodos id.","schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"A unique value per new record, such as a UUID. Retrying with the same key never makes a duplicate.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[!-~]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobNoteCreateInput"}}}},"responses":{"200":{"description":"Idempotent replay","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteResponse"}}}},"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/jobs/{id}/files":{"get":{"operationId":"job_files_list","tags":["Files"],"summary":"List files on a job","description":"Lists file details (not the bytes) on the job, newest first. Download one with the file download call. Needs `jobs.read` and `files.read`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The record's Hodos id.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"How many records to return, 1 to 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","required":false,"description":"The `page.next_cursor` from the previous page. Leave out for the first page.","schema":{"type":"string","maxLength":2048}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileListResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/jobs/{id}/files/uploads":{"post":{"operationId":"job_file_upload_init","tags":["Files"],"summary":"Start a file upload on a job","description":"Step 1 of 2 for one file. Reserves an upload and returns a one-time `upload_url`. PUT the raw bytes there with `upload_metadata` base64-encoded in an `x-metadata` header, then call finish upload. The `Idempotency-Key` must equal `request_id`. Needs `jobs.read`, `files.read` and `files.create`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The record's Hodos id.","schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"A unique value per new record, such as a UUID. Retrying with the same key never makes a duplicate.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[!-~]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileUploadInput"}}}},"responses":{"200":{"description":"Idempotent replay","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadResponse"}}}},"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"File too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/jobs/{id}/files/uploads/bulk":{"post":{"operationId":"job_file_upload_bulk_init","tags":["Files"],"summary":"Start up to 20 file uploads on a job","description":"Step 1 of 3 for many files. Reserves up to 20 uploads in one call; each answer carries its own status and `upload`, or an `error`. Repeating a `request_id` returns the same reservation. PUT each file's bytes to its `upload_url` (in parallel is fine), then call bulk finish. Needs `jobs.read`, `files.read` and `files.create`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The record's Hodos id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkUploadInput"}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkUploadResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/pipelines":{"get":{"operationId":"pipelines_list","tags":["Jobs"],"summary":"List pipelines and stages","description":"Lists the company's job pipelines in board order, each with its stages in order, so a job's `pipeline_id` and `stage_id` can be shown by name. One page holds them all. Needs `jobs.read`.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineListResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/tasks":{"get":{"operationId":"tasks_list","tags":["Tasks"],"summary":"List tasks","description":"Lists the company's tasks, newest first, open and completed. A task with `completed_at` set is done. Page through with `cursor`. Needs `tasks.read`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"How many records to return, 1 to 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","required":false,"description":"The `page.next_cursor` from the previous page. Leave out for the first page.","schema":{"type":"string","maxLength":2048}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskListResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"tasks_create","tags":["Tasks"],"summary":"Create a task","description":"Creates a task, optionally tied to a job or contact and assigned to teammates. Send a unique `Idempotency-Key`: repeating the same key with the same body returns the task already made (200) instead of a second copy, and reusing it with a different body returns 409. Needs `tasks.create`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"A unique value per new record, such as a UUID. Retrying with the same key never makes a duplicate.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[!-~]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCreateInput"}}}},"responses":{"200":{"description":"Idempotent replay","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCreatedResponse"}}}},"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCreatedResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/tasks/{id}":{"get":{"operationId":"tasks_read","tags":["Tasks"],"summary":"Get a task","description":"Returns one task by id. Needs `tasks.read`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The record's Hodos id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"tasks_update","tags":["Tasks"],"summary":"Mark a task done or reopen it","description":"Send `{\"completed\": true}` to mark the task done or `{\"completed\": false}` to reopen it. Doing the same thing twice is harmless. The job timeline records it as done by the key's owner. Needs `tasks.update`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The record's Hodos id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskUpdateInput"}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCreatedResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"tasks_archive","tags":["Tasks"],"summary":"Delete a task","description":"Deletes a task the way the app does: it is archived, not destroyed, and no longer appears in task lists or reads. Deleting it twice returns not found. Needs `tasks.archive`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The record's Hodos id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskArchivedResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/team":{"get":{"operationId":"team_list","tags":["Team"],"summary":"List teammates","description":"Lists the company's active people with their name and role, so a task's `assignee_id` or a note's `author_user_id` can be shown by name, and `assignee_ids` can be filled when creating a task. One page holds them all. Needs `tasks.read`.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamListResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/claims":{"get":{"operationId":"claims_list","tags":["Claims"],"summary":"List insurance claims","description":"Lists insurance claims on jobs this key can see. Filter with `claim_number`. A claim number is not unique across carriers, so check the job before choosing a match. Needs `jobs.read`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"How many records to return, 1 to 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","required":false,"description":"The `page.next_cursor` from the previous page. Leave out for the first page.","schema":{"type":"string","maxLength":2048}},{"name":"claim_number","in":"query","required":false,"description":"Only claims with this exact claim number.","schema":{"type":"string","maxLength":120}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimListResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/claims/{id}":{"get":{"operationId":"claims_read","tags":["Claims"],"summary":"Get an insurance claim","description":"Returns one claim with the job it belongs to. Needs `jobs.read`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The record's Hodos id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/claims/{id}/notes":{"get":{"operationId":"claim_notes_list","tags":["Claims"],"summary":"List notes on a claim's job","description":"Lists the shared notes on the claim's job, newest first. Needs `jobs.read` and `notes.read`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The record's Hodos id.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"How many records to return, 1 to 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","required":false,"description":"The `page.next_cursor` from the previous page. Leave out for the first page.","schema":{"type":"string","maxLength":2048}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteListResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"claim_notes_create","tags":["Claims"],"summary":"Add a note to a claim's job","description":"Adds a plain-text note to the claim's job timeline. The team sees it; the customer does not. Send a unique `Idempotency-Key`. Needs `jobs.read`, `notes.read` and `notes.create`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The record's Hodos id.","schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"A unique value per new record, such as a UUID. Retrying with the same key never makes a duplicate.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[!-~]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteCreateInput"}}}},"responses":{"200":{"description":"Idempotent replay","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteResponse"}}}},"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/claims/{id}/files":{"get":{"operationId":"claim_files_list","tags":["Files"],"summary":"List files on a claim's job","description":"Lists file details (not the bytes) on the claim's job. Download one with the file download call. Needs `jobs.read` and `files.read`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The record's Hodos id.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"How many records to return, 1 to 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","required":false,"description":"The `page.next_cursor` from the previous page. Leave out for the first page.","schema":{"type":"string","maxLength":2048}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileListResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/files/{id}/download":{"get":{"operationId":"file_download","tags":["Files"],"summary":"Download a file","description":"Returns the bytes of one file this key can see. Needs `jobs.read`, `files.read` and `files.download`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The record's Hodos id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"File bytes","content":{"application/octet-stream":{"schema":{"type":"string","contentEncoding":"binary"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"File too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/claims/{id}/files/uploads":{"post":{"operationId":"claim_file_upload_init","tags":["Files"],"summary":"Start a file upload","description":"Step 1 of 2. Reserves an upload for the claim's job and returns a one-time `upload_url`. PUT the raw bytes there with `upload_metadata` base64-encoded in an `x-metadata` header, then call finish upload. The `Idempotency-Key` must equal `request_id`. Needs `jobs.read`, `files.read` and `files.create`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The record's Hodos id.","schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"A unique value per new record, such as a UUID. Retrying with the same key never makes a duplicate.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[!-~]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileUploadInput"}}}},"responses":{"200":{"description":"Idempotent replay","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadResponse"}}}},"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"File too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/files/uploads/{id}/finalize":{"post":{"operationId":"file_upload_finalize","tags":["Files"],"summary":"Finish a file upload","description":"Step 2 of 2. Checks the uploaded file's type, size and the checksum you declared match what was reserved, and adds the file to the job. Returns 202 if the bytes have not arrived yet; call again shortly. Needs `jobs.read`, `files.read` and `files.create`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The record's Hodos id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Registered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinalizeResponse"}}}},"202":{"description":"Awaiting upload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinalizeResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Metadata mismatch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/files/uploads/bulk-finalize":{"post":{"operationId":"job_files_finalize_bulk","tags":["Files"],"summary":"Finish up to 20 file uploads","description":"Step 3 of 3 for many files. Checks each uploaded file against what was reserved and adds it to its job. Each answer carries its own status: 200 registered, 202 bytes not there yet (call again shortly), or an `error`. Needs `jobs.read`, `files.read` and `files.create`.","security":[{"bearerAuth":[]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkFinalizeInput"}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkFinalizeResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/notifications":{"get":{"operationId":"notifications_list","tags":["Notifications"],"summary":"List the key owner's notifications","description":"Lists the Hodos notifications of the person who owns the key, newest first, each with whether it has been read. Add `unread=true` for only the unread ones. `href` is a Hodos path, such as `/jobs/{id}`. Needs `notifications.read`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"How many records to return, 1 to 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","required":false,"description":"The `page.next_cursor` from the previous page. Leave out for the first page.","schema":{"type":"string","maxLength":2048}},{"name":"unread","in":"query","required":false,"description":"Send `true` for only the notifications not yet read.","schema":{"type":"string","enum":["true","false"]}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationListResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/notifications/{id}/read":{"post":{"operationId":"notification_read","tags":["Notifications"],"summary":"Mark a notification read","description":"Marks one notification read, by the `id` the list gave. Needs `notifications.read` and `notifications.update`.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The record's Hodos id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReadResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/notifications/read-all":{"post":{"operationId":"notifications_read_all","tags":["Notifications"],"summary":"Mark all notifications read","description":"Marks everything up to now read for the key owner. Needs `notifications.read` and `notifications.update`.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReadAllResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}