{"openapi":"3.0.3","info":{"title":"PPMdesk API","version":"2026-10","description":"The PPMdesk API lets a firm's other systems read and add its clients, sites, assets and jobs, follow quotes, invoices and compliance, and hear about events as they happen.\n\n## Keys\n\nMake a key in **Settings, API and webhooks** (owners and admins). Send it on every request:\n\n```\nX-API-Key: ppm_...\n```\n\n(`Authorization: Bearer ppm_...` works too.) A key acts as the person who made it, sees only that firm's records, and lasts up to a year. A **read** key can only look; a **write** key can also add and change. Keys work on `/api/v1` only.\n\n## Requests and responses\n\nJSON in, JSON out, field names in snake_case. Money is a number in pounds. Dates without a time are `YYYY-MM-DD`; times are ISO 8601 in UTC. Lists take `page` and `per_page` (up to 100) and return `total` and `has_more`. `updated_since` on any list gives what changed since your last sync.\n\nErrors come back with an HTTP status and `{ \"error\": \"...\", \"code\": \"...\" }`. Up to 600 requests a minute per key.\n\n## Webhooks\n\nAdd a webhook in Settings (or `POST /api/v1/webhooks`) and PPMdesk POSTs each event you choose to your address, as JSON:\n\n```json\n{ \"id\": \"evt_...\", \"event\": \"job.completed\", \"created_at\": \"2026-10-27T10:15:00Z\", \"data\": { ...the job... }, \"previous\": { \"status\": \"IN_PROGRESS\" } }\n```\n\n**Checking it's from PPMdesk.** Each call carries `X-PPMdesk-Signature: t=<unix time>,v1=<signature>`, where the signature is the hex HMAC-SHA256 of `<t>.<raw body>` with your webhook's signing secret. Refuse a call whose signature doesn't match, or whose time is more than five minutes out. In Node:\n\n```js\nconst [t, v1] = header.split(',').map((p) => p.split('=')[1]);\nconst expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');\nconst ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1)) && Math.abs(Date.now() / 1000 - t) < 300;\n```\n\n**Proving the address is yours.** When a webhook is added, PPMdesk POSTs `{ \"type\": \"webhook.verification\", \"challenge\": \"...\" }` to it; reply 200 with the challenge anywhere in the body. Zapier and Make addresses skip this.\n\n**Replying.** Reply 2xx within 15 seconds. Anything else is retried after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 12 hours. Ten failures in a row turn the webhook off and tell the firm's admins. An event can arrive more than once: use its `id` to ignore repeats. Headers `X-PPMdesk-Event` and `X-PPMdesk-Event-Id` say what it is.\n\n| Event | When |\n|---|---|\n| `job.created` | A job is raised: reactive, quoted, or a planned visit added by a PPM plan (type PPM). |\n| `job.scheduled` | A job is booked for a day, or its booked day changes. |\n| `job.status_changed` | A job moves to another status. previous.status says where it was. |\n| `job.completed` | The work is done and the job sheet is in. |\n| `job.cancelled` | A job is cancelled. |\n| `defect.reported` | An engineer reports a fault or remedial work on a visit. |\n| `quote.created` | A quote is started. |\n| `quote.sent` | A quote is sent to the client. |\n| `quote.accepted` | The client accepts a quote. |\n| `quote.declined` | The client declines a quote. |\n| `invoice.created` | An invoice is drafted. |\n| `invoice.sent` | An invoice is sent to the client. |\n| `invoice.paid` | An invoice is paid in full (also when Xero says so). |\n| `invoice.voided` | An invoice is voided. |\n| `client.created` | A client is added. |\n| `site.created` | A site is added. |\n| `contact.created` | A contact is added. |\n| `asset.created` | An asset is added. |\n| `ppm_plan.created` | A PPM plan is set up. |\n| `compliance_document.added` | A contractor or engineer document (certificate, insurance, training) is filed. |\n\n## Job requests\n\n`POST /api/v1/job-requests` sends words (a helpdesk ticket, a fault report) to PPMdesk, which reads them like an email from a client: it drafts the job, with site, trade and who should go, on Needs you for the office to approve. Follow it with `GET /api/v1/job-requests/{id}`, or the `job.created` webhook."},"servers":[{"url":"https://app.ppmdesk.co.uk"}],"security":[{"ApiKey":[]}],"tags":[{"name":"Account"},{"name":"Clients"},{"name":"Sites"},{"name":"Contacts"},{"name":"Assets"},{"name":"Jobs"},{"name":"Job requests"},{"name":"Quotes"},{"name":"Invoices"},{"name":"Contractors"},{"name":"Compliance documents"},{"name":"PPM plans"},{"name":"Defects"},{"name":"Webhooks"}],"paths":{"/api/v1":{"get":{"tags":["Account"],"summary":"Who the key belongs to","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","description":"api"},"version":{"type":"string","description":""},"firm":{"type":"object","nullable":true,"description":"The firm","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"}}},"acting_as":{"type":"object","properties":{"id":{"type":"string","description":""},"name":{"type":"string","description":""},"role":{"type":"string","description":""}}},"key":{"type":"object","properties":{"name":{"type":"string","description":""},"scopes":{"type":"array","items":{"type":"string"}}}}}}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/events":{"get":{"tags":["Webhooks"],"summary":"The events webhooks can send","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","description":"list"},"data":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":""},"group":{"type":"string","description":""},"description":{"type":"string","description":""}}}}}}}}}}}},"/api/v1/clients":{"get":{"tags":["Clients"],"summary":"List clients","parameters":[{"name":"status","in":"query","required":false,"description":"ACTIVE, INACTIVE or PROSPECT (comma for several)","schema":{"type":"string"}},{"name":"search","in":"query","required":false,"description":"Name contains","schema":{"type":"string"}},{"name":"page","in":"query","required":false,"description":"Page, from 1","schema":{"type":"integer","default":1}},{"name":"per_page","in":"query","required":false,"description":"Up to 100","schema":{"type":"integer","default":25,"maximum":100}},{"name":"updated_since","in":"query","required":false,"description":"Only records changed since (YYYY-MM-DD or ISO 8601). For keeping another system in step.","schema":{"type":"string"}},{"name":"created_since","in":"query","required":false,"description":"Only records added since","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","description":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Client"}},"page":{"type":"integer","nullable":true,"description":""},"per_page":{"type":"integer","nullable":true,"description":""},"total":{"type":"integer","nullable":true,"description":"How many match"},"has_more":{"type":"boolean","description":"Another page follows"}}}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Clients"],"summary":"Add a client","description":"Needs a write key.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Name"},"company_number":{"type":"string","nullable":true,"description":""},"vat_number":{"type":"string","nullable":true,"description":""},"billing_email":{"type":"string","nullable":true,"description":""},"billing_address":{"type":"string","nullable":true,"description":""},"payment_terms_days":{"type":"integer","nullable":true,"description":""},"requires_po":{"type":"boolean","description":""},"rams_required":{"type":"boolean","description":""},"status":{"type":"string","description":"ACTIVE, INACTIVE or PROSPECT"},"notes":{"type":"string","nullable":true,"description":""},"account_manager_id":{"type":"string","nullable":true,"description":"A user"}},"required":["name"]}}}},"responses":{"201":{"description":"Added","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Client"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/clients/{id}":{"get":{"tags":["Clients"],"summary":"Get a client","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Client"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Clients"],"summary":"Change a client","description":"Send only what changes. Needs a write key.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Name"},"company_number":{"type":"string","nullable":true,"description":""},"vat_number":{"type":"string","nullable":true,"description":""},"billing_email":{"type":"string","nullable":true,"description":""},"billing_address":{"type":"string","nullable":true,"description":""},"payment_terms_days":{"type":"integer","nullable":true,"description":""},"requires_po":{"type":"boolean","description":""},"rams_required":{"type":"boolean","description":""},"status":{"type":"string","description":"ACTIVE, INACTIVE or PROSPECT"},"notes":{"type":"string","nullable":true,"description":""},"account_manager_id":{"type":"string","nullable":true,"description":"A user"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Client"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/sites":{"get":{"tags":["Sites"],"summary":"List sites","parameters":[{"name":"client_id","in":"query","required":false,"description":"Client","schema":{"type":"string"}},{"name":"status","in":"query","required":false,"description":"ACTIVE or INACTIVE","schema":{"type":"string"}},{"name":"postcode","in":"query","required":false,"description":"Postcode starts with","schema":{"type":"string"}},{"name":"search","in":"query","required":false,"description":"Name contains","schema":{"type":"string"}},{"name":"page","in":"query","required":false,"description":"Page, from 1","schema":{"type":"integer","default":1}},{"name":"per_page","in":"query","required":false,"description":"Up to 100","schema":{"type":"integer","default":25,"maximum":100}},{"name":"updated_since","in":"query","required":false,"description":"Only records changed since (YYYY-MM-DD or ISO 8601). For keeping another system in step.","schema":{"type":"string"}},{"name":"created_since","in":"query","required":false,"description":"Only records added since","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","description":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Site"}},"page":{"type":"integer","nullable":true,"description":""},"per_page":{"type":"integer","nullable":true,"description":""},"total":{"type":"integer","nullable":true,"description":"How many match"},"has_more":{"type":"boolean","description":"Another page follows"}}}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Sites"],"summary":"Add a site","description":"Needs a write key.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string","description":"Client"},"name":{"type":"string","description":"Name"},"address_line_1":{"type":"string","nullable":true,"description":""},"address_line_2":{"type":"string","nullable":true,"description":""},"town":{"type":"string","nullable":true,"description":""},"postcode":{"type":"string","nullable":true,"description":""},"access_notes":{"type":"string","nullable":true,"description":""},"site_requirements":{"type":"string","nullable":true,"description":""},"status":{"type":"string","description":"ACTIVE or INACTIVE"}},"required":["client_id","name"]}}}},"responses":{"201":{"description":"Added","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Site"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/sites/{id}":{"get":{"tags":["Sites"],"summary":"Get a site","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Site"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Sites"],"summary":"Change a site","description":"Send only what changes. Needs a write key.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Name"},"address_line_1":{"type":"string","nullable":true,"description":""},"address_line_2":{"type":"string","nullable":true,"description":""},"town":{"type":"string","nullable":true,"description":""},"postcode":{"type":"string","nullable":true,"description":""},"access_notes":{"type":"string","nullable":true,"description":""},"site_requirements":{"type":"string","nullable":true,"description":""},"status":{"type":"string","description":"ACTIVE or INACTIVE"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Site"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/contacts":{"get":{"tags":["Contacts"],"summary":"List contacts","parameters":[{"name":"client_id","in":"query","required":false,"description":"Client","schema":{"type":"string"}},{"name":"site_id","in":"query","required":false,"description":"Site","schema":{"type":"string"}},{"name":"email","in":"query","required":false,"description":"Email is","schema":{"type":"string"}},{"name":"page","in":"query","required":false,"description":"Page, from 1","schema":{"type":"integer","default":1}},{"name":"per_page","in":"query","required":false,"description":"Up to 100","schema":{"type":"integer","default":25,"maximum":100}},{"name":"updated_since","in":"query","required":false,"description":"Only records changed since (YYYY-MM-DD or ISO 8601). For keeping another system in step.","schema":{"type":"string"}},{"name":"created_since","in":"query","required":false,"description":"Only records added since","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","description":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Contact"}},"page":{"type":"integer","nullable":true,"description":""},"per_page":{"type":"integer","nullable":true,"description":""},"total":{"type":"integer","nullable":true,"description":"How many match"},"has_more":{"type":"boolean","description":"Another page follows"}}}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Contacts"],"summary":"Add a contact","description":"Needs a write key.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string","description":"Client"},"site_id":{"type":"string","nullable":true,"description":""},"first_name":{"type":"string","description":""},"last_name":{"type":"string","nullable":true,"description":""},"job_title":{"type":"string","nullable":true,"description":""},"email":{"type":"string","nullable":true,"description":""},"phone":{"type":"string","nullable":true,"description":""},"is_primary":{"type":"boolean","description":""},"notes":{"type":"string","nullable":true,"description":""}},"required":["client_id","first_name"]}}}},"responses":{"201":{"description":"Added","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/contacts/{id}":{"get":{"tags":["Contacts"],"summary":"Get a contact","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Contacts"],"summary":"Change a contact","description":"Send only what changes. Needs a write key.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"site_id":{"type":"string","nullable":true,"description":""},"first_name":{"type":"string","description":""},"last_name":{"type":"string","nullable":true,"description":""},"job_title":{"type":"string","nullable":true,"description":""},"email":{"type":"string","nullable":true,"description":""},"phone":{"type":"string","nullable":true,"description":""},"is_primary":{"type":"boolean","description":""},"notes":{"type":"string","nullable":true,"description":""}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/assets":{"get":{"tags":["Assets"],"summary":"List assets","parameters":[{"name":"site_id","in":"query","required":false,"description":"Site","schema":{"type":"string"}},{"name":"client_id","in":"query","required":false,"description":"Client","schema":{"type":"string"}},{"name":"parent_id","in":"query","required":false,"description":"Children of this asset, or \"none\" for top-level units","schema":{"type":"string"}},{"name":"status","in":"query","required":false,"description":"ACTIVE, FAULTY or DECOMMISSIONED","schema":{"type":"string"}},{"name":"tag","in":"query","required":false,"description":"Tag is","schema":{"type":"string"}},{"name":"page","in":"query","required":false,"description":"Page, from 1","schema":{"type":"integer","default":1}},{"name":"per_page","in":"query","required":false,"description":"Up to 100","schema":{"type":"integer","default":25,"maximum":100}},{"name":"updated_since","in":"query","required":false,"description":"Only records changed since (YYYY-MM-DD or ISO 8601). For keeping another system in step.","schema":{"type":"string"}},{"name":"created_since","in":"query","required":false,"description":"Only records added since","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","description":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Asset"}},"page":{"type":"integer","nullable":true,"description":""},"per_page":{"type":"integer","nullable":true,"description":""},"total":{"type":"integer","nullable":true,"description":"How many match"},"has_more":{"type":"boolean","description":"Another page follows"}}}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Assets"],"summary":"Add a asset","description":"Needs a write key.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"site_id":{"type":"string","description":"Site"},"tag":{"type":"string","nullable":true,"description":"Left out: the next tag is given"},"name":{"type":"string","description":""},"type_id":{"type":"string","nullable":true,"description":"An asset type"},"type_name":{"type":"string","nullable":true,"description":"Or a type by name (added if new)"},"parent_id":{"type":"string","nullable":true,"description":"The unit it belongs to, on the same site"},"location_in_site":{"type":"string","nullable":true,"description":""},"manufacturer":{"type":"string","nullable":true,"description":""},"model":{"type":"string","nullable":true,"description":""},"serial_number":{"type":"string","nullable":true,"description":""},"install_date":{"type":"string","format":"date","nullable":true,"description":""},"warranty_expiry":{"type":"string","format":"date","nullable":true,"description":""},"refrigerant_type":{"type":"string","nullable":true,"description":""},"refrigerant_kg":{"type":"number","nullable":true,"description":""},"status":{"type":"string","description":"ACTIVE, FAULTY or DECOMMISSIONED"},"notes":{"type":"string","nullable":true,"description":""}},"required":["site_id","name"]}}}},"responses":{"201":{"description":"Added","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Asset"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/assets/{id}":{"get":{"tags":["Assets"],"summary":"Get a asset","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Asset"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Assets"],"summary":"Change a asset","description":"Send only what changes. Needs a write key.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"site_id":{"type":"string","description":"Move to another site"},"tag":{"type":"string","nullable":true,"description":"Left out: the next tag is given"},"name":{"type":"string","description":""},"type_id":{"type":"string","nullable":true,"description":"An asset type"},"type_name":{"type":"string","nullable":true,"description":"Or a type by name (added if new)"},"parent_id":{"type":"string","nullable":true,"description":"The unit it belongs to, on the same site"},"location_in_site":{"type":"string","nullable":true,"description":""},"manufacturer":{"type":"string","nullable":true,"description":""},"model":{"type":"string","nullable":true,"description":""},"serial_number":{"type":"string","nullable":true,"description":""},"install_date":{"type":"string","format":"date","nullable":true,"description":""},"warranty_expiry":{"type":"string","format":"date","nullable":true,"description":""},"refrigerant_type":{"type":"string","nullable":true,"description":""},"refrigerant_kg":{"type":"number","nullable":true,"description":""},"status":{"type":"string","description":"ACTIVE, FAULTY or DECOMMISSIONED"},"notes":{"type":"string","nullable":true,"description":""}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Asset"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/jobs":{"get":{"tags":["Jobs"],"summary":"List jobs","parameters":[{"name":"status","in":"query","required":false,"description":"Comma for several, e.g. OPEN,SCHEDULED","schema":{"type":"string"}},{"name":"type","in":"query","required":false,"description":"PPM, REACTIVE or QUOTED","schema":{"type":"string"}},{"name":"priority","in":"query","required":false,"description":"","schema":{"type":"string"}},{"name":"client_id","in":"query","required":false,"description":"","schema":{"type":"string"}},{"name":"site_id","in":"query","required":false,"description":"","schema":{"type":"string"}},{"name":"contractor_id","in":"query","required":false,"description":"","schema":{"type":"string"}},{"name":"ppm_plan_id","in":"query","required":false,"description":"","schema":{"type":"string"}},{"name":"reference","in":"query","required":false,"description":"e.g. WO-00042","schema":{"type":"string"}},{"name":"due_from","in":"query","required":false,"description":"Due on or after","schema":{"type":"string"}},{"name":"due_to","in":"query","required":false,"description":"Due on or before","schema":{"type":"string"}},{"name":"scheduled_from","in":"query","required":false,"description":"Booked on or after","schema":{"type":"string"}},{"name":"scheduled_to","in":"query","required":false,"description":"Booked on or before","schema":{"type":"string"}},{"name":"page","in":"query","required":false,"description":"Page, from 1","schema":{"type":"integer","default":1}},{"name":"per_page","in":"query","required":false,"description":"Up to 100","schema":{"type":"integer","default":25,"maximum":100}},{"name":"updated_since","in":"query","required":false,"description":"Only records changed since (YYYY-MM-DD or ISO 8601). For keeping another system in step.","schema":{"type":"string"}},{"name":"created_since","in":"query","required":false,"description":"Only records added since","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","description":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Job"}},"page":{"type":"integer","nullable":true,"description":""},"per_page":{"type":"integer","nullable":true,"description":""},"total":{"type":"integer","nullable":true,"description":"How many match"},"has_more":{"type":"boolean","description":"Another page follows"}}}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Jobs"],"summary":"Add a job","description":"Needs a write key.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"site_id":{"type":"string","description":"Site"},"title":{"type":"string","description":"Title"},"description":{"type":"string","nullable":true,"description":"What needs doing"},"priority":{"type":"string","description":"LOW, NORMAL, HIGH or EMERGENCY"},"trade":{"type":"string","nullable":true,"description":"A trade the firm uses"},"due_date":{"type":"string","format":"date","nullable":true,"description":"Due. Can be left out on a site under a service contract (its fix time sets it)"},"contractor_id":{"type":"string","nullable":true,"description":"Who does it"},"owner_id":{"type":"string","nullable":true,"description":"Who in the office looks after it (default: the key's person)"},"client_po_number":{"type":"string","nullable":true,"description":""},"client_price":{"type":"number","nullable":true,"description":""},"contractor_cost":{"type":"number","nullable":true,"description":""},"reported_by":{"type":"string","nullable":true,"description":""},"visit_time":{"type":"string","nullable":true,"description":"AM, PM or HH:MM"},"visit_minutes":{"type":"integer","nullable":true,"description":"15 to 720"},"asset_ids":{"type":"array","items":{"type":"string"},"description":"Assets on the site"},"job_sheet_id":{"type":"string","nullable":true,"description":"A job sheet"}},"required":["site_id","title"]}}}},"responses":{"201":{"description":"Added","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/jobs/{id}":{"get":{"tags":["Jobs"],"summary":"Get a job","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Jobs"],"summary":"Change a job","description":"Send only what changes. Needs a write key.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Title"},"description":{"type":"string","nullable":true,"description":"What needs doing"},"priority":{"type":"string","description":"LOW, NORMAL, HIGH or EMERGENCY"},"trade":{"type":"string","nullable":true,"description":"A trade the firm uses"},"due_date":{"type":"string","format":"date","nullable":true,"description":"Due. Can be left out on a site under a service contract (its fix time sets it)"},"contractor_id":{"type":"string","nullable":true,"description":"Who does it"},"owner_id":{"type":"string","nullable":true,"description":"Who in the office looks after it (default: the key's person)"},"client_po_number":{"type":"string","nullable":true,"description":""},"client_price":{"type":"number","nullable":true,"description":""},"contractor_cost":{"type":"number","nullable":true,"description":""},"reported_by":{"type":"string","nullable":true,"description":""},"visit_time":{"type":"string","nullable":true,"description":"AM, PM or HH:MM"},"visit_minutes":{"type":"integer","nullable":true,"description":"15 to 720"},"asset_ids":{"type":"array","items":{"type":"string"},"description":"Assets on the site"},"job_sheet_id":{"type":"string","nullable":true,"description":"A job sheet"},"status":{"type":"string","description":"OPEN, SCHEDULED, IN_PROGRESS, COMPLETED, INVOICED or CANCELLED"},"scheduled_date":{"type":"string","format":"date","nullable":true,"description":"Book it (sets SCHEDULED); null to unbook"},"completed_at":{"type":"string","format":"date","nullable":true,"description":"With COMPLETED"},"engineer_id":{"type":"string","nullable":true,"description":"One engineer"},"engineer_ids":{"type":"array","items":{"type":"string"},"description":"Several (engineer_id is then the lead)"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/jobs/{id}/notes":{"post":{"tags":["Jobs"],"summary":"Add a note to a job","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":"string","description":"The note"}},"required":["body"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":""},"object":{"type":"string","description":"note"},"job_id":{"type":"string","description":""},"body":{"type":"string","description":""},"created_at":{"type":"string","format":"date-time","nullable":true,"description":""}}}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/job-requests":{"post":{"tags":["Job requests"],"summary":"Send words to be drafted into a job","description":"Read by AI into a job draft on Needs you, for the office to approve. Up to 100 an hour per key. Sending the same external_ref again returns the first request.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","description":"What the job is, in words"},"site_id":{"type":"string","nullable":true,"description":"If you know the site"},"reported_by":{"type":"string","nullable":true,"description":"Who reported it"},"urgent":{"type":"boolean","description":"Unsafe or unusable"},"external_ref":{"type":"string","nullable":true,"description":"Your ticket number, to match it up"}},"required":["text"]}}}},"responses":{"201":{"description":"Drafted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/job-requests/{id}":{"get":{"tags":["Job requests"],"summary":"Where a job request has got to","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/quotes":{"get":{"tags":["Quotes"],"summary":"List quotes","parameters":[{"name":"status","in":"query","required":false,"description":"DRAFT, SENT, ACCEPTED, DECLINED or EXPIRED","schema":{"type":"string"}},{"name":"client_id","in":"query","required":false,"description":"","schema":{"type":"string"}},{"name":"site_id","in":"query","required":false,"description":"","schema":{"type":"string"}},{"name":"job_id","in":"query","required":false,"description":"","schema":{"type":"string"}},{"name":"page","in":"query","required":false,"description":"Page, from 1","schema":{"type":"integer","default":1}},{"name":"per_page","in":"query","required":false,"description":"Up to 100","schema":{"type":"integer","default":25,"maximum":100}},{"name":"updated_since","in":"query","required":false,"description":"Only records changed since (YYYY-MM-DD or ISO 8601). For keeping another system in step.","schema":{"type":"string"}},{"name":"created_since","in":"query","required":false,"description":"Only records added since","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","description":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Quote"}},"page":{"type":"integer","nullable":true,"description":""},"per_page":{"type":"integer","nullable":true,"description":""},"total":{"type":"integer","nullable":true,"description":"How many match"},"has_more":{"type":"boolean","description":"Another page follows"}}}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/quotes/{id}":{"get":{"tags":["Quotes"],"summary":"Get a quote","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Quote"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/invoices":{"get":{"tags":["Invoices"],"summary":"List invoices","parameters":[{"name":"status","in":"query","required":false,"description":"DRAFT, SENT, PAID or VOID","schema":{"type":"string"}},{"name":"client_id","in":"query","required":false,"description":"","schema":{"type":"string"}},{"name":"issued_from","in":"query","required":false,"description":"","schema":{"type":"string"}},{"name":"issued_to","in":"query","required":false,"description":"","schema":{"type":"string"}},{"name":"page","in":"query","required":false,"description":"Page, from 1","schema":{"type":"integer","default":1}},{"name":"per_page","in":"query","required":false,"description":"Up to 100","schema":{"type":"integer","default":25,"maximum":100}},{"name":"updated_since","in":"query","required":false,"description":"Only records changed since (YYYY-MM-DD or ISO 8601). For keeping another system in step.","schema":{"type":"string"}},{"name":"created_since","in":"query","required":false,"description":"Only records added since","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","description":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Invoice"}},"page":{"type":"integer","nullable":true,"description":""},"per_page":{"type":"integer","nullable":true,"description":""},"total":{"type":"integer","nullable":true,"description":"How many match"},"has_more":{"type":"boolean","description":"Another page follows"}}}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/invoices/{id}":{"get":{"tags":["Invoices"],"summary":"Get a invoice","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Invoice"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/contractors":{"get":{"tags":["Contractors"],"summary":"List contractors","parameters":[{"name":"search","in":"query","required":false,"description":"Name contains","schema":{"type":"string"}},{"name":"in_house","in":"query","required":false,"description":"true for the firm's own team","schema":{"type":"string"}},{"name":"page","in":"query","required":false,"description":"Page, from 1","schema":{"type":"integer","default":1}},{"name":"per_page","in":"query","required":false,"description":"Up to 100","schema":{"type":"integer","default":25,"maximum":100}},{"name":"updated_since","in":"query","required":false,"description":"Only records changed since (YYYY-MM-DD or ISO 8601). For keeping another system in step.","schema":{"type":"string"}},{"name":"created_since","in":"query","required":false,"description":"Only records added since","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","description":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Contractor"}},"page":{"type":"integer","nullable":true,"description":""},"per_page":{"type":"integer","nullable":true,"description":""},"total":{"type":"integer","nullable":true,"description":"How many match"},"has_more":{"type":"boolean","description":"Another page follows"}}}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/contractors/{id}":{"get":{"tags":["Contractors"],"summary":"Get a contractor","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contractor"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/compliance-documents":{"get":{"tags":["Compliance documents"],"summary":"List compliance documents","parameters":[{"name":"contractor_id","in":"query","required":false,"description":"","schema":{"type":"string"}},{"name":"expires_before","in":"query","required":false,"description":"Expiring on or before","schema":{"type":"string"}},{"name":"page","in":"query","required":false,"description":"Page, from 1","schema":{"type":"integer","default":1}},{"name":"per_page","in":"query","required":false,"description":"Up to 100","schema":{"type":"integer","default":25,"maximum":100}},{"name":"updated_since","in":"query","required":false,"description":"Only records changed since (YYYY-MM-DD or ISO 8601). For keeping another system in step.","schema":{"type":"string"}},{"name":"created_since","in":"query","required":false,"description":"Only records added since","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","description":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/ComplianceDocument"}},"page":{"type":"integer","nullable":true,"description":""},"per_page":{"type":"integer","nullable":true,"description":""},"total":{"type":"integer","nullable":true,"description":"How many match"},"has_more":{"type":"boolean","description":"Another page follows"}}}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/compliance-documents/{id}":{"get":{"tags":["Compliance documents"],"summary":"Get a compliance document","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComplianceDocument"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/ppm-plans":{"get":{"tags":["PPM plans"],"summary":"List ppm plans","parameters":[{"name":"site_id","in":"query","required":false,"description":"","schema":{"type":"string"}},{"name":"client_id","in":"query","required":false,"description":"","schema":{"type":"string"}},{"name":"page","in":"query","required":false,"description":"Page, from 1","schema":{"type":"integer","default":1}},{"name":"per_page","in":"query","required":false,"description":"Up to 100","schema":{"type":"integer","default":25,"maximum":100}},{"name":"updated_since","in":"query","required":false,"description":"Only records changed since (YYYY-MM-DD or ISO 8601). For keeping another system in step.","schema":{"type":"string"}},{"name":"created_since","in":"query","required":false,"description":"Only records added since","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","description":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/PpmPlan"}},"page":{"type":"integer","nullable":true,"description":""},"per_page":{"type":"integer","nullable":true,"description":""},"total":{"type":"integer","nullable":true,"description":"How many match"},"has_more":{"type":"boolean","description":"Another page follows"}}}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/ppm-plans/{id}":{"get":{"tags":["PPM plans"],"summary":"Get a ppm plan","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PpmPlan"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/defects":{"get":{"tags":["Defects"],"summary":"List defects","parameters":[{"name":"status","in":"query","required":false,"description":"OPEN, QUOTED, ACCEPTED, DECLINED or NO_ACTION","schema":{"type":"string"}},{"name":"site_id","in":"query","required":false,"description":"","schema":{"type":"string"}},{"name":"asset_id","in":"query","required":false,"description":"","schema":{"type":"string"}},{"name":"page","in":"query","required":false,"description":"Page, from 1","schema":{"type":"integer","default":1}},{"name":"per_page","in":"query","required":false,"description":"Up to 100","schema":{"type":"integer","default":25,"maximum":100}},{"name":"updated_since","in":"query","required":false,"description":"Only records changed since (YYYY-MM-DD or ISO 8601). For keeping another system in step.","schema":{"type":"string"}},{"name":"created_since","in":"query","required":false,"description":"Only records added since","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","description":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Defect"}},"page":{"type":"integer","nullable":true,"description":""},"per_page":{"type":"integer","nullable":true,"description":""},"total":{"type":"integer","nullable":true,"description":"How many match"},"has_more":{"type":"boolean","description":"Another page follows"}}}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/defects/{id}":{"get":{"tags":["Defects"],"summary":"Get a defect","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Defect"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/webhooks":{"get":{"tags":["Webhooks"],"summary":"List webhooks","description":"Owners and admins.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","description":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}}}}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Webhooks"],"summary":"Add a webhook","description":"For tools that subscribe themselves (Zapier, Make). The address is checked first (see Webhooks above). The signing secret is in the reply, once.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"https address"},"events":{"type":"array","items":{"type":"string"},"description":"Event names, or [\"*\"]"},"description":{"type":"string","nullable":true,"description":""},"secret":{"type":"string","nullable":true,"description":"Your own, 24+ characters; left out, one is made"}},"required":["url","events"]}}}},"responses":{"201":{"description":"Added","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Webhook"},{"type":"object","properties":{"secret":{"type":"string","description":"Signing secret: keep it, it is not shown again"}}}]}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/webhooks/{id}":{"patch":{"tags":["Webhooks"],"summary":"Change a webhook","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"events":{"type":"array","items":{"type":"string"}},"description":{"type":"string","nullable":true,"description":""},"is_active":{"type":"boolean","description":"Turn on or off"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Webhooks"],"summary":"Remove a webhook","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Removed"},"400":{"description":"Something in the request is wrong","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, or a key that is revoked or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key cannot do this (a read-only key, or the person is not an admin)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not this firm's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"ApiKey":{"type":"apiKey","in":"header","name":"X-API-Key"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","description":"What went wrong, in words"},"code":{"type":"string","description":"A code to act on, e.g. VALIDATION, NOT_FOUND, SCOPE_REQUIRED, RATE_LIMITED"}}},"Client":{"type":"object","properties":{"id":{"type":"string","description":"ID"},"created_at":{"type":"string","format":"date-time","nullable":true,"description":"When it was added"},"updated_at":{"type":"string","format":"date-time","nullable":true,"description":"When it last changed"},"object":{"type":"string","description":"client"},"name":{"type":"string","description":"Name"},"status":{"type":"string","description":"ACTIVE, INACTIVE or PROSPECT"},"company_number":{"type":"string","nullable":true,"description":"Companies House number"},"vat_number":{"type":"string","nullable":true,"description":"VAT number"},"billing_email":{"type":"string","nullable":true,"description":"Where invoices go"},"billing_address":{"type":"string","nullable":true,"description":"Billing address"},"payment_terms_days":{"type":"integer","nullable":true,"description":"Days to pay"},"requires_po":{"type":"boolean","description":"Jobs need a PO number"},"rams_required":{"type":"boolean","description":"Jobs need RAMS"},"account_manager":{"type":"object","nullable":true,"description":"Account manager","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"}}},"notes":{"type":"string","nullable":true,"description":"Notes"},"url":{"type":"string","description":"In PPMdesk"}}},"Site":{"type":"object","properties":{"id":{"type":"string","description":"ID"},"created_at":{"type":"string","format":"date-time","nullable":true,"description":"When it was added"},"updated_at":{"type":"string","format":"date-time","nullable":true,"description":"When it last changed"},"object":{"type":"string","description":"site"},"name":{"type":"string","description":"Name"},"status":{"type":"string","description":"ACTIVE or INACTIVE"},"client":{"type":"object","nullable":true,"description":"Client","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"}}},"address_line_1":{"type":"string","nullable":true,"description":"Address"},"address_line_2":{"type":"string","nullable":true,"description":"Address"},"town":{"type":"string","nullable":true,"description":"Town"},"postcode":{"type":"string","nullable":true,"description":"Postcode"},"latitude":{"type":"number","nullable":true},"longitude":{"type":"number","nullable":true},"access_notes":{"type":"string","nullable":true,"description":"Access notes"},"site_requirements":{"type":"string","nullable":true,"description":"Site requirements"},"url":{"type":"string","description":"In PPMdesk"}}},"Contact":{"type":"object","properties":{"id":{"type":"string","description":"ID"},"created_at":{"type":"string","format":"date-time","nullable":true,"description":"When it was added"},"updated_at":{"type":"string","format":"date-time","nullable":true,"description":"When it last changed"},"object":{"type":"string","description":"contact"},"first_name":{"type":"string","description":"First name"},"last_name":{"type":"string","nullable":true,"description":"Last name"},"name":{"type":"string","description":"Full name"},"job_title":{"type":"string","nullable":true,"description":"Job title"},"email":{"type":"string","nullable":true,"description":"Email"},"phone":{"type":"string","nullable":true,"description":"Phone"},"is_primary":{"type":"boolean","description":"The client's main contact"},"client":{"type":"object","nullable":true,"description":"Client","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"}}},"site":{"type":"object","nullable":true,"description":"Site, if they belong to one","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"}}},"notes":{"type":"string","nullable":true,"description":"Notes"}}},"Asset":{"type":"object","properties":{"id":{"type":"string","description":"ID"},"created_at":{"type":"string","format":"date-time","nullable":true,"description":"When it was added"},"updated_at":{"type":"string","format":"date-time","nullable":true,"description":"When it last changed"},"object":{"type":"string","description":"asset"},"tag":{"type":"string","description":"Asset tag (on its label)"},"name":{"type":"string","description":"Name"},"status":{"type":"string","description":"ACTIVE, FAULTY or DECOMMISSIONED"},"type":{"type":"object","nullable":true,"description":"Asset type","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"}}},"site":{"type":"object","nullable":true,"description":"Site","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"}}},"client_id":{"type":"string","nullable":true,"description":"Client"},"parent":{"type":"object","nullable":true,"description":"The unit it belongs to (e.g. an outdoor unit for an indoor one)","properties":{"id":{"type":"string","description":"ID"},"tag":{"type":"string","description":"Tag"},"name":{"type":"string","description":"Name"}}},"location_in_site":{"type":"string","nullable":true,"description":"Where on site"},"manufacturer":{"type":"string","nullable":true,"description":"Make"},"model":{"type":"string","nullable":true,"description":"Model"},"serial_number":{"type":"string","nullable":true,"description":"Serial"},"install_date":{"type":"string","format":"date","nullable":true,"description":"Installed"},"warranty_expiry":{"type":"string","format":"date","nullable":true,"description":"Warranty ends"},"refrigerant_type":{"type":"string","nullable":true,"description":"e.g. R32"},"refrigerant_kg":{"type":"number","nullable":true,"description":"Charge in kg"},"notes":{"type":"string","nullable":true,"description":"Notes"},"url":{"type":"string","description":"In PPMdesk"}}},"Job":{"type":"object","properties":{"id":{"type":"string","description":"ID"},"created_at":{"type":"string","format":"date-time","nullable":true,"description":"When it was added"},"updated_at":{"type":"string","format":"date-time","nullable":true,"description":"When it last changed"},"object":{"type":"string","description":"job"},"reference":{"type":"string","description":"e.g. WO-00042"},"type":{"type":"string","description":"PPM, REACTIVE or QUOTED"},"status":{"type":"string","description":"OPEN, DATE_PROPOSED, SCHEDULED, IN_PROGRESS, COMPLETED, INVOICED or CANCELLED"},"priority":{"type":"string","description":"LOW, NORMAL, HIGH or EMERGENCY"},"title":{"type":"string","description":"Title"},"description":{"type":"string","nullable":true,"description":"What needs doing"},"trade":{"type":"string","nullable":true,"description":"Trade"},"client":{"type":"object","nullable":true,"description":"Client","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"}}},"site":{"type":"object","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"},"postcode":{"type":"string","nullable":true,"description":"Postcode"}}},"contractor":{"type":"object","nullable":true,"description":"Who does it (a contractor, or the firm's own team)","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"}}},"engineer":{"type":"object","nullable":true,"description":"Lead engineer","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"}}},"owner":{"type":"object","nullable":true,"description":"Who in the office looks after it","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"}}},"ppm_plan_id":{"type":"string","nullable":true,"description":"The PPM plan, for a planned visit"},"contract_id":{"type":"string","nullable":true,"description":"The service contract it falls under"},"due_date":{"type":"string","format":"date","nullable":true,"description":"Due"},"scheduled_date":{"type":"string","format":"date","nullable":true,"description":"Booked for"},"visit_time":{"type":"string","nullable":true,"description":"AM, PM or HH:MM"},"visit_minutes":{"type":"integer","nullable":true,"description":"Visit length"},"attend_by":{"type":"string","format":"date-time","nullable":true,"description":"Contract response time"},"fix_by":{"type":"string","format":"date-time","nullable":true,"description":"Contract fix time"},"attended_at":{"type":"string","format":"date-time","nullable":true,"description":"Engineer arrived"},"completed_at":{"type":"string","format":"date-time","nullable":true,"description":"Finished"},"on_hold":{"type":"object","nullable":true,"properties":{"reason":{"type":"string","description":"Why"},"note":{"type":"string","nullable":true,"description":"Note"},"since":{"type":"string","format":"date-time","nullable":true,"description":"Since"},"until":{"type":"string","format":"date-time","nullable":true,"description":"Until"}}},"client_po_number":{"type":"string","nullable":true,"description":"Client's PO"},"client_price":{"type":"number","nullable":true,"description":"Price to the client"},"contractor_cost":{"type":"number","nullable":true,"description":"Cost"},"reported_by":{"type":"string","nullable":true,"description":"Who reported it"},"invoice_id":{"type":"string","nullable":true,"description":"Invoice it is on"},"url":{"type":"string","description":"In PPMdesk"}}},"Line":{"type":"object","properties":{"description":{"type":"string","description":"Line"},"quantity":{"type":"number"},"unit_price":{"type":"number"},"vat_rate":{"type":"number","description":"Per cent"}}},"Quote":{"type":"object","properties":{"id":{"type":"string","description":"ID"},"created_at":{"type":"string","format":"date-time","nullable":true,"description":"When it was added"},"updated_at":{"type":"string","format":"date-time","nullable":true,"description":"When it last changed"},"object":{"type":"string","description":"quote"},"reference":{"type":"string","description":"e.g. Q-00017"},"status":{"type":"string","description":"DRAFT, SENT, ACCEPTED, DECLINED or EXPIRED"},"title":{"type":"string","description":"Title"},"summary":{"type":"string","nullable":true,"description":"Summary"},"client":{"type":"object","nullable":true,"description":"Client","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"}}},"site":{"type":"object","nullable":true,"description":"Site","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"}}},"job_id":{"type":"string","nullable":true,"description":"Job it is for"},"ppm_plan_id":{"type":"string","nullable":true,"description":"Plan it renews"},"valid_until":{"type":"string","format":"date","nullable":true,"description":"Valid until"},"subtotal":{"type":"number","nullable":true,"description":"Before VAT"},"vat":{"type":"number","nullable":true,"description":"VAT"},"total":{"type":"number","nullable":true,"description":"Total"},"lines":{"type":"array","items":{"$ref":"#/components/schemas/Line"}},"sent_to":{"type":"string","nullable":true,"description":"Sent to"},"sent_at":{"type":"string","format":"date-time","nullable":true,"description":"Sent"},"accepted_at":{"type":"string","format":"date-time","nullable":true,"description":"Accepted"},"declined_at":{"type":"string","format":"date-time","nullable":true,"description":"Declined"},"decided_by":{"type":"string","nullable":true,"description":"Who decided"},"decision_note":{"type":"string","nullable":true,"description":"Their note (often a PO number)"}}},"Invoice":{"type":"object","properties":{"id":{"type":"string","description":"ID"},"created_at":{"type":"string","format":"date-time","nullable":true,"description":"When it was added"},"updated_at":{"type":"string","format":"date-time","nullable":true,"description":"When it last changed"},"object":{"type":"string","description":"invoice"},"number":{"type":"string","description":"Invoice number"},"status":{"type":"string","description":"DRAFT, SENT, PAID or VOID"},"client":{"type":"object","nullable":true,"description":"Client","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"}}},"issue_date":{"type":"string","format":"date","nullable":true,"description":"Issued"},"due_date":{"type":"string","format":"date","nullable":true,"description":"Due"},"po_number":{"type":"string","nullable":true,"description":"PO number"},"subtotal":{"type":"number","nullable":true,"description":"Before VAT"},"vat":{"type":"number","nullable":true,"description":"VAT"},"total":{"type":"number","nullable":true,"description":"Total"},"amount_paid":{"type":"number","nullable":true,"description":"Paid so far"},"amount_due":{"type":"number","nullable":true,"description":"Still to pay"},"lines":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/Line"},{"type":"object","properties":{"job_id":{"type":"string","nullable":true,"description":"Job"}}}]}},"sent_to":{"type":"string","nullable":true,"description":"Sent to"},"sent_at":{"type":"string","format":"date-time","nullable":true,"description":"Sent"},"paid_at":{"type":"string","format":"date-time","nullable":true,"description":"Paid"},"xero_invoice_id":{"type":"string","nullable":true,"description":"In Xero"},"url":{"type":"string","description":"In PPMdesk"}}},"Contractor":{"type":"object","properties":{"id":{"type":"string","description":"ID"},"created_at":{"type":"string","format":"date-time","nullable":true,"description":"When it was added"},"updated_at":{"type":"string","format":"date-time","nullable":true,"description":"When it last changed"},"object":{"type":"string","description":"contractor"},"name":{"type":"string","description":"Name"},"status":{"type":"string","description":"Status"},"in_house":{"type":"boolean","description":"The firm's own team"},"company_number":{"type":"string","nullable":true,"description":"Companies House number"},"vat_number":{"type":"string","nullable":true,"description":"VAT number"},"email":{"type":"string","nullable":true,"description":"Email"},"phone":{"type":"string","nullable":true,"description":"Phone"},"base_postcode":{"type":"string","nullable":true,"description":"Where they work from"},"cover_miles":{"type":"integer","nullable":true,"description":"How far they travel"},"url":{"type":"string","description":"In PPMdesk"}}},"ComplianceDocument":{"type":"object","properties":{"id":{"type":"string","description":"ID"},"created_at":{"type":"string","format":"date-time","nullable":true,"description":"When it was added"},"updated_at":{"type":"string","format":"date-time","nullable":true,"description":"When it last changed"},"object":{"type":"string","description":"compliance_document"},"type":{"type":"object","nullable":true,"description":"e.g. Public liability insurance, F-gas certificate","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"}}},"status":{"type":"string","description":"Status"},"contractor":{"type":"object","nullable":true,"description":"Contractor","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"}}},"engineer":{"type":"object","nullable":true,"description":"Engineer, for a personal certificate","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"}}},"reference":{"type":"string","nullable":true,"description":"Policy or certificate number"},"issued_at":{"type":"string","format":"date","nullable":true,"description":"Issued"},"expires_at":{"type":"string","format":"date","nullable":true,"description":"Expires"},"verified_at":{"type":"string","format":"date-time","nullable":true,"description":"Checked by the office"}}},"PpmPlan":{"type":"object","properties":{"id":{"type":"string","description":"ID"},"created_at":{"type":"string","format":"date-time","nullable":true,"description":"When it was added"},"updated_at":{"type":"string","format":"date-time","nullable":true,"description":"When it last changed"},"object":{"type":"string","description":"ppm_plan"},"title":{"type":"string","description":"Title"},"status":{"type":"string","description":"Status"},"trade":{"type":"string","nullable":true,"description":"Trade"},"site":{"type":"object","nullable":true,"description":"Site","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"}}},"client":{"type":"object","nullable":true,"description":"Client","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"}}},"contractor":{"type":"object","nullable":true,"description":"Who does the visits","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"}}},"interval_months":{"type":"integer","nullable":true,"description":"Months between visits"},"first_due_date":{"type":"string","format":"date","nullable":true,"description":"First visit due"},"cycle_end_date":{"type":"string","format":"date","nullable":true,"description":"Contract cycle ends"},"client_price_per_visit":{"type":"number","nullable":true,"description":"Price per visit"},"cost_per_visit":{"type":"number","nullable":true,"description":"Cost per visit"},"scope_of_works":{"type":"string","nullable":true,"description":"Scope of works"},"url":{"type":"string","description":"In PPMdesk"}}},"Defect":{"type":"object","properties":{"id":{"type":"string","description":"ID"},"created_at":{"type":"string","format":"date-time","nullable":true,"description":"When it was added"},"updated_at":{"type":"string","format":"date-time","nullable":true,"description":"When it last changed"},"object":{"type":"string","description":"defect"},"title":{"type":"string","description":"What was found"},"note":{"type":"string","nullable":true,"description":"Engineer's note"},"status":{"type":"string","description":"OPEN, QUOTED, ACCEPTED, DECLINED or NO_ACTION"},"urgent":{"type":"boolean","description":"Unsafe or unusable"},"site":{"type":"object","nullable":true,"description":"Site","properties":{"id":{"type":"string","description":"ID"},"name":{"type":"string","description":"Name"}}},"asset":{"type":"object","nullable":true,"properties":{"id":{"type":"string","description":"ID"},"tag":{"type":"string","description":"Tag"},"name":{"type":"string","description":"Name"}}},"found_on_job":{"type":"object","nullable":true,"properties":{"id":{"type":"string","description":"ID"},"reference":{"type":"string","description":"Reference"}}},"reported_by":{"type":"string","nullable":true,"description":"Engineer"},"quote_id":{"type":"string","nullable":true,"description":"Remedial quote"},"remedial_job_id":{"type":"string","nullable":true,"description":"Job to fix it"}}},"JobRequest":{"type":"object","properties":{"id":{"type":"string","description":"ID"},"object":{"type":"string","description":"job_request"},"status":{"type":"string","description":"PENDING (waiting on Needs you), APPROVED or REJECTED"},"title":{"type":"string","description":"What PPMdesk read it as"},"external_ref":{"type":"string","nullable":true,"description":"Your reference"},"job_id":{"type":"string","nullable":true,"description":"The job, once approved"},"created_at":{"type":"string","format":"date-time","nullable":true,"description":"Sent"},"decided_at":{"type":"string","format":"date-time","nullable":true,"description":"Approved or rejected"}}},"Webhook":{"type":"object","properties":{"id":{"type":"string","description":"ID"},"object":{"type":"string","description":"webhook"},"url":{"type":"string","description":"Where events go"},"events":{"type":"array","items":{"type":"string"},"description":"Event names, or [\"*\"] for all"},"description":{"type":"string","nullable":true,"description":"What it is for"},"source":{"type":"string","description":"CUSTOM, ZAPIER or MAKE"},"is_active":{"type":"boolean","description":"Sending"},"failure_count":{"type":"integer","nullable":true,"description":"Failed deliveries in a row"},"last_success":{"type":"string","format":"date-time","nullable":true,"description":""},"last_failure":{"type":"string","format":"date-time","nullable":true,"description":""},"last_error":{"type":"string","nullable":true,"description":""},"disabled_at":{"type":"string","format":"date-time","nullable":true,"description":"Turned off after failures"},"created_at":{"type":"string","format":"date-time","nullable":true,"description":""}}},"Event":{"type":"object","properties":{"id":{"type":"string","description":"Event ID (evt_...). The same for every webhook told about this event; use it to ignore repeats."},"event":{"type":"string","description":"e.g. job.completed"},"created_at":{"type":"string","format":"date-time","nullable":true,"description":"When it happened"},"data":{"type":"object","description":"The record, exactly as the API returns it (a Job for job.*, an Invoice for invoice.*, ...)"},"previous":{"type":"object","nullable":true,"description":"What changed from, e.g. { \"status\": \"IN_PROGRESS\" }"}}}}}}