{"openapi":"3.1.0","info":{"title":"Kazee CRM API","version":"0.1.0","description":"Everything a person can do in Kazee CRM (leads, deals, accounts, contacts, quotes, subscriptions, the help desk and calls) as plain HTTPS calls that answer in JSON.\n\n**Sign in.** Send `Authorization: Bearer <token>` with every call. The token comes from Kazee sign-in, and each call lists the permission it needs. Your organisation comes from the token, so calls never ask for it.\n\n**IDs and dates.** IDs are UUIDs, e.g. `0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d`. Dates are ISO 8601 in UTC, e.g. `2026-10-20T09:00:00.000Z`.\n\n**Errors.** A call that fails answers with a 4xx or 5xx status and `{ \"success\": false, \"error\": { \"code\": \"NOT_FOUND\", \"message\": \"what went wrong\" } }`.\n\n**For bots.** This page is built from the CRM code itself. The raw file is at `/api/swagger`."},"tags":[{"name":"Leads","description":"People and companies who may buy, before they become customers."},{"name":"Deals","description":"Sales opportunities and the stage each one is at."},{"name":"Accounts","description":"Customer companies and households, and the people linked to them."},{"name":"Contacts","description":"The people you deal with."},{"name":"Quotes","description":"Price quotes sent to customers."},{"name":"Products & plans","description":"What you sell: products, service plans and their prices."},{"name":"Pipelines","description":"The stages leads, deals and other records move through."},{"name":"Subscriptions","description":"Customers' running services and their changes."},{"name":"Billing","description":"Invoices, payments and balances, read from Kazee Books."},{"name":"Tickets","description":"Support tickets from customers."},{"name":"Requests","description":"Service requests, such as installations and moves."},{"name":"Desk","description":"The help desk inbox and conversations."},{"name":"Knowledge base","description":"Help articles for customers and staff."},{"name":"Live chat","description":"Chats with website visitors."},{"name":"Help desk setup","description":"Channels, SLAs, business hours, macros, canned replies and satisfaction surveys."},{"name":"Calls","description":"Phone calls, their recordings and what they are filed against."},{"name":"Comments","description":"Notes and comments on records."},{"name":"Notifications","description":"A person's notifications."},{"name":"Reminders","description":"Reminders set on records."},{"name":"Search","description":"One search across CRM records."},{"name":"Saved filters","description":"Saved list filters."},{"name":"Dashboards","description":"Dashboard figures."},{"name":"Exports","description":"Record exports."},{"name":"Activity","description":"Who did what, and when."},{"name":"Custom fields & modules","description":"Fields and record types an organisation adds itself."},{"name":"Layouts","description":"How record pages and the menu are laid out."},{"name":"Automations","description":"Rules, buttons and the AI pipeline assistant that act on records."},{"name":"Integrations","description":"Connections to other systems."},{"name":"Teams & people","description":"Teams, people and their preferences."},{"name":"Settings","description":"Organisation settings."}],"paths":{"/search":{"get":{"tags":["Search"],"summary":"Search all records","description":"Searches leads, contacts, accounts, deals, tickets, requests, help desk customers, subscriptions, quotes, products, people and custom records at once, including their custom fields, and answers the best matches grouped by record type. A search under 2 characters answers no hits. Fields hidden from you are neither searched nor shown, and tickets you may not see stay out.\n\n**Permission:** `crm.search.read` (View search).","operationId":"globalSearch","parameters":[{"name":"modules","in":"query","required":false,"schema":{"type":"array","items":{"type":"string","enum":["leads","contacts","accounts","deals","tickets","requests","helpdeskCustomers","subscriptions","quotes","products","users","customRecords"]}},"description":"Record types to search, as a list or comma-separated (e.g. leads,contacts). Unknown names are ignored; leave out (or name none that exist) to search all","example":["leads","contacts"]},{"name":"perModuleLimit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":25},"description":"Most hits per record type, from 1 to 25 (default 5)","example":5},{"name":"q","in":"query","required":true,"schema":{"type":"string"},"description":"What to search for: at least 2 characters after trimming, or nothing is found","example":"wanjiku"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"query":{"type":"string","description":"The search text, trimmed"},"total":{"type":"integer","description":"How many hits came back, across all groups"},"groups":{"type":"array","items":{"type":"object","properties":{"module":{"type":"string","description":"leads, contacts, accounts, deals, tickets, requests, helpdeskCustomers, subscriptions, quotes, products, users or customRecords"},"label":{"type":"string"},"hits":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The record's ID"},"module":{"type":"string"},"label":{"type":"string","description":"The record's name or title"},"secondary":{"type":"string","description":"A second line, such as an email or account number (left out when there is none)"},"href":{"type":"string","description":"Where the record opens in the CRM web app. A custom record's hit also carries customModuleSlug, its custom module"},"score":{"type":"number","description":"How well it matched, from 0 to 1 (higher is better)"}}}}}}}}}}},"example":{"success":true,"data":{"query":"wanjiku","total":2,"groups":[{"module":"leads","label":"Leads","hits":[{"id":"6e7f8a9b-0c1d-4e2f-8a3b-4c5d6e7f8a9b","module":"leads","label":"Grace Wanjiku","secondary":"grace.wanjiku@example.com","href":"/sales/leads/6e7f8a9b-0c1d-4e2f-8a3b-4c5d6e7f8a9b","score":0.82}]},{"module":"contacts","label":"Contacts","hits":[{"id":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","module":"contacts","label":"Peter Wanjiku","secondary":"+254 700 000 123","href":"/sales/contacts/b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","score":0.64}]}]}}}}},"400":{"description":"q is missing or blank","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.search.read"]}],"x-required-scopes":["crm.search.read"]}},"/lookup-registry":{"get":{"tags":["Search"],"summary":"List the record types a reference can point at","description":"Answers every record type an ID in CRM can point at (built-in modules plus your organisation's active own modules): where to fetch or search it and which fields name it. It describes shapes only, never record data.\n\n**Permission:** `crm.customFields.read` (View custom fields).","operationId":"getLookupRegistry","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"modules":{"type":"array","items":{"type":"object","properties":{"module":{"type":"string","description":"The module's key, as a lookup field's lookupModule names it; your own modules are custom:<slug>"},"label":{"type":"string"},"listPath":{"type":"string","description":"The list call, for searching; null when there is none"},"byIdPath":{"type":"string","description":"The get-one call; :id stands for the record's ID"},"displayFields":{"type":"array","items":{"type":"string"},"description":"Fields that name a record, in order: the first one with a value wins (two names in one entry are joined with a space)"},"searchParam":{"type":"string","description":"The list call's search input, or null"},"panelEntity":{"type":"string"},"readScope":{"type":"string","description":"The permission needed to read the module"},"strategy":{"type":"string","description":"byId (fetch by ID), embedded (the parent record already holds it) or none (no call)"},"reachable":{"type":"boolean","description":"false when the API has no call for the module"}}}},"nativeFkColumns":{"type":"object","properties":{"leadId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"ownerId":{"type":"string"}},"description":"Built-in ID fields (e.g. accountId, ownerId) and the module each points at"}}}}},"example":{"success":true,"data":{"modules":[{"module":"accounts","label":"Accounts","listPath":"/accounts","byIdPath":"/accounts/:id","displayFields":["name"],"searchParam":"search","panelEntity":"account","readScope":"accounts.read","strategy":"byId","reachable":true},{"module":"custom:site-surveys","label":"Site surveys","listPath":"/custom-modules/site-surveys/records","byIdPath":"/custom-modules/site-surveys/records/:id","displayFields":["site_name","name","title","label","subject"],"searchParam":"search","panelEntity":"custom","readScope":"customModules.read","strategy":"byId","reachable":true}],"nativeFkColumns":{"leadId":"leads","accountId":"accounts","contactId":"contacts","ownerId":"users"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customFields.read"]}],"x-required-scopes":["crm.customFields.read"]}},"/products":{"get":{"tags":["Products & plans"],"summary":"List products","description":"List your organisation's products (the catalogue quotes are made from), by name, a page at a time. Prices are before tax.\n\n**Permission:** `crm.products.read` (View products).","operationId":"getProducts","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer"},"description":"Products per page (default 50, at most 500)","example":50},{"name":"page","in":"query","required":false,"schema":{"type":"integer"},"description":"Page number, from 1 (default 1)","example":1},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text to find in the name or SKU","example":"fibre"},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["active","inactive","discontinued"]},"description":"Only products with this status","example":"active"},{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["service","item"]},"description":"Only services or only items","example":"service"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"products":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","description":"service or item"},"price":{"type":"string","description":"Price before tax, a decimal string (two decimals)"},"sku":{"type":"string"},"status":{"type":"string","description":"active, inactive or discontinued"},"description":{"type":"string"},"taxId":{"type":"string","description":"The tax quotes add to it: one of the organisation's taxes, or a Books item's tax for a product mirrored from Books; null for none"},"unit":{"type":"string"},"recurringPeriod":{"type":"string","description":"week, month, quarter, half_year or year; null for a one-off product"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"pages":{"type":"integer","description":"Number of pages"},"hasMore":{"type":"boolean","description":"There is a next page"}}}}},"example":{"success":true,"products":[{"id":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"50 Mbps Fibre","type":"service","price":"4500.00","sku":"FIB-50","status":"active","description":"Monthly fibre internet, 50 Mbps","taxId":"460000000038029","unit":"month","recurringPeriod":"month","customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}],"pagination":{"page":1,"limit":50,"total":1,"pages":1,"hasMore":false}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.products.read"]}],"x-required-scopes":["crm.products.read"]}},"/getProducts":{"get":{"tags":["Products & plans"],"summary":"List products","description":"List your organisation's products (the catalogue quotes are made from), by name, a page at a time. Prices are before tax.\n\n**Permission:** `crm.products.read` (View products).","operationId":"getProducts_2","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer"},"description":"Products per page (default 50, at most 500)","example":50},{"name":"page","in":"query","required":false,"schema":{"type":"integer"},"description":"Page number, from 1 (default 1)","example":1},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text to find in the name or SKU","example":"fibre"},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["active","inactive","discontinued"]},"description":"Only products with this status","example":"active"},{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["service","item"]},"description":"Only services or only items","example":"service"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"products":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","description":"service or item"},"price":{"type":"string","description":"Price before tax, a decimal string (two decimals)"},"sku":{"type":"string"},"status":{"type":"string","description":"active, inactive or discontinued"},"description":{"type":"string"},"taxId":{"type":"string","description":"The tax quotes add to it: one of the organisation's taxes, or a Books item's tax for a product mirrored from Books; null for none"},"unit":{"type":"string"},"recurringPeriod":{"type":"string","description":"week, month, quarter, half_year or year; null for a one-off product"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"pages":{"type":"integer","description":"Number of pages"},"hasMore":{"type":"boolean","description":"There is a next page"}}}}},"example":{"success":true,"products":[{"id":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"50 Mbps Fibre","type":"service","price":"4500.00","sku":"FIB-50","status":"active","description":"Monthly fibre internet, 50 Mbps","taxId":"460000000038029","unit":"month","recurringPeriod":"month","customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}],"pagination":{"page":1,"limit":50,"total":1,"pages":1,"hasMore":false}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.products.read"]}],"x-required-scopes":["crm.products.read"]}},"/products/analytics":{"get":{"tags":["Products & plans"],"summary":"Get product figures","description":"Product figures for your organisation: how many products by status and by type, price statistics (before tax) and the 10 highest-priced products. dateFrom and dateTo count only products created in that window.\n\n**Permission:** `crm.products.read` (View products).","operationId":"getProductAnalytics","parameters":[{"name":"dateFrom","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only products created on or after this","example":"2026-01-01T00:00:00.000Z"},{"name":"dateTo","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only products created on or before this","example":"2026-12-31T23:59:59.000Z"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"overview":{"type":"object","properties":{"totalProducts":{"type":"integer"},"activeProducts":{"type":"integer"},"inactiveProducts":{"type":"integer"}},"description":"totalProducts, plus <status>Products for each status present (e.g. activeProducts)"},"breakdown":{"type":"object","properties":{"byStatus":{"type":"object","properties":{"active":{"type":"integer"},"inactive":{"type":"integer"}},"description":"Products per status"},"byType":{"type":"object","properties":{"service":{"type":"integer"},"item":{"type":"integer"}},"description":"Products per type (service, item)"}}},"pricing":{"type":"object","properties":{"averagePrice":{"type":"integer"},"minimumPrice":{"type":"integer"},"maximumPrice":{"type":"integer"},"totalValue":{"type":"integer","description":"Sum of the products' prices"}}},"topProducts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"price":{"type":"string"},"sku":{"type":"string"},"status":{"type":"string"},"description":{"type":"string"},"taxId":{"type":"string"},"unit":{"type":"string"},"recurringPeriod":{"type":"string"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"description":"The 10 highest-priced products, as stored"}}},"filters":{"type":"object","properties":{"dateFrom":{"type":"string","format":"date-time"},"dateTo":{}}}}},"example":{"success":true,"data":{"overview":{"totalProducts":3,"activeProducts":2,"inactiveProducts":1},"breakdown":{"byStatus":{"active":2,"inactive":1},"byType":{"service":2,"item":1}},"pricing":{"averagePrice":4000,"minimumPrice":3500,"maximumPrice":4500,"totalValue":12000},"topProducts":[{"id":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"50 Mbps Fibre","type":"service","price":"4500.00","sku":"FIB-50","status":"active","description":"Monthly fibre internet, 50 Mbps","taxId":"460000000038029","unit":"month","recurringPeriod":"month","customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}]},"filters":{"dateFrom":"2026-01-01T00:00:00.000Z","dateTo":null}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.products.read"]}],"x-required-scopes":["crm.products.read"]}},"/getProductAnalytics":{"get":{"tags":["Products & plans"],"summary":"Get product figures","description":"Product figures for your organisation: how many products by status and by type, price statistics (before tax) and the 10 highest-priced products. dateFrom and dateTo count only products created in that window.\n\n**Permission:** `crm.products.read` (View products).","operationId":"getProductAnalytics_2","parameters":[{"name":"dateFrom","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only products created on or after this","example":"2026-01-01T00:00:00.000Z"},{"name":"dateTo","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only products created on or before this","example":"2026-12-31T23:59:59.000Z"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"overview":{"type":"object","properties":{"totalProducts":{"type":"integer"},"activeProducts":{"type":"integer"},"inactiveProducts":{"type":"integer"}},"description":"totalProducts, plus <status>Products for each status present (e.g. activeProducts)"},"breakdown":{"type":"object","properties":{"byStatus":{"type":"object","properties":{"active":{"type":"integer"},"inactive":{"type":"integer"}},"description":"Products per status"},"byType":{"type":"object","properties":{"service":{"type":"integer"},"item":{"type":"integer"}},"description":"Products per type (service, item)"}}},"pricing":{"type":"object","properties":{"averagePrice":{"type":"integer"},"minimumPrice":{"type":"integer"},"maximumPrice":{"type":"integer"},"totalValue":{"type":"integer","description":"Sum of the products' prices"}}},"topProducts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"price":{"type":"string"},"sku":{"type":"string"},"status":{"type":"string"},"description":{"type":"string"},"taxId":{"type":"string"},"unit":{"type":"string"},"recurringPeriod":{"type":"string"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"description":"The 10 highest-priced products, as stored"}}},"filters":{"type":"object","properties":{"dateFrom":{"type":"string","format":"date-time"},"dateTo":{}}}}},"example":{"success":true,"data":{"overview":{"totalProducts":3,"activeProducts":2,"inactiveProducts":1},"breakdown":{"byStatus":{"active":2,"inactive":1},"byType":{"service":2,"item":1}},"pricing":{"averagePrice":4000,"minimumPrice":3500,"maximumPrice":4500,"totalValue":12000},"topProducts":[{"id":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"50 Mbps Fibre","type":"service","price":"4500.00","sku":"FIB-50","status":"active","description":"Monthly fibre internet, 50 Mbps","taxId":"460000000038029","unit":"month","recurringPeriod":"month","customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}]},"filters":{"dateFrom":"2026-01-01T00:00:00.000Z","dateTo":null}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.products.read"]}],"x-required-scopes":["crm.products.read"]}},"/plans":{"get":{"tags":["Products & plans"],"summary":"List plans","description":"Lists your organisation's service plans in name order, a page at a time, each with the product it belongs to. Narrow it by a search on the name, status, service type, product, connection type or field conditions; includeStats adds counts by status and connection type.\n\n**Permission:** `crm.plans.read` (View plans).","operationId":"getPlans","parameters":[{"name":"connectionType","in":"query","required":false,"schema":{"type":"array","items":{"type":"string","enum":["Fiber","Wireless"]}},"description":"Only plans offered on any of these connection types (one value or a list)","example":["Fiber"]},{"name":"fieldFilters","in":"query","required":false,"schema":{"type":"object"},"description":"Older form: conditions on built-in fields, keyed by name","example":{"status":["active"]}},{"name":"filters","in":"query","required":false,"schema":{"type":"object"},"description":"Field conditions keyed by filter field ID (a native field name, or cf:<key> for your own fields; GET /filter-fields lists them), each { operator, value } or { operator, values }; a bare value means \"is\". A JSON object in a string also works. An invalid condition answers 400 naming the field","example":{"status":{"operator":"is","values":["active"]}}},{"name":"includeStats","in":"query","required":false,"schema":{"type":"boolean"},"description":"true to add stats: counts of the matching plans","example":true},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25},"description":"Plans per page, 1 to 500","example":25},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"product","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Older name for productId"},{"name":"productId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only plans of this product","example":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f"},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text to look for in the plan name","example":"Fibre"},{"name":"serviceType","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Older name for serviceTypeId"},{"name":"serviceTypeId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only plans of this service type","example":"e3d02e5a-7b9c-4e6f-9c1d-5a6b7c8d9e0f"},{"name":"sort","in":"query","required":false,"schema":{"type":"object"},"description":"Accepted but not used yet: plans always come in name order"},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["active","inactive","suspended"]},"description":"Only plans in this status","example":"active"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"limitStr":{"type":"string"},"burstMaxDown":{"description":"Burst settings (burstMax*, burstThresh*, burstTime*): the router's burst speed, threshold and time, or null"},"burstMaxUp":{},"burstThreshDown":{},"burstThreshUp":{},"burstTimeDown":{},"burstTimeUp":{},"connectionType":{"type":"array","items":{"type":"string"},"description":"Fiber, Wireless or both"},"installationFee":{"type":"string","description":"Installation fee, a decimal in a string"},"maxLimitDown":{"type":"integer","description":"Download speed limit, in kbit/s"},"maxLimitUp":{"type":"integer","description":"Upload speed limit, in kbit/s"},"rateLimit":{"type":"string","description":"The router's rate-limit text, e.g. 50M/25M"},"serviceTypeId":{"type":"string"},"statusLevelId":{},"status":{"type":"string","description":"active, inactive or suspended"},"productId":{"type":"string","description":"The product this plan is a speed option of"},"booksItemId":{"type":"string","description":"The Kazee Books item the plan is sold as (the first month of its quotes), or null"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"product":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"sku":{"type":"string"},"price":{"type":"string"}},"description":"The product the plan belongs to: ID, name, SKU and price"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}}},"stats":{"type":"object","properties":{"totalPlans":{"type":"integer"},"activePlans":{"type":"integer"},"inactivePlans":{"type":"integer"},"suspendedPlans":{"type":"integer"},"fiberPlans":{"type":"integer"},"wirelessPlans":{"type":"integer"}},"description":"Only with includeStats: how many matching plans there are, by status and connection type"}}},"example":{"success":true,"data":[{"id":"9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Home Fibre 50 Mbps","limitStr":"","burstMaxDown":null,"burstMaxUp":null,"burstThreshDown":null,"burstThreshUp":null,"burstTimeDown":null,"burstTimeUp":null,"connectionType":["Fiber"],"installationFee":"2000.00","maxLimitDown":51200,"maxLimitUp":25600,"rateLimit":"50M/25M","serviceTypeId":"e3d02e5a-7b9c-4e6f-9c1d-5a6b7c8d9e0f","statusLevelId":null,"status":"active","productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","booksItemId":"itm_8c1d4e5f60718293a4b5c6d7e8f9a0b1","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","product":{"id":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","name":"Home Fibre","sku":"HF-50","price":"4500.00"}}],"pagination":{"page":1,"limit":25,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false},"stats":{"totalPlans":1,"activePlans":1,"inactivePlans":0,"suspendedPlans":0,"fiberPlans":1,"wirelessPlans":0}}}}},"400":{"description":"A field condition is not valid (the message names the field)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.plans.read"]}],"x-required-scopes":["crm.plans.read"]},"post":{"tags":["Products & plans"],"summary":"Create a plan","description":"Creates a service plan (a speed option) under one of your products, as an Internet plan. The name must be unique in your organisation. Everyone in the organisation is notified.\n\n**Permission:** `crm.plans.create` (Create plans).","operationId":"createPlan","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"burstMaxDown":{"type":"integer","description":"Burst download speed, in kbit/s"},"burstMaxUp":{"type":"integer","description":"Burst upload speed, in kbit/s"},"burstThreshDown":{"type":"integer","description":"Download burst threshold, in kbit/s"},"burstThreshUp":{"type":"integer","description":"Upload burst threshold, in kbit/s"},"burstTimeDown":{"type":"integer","description":"Download burst time, in seconds"},"burstTimeUp":{"type":"integer","description":"Upload burst time, in seconds"},"connectionType":{"type":"array","items":{"type":"string","enum":["Fiber","Wireless"]},"description":"Connection types the plan is offered on","examples":[["Fiber"]]},"installationFee":{"type":"number","default":0,"description":"Installation fee","examples":[2000]},"maxLimitDown":{"type":"integer","description":"Download speed limit, in kbit/s","examples":[51200]},"maxLimitUp":{"type":"integer","description":"Upload speed limit, in kbit/s","examples":[25600]},"name":{"type":"string","description":"The plan's name, unique in your organisation","examples":["Home Fibre 50 Mbps"]},"product":{"type":"string","format":"uuid","description":"The product the plan belongs to","examples":["8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f"]},"rateLimit":{"type":"string","description":"The router's rate-limit text","examples":["50M/25M"]},"status":{"type":"string","enum":["active","inactive","suspended"],"default":"active","description":"The plan's status","examples":["active"]}},"required":["maxLimitDown","maxLimitUp","name","product"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"limitStr":{"type":"string"},"burstMaxDown":{"description":"Burst settings (burstMax*, burstThresh*, burstTime*): the router's burst speed, threshold and time, or null"},"burstMaxUp":{},"burstThreshDown":{},"burstThreshUp":{},"burstTimeDown":{},"burstTimeUp":{},"connectionType":{"type":"array","items":{"type":"string"},"description":"Fiber, Wireless or both"},"installationFee":{"type":"string","description":"Installation fee, a decimal in a string"},"maxLimitDown":{"type":"integer","description":"Download speed limit, in kbit/s"},"maxLimitUp":{"type":"integer","description":"Upload speed limit, in kbit/s"},"rateLimit":{"type":"string","description":"The router's rate-limit text, e.g. 50M/25M"},"serviceTypeId":{"type":"string"},"statusLevelId":{},"status":{"type":"string","description":"active, inactive or suspended"},"productId":{"type":"string","description":"The product this plan is a speed option of"},"booksItemId":{"description":"The Kazee Books item the plan is sold as (the first month of its quotes), or null"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Home Fibre 50 Mbps","limitStr":"","burstMaxDown":null,"burstMaxUp":null,"burstThreshDown":null,"burstThreshUp":null,"burstTimeDown":null,"burstTimeUp":null,"connectionType":["Fiber"],"installationFee":"2000.00","maxLimitDown":51200,"maxLimitUp":25600,"rateLimit":"50M/25M","serviceTypeId":"e3d02e5a-7b9c-4e6f-9c1d-5a6b7c8d9e0f","statusLevelId":null,"status":"active","productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","booksItemId":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}}}}},"400":{"description":"A connection type or status is not valid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"The product does not exist, or your organisation has no Internet service type","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A plan with that name already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.plans.create"]}],"x-required-scopes":["crm.plans.create"]}},"/plans/{planId}":{"get":{"tags":["Products & plans"],"summary":"Get a plan","description":"Gets one service plan with the product it belongs to.\n\n**Permission:** `crm.plans.read` (View plans).","operationId":"getPlan","parameters":[{"name":"planId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The plan to get","example":"9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"limitStr":{"type":"string"},"burstMaxDown":{"description":"Burst settings (burstMax*, burstThresh*, burstTime*): the router's burst speed, threshold and time, or null"},"burstMaxUp":{},"burstThreshDown":{},"burstThreshUp":{},"burstTimeDown":{},"burstTimeUp":{},"connectionType":{"type":"array","items":{"type":"string"},"description":"Fiber, Wireless or both"},"installationFee":{"type":"string","description":"Installation fee, a decimal in a string"},"maxLimitDown":{"type":"integer","description":"Download speed limit, in kbit/s"},"maxLimitUp":{"type":"integer","description":"Upload speed limit, in kbit/s"},"rateLimit":{"type":"string","description":"The router's rate-limit text, e.g. 50M/25M"},"serviceTypeId":{"type":"string"},"statusLevelId":{},"status":{"type":"string","description":"active, inactive or suspended"},"productId":{"type":"string","description":"The product this plan is a speed option of"},"booksItemId":{"type":"string","description":"The Kazee Books item the plan is sold as (the first month of its quotes), or null"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"product":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"sku":{"type":"string"},"price":{"type":"string"}},"description":"The product the plan belongs to: ID, name, SKU and price"}}}}},"example":{"success":true,"data":{"id":"9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Home Fibre 50 Mbps","limitStr":"","burstMaxDown":null,"burstMaxUp":null,"burstThreshDown":null,"burstThreshUp":null,"burstTimeDown":null,"burstTimeUp":null,"connectionType":["Fiber"],"installationFee":"2000.00","maxLimitDown":51200,"maxLimitUp":25600,"rateLimit":"50M/25M","serviceTypeId":"e3d02e5a-7b9c-4e6f-9c1d-5a6b7c8d9e0f","statusLevelId":null,"status":"active","productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","booksItemId":"itm_8c1d4e5f60718293a4b5c6d7e8f9a0b1","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","product":{"id":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","name":"Home Fibre","sku":"HF-50","price":"4500.00"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No plan with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.plans.read"]}],"x-required-scopes":["crm.plans.read"]},"put":{"tags":["Products & plans"],"summary":"Update a plan","description":"Changes a service plan: only the fields you send change. Everyone in the organisation is notified. The plan's Kazee Books item is set with PUT /plans/:planId/books-item instead.\n\n**Permission:** `crm.plans.update` (Update plans).","operationId":"updatePlan","parameters":[{"name":"planId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The plan to change","example":"9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"burstMaxDown":{"type":"integer","description":"Burst download speed, in kbit/s"},"burstMaxUp":{"type":"integer","description":"Burst upload speed, in kbit/s"},"burstThreshDown":{"type":"integer","description":"Download burst threshold, in kbit/s"},"burstThreshUp":{"type":"integer","description":"Upload burst threshold, in kbit/s"},"burstTimeDown":{"type":"integer","description":"Download burst time, in seconds"},"burstTimeUp":{"type":"integer","description":"Upload burst time, in seconds"},"connectionType":{"type":"array","items":{"type":"string","enum":["Fiber","Wireless"]},"description":"Connection types the plan is offered on","examples":[["Fiber","Wireless"]]},"installationFee":{"type":"number","description":"Installation fee","examples":[2500]},"maxLimitDown":{"type":"integer","description":"Download speed limit, in kbit/s","examples":[51200]},"maxLimitUp":{"type":"integer","description":"Upload speed limit, in kbit/s","examples":[25600]},"name":{"type":"string","description":"New name, unique in your organisation","examples":["Home Fibre 50 Mbps"]},"product":{"type":"string","format":"uuid","description":"Move the plan to this product","examples":["8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f"]},"rateLimit":{"type":"string","description":"The router's rate-limit text","examples":["50M/25M"]},"serviceType":{"type":"string","format":"uuid","description":"Move the plan to this service type","examples":["e3d02e5a-7b9c-4e6f-9c1d-5a6b7c8d9e0f"]},"status":{"type":"string","enum":["active","inactive","suspended"],"description":"New status","examples":["inactive"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"limitStr":{"type":"string"},"burstMaxDown":{"description":"Burst settings (burstMax*, burstThresh*, burstTime*): the router's burst speed, threshold and time, or null"},"burstMaxUp":{},"burstThreshDown":{},"burstThreshUp":{},"burstTimeDown":{},"burstTimeUp":{},"connectionType":{"type":"array","items":{"type":"string"},"description":"Fiber, Wireless or both"},"installationFee":{"type":"string","description":"Installation fee, a decimal in a string"},"maxLimitDown":{"type":"integer","description":"Download speed limit, in kbit/s"},"maxLimitUp":{"type":"integer","description":"Upload speed limit, in kbit/s"},"rateLimit":{"type":"string","description":"The router's rate-limit text, e.g. 50M/25M"},"serviceTypeId":{"type":"string"},"statusLevelId":{},"status":{"type":"string","description":"active, inactive or suspended"},"productId":{"type":"string","description":"The product this plan is a speed option of"},"booksItemId":{"type":"string","description":"The Kazee Books item the plan is sold as (the first month of its quotes), or null"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Home Fibre 50 Mbps","limitStr":"","burstMaxDown":null,"burstMaxUp":null,"burstThreshDown":null,"burstThreshUp":null,"burstTimeDown":null,"burstTimeUp":null,"connectionType":["Fiber"],"installationFee":"2000.00","maxLimitDown":51200,"maxLimitUp":25600,"rateLimit":"50M/25M","serviceTypeId":"e3d02e5a-7b9c-4e6f-9c1d-5a6b7c8d9e0f","statusLevelId":null,"status":"active","productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","booksItemId":"itm_8c1d4e5f60718293a4b5c6d7e8f9a0b1","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}}}}},"400":{"description":"A connection type or status is not valid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No plan with that ID, or the service type or product does not exist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Another plan already has that name","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.plans.update"]}],"x-required-scopes":["crm.plans.update"]},"delete":{"tags":["Products & plans"],"summary":"Delete a plan","description":"Deletes a service plan, as long as no subscription uses it. Everyone in the organisation is notified.\n\n**Permission:** `crm.plans.delete` (Delete plans).","operationId":"deletePlan","parameters":[{"name":"planId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The plan to delete","example":"9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}}}},"example":{"success":true,"message":"Plan deleted successfully","data":{"id":"9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a","name":"Home Fibre 50 Mbps"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No plan with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Subscriptions still use the plan (meta.subscriptionCount says how many)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.plans.delete"]}],"x-required-scopes":["crm.plans.delete"]}},"/leads/export":{"get":{"tags":["Leads"],"summary":"Export leads as CSV","description":"Exports leads as CSV text inside the JSON reply: either the leads you list in leadIds, or the newest 500 leads matching the filters. Columns: ID, names, business name, source, status, email, phone, assignee, notes, created and updated dates and converted account ID. Hidden fields print as dots.\n\n**Permission:** `crm.leads.read` (View leads).","operationId":"exportLeads","parameters":[{"name":"assignedTo","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only leads assigned to this user","example":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"},{"name":"dateFrom","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only leads created at or after this time","example":"2026-10-01T00:00:00.000Z"},{"name":"dateTo","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only leads created at or before this time","example":"2026-10-31T23:59:59.000Z"},{"name":"leadIds","in":"query","required":false,"schema":{"type":"array","items":{"type":"string","format":"uuid"}},"description":"Export just these leads (a comma-separated list in a string also works); the filters are then ignored","example":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]},{"name":"source","in":"query","required":false,"schema":{"type":"string"},"description":"Only leads from this source","example":"website"},{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"Only leads at this stage (the stage slug, exactly)","example":"warm_leads"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"csvContent":{"type":"string","description":"The CSV file's text, header row first"},"exportType":{"type":"string","description":"selected (leadIds given) or filtered (the filters; at most 500 leads)"},"leadCount":{"type":"integer","description":"Leads in the file"}}},"example":{"success":true,"csvContent":"ID,First Name,Last Name,Business Name,Source,Status,Email,Phone,Assigned To,Notes,Created Date,Updated Date,Converted Account ID\n0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d,Amina,Wanjiru,,website,warm_leads,amina.wanjiru@example.com,+254700000001,Brian Otieno,Asked about home fibre,2026-10-01,2026-10-15,","exportType":"filtered","leadCount":1}}}},"400":{"description":"leadIds is neither a list nor a string","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.read"]}],"x-required-scopes":["crm.leads.read"]}},"/exportLeadsPDF":{"get":{"tags":["Leads"],"summary":"Export leads as PDF","description":"Exports leads as an A4 PDF report, sent base64-encoded inside the JSON reply: either the leads you list in leadIds, or the newest 500 leads matching the filters. Hidden fields print as dots.\n\n**Permission:** `crm.leads.read` (View leads).","operationId":"exportLeadsPDF","parameters":[{"name":"assignedTo","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only leads assigned to this user","example":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"},{"name":"dateFrom","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only leads created at or after this time","example":"2026-10-01T00:00:00.000Z"},{"name":"dateTo","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only leads created at or before this time","example":"2026-10-31T23:59:59.000Z"},{"name":"leadIds","in":"query","required":false,"schema":{"type":"array","items":{"type":"string","format":"uuid"}},"description":"Export just these leads (a comma-separated list in a string also works); the filters are then ignored","example":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text to find in the name, business name, email or phone","example":"Wanjiru"},{"name":"source","in":"query","required":false,"schema":{"type":"string"},"description":"Only leads from this source","example":"website"},{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"Only leads at this stage (the stage slug, exactly)","example":"warm_leads"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"pdfContent":{"type":"string","description":"The PDF file, base64-encoded"},"exportType":{"type":"string","description":"selected (leadIds given) or filtered (the filters; at most 500 leads)"},"leadCount":{"type":"integer","description":"Leads in the report"},"contentType":{"type":"string"}}},"example":{"success":true,"pdfContent":"JVBERi0xLjQKJcOkw7zDtsOfCjIgMCBvYmoK...","exportType":"filtered","leadCount":1,"contentType":"application/pdf"}}}},"400":{"description":"leadIds holds no lead ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.read"]}],"x-required-scopes":["crm.leads.read"]}},"/leads/analytics":{"get":{"tags":["Leads"],"summary":"Get lead analytics","description":"Lead figures for your organisation: totals, how many converted, counts by stage and by source, and leads created per month. Narrow it by creation date, source or assignee.\n\n**Permission:** `crm.leads.read` (View leads).","operationId":"getLeadAnalytics","parameters":[{"name":"assignedTo","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only leads assigned to this user","example":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"},{"name":"dateFrom","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only leads created at or after this time","example":"2026-10-01T00:00:00.000Z"},{"name":"dateTo","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only leads created at or before this time","example":"2026-10-31T23:59:59.000Z"},{"name":"source","in":"query","required":false,"schema":{"type":"string"},"description":"Only leads from this source","example":"website"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"summary":{"type":"object","properties":{"totalLeads":{"type":"integer"},"total":{"type":"integer"},"qualified":{"type":"integer","description":"Leads at a stage named exactly qualified (0 when your pipeline has no such stage); proposal and lost likewise"},"proposal":{"type":"integer"},"converted":{"type":"integer","description":"Leads linked to a converted account"},"lost":{"type":"integer"},"conversionRate":{"type":"number","description":"converted divided by total (0 to 1)"}}},"breakdown":{"type":"object","properties":{"byStatus":{"type":"object","properties":{"warm_leads":{"type":"integer"},"hot_lead":{"type":"integer"},"converted":{"type":"integer"},"not_qualified":{"type":"integer"}},"description":"Leads at each stage, by stage slug"},"bySource":{"type":"object","properties":{"website":{"type":"integer"},"referral":{"type":"integer"}},"description":"Leads from each source"}}},"trends":{"type":"object","properties":{"monthly":{"type":"array","items":{"type":"object","properties":{"month":{"type":"string","description":"Month created, as YYYY-MM"},"count":{"type":"integer"},"converted":{"type":"integer","description":"Of those, how many have converted"}}}}}},"dateRange":{"type":"object","properties":{"from":{"type":"string","format":"date-time"},"to":{},"appliedFilters":{"type":"object","properties":{"source":{},"assignedTo":{}}}}}}}}},"example":{"success":true,"data":{"summary":{"totalLeads":120,"total":120,"qualified":0,"proposal":0,"converted":18,"lost":0,"conversionRate":0.15},"breakdown":{"byStatus":{"warm_leads":70,"hot_lead":22,"converted":18,"not_qualified":10},"bySource":{"website":80,"referral":40}},"trends":{"monthly":[{"month":"2026-09","count":55,"converted":10},{"month":"2026-10","count":65,"converted":8}]},"dateRange":{"from":"2026-09-01T00:00:00.000Z","to":null,"appliedFilters":{"source":null,"assignedTo":null}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.read"]}],"x-required-scopes":["crm.leads.read"]}},"/leads/stage-counts":{"get":{"tags":["Leads"],"summary":"Count leads by stage","description":"Counts your organisation's leads at each stage, for the board's column totals. Takes the same filters as listing leads, so the totals match the cards.\n\n**Permission:** `crm.leads.read` (View leads).","operationId":"getLeadStageCounts","parameters":[{"name":"assignedTo","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only leads assigned to this user","example":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"},{"name":"customFieldFilters","in":"query","required":false,"schema":{"type":"object"},"description":"Older form: conditions on your own fields, keyed by field key (without cf:)","example":{"region":"Nairobi"}},{"name":"entityType","in":"query","required":false,"schema":{"type":"string","enum":["individual","company"]},"description":"Only individuals or only companies","example":"company"},{"name":"fieldFilters","in":"query","required":false,"schema":{"type":"object"},"description":"Older form: conditions on built-in fields, keyed by name","example":{"status":["active"]}},{"name":"filters","in":"query","required":false,"schema":{"type":"object"},"description":"Field conditions keyed by filter field ID (a native field name, or cf:<key> for your own fields; GET /filter-fields lists them), each { operator, value } or { operator, values }; a bare value means \"is\". A JSON object in a string also works. An invalid condition answers 400 naming the field","example":{"status":{"operator":"is","values":["active"]}}},{"name":"ownerId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only leads owned by this user","example":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text to find in the name, business name, email or phone, as when listing leads","example":"Wanjiru"},{"name":"source","in":"query","required":false,"schema":{"type":"string"},"description":"Only leads from this source","example":"website"},{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"Only leads at this stage (a stage slug; a list of slugs also works)","example":"warm_leads"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"byStatus":{"type":"object","properties":{"warm_leads":{"type":"integer"},"hot_lead":{"type":"integer"},"converted":{"type":"integer"}},"description":"Number of matching leads at each stage, by stage slug; stages with none are left out"}}},"example":{"byStatus":{"warm_leads":42,"hot_lead":17,"converted":9}}}}},"400":{"description":"A filter is not valid JSON, names an unknown field or operator, or there are over 50 conditions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.read"]}],"x-required-scopes":["crm.leads.read"]}},"/leads/stale":{"get":{"tags":["Leads"],"summary":"List stale leads","description":"Lists the leads nobody has updated for daysOld days (30 by default), least recently updated first, with how many days each has waited and a count by stage. Converted and closed leads are included unless you filter by status.\n\n**Permission:** `crm.leads.read` (View leads).","operationId":"getStaleLeads","parameters":[{"name":"assignedTo","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only leads assigned to this user","example":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"},{"name":"daysOld","in":"query","required":false,"schema":{"type":"integer","default":30},"description":"How many days without an update makes a lead stale","example":30},{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"Only leads at this stage (the stage slug, exactly)","example":"warm_leads"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"staleLeads":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"businessName":{},"status":{"type":"string"},"assignedTo":{"type":"string"},"daysSinceUpdate":{"type":"integer","description":"Whole days since the lead was last updated"},"updatedAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}}},"summary":{"type":"object","properties":{"total":{"type":"integer"},"daysOld":{"type":"integer"},"byStatus":{"type":"object","properties":{"warm_leads":{"type":"integer"}},"description":"Stale leads at each stage, by stage slug"}}}}}}},"example":{"success":true,"data":{"staleLeads":[{"id":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","firstName":"Amina","lastName":"Wanjiru","businessName":null,"status":"warm_leads","assignedTo":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","daysSinceUpdate":41,"updatedAt":"2026-09-10T08:00:00.000Z","createdAt":"2026-09-01T08:30:00.000Z"}],"summary":{"total":1,"daysOld":30,"byStatus":{"warm_leads":1}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.read"]}],"x-required-scopes":["crm.leads.read"]}},"/leads":{"get":{"tags":["Leads"],"summary":"List leads","description":"Lists your organisation's leads, newest first, a page at a time, with the owner, assignee, primary contact and primary account of each. Narrow it with the search and filter inputs. With perStageLimit it answers the top leads of every stage instead (the board view): ordered by the leads pipeline's card date field when it has one (earliest first, leads without the date last), else most recently updated first; sortBy=cardDate pages a stage in that same order. With includeStats=true it adds counts by stage, source and month.\n\n**Permission:** `crm.leads.read` (View leads).","operationId":"getLeads","parameters":[{"name":"assignedTo","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only leads assigned to this user","example":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"},{"name":"customFieldFilters","in":"query","required":false,"schema":{"type":"object"},"description":"Older form: conditions on your own fields, keyed by field key (without cf:)","example":{"region":"Nairobi"}},{"name":"entityType","in":"query","required":false,"schema":{"type":"string","enum":["individual","company"]},"description":"Only individuals or only companies","example":"individual"},{"name":"fieldFilters","in":"query","required":false,"schema":{"type":"object"},"description":"Older form: conditions on built-in fields, keyed by name","example":{"status":["active"]}},{"name":"filters","in":"query","required":false,"schema":{"type":"object"},"description":"Field conditions keyed by filter field ID (a native field name, or cf:<key> for your own fields; GET /filter-fields lists them), each { operator, value } or { operator, values }; a bare value means \"is\". A JSON object in a string also works. An invalid condition answers 400 naming the field","example":{"status":{"operator":"is","values":["active"]}}},{"name":"includeStats","in":"query","required":false,"schema":{"type":"boolean"},"description":"true adds stats: counts by stage, by source and by month for the same filter","example":false},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25},"description":"Leads per page, 1 to 500","example":25},{"name":"ownerId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only leads owned by this user","example":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"perStageLimit","in":"query","required":false,"schema":{"type":"integer"},"description":"Board view: answer up to this many leads of every stage instead of one page, ordered by the leads pipeline's card date field (earliest first, leads without it last, then most recently updated) or, without one, most recently updated first; pagination then covers them all","example":20},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text to find in the first name, last name, business name, email or phone (any spelling of the number); a Kazee Tickets ID also finds the lead it came from","example":"Wanjiru"},{"name":"sort","in":"query","required":false,"schema":{"type":"object"},"description":"Sort as { \"field\": \"createdAt\" | \"updatedAt\" | \"status\" | \"cardDate\", \"order\": \"asc\" | \"desc\" }; wins over sortBy and sortOrder. Default: newest created first. cardDate is the board order (see sortBy)","example":{"field":"updatedAt","order":"desc"}},{"name":"sortBy","in":"query","required":false,"schema":{"type":"string","enum":["createdAt","updatedAt","status","cardDate"]},"description":"Field to sort by (any other value sorts by createdAt). cardDate is the board's order, so a stage's next pages continue its first cards: the leads pipeline's card date field earliest first, leads without it last, then most recently updated; sortOrder does not change it. Without a card date field it is most recently updated first","example":"updatedAt"},{"name":"sortOrder","in":"query","required":false,"schema":{"type":"string","enum":["asc","desc"]},"description":"Sort direction (default desc)","example":"desc"},{"name":"source","in":"query","required":false,"schema":{"type":"string"},"description":"Only leads from this source","example":"website"},{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"Only leads at this stage (a stage slug; a list of slugs also works). Old names of a stage are understood","example":"warm_leads"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"partyId":{},"firstName":{"type":"string"},"lastName":{"type":"string"},"salutation":{"type":"string"},"businessName":{},"kraPin":{},"source":{"type":"string"},"entityType":{"type":"string","description":"individual or company"},"contactInfo":{"type":"object","properties":{"email":{"type":"string"},"phone":{"type":"string"}},"description":"The lead's email and phone"},"ownerId":{"type":"string"},"assignedTo":{"type":"string"},"status":{"type":"string","description":"The lead's pipeline stage, as its slug (e.g. warm_leads, hot_lead, converted, not_qualified); the stages are set per organisation"},"documentCompletionStatus":{"type":"object","properties":{"required":{"type":"integer"},"uploaded":{"type":"integer"},"approved":{"type":"integer"}},"description":"How many documents the lead is asked for, has uploaded and has had approved"},"notes":{"type":"string"},"relatedContactIds":{"type":"array"},"relatedAccountIds":{"type":"array"},"primaryContactId":{},"primaryAccountId":{},"convertedAccountId":{},"convertedContactId":{},"customFields":{"type":"object","properties":{"town":{"type":"string"},"connection_type":{"type":"string"}},"description":"Values of the organisation's own lead fields, by field key"},"externalIds":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"externalId":{"type":"string"}}}},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"owner":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"}}},"assignee":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"}}},"primaryContact":{},"primaryAccount":{},"_expanded":{"type":"object","properties":{"ownerId":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"module":{"type":"string"}}},"assignedTo":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"module":{"type":"string"}}}},"description":"Names for the IDs the lead holds (owner, contacts, accounts, lookup fields), by field path"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}}},"stats":{"type":"object","properties":{"totalCount":{"type":"integer"},"newCount":{"type":"integer"},"qualifiedCount":{"type":"integer"},"convertedCount":{"type":"integer"},"staleCount":{"type":"integer"},"rejectedCount":{"type":"integer"},"growthStats":{"type":"array","items":{"type":"object","properties":{"month":{"type":"string"},"count":{"type":"integer"}}}},"sourceStats":{"type":"object","properties":{"website":{"type":"integer"}}}},"description":"Only with includeStats=true: newCount, qualifiedCount, convertedCount and rejectedCount are the warm_leads, hot_lead, converted and not_qualified stages"}}},"example":{"success":true,"data":[{"id":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","partyId":null,"firstName":"Amina","lastName":"Wanjiru","salutation":"Ms.","businessName":null,"kraPin":null,"source":"website","entityType":"individual","contactInfo":{"email":"amina.wanjiru@example.com","phone":"+254700000001"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","assignedTo":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","status":"warm_leads","documentCompletionStatus":{"required":2,"uploaded":1,"approved":0},"notes":"Asked about home fibre in Kilimani","relatedContactIds":[],"relatedAccountIds":[],"primaryContactId":null,"primaryAccountId":null,"convertedAccountId":null,"convertedContactId":null,"customFields":{"town":"Nairobi","connection_type":"Home"},"externalIds":[{"source":"website","externalId":"web-10442"}],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","owner":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","firstName":"Brian","lastName":"Otieno"},"assignee":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","firstName":"Brian","lastName":"Otieno"},"primaryContact":null,"primaryAccount":null,"_expanded":{"ownerId":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","label":"Brian Otieno","module":"users"},"assignedTo":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","label":"Brian Otieno","module":"users"}}}],"pagination":{"page":1,"limit":25,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false},"stats":{"totalCount":1,"newCount":1,"qualifiedCount":0,"convertedCount":0,"staleCount":0,"rejectedCount":0,"growthStats":[{"month":"2026-10","count":1}],"sourceStats":{"website":1}}}}}},"400":{"description":"A filter is not valid JSON, names an unknown field or operator, or there are over 50 conditions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.read"]}],"x-required-scopes":["crm.leads.read"]},"post":{"tags":["Leads"],"summary":"Create a lead","description":"Creates a lead from at least a phone number or an email. If an open lead already has the same phone (or else email), that lead is filled in instead (existing values win) and the reply says existing: true. Contacts already holding that email or phone, and their accounts, are linked to the lead and listed in existingData. New leads without an owner go to the organisation's auto-assign rule.\n\n**Permission:** `crm.leads.create` (Create leads).","operationId":"createLead","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"assignedTo":{"type":"string","format":"uuid","description":"User to assign the lead to","examples":["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]},"businessName":{"type":"string","description":"Company name; required when entityType is company","examples":["Kijani Traders Ltd"]},"customFields":{"type":"object","description":"Values for the organisation's own lead fields, by field key; each is checked against its field's rules","examples":[{"town":"Nairobi","connection_type":"Home"}]},"email":{"type":"string","format":"email","description":"Email address; email or phone is required","examples":["amina.wanjiru@example.com"]},"entityType":{"type":"string","enum":["individual","company"],"description":"A person or a company (default individual)","examples":["individual"]},"firstName":{"type":"string","description":"First name","examples":["Amina"]},"lastName":{"type":"string","description":"Last name","examples":["Wanjiru"]},"notes":{"type":"string","description":"Free-text notes","examples":["Asked about home fibre in Kilimani"]},"ownerId":{"type":"string","format":"uuid","description":"Owner of the lead. Leave out (with assignedTo) to let the auto-assign rule pick one","examples":["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]},"phone":{"type":"string","description":"Phone number; email or phone is required","examples":["+254700000001"]},"salutation":{"type":"string","enum":["Mr.","Mrs.","Ms.","Dr.","Prof.",""],"description":"Title before the name (spellings without the dot are accepted too)","examples":["Ms."]},"source":{"type":"string","description":"Where the lead came from","examples":["website"]}},"required":["source"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"existing":{"type":"boolean","description":"Only when an open lead with the same phone or email was filled in instead of creating a new one"},"enrichedFields":{"type":"array","items":{"type":"string"},"description":"Only with existing: the fields this call filled in on that lead"},"data":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"salutation":{"type":"string"},"businessName":{},"kraPin":{},"fullName":{"type":"string"},"displayName":{"type":"string"},"source":{"type":"string"},"entityType":{"type":"string","description":"individual or company"},"contactInfo":{"type":"object","properties":{"email":{"type":"string"},"phone":{"type":"string"}},"description":"The lead's email and phone"},"assignedTo":{"type":"string"},"status":{"type":"string","description":"The lead's pipeline stage, as its slug (e.g. warm_leads, hot_lead, converted, not_qualified); the stages are set per organisation"},"documentCompletionStatus":{"type":"object","properties":{"required":{"type":"integer"},"uploaded":{"type":"integer"},"approved":{"type":"integer"}},"description":"How many documents the lead is asked for, has uploaded and has had approved"},"notes":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"convertedAccountId":{},"convertedContactId":{},"relatedContactIds":{"type":"array","items":{"type":"string"}},"relatedAccountIds":{"type":"array","items":{"type":"string"}},"primaryContactId":{"type":"string"},"primaryAccountId":{"type":"string"},"ownerId":{"type":"string"},"customFields":{"type":"object","properties":{"town":{"type":"string"},"connection_type":{"type":"string"}},"description":"Values of the organisation's own lead fields, by field key"}}},"existingData":{"type":"object","properties":{"contacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"status":{"type":"string"}}}},"accounts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"accountNumber":{"type":"string"},"status":{"type":"string"}}}}},"description":"Contacts that already have this email or phone, and their accounts that are not terminated; null when there are none"}}},"example":{"success":true,"existing":true,"enrichedFields":["lastName","email"],"data":{"_id":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","id":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","firstName":"Amina","lastName":"Wanjiru","salutation":"Ms.","businessName":null,"kraPin":null,"fullName":"Amina Wanjiru","displayName":"Amina Wanjiru","source":"website","entityType":"individual","contactInfo":{"email":"amina.wanjiru@example.com","phone":"+254700000001"},"assignedTo":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","status":"warm_leads","documentCompletionStatus":{"required":2,"uploaded":1,"approved":0},"notes":"Asked about home fibre in Kilimani","createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","convertedAccountId":null,"convertedContactId":null,"relatedContactIds":["4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"],"relatedAccountIds":["2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"],"primaryContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","primaryAccountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","customFields":{"town":"Nairobi","connection_type":"Home"}},"existingData":{"contacts":[{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","firstName":"Amina","lastName":"Wanjiru","email":"amina.wanjiru@example.com","phone":"+254700000001","status":"active"}],"accounts":[{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","name":"Amina Wanjiru","accountNumber":"ACC-00412","status":"active"}]}}}}},"400":{"description":"Neither email nor phone given, a company lead without businessName, an unknown salutation or entityType, an invalid email, or a custom field value that breaks its field's rules","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.create"]}],"x-required-scopes":["crm.leads.create"]}},"/audit/leads/{leadId}":{"get":{"tags":["Activity"],"summary":"Get a record's history","description":"Answers a record's history, newest first: field changes, creation, deletion and pipeline stage moves, each with who did it. Works for leads (/audit/leads/:leadId) and any record type (/audit/:entityType/:entityId). Passwords in the history are masked.\n\n**Permission:** `crm.leads.read` (View leads).","operationId":"getLeadAuditEvents","parameters":[{"name":"leadId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The lead's ID (the /audit/leads/:leadId route only)","example":"6e7f8a9b-0c1d-4e2f-8a3b-4c5d6e7f8a9b"},{"name":"entityId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"The record's ID","example":"0f1a2b3c-4d5e-4f6a-9b7c-8d9e0f1a2b3c"},{"name":"entityType","in":"query","required":false,"schema":{"type":"string"},"description":"The record type, singular or plural: lead, account, contact, deal, quote, request, subscription, product, plan, ticket, ...","example":"deal"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"Events per page, 1 to 200 (default 50)","example":50},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1},"description":"Page number, from 1","example":1}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"event":{"type":"string","description":"What happened, in words (e.g. Status Changed, or a stage move like New → Contacted — No response)"},"action":{"type":"string","description":"The machine name of what happened, e.g. created, updated or stage_changed"},"at":{"type":"string","format":"date-time"},"by":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"}},"description":"Who did it, or null when the system did"},"actedBy":{"description":"The bot that did it for the person in by (or as itself), { id, name, ai: true }, shown with the AI diamond; null when a person or the system did"},"details":{"type":"object","properties":{"changes":{"type":"object","properties":{"before":{"type":"object","properties":{"stage":{"type":"string"}}},"after":{"type":"object","properties":{"stage":{"type":"string"}}},"diff":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"}}}}},"description":"What changed: before, after and diff (a list of field, from, to). Passwords show as a marker"},"metadata":{"type":"object","properties":{"source":{"type":"string"},"transitionName":{"type":"string"},"transitionId":{"type":"string"},"pipelineId":{"type":"string"},"notes":{"type":"string"},"fieldValues":{"type":"object","properties":{}}},"description":"Extra facts about the event, such as where it came from"}}}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"},"truncated":{"type":"boolean","description":"True when the record has over 500 history rows, so the oldest may be missing"}}}}},"example":{"success":true,"data":[{"id":"d4e5f6a7-b8c9-4d0e-9f1a-3b4c5d6e7f8a","event":"New → Contacted — Called, will decide Friday","action":"stage_changed","at":"2026-10-20T09:00:00.000Z","by":{"id":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","name":"Amina Odhiambo","email":"amina@example.com"},"actedBy":null,"details":{"changes":{"before":{"stage":"New"},"after":{"stage":"Contacted"},"diff":[{"field":"stage","from":"New","to":"Contacted"}]},"metadata":{"source":"pipeline","transitionName":"Contact","transitionId":"f6a7b8c9-d0e1-4f2a-8b3c-5d6e7f8a9b0c","pipelineId":"a7b8c9d0-e1f2-4a3b-9c4d-6e7f8a9b0c1d","notes":"Called, will decide Friday","fieldValues":{}}}}],"pagination":{"page":1,"limit":50,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false,"truncated":false}}}}},"400":{"description":"entityType is missing or not valid, or the record ID is not a UUID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"On /audit/leads/:leadId: no lead with that ID in your organisation. For a ticket: you may not see it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.read"]}],"x-required-scopes":["crm.leads.read"]}},"/audit/{entityType}/{entityId}":{"get":{"tags":["Activity"],"summary":"Get a record's history","description":"Answers a record's history, newest first: field changes, creation, deletion and pipeline stage moves, each with who did it. Works for leads (/audit/leads/:leadId) and any record type (/audit/:entityType/:entityId). Passwords in the history are masked.\n\n**Permission:** `crm.leads.read` (View leads).","operationId":"getLeadAuditEvents_2","parameters":[{"name":"entityType","in":"path","required":true,"schema":{"type":"string"},"description":"The record type, singular or plural: lead, account, contact, deal, quote, request, subscription, product, plan, ticket, ...","example":"deal"},{"name":"entityId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The record's ID","example":"0f1a2b3c-4d5e-4f6a-9b7c-8d9e0f1a2b3c"},{"name":"leadId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"The lead's ID (the /audit/leads/:leadId route only)","example":"6e7f8a9b-0c1d-4e2f-8a3b-4c5d6e7f8a9b"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"Events per page, 1 to 200 (default 50)","example":50},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1},"description":"Page number, from 1","example":1}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"event":{"type":"string","description":"What happened, in words (e.g. Status Changed, or a stage move like New → Contacted — No response)"},"action":{"type":"string","description":"The machine name of what happened, e.g. created, updated or stage_changed"},"at":{"type":"string","format":"date-time"},"by":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"}},"description":"Who did it, or null when the system did"},"actedBy":{"description":"The bot that did it for the person in by (or as itself), { id, name, ai: true }, shown with the AI diamond; null when a person or the system did"},"details":{"type":"object","properties":{"changes":{"type":"object","properties":{"before":{"type":"object","properties":{"stage":{"type":"string"}}},"after":{"type":"object","properties":{"stage":{"type":"string"}}},"diff":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"}}}}},"description":"What changed: before, after and diff (a list of field, from, to). Passwords show as a marker"},"metadata":{"type":"object","properties":{"source":{"type":"string"},"transitionName":{"type":"string"},"transitionId":{"type":"string"},"pipelineId":{"type":"string"},"notes":{"type":"string"},"fieldValues":{"type":"object","properties":{}}},"description":"Extra facts about the event, such as where it came from"}}}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"},"truncated":{"type":"boolean","description":"True when the record has over 500 history rows, so the oldest may be missing"}}}}},"example":{"success":true,"data":[{"id":"d4e5f6a7-b8c9-4d0e-9f1a-3b4c5d6e7f8a","event":"New → Contacted — Called, will decide Friday","action":"stage_changed","at":"2026-10-20T09:00:00.000Z","by":{"id":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","name":"Amina Odhiambo","email":"amina@example.com"},"actedBy":null,"details":{"changes":{"before":{"stage":"New"},"after":{"stage":"Contacted"},"diff":[{"field":"stage","from":"New","to":"Contacted"}]},"metadata":{"source":"pipeline","transitionName":"Contact","transitionId":"f6a7b8c9-d0e1-4f2a-8b3c-5d6e7f8a9b0c","pipelineId":"a7b8c9d0-e1f2-4a3b-9c4d-6e7f8a9b0c1d","notes":"Called, will decide Friday","fieldValues":{}}}}],"pagination":{"page":1,"limit":50,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false,"truncated":false}}}}},"400":{"description":"entityType is missing or not valid, or the record ID is not a UUID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"On /audit/leads/:leadId: no lead with that ID in your organisation. For a ticket: you may not see it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.read"]}],"x-required-scopes":["crm.leads.read"]}},"/calls/settings/ai-brief":{"get":{"tags":["Calls"],"summary":"Get the AI caller brief setting","description":"Answers whether the AI caller brief (a short summary of the customer shown to the agent on a call) is on for your organisation, which provider and model it uses, and what would stop it right now. Needs the calls.admin permission.\n\n**Permission:** `crm.calls.admin` (Configure calls).","operationId":"getAiCallerBriefSettings","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"enabled":{"type":"boolean"},"provider":{"type":"string","description":"openai or anthropic; null until one is chosen"},"model":{"description":"The model chosen, or null for the provider's default (see effectiveModel)"},"effectiveModel":{"type":"string"},"version":{"type":"integer","description":"Send it back when you change the setting, to avoid overwriting someone else's change"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"},"killSwitch":{"type":"string","description":"off when the brief is switched off for the whole server"},"keyConfigured":{"type":"object","properties":{"openai":{"type":"boolean"},"anthropic":{"type":"boolean"}},"description":"Whether each provider's key is set up on the server (never the key)"},"active":{"type":"boolean","description":"True only when briefs are really made and shown: on, not switched off, and the provider's key is set up"}}}}},"example":{"success":true,"data":{"enabled":true,"provider":"anthropic","model":null,"effectiveModel":"claude-haiku-5-5","version":3,"updatedAt":"2026-10-15T14:05:00.000Z","updatedBy":"0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac","killSwitch":"on","keyConfigured":{"openai":false,"anthropic":true},"active":true}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.calls.admin"]}],"x-required-scopes":["crm.calls.admin"]},"put":{"tags":["Calls"],"summary":"Change the AI caller brief setting","description":"Turns the AI caller brief on or off for your organisation and chooses its provider and model; only the fields sent change. Needs the calls.admin permission. Turning it on needs a provider (never chosen for you, since it decides who sees customer data). Turning it off deletes every stored brief. Each change is written to the audit trail.\n\n**Permission:** `crm.calls.admin` (Configure calls).","operationId":"updateAiCallerBriefSettings","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Turn the brief on (true) or off (false)","examples":[true]},"model":{"type":"string","description":"The model ID, up to 100 characters (letters, digits, . _ : / -); null or empty uses the provider's default","examples":["claude-haiku-5-5"]},"provider":{"type":"string","enum":["openai","anthropic"],"description":"The AI provider; null or empty clears it","examples":["anthropic"]},"version":{"type":"integer","description":"The version you last read; when sent, the change is refused (409) if someone changed the setting since","examples":[3]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"enabled":{"type":"boolean"},"provider":{"type":"string","description":"openai or anthropic; null until one is chosen"},"model":{"description":"The model chosen, or null for the provider's default (see effectiveModel)"},"effectiveModel":{"type":"string"},"version":{"type":"integer","description":"Send it back when you change the setting, to avoid overwriting someone else's change"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"},"killSwitch":{"type":"string","description":"off when the brief is switched off for the whole server"},"keyConfigured":{"type":"object","properties":{"openai":{"type":"boolean"},"anthropic":{"type":"boolean"}},"description":"Whether each provider's key is set up on the server (never the key)"},"active":{"type":"boolean","description":"True only when briefs are really made and shown: on, not switched off, and the provider's key is set up"},"deletedBriefs":{"type":"integer","description":"How many stored briefs were deleted (only when you turned it off)"}}}}},"example":{"success":true,"data":{"enabled":true,"provider":"anthropic","model":null,"effectiveModel":"claude-haiku-5-5","version":4,"updatedAt":"2026-10-15T14:05:00.000Z","updatedBy":"0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac","killSwitch":"on","keyConfigured":{"openai":false,"anthropic":true},"active":true,"deletedBriefs":0}}}}},"400":{"description":"enabled is not true or false, provider is not openai or anthropic, model is not a valid model ID, version is not a whole number, or you enabled it with no provider","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"version is not the current one: someone else changed the setting since you read it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.calls.admin"]}],"x-required-scopes":["crm.calls.admin"]}},"/calls/settings":{"get":{"tags":["Calls"],"summary":"Get call settings","description":"Answers your organisation's call settings, with the defaults filled in, and whether this server takes in calls and can play recordings.\n\n**Permission:** `crm.calls.read` (View calls).","operationId":"getCallSettings","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"autoCreateContact":{"type":"boolean","description":"Whether an unknown caller becomes a new contact automatically (default off)"},"notifyMissed":{"type":"boolean","description":"Whether missed calls send a notification (default on)"},"defaultCountry":{"type":"string","description":"Two-letter country code used to read local numbers (default KE)"},"transcribe":{"type":"boolean","description":"Whether ready recordings are transcribed overnight (default off)"},"transcriptLanguages":{"type":"array","items":{"type":"string"},"description":"The languages the transcriber picks from (default en and sw)"},"consumerEnabled":{"type":"boolean","description":"Whether this server is taking in calls from the phone system"},"recordingsConfigured":{"type":"boolean","description":"Whether recording playback is set up on this server"}}}}},"example":{"success":true,"data":{"autoCreateContact":false,"notifyMissed":true,"defaultCountry":"KE","transcribe":true,"transcriptLanguages":["en","sw"],"consumerEnabled":true,"recordingsConfigured":true}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.calls.read"]}],"x-required-scopes":["crm.calls.read"]},"put":{"tags":["Calls"],"summary":"Change call settings","description":"Changes your organisation's call settings; only the settings sent change. Needs the calls.admin permission. Answers all five settings as they are now.\n\n**Permission:** `crm.calls.admin` (Configure calls).","operationId":"updateCallSettings","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"autoCreateContact":{"type":"boolean","description":"Make an unknown caller a new contact automatically, filed with the call","examples":[false]},"defaultCountry":{"type":"string","description":"Two-letter country code used to read local numbers","examples":["KE"]},"notifyMissed":{"type":"boolean","description":"Send a notification for missed calls","examples":[true]},"transcribe":{"type":"boolean","description":"Transcribe ready recordings overnight","examples":[true]},"transcriptLanguages":{"type":"array","items":{"type":"string"},"description":"1 to 4 lower-case two-letter language codes the transcriber may pick between, e.g. en, sw","examples":[["en","sw"]]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"autoCreateContact":{"type":"boolean"},"notifyMissed":{"type":"boolean"},"defaultCountry":{"type":"string"},"transcribe":{"type":"boolean"},"transcriptLanguages":{"type":"array","items":{"type":"string"}}}}}},"example":{"success":true,"data":{"autoCreateContact":false,"notifyMissed":true,"defaultCountry":"KE","transcribe":true,"transcriptLanguages":["en","sw"]}}}}},"400":{"description":"A true/false setting is not true or false, defaultCountry is not a two-letter code, or transcriptLanguages is not 1 to 4 supported language codes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.calls.admin"]}],"x-required-scopes":["crm.calls.admin"]}},"/calls/facets":{"get":{"tags":["Calls"],"summary":"Count calls by agent, queue and transcript","description":"Counts your organisation's calls per agent extension and per queue, and by transcript state, under the same filters as the calls list. The Calls screen uses it to fill its filter pickers.\n\n**Permission:** `crm.calls.read` (View calls).","operationId":"getCallFacets","parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only calls filed under this account","example":"c26d9a7f-4b8e-4fbd-9a6c-7e9f1b3d5a68"},{"name":"contactId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only calls filed under this contact","example":"b15c8f6e-3a7d-4eac-8f5b-6d8e0a2c4f57"},{"name":"dealId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only the calls of this deal's contact and lead. Needs the deals.read permission too","example":"e48f1c9b-6d0a-4bdf-9c8e-9a1b3d5f7c8a"},{"name":"direction","in":"query","required":false,"schema":{"type":"string","enum":["inbound","outbound","internal"]},"description":"Only calls in this direction","example":"inbound"},{"name":"extension","in":"query","required":false,"schema":{"type":"string"},"description":"Only calls on this agent extension","example":"204"},{"name":"filters","in":"query","required":false,"schema":{"type":"string"},"description":"Field conditions from the filter drawer, as a JSON object by field ID: { \"<field>\": { \"operator\": \"is\", \"value\": … } }. An unknown field or operator answers 400","example":"{\"outcome\":{\"operator\":\"is\",\"values\":[\"missed\",\"busy\"]}}"},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only calls that started at or after this time","example":"2026-10-01T00:00:00.000Z"},{"name":"hasRecording","in":"query","required":false,"schema":{"type":"boolean"},"description":"true: only calls with a ready recording; false: only calls without one","example":true},{"name":"leadId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only calls filed under this lead","example":"d37e0b8a-5c9f-4ace-8b7d-8f0a2c4e6b79"},{"name":"linked","in":"query","required":false,"schema":{"type":"string","enum":["contact","account","lead","none"]},"description":"Only calls filed under this kind of record, or none for calls filed nowhere","example":"contact"},{"name":"outcome","in":"query","required":false,"schema":{"type":"string"},"description":"Only calls with this outcome, or a comma list: pending, answered, missed, busy, failed, voicemail, cancelled","example":"missed,busy"},{"name":"partyId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only calls with this party (the person or company behind contacts and accounts)","example":"f59a2d0c-7e1b-4cea-8d9f-0b2c4e6a8d9b"},{"name":"q","in":"query","required":false,"schema":{"type":"string"},"description":"Search: a number (6 or more digits, any spelling: 0722…, +254722…), or a name of the caller, agent, contact, account or lead. With the calls.listen permission it also matches words said in the call","example":"0700000123"},{"name":"queue","in":"query","required":false,"schema":{"type":"string"},"description":"Only calls through this queue","example":"support"},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only calls that started before this time","example":"2026-11-01T00:00:00.000Z"},{"name":"transcript","in":"query","required":false,"schema":{"type":"string","enum":["ready","missing","failed"]},"description":"Only calls whose transcript is ready, failed, or missing (anything else)","example":"ready"},{"name":"unmatched","in":"query","required":false,"schema":{"type":"boolean"},"description":"true: only calls not filed under any record yet","example":false}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"agents":{"type":"array","items":{"type":"object","properties":{"extension":{"type":"string"},"agentName":{"type":"string"},"calls":{"type":"integer"}}},"description":"Each agent extension with calls, most calls first (at most 200)"},"queues":{"type":"array","items":{"type":"object","properties":{"queue":{"type":"string"},"calls":{"type":"integer"}}},"description":"Each queue with calls, most calls first (at most 100)"},"transcript":{"type":"object","properties":{"ready":{"type":"integer"},"missing":{"type":"integer"},"failed":{"type":"integer"}},"description":"How many calls have a ready transcript, a failed one, or none yet (missing)"}}}}},"example":{"success":true,"data":{"agents":[{"extension":"204","agentName":"Peter Otieno","calls":42}],"queues":[{"queue":"support","calls":37}],"transcript":{"ready":30,"missing":10,"failed":2}}}}}},"400":{"description":"A filter is not valid: an unknown direction, outcome, transcript or linked value, a date that is not a date, a dealId that is not an ID, or a bad filters condition","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You filtered by dealId without the deals.read permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.calls.read"]}],"x-required-scopes":["crm.calls.read"]}},"/calls":{"get":{"tags":["Calls"],"summary":"List calls","description":"Lists the phone system's calls for your organisation, newest first, with the record each one is filed under. Filter by direction, outcome, agent, queue, dates, the record, or search by number or name. Searching the words said in calls (and the matching snippet) needs the calls.listen permission; without it q only matches numbers and names.\n\n**Permission:** `crm.calls.read` (View calls).","operationId":"getCalls","parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only calls filed under this account","example":"c26d9a7f-4b8e-4fbd-9a6c-7e9f1b3d5a68"},{"name":"contactId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only calls filed under this contact","example":"b15c8f6e-3a7d-4eac-8f5b-6d8e0a2c4f57"},{"name":"dealId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only the calls of this deal's contact and lead. Needs the deals.read permission too","example":"e48f1c9b-6d0a-4bdf-9c8e-9a1b3d5f7c8a"},{"name":"direction","in":"query","required":false,"schema":{"type":"string","enum":["inbound","outbound","internal"]},"description":"Only calls in this direction","example":"inbound"},{"name":"extension","in":"query","required":false,"schema":{"type":"string"},"description":"Only calls on this agent extension","example":"204"},{"name":"filters","in":"query","required":false,"schema":{"type":"string"},"description":"Field conditions from the filter drawer, as a JSON object by field ID: { \"<field>\": { \"operator\": \"is\", \"value\": … } }. An unknown field or operator answers 400","example":"{\"outcome\":{\"operator\":\"is\",\"values\":[\"missed\",\"busy\"]}}"},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only calls that started at or after this time","example":"2026-10-01T00:00:00.000Z"},{"name":"hasRecording","in":"query","required":false,"schema":{"type":"boolean"},"description":"true: only calls with a ready recording; false: only calls without one","example":true},{"name":"leadId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only calls filed under this lead","example":"d37e0b8a-5c9f-4ace-8b7d-8f0a2c4e6b79"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25},"description":"Calls per page, at most 500","example":25},{"name":"linked","in":"query","required":false,"schema":{"type":"string","enum":["contact","account","lead","none"]},"description":"Only calls filed under this kind of record, or none for calls filed nowhere","example":"contact"},{"name":"outcome","in":"query","required":false,"schema":{"type":"string"},"description":"Only calls with this outcome, or a comma list: pending, answered, missed, busy, failed, voicemail, cancelled","example":"missed,busy"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"partyId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only calls with this party (the person or company behind contacts and accounts)","example":"f59a2d0c-7e1b-4cea-8d9f-0b2c4e6a8d9b"},{"name":"q","in":"query","required":false,"schema":{"type":"string"},"description":"Search: a number (6 or more digits, any spelling: 0722…, +254722…), or a name of the caller, agent, contact, account or lead. With the calls.listen permission it also matches words said in the call","example":"0700000123"},{"name":"queue","in":"query","required":false,"schema":{"type":"string"},"description":"Only calls through this queue","example":"support"},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only calls that started before this time","example":"2026-11-01T00:00:00.000Z"},{"name":"transcript","in":"query","required":false,"schema":{"type":"string","enum":["ready","missing","failed"]},"description":"Only calls whose transcript is ready, failed, or missing (anything else)","example":"ready"},{"name":"unmatched","in":"query","required":false,"schema":{"type":"boolean"},"description":"true: only calls not filed under any record yet","example":false}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"pbxCallId":{"type":"string","description":"The phone system's own ID for the call"},"direction":{"type":"string","description":"inbound, outbound or internal"},"externalNumber":{"type":"string","description":"The customer's number (E.164), or null for an internal call"},"externalName":{"type":"string"},"internalNumber":{"type":"string"},"extension":{"type":"string"},"agentName":{"type":"string"},"agentUserId":{"type":"string"},"centralParticipants":{"type":"array","items":{"type":"object","properties":{"centralUserId":{"type":"string"},"direction":{"type":"string"},"extensions":{"type":"array","items":{"type":"string"}},"answeredAt":{"type":"string","format":"date-time"}}},"description":"The Kazee people who took part, as Kazee Phone recorded them"},"queue":{"type":"string"},"did":{"type":"string"},"startedAt":{"type":"string","format":"date-time"},"answeredAt":{"type":"string","format":"date-time"},"endedAt":{"type":"string","format":"date-time"},"durationSeconds":{"type":"integer"},"talkSeconds":{"type":"integer"},"outcome":{"type":"string","description":"pending, answered, missed, busy, failed, voicemail or cancelled"},"hangupCause":{"type":"string"},"hasRecording":{"type":"boolean"},"recordingStatus":{"type":"string","description":"none, pending (still being archived), ready or failed"},"partyId":{"type":"string"},"contactId":{"type":"string"},"contactName":{"type":"string"},"accountId":{"type":"string"},"accountName":{"type":"string"},"leadId":{},"leadName":{},"linkedTo":{"type":"string","description":"Which record the call is filed under: contact, account, lead or none"},"matchMethod":{"type":"string","description":"How it was filed: none, cache, contacts, created, manual or lead. manual (a person filed it) is never changed by automatic matching"},"notes":{"type":"string"},"disposition":{"type":"string","description":"The agent's outcome code for the call, from the organisation's list, or null"},"metadata":{"type":"object","properties":{}},"transcriptStatus":{"type":"string","description":"none, queued, processing, ready, failed or skipped"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"transcriptSnippet":{"type":"string","description":"Only when you searched with q and hold calls.listen: the passage of the transcript that matched, the hits wrapped in <mark></mark>; empty when the row matched on its number or a name"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}}}}},"example":{"success":true,"data":[{"id":"7d1e4b2a-9c3f-4a6e-8b1d-2f4a6c8e0b13","pbxCallId":"1729500000.4521","direction":"inbound","externalNumber":"+254700000123","externalName":"JANE WANJIKU","internalNumber":"+254200000100","extension":"204","agentName":"Peter Otieno","agentUserId":"0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac","centralParticipants":[{"centralUserId":"65f1a2b3c4d5e6f7a8b9c0d1","direction":"incoming","extensions":["204"],"answeredAt":"2026-10-20T09:00:12.000Z"}],"queue":"support","did":"+254200000100","startedAt":"2026-10-20T09:00:00.000Z","answeredAt":"2026-10-20T09:00:12.000Z","endedAt":"2026-10-20T09:04:30.000Z","durationSeconds":270,"talkSeconds":258,"outcome":"answered","hangupCause":"NORMAL_CLEARING","hasRecording":true,"recordingStatus":"ready","partyId":"f59a2d0c-7e1b-4cea-8d9f-0b2c4e6a8d9b","contactId":"b15c8f6e-3a7d-4eac-8f5b-6d8e0a2c4f57","contactName":"Jane Wanjiku","accountId":"c26d9a7f-4b8e-4fbd-9a6c-7e9f1b3d5a68","accountName":"Wanjiku Household","leadId":null,"leadName":null,"linkedTo":"contact","matchMethod":"contacts","notes":"Router reset; customer will call back if the line drops again","disposition":"resolved","metadata":{},"transcriptStatus":"ready","createdAt":"2026-10-20T09:00:01.000Z","updatedAt":"2026-10-20T09:05:00.000Z","transcriptSnippet":""}],"pagination":{"page":1,"limit":25,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false}}}}},"400":{"description":"A filter is not valid: an unknown direction, outcome, transcript or linked value, a date that is not a date, a dealId that is not an ID, or a bad filters condition","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You filtered by dealId without the deals.read permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.calls.read"]}],"x-required-scopes":["crm.calls.read"]}},"/calls/{callId}/transcript":{"get":{"tags":["Calls"],"summary":"Get a call's transcript","description":"Answers a call's transcript with what the transcript panel may offer around it. Needs the calls.listen permission, like playing the recording, and reading a ready transcript is logged as an access to the recording (who and when).\n\n**Permission:** `crm.calls.listen` (Listen to calls).","operationId":"getCallTranscript","parameters":[{"name":"callId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The call's ID","example":"7d1e4b2a-9c3f-4a6e-8b1d-2f4a6c8e0b13"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"status":{"type":"string","description":"none (never transcribed), queued, processing, ready, failed or skipped"},"transcribe":{"type":"boolean","description":"Whether transcripts are on for your organisation"},"recordingReady":{"type":"boolean","description":"Whether the call has a recording to transcribe"},"canRetry":{"type":"boolean","description":"Whether you may queue it again now (you hold calls.update and calls.listen, transcripts are on, and it failed or never ran on a ready recording)"},"transcript":{"type":"object","properties":{"id":{"type":"string"},"callLogId":{"type":"string"},"status":{"type":"string"},"pipelineVersion":{"type":"string"},"engine":{"type":"object","properties":{"name":{"type":"string"},"model":{"type":"string"}}},"languages":{"type":"array","items":{"type":"string"}},"languageShare":{"type":"object","properties":{"en":{"type":"number"},"sw":{"type":"number"}},"description":"Share of the speech in each language, 0 to 1"},"speechSeconds":{"type":"integer"},"wallSeconds":{"type":"integer"},"attempts":{"type":"integer"},"error":{},"queuedAt":{"type":"string","format":"date-time"},"startedAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"},"segments":{"type":"array","items":{"type":"object","properties":{"seq":{"type":"integer"},"speaker":{"type":"string"},"t0Ms":{"type":"integer"},"t1Ms":{"type":"integer"},"lang":{"type":"string"},"text":{"type":"string"}}},"description":"What was said, in order: who spoke (customer or agent), from and to (milliseconds into the call), language and words"}},"description":"null when the call was never queued; the segments are filled only when status is ready"}}}}},"example":{"success":true,"data":{"status":"ready","transcribe":true,"recordingReady":true,"canRetry":false,"transcript":{"id":"8e2f5c3b-0d4a-4b7f-9c2e-3a5b7d9f1c24","callLogId":"7d1e4b2a-9c3f-4a6e-8b1d-2f4a6c8e0b13","status":"ready","pipelineVersion":"2026.10.1","engine":{"name":"whisper","model":"large-v3"},"languages":["en","sw"],"languageShare":{"en":0.8,"sw":0.2},"speechSeconds":240,"wallSeconds":31,"attempts":1,"error":null,"queuedAt":"2026-10-20T21:00:00.000Z","startedAt":"2026-10-20T21:02:10.000Z","completedAt":"2026-10-20T21:02:41.000Z","segments":[{"seq":0,"speaker":"customer","t0Ms":0,"t1Ms":4200,"lang":"en","text":"Hello, my internet has been off since morning."},{"seq":1,"speaker":"agent","t0Ms":4300,"t1Ms":9100,"lang":"en","text":"Sorry about that. Let me check your router from here."}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No call with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.calls.listen"]}],"x-required-scopes":["crm.calls.listen"]}},"/calls/{callId}/recording-url":{"get":{"tags":["Calls"],"summary":"Get a call recording's playback link","description":"Answers a 15-minute link to play one call's recording. Needs the calls.listen permission (hearing a call is a right of its own, beyond seeing it happened). Each link handed out is logged: who, when and until when.\n\n**Permission:** `crm.calls.listen` (Listen to calls).","operationId":"getCallRecordingUrl","parameters":[{"name":"callId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The call's ID","example":"7d1e4b2a-9c3f-4a6e-8b1d-2f4a6c8e0b13"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"url":{"type":"string","description":"A signed link to play or download the recording; it stops working at expiresAt"},"expiresAt":{"type":"string","format":"date-time"},"ttlSeconds":{"type":"integer","description":"How long the link works, in seconds (15 minutes)"},"accessId":{"type":"string","description":"The ID of the access log entry written for this hand-out"}}}}},"example":{"success":true,"data":{"url":"https://recordings.example.com/org-65f1a2b3/2026/10/20/1729500000.4521.mp3?X-Amz-Signature=abc123","expiresAt":"2026-10-20T09:20:00.000Z","ttlSeconds":900,"accessId":"a04b7e5d-2f6c-4d9b-9e4a-5c7d9f1b3e46"}}}}},"400":{"description":"The recording's stored location is not valid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No call with that ID in your organisation, or the call has no recording yet (still being archived, failed, or none)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"description":"Call recordings are not set up on this server","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.calls.listen"]}],"x-required-scopes":["crm.calls.listen"]}},"/calls/{callId}":{"get":{"tags":["Calls"],"summary":"Get a call","description":"Answers one phone call with the record it is filed under (contact, account or lead) and the notes added to it from Kazee Phone, one entry each.\n\n**Permission:** `crm.calls.read` (View calls).","operationId":"getCall","parameters":[{"name":"callId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The call's ID","example":"7d1e4b2a-9c3f-4a6e-8b1d-2f4a6c8e0b13"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"pbxCallId":{"type":"string","description":"The phone system's own ID for the call"},"direction":{"type":"string","description":"inbound, outbound or internal"},"externalNumber":{"type":"string","description":"The customer's number (E.164), or null for an internal call"},"externalName":{"type":"string"},"internalNumber":{"type":"string"},"extension":{"type":"string"},"agentName":{"type":"string"},"agentUserId":{"type":"string"},"centralParticipants":{"type":"array","items":{"type":"object","properties":{"centralUserId":{"type":"string"},"direction":{"type":"string"},"extensions":{"type":"array","items":{"type":"string"}},"answeredAt":{"type":"string","format":"date-time"}}},"description":"The Kazee people who took part, as Kazee Phone recorded them"},"queue":{"type":"string"},"did":{"type":"string"},"startedAt":{"type":"string","format":"date-time"},"answeredAt":{"type":"string","format":"date-time"},"endedAt":{"type":"string","format":"date-time"},"durationSeconds":{"type":"integer"},"talkSeconds":{"type":"integer"},"outcome":{"type":"string","description":"pending, answered, missed, busy, failed, voicemail or cancelled"},"hangupCause":{"type":"string"},"hasRecording":{"type":"boolean"},"recordingStatus":{"type":"string","description":"none, pending (still being archived), ready or failed"},"partyId":{"type":"string"},"contactId":{"type":"string"},"contactName":{"type":"string"},"accountId":{"type":"string"},"accountName":{"type":"string"},"leadId":{},"leadName":{},"linkedTo":{"type":"string","description":"Which record the call is filed under: contact, account, lead or none"},"matchMethod":{"type":"string","description":"How it was filed: none, cache, contacts, created, manual or lead. manual (a person filed it) is never changed by automatic matching"},"notes":{"type":"string","description":"The call's notes as one text: a note typed in CRM, then one line per note added from Kazee Phone"},"disposition":{"type":"string","description":"The agent's outcome code for the call, from the organisation's list, or null"},"metadata":{"type":"object","properties":{}},"transcriptStatus":{"type":"string","description":"none, queued, processing, ready, failed or skipped"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"noteEntries":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"authorUserId":{"type":"string"},"text":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}},"description":"Each note added from Kazee Phone, oldest first; authorUserId is the writer's Kazee (Central) user ID"}}}}},"example":{"success":true,"data":{"id":"7d1e4b2a-9c3f-4a6e-8b1d-2f4a6c8e0b13","pbxCallId":"1729500000.4521","direction":"inbound","externalNumber":"+254700000123","externalName":"JANE WANJIKU","internalNumber":"+254200000100","extension":"204","agentName":"Peter Otieno","agentUserId":"0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac","centralParticipants":[{"centralUserId":"65f1a2b3c4d5e6f7a8b9c0d1","direction":"incoming","extensions":["204"],"answeredAt":"2026-10-20T09:00:12.000Z"}],"queue":"support","did":"+254200000100","startedAt":"2026-10-20T09:00:00.000Z","answeredAt":"2026-10-20T09:00:12.000Z","endedAt":"2026-10-20T09:04:30.000Z","durationSeconds":270,"talkSeconds":258,"outcome":"answered","hangupCause":"NORMAL_CLEARING","hasRecording":true,"recordingStatus":"ready","partyId":"f59a2d0c-7e1b-4cea-8d9f-0b2c4e6a8d9b","contactId":"b15c8f6e-3a7d-4eac-8f5b-6d8e0a2c4f57","contactName":"Jane Wanjiku","accountId":"c26d9a7f-4b8e-4fbd-9a6c-7e9f1b3d5a68","accountName":"Wanjiku Household","leadId":null,"leadName":null,"linkedTo":"contact","matchMethod":"contacts","notes":"Router reset; customer will call back if the line drops again","disposition":"resolved","metadata":{},"transcriptStatus":"ready","createdAt":"2026-10-20T09:00:01.000Z","updatedAt":"2026-10-20T09:05:00.000Z","noteEntries":[{"id":"9f3a6d4c-1e5b-4c8a-8d3f-4b6c8e0a2d35","authorUserId":"65f1a2b3c4d5e6f7a8b9c0d1","text":"Router reset; customer will call back if the line drops again","createdAt":"2026-10-20T09:05:00.000Z"}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No call with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.calls.read"]}],"x-required-scopes":["crm.calls.read"]}},"/leads/{leadId}/existing-customer":{"get":{"tags":["Leads"],"summary":"Find the existing customers a lead matches","description":"Lists the existing customers this lead's phone or email already belongs to: accounts with at least one service, reached through a contact with the same phone (last nine digits) or email. Customers with the most live services come first. Read only; used for the lead's \"Existing customer\" banner and Start site sale.\n\n**Permission:** `crm.leads.read` (View leads).","operationId":"getLeadExistingCustomer","parameters":[{"name":"leadId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The lead's ID","example":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"customers":{"type":"array","items":{"type":"object","properties":{"accountId":{"type":"string"},"name":{"type":"string"},"contactId":{"type":"string"},"contactName":{"type":"string"},"matchedOn":{"type":"string","description":"phone or email"},"services":{"type":"integer","description":"All the account's services"},"liveServices":{"type":"integer","description":"Its services that are not terminated"},"booksLinked":{"type":"boolean","description":"Whether the account is linked to a Kazee Books customer"}}}},"differentCustomerConfirmed":{"type":"boolean","description":"Someone ticked Different customer (confirmed) on the lead, so converting it is allowed despite the matches"}}}}},"example":{"success":true,"data":{"customers":[{"accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","name":"Amina Wanjiru","contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","contactName":"Amina Wanjiru","matchedOn":"phone","services":2,"liveServices":1,"booksLinked":true}],"differentCustomerConfirmed":false}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No lead with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.read"]}],"x-required-scopes":["crm.leads.read"]}},"/leads/{leadId}":{"get":{"tags":["Leads"],"summary":"Get a lead","description":"Gets one lead of your organisation with its quotes, its uploaded documents (with fresh download links) and counts of both by status. documentCompletionStatus is worked out on every read from the lead's document checklist.\n\n**Permission:** `crm.leads.read` (View leads).","operationId":"getLead","parameters":[{"name":"leadId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The lead's ID","example":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"lead":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"partyId":{},"firstName":{"type":"string"},"lastName":{"type":"string"},"salutation":{"type":"string"},"businessName":{},"kraPin":{},"source":{"type":"string"},"entityType":{"type":"string","description":"individual or company"},"contactInfo":{"type":"object","properties":{"email":{"type":"string"},"phone":{"type":"string"}},"description":"The lead's email and phone"},"ownerId":{"type":"string"},"assignedTo":{"type":"string"},"status":{"type":"string","description":"The lead's pipeline stage, as its slug (e.g. warm_leads, hot_lead, converted, not_qualified); the stages are set per organisation"},"documentCompletionStatus":{"type":"object","properties":{"required":{"type":"integer"},"uploaded":{"type":"integer"},"approved":{"type":"integer"},"missingDocuments":{"type":"array","items":{"type":"string"}}},"description":"Worked out on read: documents required by the checklist, uploaded and approved, and the document types still missing"},"notes":{"type":"string"},"relatedContactIds":{"type":"array"},"relatedAccountIds":{"type":"array"},"primaryContactId":{},"primaryAccountId":{},"convertedAccountId":{},"convertedContactId":{},"customFields":{"type":"object","properties":{"town":{"type":"string"},"connection_type":{"type":"string"}},"description":"Values of the organisation's own lead fields, by field key"},"externalIds":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"externalId":{"type":"string"}}}},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"owner":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"}}},"assignee":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"}}},"primaryContact":{},"primaryAccount":{},"_expanded":{"type":"object","properties":{"ownerId":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"module":{"type":"string"}}},"assignedTo":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"module":{"type":"string"}}}},"description":"Names for the IDs the lead holds (owner, contacts, accounts, lookup fields), by field path"}}},"quotes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"quoteNumber":{"type":"string"},"leadId":{"type":"string"},"accountId":{"type":"string"},"subscriptionId":{},"requestId":{},"dealId":{"type":"string"},"ownerId":{"type":"string"},"type":{"type":"string"},"subtotal":{"type":"string"},"total":{"type":"string"},"status":{"type":"string","description":"draft, sent, accepted, declined or expired"},"paidAmount":{"type":"string"},"balance":{"type":"string"},"paidAt":{},"validityStart":{"type":"string","format":"date-time"},"validityEnd":{"type":"string","format":"date-time"},"sentAt":{},"acceptedAt":{},"declinedAt":{},"expiredAt":{},"notes":{"type":"string"},"revisionOf":{},"revisionNumber":{"type":"integer"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"description":"The lead's quotes, newest first (at most 100)"},"documents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"leadId":{"type":"string"},"documentType":{"type":"string"},"fileName":{"type":"string"},"originalName":{"type":"string"},"fileUrl":{"type":"string","description":"A short-lived download link, signed afresh on every read"},"objectName":{"type":"string"},"fileSize":{"type":"integer"},"mimeType":{"type":"string"},"uploadedAt":{"type":"string","format":"date-time"},"uploadedBy":{},"entityType":{"type":"string"},"isRequired":{"type":"boolean"},"status":{"type":"string","description":"pending, approved or rejected"},"notes":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"statistics":{"type":"object","properties":{"documents":{"type":"object","properties":{"total":{"type":"integer"},"approved":{"type":"integer"},"rejected":{"type":"integer"},"pending":{"type":"integer"}}},"quotes":{"type":"object","properties":{"total":{"type":"integer"},"draft":{"type":"integer"},"sent":{"type":"integer"},"accepted":{"type":"integer"},"declined":{"type":"integer"},"expired":{"type":"integer"},"totalValue":{"type":"integer"}}}}}}}}},"example":{"success":true,"data":{"lead":{"id":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","partyId":null,"firstName":"Amina","lastName":"Wanjiru","salutation":"Ms.","businessName":null,"kraPin":null,"source":"website","entityType":"individual","contactInfo":{"email":"amina.wanjiru@example.com","phone":"+254700000001"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","assignedTo":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","status":"warm_leads","documentCompletionStatus":{"required":2,"uploaded":1,"approved":0,"missingDocuments":["kra_pin_certificate"]},"notes":"Asked about home fibre in Kilimani","relatedContactIds":[],"relatedAccountIds":[],"primaryContactId":null,"primaryAccountId":null,"convertedAccountId":null,"convertedContactId":null,"customFields":{"town":"Nairobi","connection_type":"Home"},"externalIds":[{"source":"website","externalId":"web-10442"}],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","owner":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","firstName":"Brian","lastName":"Otieno"},"assignee":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","firstName":"Brian","lastName":"Otieno"},"primaryContact":null,"primaryAccount":null,"_expanded":{"ownerId":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","label":"Brian Otieno","module":"users"},"assignedTo":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","label":"Brian Otieno","module":"users"}}},"quotes":[{"id":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","quoteNumber":"QUOTE-00042","leadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","subscriptionId":null,"requestId":null,"dealId":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","type":"quote","subtotal":"6500.00","total":"7540.00","status":"draft","paidAmount":"0.00","balance":"7540.00","paidAt":null,"validityStart":"2026-10-01T08:30:00.000Z","validityEnd":"2026-11-01T00:00:00.000Z","sentAt":null,"acceptedAt":null,"declinedAt":null,"expiredAt":null,"notes":"Includes free router for 12 months","revisionOf":null,"revisionNumber":1,"customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}],"documents":[{"id":"af9c8a1c-3d5e-4a2b-9e7f-1c2d3e4f5a6b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","leadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","documentType":"national_id","fileName":"national_id-1760000000000.pdf","originalName":"amina-id.pdf","fileUrl":"https://files.example.com/crm/leads/0b9e8c1e/documents/individual/national_id-1760000000000.pdf?X-Amz-Signature=abc123","objectName":"leads/0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d/documents/individual/national_id-1760000000000.pdf","fileSize":248311,"mimeType":"application/pdf","uploadedAt":"2026-10-15T14:05:00.000Z","uploadedBy":null,"entityType":"individual","isRequired":true,"status":"pending","notes":null,"deletedAt":null,"createdAt":"2026-10-15T14:05:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}],"statistics":{"documents":{"total":1,"approved":0,"rejected":0,"pending":1},"quotes":{"total":1,"draft":1,"sent":0,"accepted":0,"declined":0,"expired":0,"totalValue":7540}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No lead with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.read"]}],"x-required-scopes":["crm.leads.read"]},"put":{"tags":["Leads"],"summary":"Update a lead","description":"Changes the fields you send on a lead and leaves the rest alone. It cannot move the lead to another stage: use the pipeline transitions for that. Send expected (the values you saw) to have the save refused with 409 if someone changed those fields in the meantime. The organisation's automations for an updated lead run afterwards.\n\n**Permission:** `crm.leads.update` (Update leads).","operationId":"updateLead","parameters":[{"name":"leadId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The lead's ID","example":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"assignedTo":{"type":"string","format":"uuid","description":"User the lead is assigned to","examples":["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]},"businessName":{"type":"string","description":"Company name","examples":["Kijani Traders Ltd"]},"comment":{"type":"string","description":"Accepted but not used","examples":["Updated after the call"]},"customFields":{"type":"object","description":"Values for the organisation's own lead fields, by field key, laid over the stored ones. Use customFieldsPatch instead when other writers may change the lead at the same time","examples":[{"town":"Nairobi"}]},"customFieldsPatch":{"type":"object","description":"Custom field values to set, by field key, merged in one step so no other key is lost. Not together with customFields","examples":[{"connection_type":"Business"}]},"email":{"type":"string","format":"email","description":"Email address","examples":["amina.wanjiru@example.com"]},"entityType":{"type":"string","enum":["individual","company"],"description":"A person or a company","examples":["company"]},"expected":{"type":"object","description":"The values you saw before editing, for the fields this save changes: { \"<field>\": <value>, \"customFields\": { \"<key>\": <value> } } (null for a field you saw empty). Fields: firstName, lastName, salutation, businessName, entityType, source, email, phone, assignedTo, notes, ownerId","examples":[{"firstName":"Amina","customFields":{"town":null}}]},"firstName":{"type":"string","description":"First name (not empty)","examples":["Amina"]},"kraPin":{"type":"string","description":"Tax number (KRA PIN), up to 32 characters","examples":["A012345678Z"]},"lastName":{"type":"string","description":"Last name (not empty)","examples":["Wanjiru"]},"notes":{"type":"string","description":"Free-text notes","examples":["Prefers a call after 5 pm"]},"ownerId":{"type":"string","format":"uuid","description":"Owner of the lead","examples":["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]},"phone":{"type":"string","description":"Phone number","examples":["+254700000001"]},"salutation":{"type":"string","enum":["Mr.","Mrs.","Ms.","Dr.","Prof.",""],"description":"Title before the name (spellings without the dot are accepted too)","examples":["Ms."]},"source":{"type":"string","description":"Where the lead came from","examples":["referral"]},"status":{"type":"string","description":"Refused unless it equals the lead's current stage: stages change only through pipeline transitions","examples":["warm_leads"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"salutation":{"type":"string"},"businessName":{},"kraPin":{},"fullName":{"type":"string"},"displayName":{"type":"string"},"source":{"type":"string"},"entityType":{"type":"string","description":"individual or company"},"contactInfo":{"type":"object","properties":{"email":{"type":"string"},"phone":{"type":"string"}},"description":"The lead's email and phone"},"assignedTo":{"type":"string"},"status":{"type":"string","description":"The lead's pipeline stage, as its slug (e.g. warm_leads, hot_lead, converted, not_qualified); the stages are set per organisation"},"documentCompletionStatus":{"type":"object","properties":{"required":{"type":"integer"},"uploaded":{"type":"integer"},"approved":{"type":"integer"}},"description":"How many documents the lead is asked for, has uploaded and has had approved"},"notes":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"convertedAccountId":{},"convertedContactId":{},"relatedContactIds":{"type":"array","items":{"type":"string"}},"relatedAccountIds":{"type":"array","items":{"type":"string"}},"primaryContactId":{"type":"string"},"primaryAccountId":{"type":"string"},"ownerId":{"type":"string"},"customFields":{"type":"object","properties":{"town":{"type":"string"},"connection_type":{"type":"string"}},"description":"Values of the organisation's own lead fields, by field key"}}}}},"example":{"success":true,"data":{"_id":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","id":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","firstName":"Amina","lastName":"Wanjiru","salutation":"Ms.","businessName":null,"kraPin":null,"fullName":"Amina Wanjiru","displayName":"Amina Wanjiru","source":"website","entityType":"individual","contactInfo":{"email":"amina.wanjiru@example.com","phone":"+254700000001"},"assignedTo":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","status":"warm_leads","documentCompletionStatus":{"required":2,"uploaded":1,"approved":0},"notes":"Asked about home fibre in Kilimani","createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","convertedAccountId":null,"convertedContactId":null,"relatedContactIds":["4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"],"relatedAccountIds":["2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"],"primaryContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","primaryAccountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","customFields":{"town":"Nairobi","connection_type":"Home"}}}}}},"400":{"description":"status differs from the lead's stage, both customFields and customFieldsPatch sent, expected names a field it cannot check, an unknown salutation or entityType, an invalid email, a company without businessName, or a custom field value that breaks its field's rules","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No lead with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A field named in expected was changed by someone else since you opened the lead (FIELD_CONFLICT; meta.fields lists them)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.update"]}],"x-required-scopes":["crm.leads.update"]},"delete":{"tags":["Leads"],"summary":"Delete a lead","description":"Deletes a lead. It is only marked deleted (an admin can restore it), and disappears from lists and searches.\n\n**Permission:** `crm.leads.delete` (Delete leads).","operationId":"deleteLead","parameters":[{"name":"leadId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The lead's ID","example":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"},{"name":"deletedBy","in":"query","required":true,"schema":{"type":"string","format":"uuid"},"description":"Your user ID. Required, though the call does not store it (the change log records the signed-in person)","example":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"},{"name":"reason","in":"query","required":false,"schema":{"type":"string"},"description":"Why it is deleted (accepted but not stored)","example":"Duplicate of another lead"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"deletedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Lead Amina Wanjiru deleted","data":{"id":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","deletedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No lead with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.delete"]}],"x-required-scopes":["crm.leads.delete"]}},"/getDocumentRequirements":{"get":{"tags":["Leads"],"summary":"List a lead's document requirements","description":"Lists the documents a lead is asked for: every document slot whose conditions the lead meets (required or optional), plus any slot already holding a document. Also says which empty lead fields would unlock more slots once filled in.\n\n**Permission:** `crm.leads.read` (View leads).","operationId":"getDocumentRequirements","parameters":[{"name":"leadId","in":"query","required":true,"schema":{"type":"string","format":"uuid"},"description":"The lead's ID","example":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"entityType":{"type":"string","description":"The lead's type: individual or company"},"requirements":{"type":"array","items":{"type":"object","properties":{"documentType":{"type":"string","description":"The slot's key, used when uploading"},"documentTypeLabel":{"type":"string"},"isRequired":{"type":"boolean"},"inChecklist":{"type":"boolean","description":"Whether the lead meets the slot's conditions (false: listed only because a document is already stored in it)"},"multiple":{"type":"boolean","description":"Whether the slot takes more than one file"},"accept":{"type":"array","items":{"type":"string"},"description":"File types the slot takes (empty: PDF, JPEG, PNG, WebP and Word)"},"maxBytes":{"type":"integer","description":"Largest file the slot takes, in bytes (null: the 25 MB cap)"}}}},"waitingOn":{"type":"array","items":{"type":"object","properties":{"fieldId":{"type":"string"},"label":{"type":"string"}}},"description":"Lead fields still empty that more document slots depend on"}}}}},"example":{"success":true,"data":{"entityType":"individual","requirements":[{"documentType":"national_id","documentTypeLabel":"National ID","isRequired":true,"inChecklist":true,"multiple":false,"accept":["application/pdf","image/jpeg","image/png"],"maxBytes":5242880}],"waitingOn":[{"fieldId":"connection_type","label":"Connection type"}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No lead with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.read"]}],"x-required-scopes":["crm.leads.read"]}},"/me/permissions":{"get":{"tags":["Teams & people"],"summary":"List my permissions","description":"Answers the permissions of the person (or app) making the call, so a screen can hide what they may not do. Anyone signed in may call it, and it only ever shows your own permissions.\n\n**Permission:** any signed-in member.","operationId":"getMyPermissions","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"scopes":{"type":"array","items":{"type":"string"},"description":"Every permission your sign-in carries; * stands for any part"},"canEditBooksDocuments":{"type":"boolean","description":"You may change Kazee Books documents (books.sales.update)"},"canCreateBooksDocuments":{"type":"boolean","description":"You may create Kazee Books documents (books.sales.create)"},"canLinkCalls":{"type":"boolean","description":"You may file a call under a record or make a contact from it (crm.calls.update)"},"canConfigureCalls":{"type":"boolean","description":"You may change the organisation's call settings (crm.calls.admin)"},"canListenCalls":{"type":"boolean","description":"You may play call recordings and read transcripts (crm.calls.listen)"}}}}},"example":{"success":true,"data":{"scopes":["crm.leads.*","crm.calls.read","books.sales.update"],"canEditBooksDocuments":true,"canCreateBooksDocuments":false,"canLinkCalls":false,"canConfigureCalls":false,"canListenCalls":false}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":[]}]}},"/quotes/analytics":{"get":{"tags":["Quotes"],"summary":"Get quote analytics","description":"Quote figures: how many quotes, their value, and the share accepted, declined and expired, with the value won, still open and lost, and a count per status. Narrow it by lead, account, type or status. The rates and values are worked out from the 500 newest matching quotes.\n\n**Permission:** `crm.quotes.read` (View quotes).","operationId":"getQuoteAnalytics","parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this account's quotes","example":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"},{"name":"dateFrom","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Accepted but not used yet: the figures cover all dates"},{"name":"dateTo","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Accepted but not used yet: the figures cover all dates"},{"name":"leadId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this lead's quotes","example":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["draft","sent","accepted","declined","expired","paid"]},"description":"Only quotes in this status","example":"sent"},{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["quote","request","subscription"]},"description":"Only quotes of this type","example":"quote"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"summary":{"type":"object","properties":{"totalQuotes":{"type":"integer"},"totalValue":{"type":"integer"},"avgQuoteValue":{"type":"integer"},"acceptanceRate":{"type":"integer","description":"Percent of the quotes accepted (0 to 100)"},"declineRate":{"type":"integer","description":"Percent of the quotes declined"},"expirationRate":{"type":"integer","description":"Percent of the quotes expired"}}},"revenue":{"type":"object","properties":{"acceptedValue":{"type":"integer","description":"Total of accepted quotes"},"potentialValue":{"type":"integer","description":"Total of sent quotes still waiting for an answer"},"lostValue":{"type":"integer","description":"Total of declined and expired quotes"}}},"breakdown":{"type":"object","properties":{"byStatus":{"type":"object","properties":{"draft":{"type":"integer"},"sent":{"type":"integer"},"accepted":{"type":"integer"},"declined":{"type":"integer"},"expired":{"type":"integer"}},"description":"Number of quotes in each status"}}}}}}},"example":{"success":true,"data":{"summary":{"totalQuotes":40,"totalValue":301600,"avgQuoteValue":7540,"acceptanceRate":45,"declineRate":10,"expirationRate":5},"revenue":{"acceptedValue":135720,"potentialValue":90480,"lostValue":45240},"breakdown":{"byStatus":{"draft":4,"sent":12,"accepted":18,"declined":4,"expired":2}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.quotes.read"]}],"x-required-scopes":["crm.quotes.read"]}},"/quotes":{"get":{"tags":["Quotes"],"summary":"List quotes","description":"Lists quotes, newest first, a page at a time, each with its account, lead, owner and lines, and where it can move next in the quote pipeline. Narrow it by lead, account, deal, request, subscription, type, status, owner, a search on the quote number or notes, or field conditions.\n\n**Permission:** `crm.quotes.read` (View quotes).","operationId":"getQuotes","parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this account's quotes","example":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"},{"name":"customFieldFilters","in":"query","required":false,"schema":{"type":"object"},"description":"Older form: conditions on your own fields, keyed by field key (without cf:)","example":{"region":"Nairobi"}},{"name":"dealId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only the quotes made from this deal","example":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e"},{"name":"fieldFilters","in":"query","required":false,"schema":{"type":"object"},"description":"Older form: conditions on built-in fields, keyed by name","example":{"status":["active"]}},{"name":"filters","in":"query","required":false,"schema":{"type":"object"},"description":"Field conditions keyed by filter field ID (a native field name, or cf:<key> for your own fields; GET /filter-fields lists them), each { operator, value } or { operator, values }; a bare value means \"is\". A JSON object in a string also works. An invalid condition answers 400 naming the field","example":{"status":{"operator":"is","values":["active"]}}},{"name":"includeExpired","in":"query","required":false,"schema":{"type":"boolean","default":false},"description":"Leave out or false to list only quotes whose validity ends within a year from now; true to drop that limit","example":false},{"name":"leadId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this lead's quotes","example":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25},"description":"Quotes per page, 1 to 500","example":25},{"name":"ownerId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only quotes owned by this person","example":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"requestId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only the quotes for this service request"},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text to look for in the quote number or notes","example":"QUOTE-0004"},{"name":"sort","in":"query","required":false,"schema":{"type":"object"},"description":"Accepted but not used yet: quotes always come newest first"},{"name":"status","in":"query","required":false,"schema":{"type":"array","items":{"type":"string","enum":["draft","sent","accepted","declined","expired","paid"]}},"description":"Only quotes in this status, or any of these statuses (one value or a list). Unknown statuses are ignored","example":["draft","sent"]},{"name":"subscriptionId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only the quotes for this subscription"},{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["quote","request","subscription"]},"description":"Only quotes of this type","example":"quote"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"quoteNumber":{"type":"string","description":"The quote's number, unique in your organisation (a revision adds -r2, -r3 ...)"},"leadId":{"type":"string"},"accountId":{"type":"string"},"subscriptionId":{},"requestId":{},"dealId":{"type":"string","description":"The deal the quote was made from, if any"},"ownerId":{"type":"string"},"type":{"type":"string","description":"quote (for a lead or account), request (for a service request) or subscription (for a running service)"},"subtotal":{"type":"string","description":"Sum of the lines before tax, a decimal in a string"},"total":{"type":"string","description":"Sum of the lines with tax, a decimal in a string"},"status":{"type":"string","description":"draft, sent, accepted, declined, expired or paid"},"paidAmount":{"type":"string","description":"What the customer has paid so far"},"balance":{"type":"string","description":"What is still to pay: total less paidAmount"},"paidAt":{},"validityStart":{"type":"string","format":"date-time"},"validityEnd":{"type":"string","format":"date-time","description":"The last moment the customer can accept the quote"},"sentAt":{},"acceptedAt":{},"declinedAt":{},"expiredAt":{},"notes":{"type":"string"},"revisionOf":{"description":"The quote this one revises, if it is a revision"},"revisionNumber":{"type":"integer"},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array","description":"IDs of this quote in other systems (e.g. its Kazee Books estimate or Zoho quotation)"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"account":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}},"lead":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"}}},"owner":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"}}},"lineItems":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"quoteId":{"type":"string"},"productId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"quantity":{"type":"string"},"unitPrice":{"type":"string"},"discount":{"type":"string"},"taxAmount":{"type":"string"},"subtotal":{"type":"string"},"total":{"type":"string"},"sortOrder":{"type":"integer"},"metadata":{"type":"object","properties":{"kind":{"type":"string"},"treatment":{"type":"string"}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"pipeline":{"type":"string","description":"Always quote"},"currentStatus":{"type":"string","description":"The quote's status"},"nextStages":{"type":"array","items":{"type":"string"},"description":"Statuses the quote can move to from here"},"previousStages":{"type":"array","items":{"type":"string"},"description":"Statuses that can move to this one"},"isExpired":{"type":"boolean","description":"True when validityEnd has passed"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}}}}},"example":{"success":true,"data":[{"id":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","quoteNumber":"QUOTE-00042","leadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","subscriptionId":null,"requestId":null,"dealId":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","type":"quote","subtotal":"6500.00","total":"7540.00","status":"draft","paidAmount":"0.00","balance":"7540.00","paidAt":null,"validityStart":"2026-10-01T08:30:00.000Z","validityEnd":"2026-11-01T00:00:00.000Z","sentAt":null,"acceptedAt":null,"declinedAt":null,"expiredAt":null,"notes":"Includes free router for 12 months","revisionOf":null,"revisionNumber":1,"customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","account":{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","name":"Baraka Traders Ltd"},"lead":{"id":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","firstName":"Wanjiru","lastName":"Kamau"},"owner":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","firstName":"Amina","lastName":"Otieno"},"lineItems":[{"id":"16a7b8c9-0d1e-4f2a-8b3c-4d5e6f7a8b9c","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","quoteId":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","name":"Home Fibre 50 Mbps","description":"First month of service","quantity":"1.00","unitPrice":"4500.00","discount":"0.00","taxAmount":"720.00","subtotal":"4500.00","total":"5220.00","sortOrder":0,"metadata":{"kind":"first_month","treatment":"invoice"},"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-01T08:30:00.000Z"},{"id":"27b8c9d0-1e2f-4a3b-9c4d-5e6f7a8b9c0d","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","quoteId":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","productId":null,"name":"Installation","description":"Fibre installation and router setup","quantity":"1.00","unitPrice":"2000.00","discount":"0.00","taxAmount":"320.00","subtotal":"2000.00","total":"2320.00","sortOrder":1,"metadata":{"kind":"installation","treatment":"invoice"},"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-01T08:30:00.000Z"}],"pipeline":"quote","currentStatus":"draft","nextStages":["sent","declined"],"previousStages":["declined","expired"],"isExpired":false}],"pagination":{"page":1,"limit":25,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false}}}}},"400":{"description":"dealId is not a deal ID, or a field condition is not valid (the message names the field)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.quotes.read"]}],"x-required-scopes":["crm.quotes.read"]},"post":{"tags":["Quotes"],"summary":"Create a quote","description":"Creates a quote in draft for a lead or account (type quote), a service request (type request) or a running subscription (type subscription), numbered QUOTE-<n>. A lead must be at pending_documents or documents_received, a request new, assigned or in progress, a subscription active. For a lead, at least one line (on this or the lead's other open quotes) must be a product with plans. Known issue: this call does not save the lines yet, so the quote starts with no lines and a total of 0; quotes from a deal's quote dialog (POST /deals/:dealId/quote) carry their lines.\n\n**Permission:** `crm.quotes.create` (Create quotes).","operationId":"createQuote","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid","description":"The account the quote is for (type quote: leadId or accountId)","examples":["2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"]},"customFields":{"type":"object","description":"Your organisation's own quote fields, by field key. Accepted but not saved by this call at present"},"leadId":{"type":"string","format":"uuid","description":"The lead the quote is for (type quote: leadId or accountId)","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]},"lineItems":{"type":"array","items":{"type":"object","required":["quantity"],"properties":{"productId":{"type":["string","null"],"format":"uuid","description":"The product, or null for a line that is not one"},"name":{"type":"string","description":"Line name"},"description":{"type":["string","null"]},"quantity":{"type":"number","minimum":0},"unitPrice":{"type":"number","minimum":0,"description":"Price of one; price is the older name and also works"},"discount":{"type":"number","minimum":0,"description":"Amount off"},"taxAmount":{"type":"number","minimum":0,"description":"Tax on the line; tax is the older name and also works"},"sortOrder":{"type":"number"},"metadata":{"type":"object"}}},"description":"The quote's lines, at least one; each needs a quantity, a price (unitPrice or price) and a name or description","examples":[[{"productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","name":"Home Fibre 50 Mbps","description":"First month of service","quantity":1,"unitPrice":4500,"taxAmount":720}]]},"notes":{"type":"string","description":"Notes on the quote","examples":["Includes free router for 12 months"]},"requestId":{"type":"string","format":"uuid","description":"The service request, for type request"},"subscriptionId":{"type":"string","format":"uuid","description":"The subscription, for type subscription"},"type":{"type":"string","enum":["quote","request","subscription"],"default":"quote","description":"What the quote is for","examples":["quote"]},"validityEnd":{"type":"string","format":"date-time","description":"Until when the customer can accept; must be in the future","examples":["2026-11-01T00:00:00.000Z"]}},"required":["lineItems","validityEnd"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"quote (for a lead or account), request (for a service request) or subscription (for a running service)"},"leadId":{"type":"string"},"accountId":{"type":"string"},"subscriptionId":{},"requestId":{},"total":{"type":"string","description":"Sum of the lines with tax, a decimal in a string"},"status":{"type":"string","description":"draft, sent, accepted, declined, expired or paid"},"validityStart":{"type":"string","format":"date-time"},"validityEnd":{"type":"string","format":"date-time","description":"The last moment the customer can accept the quote"},"notes":{"type":"string"},"revisionNumber":{"type":"integer"},"quoteNumber":{"type":"string","description":"The quote's number, unique in your organisation (a revision adds -r2, -r3 ...)"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"sentAt":{},"acceptedAt":{},"declinedAt":{},"expiredAt":{},"emailSent":{"description":"Always null here (only a status change to sent emails)"},"emailError":{},"message":{}}}}},"example":{"success":true,"data":{"id":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","type":"quote","leadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","subscriptionId":null,"requestId":null,"total":"0","status":"draft","validityStart":"2026-10-01T08:30:00.000Z","validityEnd":"2026-11-01T00:00:00.000Z","notes":"Includes free router for 12 months","revisionNumber":1,"quoteNumber":"QUOTE-00042","createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","sentAt":null,"acceptedAt":null,"declinedAt":null,"expiredAt":null,"emailSent":null,"emailError":null,"message":null}}}}},"400":{"description":"A required input is missing, lineItems is not valid, validityEnd is not in the future, the lead, request or subscription was not found or is at a status that cannot be quoted, or no line is a plan product for the lead","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"The quote number was taken at the same moment; try again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.quotes.create"]}],"x-required-scopes":["crm.quotes.create"]}},"/quotes/{quoteId}":{"get":{"tags":["Quotes"],"summary":"Get a quote","description":"Gets one quote with its account, lead, owner and lines. Each line also says what it becomes when paid and what may be edited on it (treatment, discount type, locked flags).\n\n**Permission:** `crm.quotes.read` (View quotes).","operationId":"getQuote","parameters":[{"name":"quoteId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The quote to get","example":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"quoteNumber":{"type":"string","description":"The quote's number, unique in your organisation (a revision adds -r2, -r3 ...)"},"leadId":{"type":"string"},"accountId":{"type":"string"},"subscriptionId":{},"requestId":{},"dealId":{"type":"string","description":"The deal the quote was made from, if any"},"ownerId":{"type":"string"},"type":{"type":"string","description":"quote (for a lead or account), request (for a service request) or subscription (for a running service)"},"subtotal":{"type":"string","description":"Sum of the lines before tax, a decimal in a string"},"total":{"type":"string","description":"Sum of the lines with tax, a decimal in a string"},"status":{"type":"string","description":"draft, sent, accepted, declined, expired or paid"},"paidAmount":{"type":"string","description":"What the customer has paid so far"},"balance":{"type":"string","description":"What is still to pay: total less paidAmount"},"paidAt":{},"validityStart":{"type":"string","format":"date-time"},"validityEnd":{"type":"string","format":"date-time","description":"The last moment the customer can accept the quote"},"sentAt":{},"acceptedAt":{},"declinedAt":{},"expiredAt":{},"notes":{"type":"string"},"revisionOf":{"description":"The quote this one revises, if it is a revision"},"revisionNumber":{"type":"integer"},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array","description":"IDs of this quote in other systems (e.g. its Kazee Books estimate or Zoho quotation)"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"account":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"description":"The account's ID and name, or null"},"lead":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"}},"description":"The lead's ID and name, or null"},"owner":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"}},"description":"The salesperson who owns the quote, or null"},"lineItems":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"quoteId":{"type":"string"},"productId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"quantity":{"type":"string"},"unitPrice":{"type":"string"},"discount":{"type":"string"},"taxAmount":{"type":"string"},"subtotal":{"type":"string"},"total":{"type":"string"},"sortOrder":{"type":"integer"},"metadata":{"type":"object","properties":{"kind":{"type":"string"},"treatment":{"type":"string"}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"treatment":{"type":"string","description":"What the line becomes when paid: invoice or retainer"},"discountType":{"type":"string","description":"amount or percent; discount is always the amount"},"discountPercent":{},"locked":{"type":"boolean","description":"True on the plan line: its item, quantity and rate come from the plan"},"discountLocked":{"type":"boolean","description":"True when the plan line may not be discounted"},"descriptionLocked":{"type":"boolean","description":"True when the line's wording is the organisation's, not editable"}}}}}}}},"example":{"success":true,"data":{"id":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","quoteNumber":"QUOTE-00042","leadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","subscriptionId":null,"requestId":null,"dealId":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","type":"quote","subtotal":"6500.00","total":"7540.00","status":"draft","paidAmount":"0.00","balance":"7540.00","paidAt":null,"validityStart":"2026-10-01T08:30:00.000Z","validityEnd":"2026-11-01T00:00:00.000Z","sentAt":null,"acceptedAt":null,"declinedAt":null,"expiredAt":null,"notes":"Includes free router for 12 months","revisionOf":null,"revisionNumber":1,"customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","account":{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","name":"Baraka Traders Ltd"},"lead":{"id":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","firstName":"Wanjiru","lastName":"Kamau"},"owner":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","firstName":"Amina","lastName":"Otieno"},"lineItems":[{"id":"16a7b8c9-0d1e-4f2a-8b3c-4d5e6f7a8b9c","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","quoteId":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","name":"Home Fibre 50 Mbps","description":"First month of service","quantity":"1.00","unitPrice":"4500.00","discount":"0.00","taxAmount":"720.00","subtotal":"4500.00","total":"5220.00","sortOrder":0,"metadata":{"kind":"first_month","treatment":"invoice"},"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-01T08:30:00.000Z","treatment":"invoice","discountType":"amount","discountPercent":null,"locked":false,"discountLocked":false,"descriptionLocked":false},{"id":"27b8c9d0-1e2f-4a3b-9c4d-5e6f7a8b9c0d","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","quoteId":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","productId":null,"name":"Installation","description":"Fibre installation and router setup","quantity":"1.00","unitPrice":"2000.00","discount":"0.00","taxAmount":"320.00","subtotal":"2000.00","total":"2320.00","sortOrder":1,"metadata":{"kind":"installation","treatment":"invoice"},"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-01T08:30:00.000Z","treatment":"invoice","discountType":"amount","discountPercent":null,"locked":false,"discountLocked":false,"descriptionLocked":false}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No quote with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.quotes.read"]}],"x-required-scopes":["crm.quotes.read"]},"put":{"tags":["Quotes"],"summary":"Update a quote","description":"Changes a quote's lines, validity end or notes; only what you send changes. New lines replace all the lines and the totals are worked out again. A quote made by a deal's quote dialog has its lines rebuilt on the server from the deal's plan and the price source (Kazee Books when connected), and an approved Books estimate goes back for approval only with confirmReapproval. The edit is also sent to the quote's Books estimate. Needs Books item read and sales update access beside quote update. The reply is the saved quote; for a quote rebuilt from a quote dialog it is the whole stored quote record. To change the status use PUT /quotes/:quoteId/status.\n\n**Permission:** `crm.quotes.update` (Update quotes) and `crm.books.items.read` and `crm.books.sales.update`.","operationId":"updateQuote","parameters":[{"name":"quoteId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The quote to change","example":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"confirmReapproval":{"type":"boolean","description":"true when the person agreed that saving sends an approved Books estimate back for approval; without it such an edit is refused","examples":[false]},"customFields":{"type":"object","description":"Your organisation's own quote fields. Accepted but not saved by this call at present"},"lineItems":{"type":"array","items":{"type":"object","required":["quantity"],"properties":{"productId":{"type":["string","null"],"format":"uuid","description":"The product, or null for a line that is not one"},"name":{"type":"string","description":"Line name"},"description":{"type":["string","null"]},"quantity":{"type":"number","minimum":0},"unitPrice":{"type":"number","minimum":0,"description":"Price of one; price is the older name and also works"},"discount":{"type":"number","minimum":0,"description":"Amount off"},"taxAmount":{"type":"number","minimum":0,"description":"Tax on the line; tax is the older name and also works"},"sortOrder":{"type":"number"},"metadata":{"type":"object"}}},"description":"New lines, replacing all the quote's lines; each needs a quantity, a price (unitPrice or price) and a name or description","examples":[[{"productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","name":"Home Fibre 50 Mbps","description":"First month of service","quantity":1,"unitPrice":4500,"taxAmount":720}]]},"lineVersion":{"type":"string","enum":["2"],"description":"2 when the lines come from the plan-based quote dialog: they are rebuilt on the server from the deal's plan and Kazee Books","examples":["2"]},"notes":{"type":"string","description":"New notes","examples":["Includes free router for 12 months"]},"quoteDiscount":{"type":"object","properties":{"type":{"type":"string","enum":["amount","percent"]},"value":{"type":"number"}},"description":"A discount off the whole quote, for quotes of the generic quote dialog: { type: amount or percent, value }","examples":[{"type":"percent","value":10}]},"status":{"type":"string","description":"Not allowed here: sending it answers 400. Use PUT /quotes/:quoteId/status"},"validityEnd":{"type":"string","format":"date-time","description":"New end of validity; must be in the future","examples":["2026-11-01T00:00:00.000Z"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"quote (for a lead or account), request (for a service request) or subscription (for a running service)"},"leadId":{"type":"string"},"accountId":{"type":"string"},"subscriptionId":{},"requestId":{},"total":{"type":"string","description":"Sum of the lines with tax, a decimal in a string"},"status":{"type":"string","description":"draft, sent, accepted, declined, expired or paid"},"validityStart":{"type":"string","format":"date-time"},"validityEnd":{"type":"string","format":"date-time","description":"The last moment the customer can accept the quote"},"notes":{"type":"string"},"revisionNumber":{"type":"integer"},"quoteNumber":{"type":"string","description":"The quote's number, unique in your organisation (a revision adds -r2, -r3 ...)"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"sentAt":{},"acceptedAt":{},"declinedAt":{},"expiredAt":{},"emailSent":{"description":"Always null here"},"emailError":{},"message":{}}}}},"example":{"success":true,"data":{"id":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","type":"quote","leadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","subscriptionId":null,"requestId":null,"total":"7540.00","status":"draft","validityStart":"2026-10-01T08:30:00.000Z","validityEnd":"2026-11-01T00:00:00.000Z","notes":"Includes free router for 12 months","revisionNumber":1,"quoteNumber":"QUOTE-00042","createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","sentAt":null,"acceptedAt":null,"declinedAt":null,"expiredAt":null,"emailSent":null,"emailError":null,"message":null}}}}},"400":{"description":"status was sent (use the status call), lineItems or validityEnd is not valid, the quote was raised in Zoho, the lines break the deal's plan rules or the Books catalogue, or Books refuses the change (the message says why)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No quote with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The quote's Books estimate is not a draft, is waiting for approval, or is approved and confirmReapproval was not true (reason estimate_not_draft, approval_pending or reapproval_required)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.quotes.update","crm.books.items.read","crm.books.sales.update"]}],"x-required-scopes":["crm.quotes.update","crm.books.items.read","crm.books.sales.update"],"x-scope-mode":"all"}},"/quote-templates":{"get":{"tags":["Quotes"],"summary":"List quote templates","description":"Lists your organisation's quote templates (the named sets of lines a deal's quote can start from) in name order, with the plans and products each is the default for. Only active templates unless includeArchived is true.\n\n**Permission:** `crm.quotes.read` (View quotes).","operationId":"listQuoteTemplates","parameters":[{"name":"includeArchived","in":"query","required":false,"schema":{"type":"boolean"},"description":"true to include archived templates as well","example":false}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","description":"active (offered in the quote dialog) or archived"},"planIds":{"type":"array","items":{"type":"string"},"description":"Plans this template is the default for; a plan is the default of one active template at most"},"productIds":{"type":"array","description":"Products this template is the default for; a product is the default of one active template at most"},"lines":{"type":"array","items":{"type":"object","properties":{"booksItemId":{"type":"string","description":"The Kazee Books item the line is (empty for a line of one of your products, when Books is not connected)"},"booksItemName":{"type":"string","description":"The item's name, as Books has it"},"productId":{},"productName":{},"description":{"type":"string"},"quantity":{"type":"integer"},"unitPrice":{"type":"integer","description":"A fixed price, or null to use the item's price at the time the quote is made"},"discountType":{"type":"string","description":"amount (discountValue is money off the line) or percent (0 to 100)"},"discountValue":{"type":"integer"},"treatment":{"type":"string","description":"What the line becomes when paid: invoice or retainer"}}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}},"example":{"success":true,"data":[{"id":"7c6f5d8f-0a2b-4d9e-8b4c-8f9a0b1c2d3e","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Home fibre standard","description":"Installation and first month for home fibre","status":"active","planIds":["9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a"],"productIds":[],"lines":[{"booksItemId":"itm_3f9a2b7c1d4e5f60718293a4b5c6d7e8","booksItemName":"Router installation","productId":null,"productName":null,"description":"Router and fibre drop","quantity":1,"unitPrice":2000,"discountType":"amount","discountValue":0,"treatment":"invoice"}],"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"description":"Quote templates are not enabled yet on this server","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.quotes.read"]}],"x-required-scopes":["crm.quotes.read"]},"post":{"tags":["Quotes"],"summary":"Create a quote template","description":"Creates a quote template: a named set of lines a deal's quote can start from, and the plans or products it is the default for. Each line's item is checked in Kazee Books (or each product in your catalogue) first. Needs the Administer quotes permission and Books item read access.\n\n**Permission:** `crm.quotes.admin` (Administer quotes) and `crm.books.items.read`.","operationId":"createQuoteTemplate","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"description":{"type":"string","description":"What the template is for, up to 2,000 characters","examples":["Installation and first month for home fibre"]},"lines":{"type":"array","items":{"type":"object","properties":{"booksItemId":{"type":"string","description":"Kazee Books item ID (itm_ and 32 hex characters); leave out for a line of one of your products"},"productId":{"type":"string","format":"uuid","description":"One of your products, for a line that is not a Books item"},"description":{"type":"string","description":"Line wording, up to 500 characters"},"quantity":{"type":"number","description":"More than zero, at most 2 decimals; default 1"},"unitPrice":{"type":"number","description":"A fixed price (zero or more); leave out to use the item's price when the quote is made"},"discountType":{"type":"string","enum":["amount","percent"]},"discountValue":{"type":"number","description":"Money off the line, or a percent 0 to 100; default 0"},"treatment":{"type":"string","enum":["invoice","retainer"],"description":"What the line becomes when paid; default invoice"}}},"description":"The template's lines, at least one (a JSON array in a string also works). With Kazee Books connected each line names a Books item, which must exist, be active and be sold; its name is taken from Books. Without Books, each line names one of your products","examples":[[{"booksItemId":"itm_3f9a2b7c1d4e5f60718293a4b5c6d7e8","quantity":1,"unitPrice":2000,"discountType":"amount","discountValue":0,"treatment":"invoice"}]]},"name":{"type":"string","description":"The template's name, up to 160 characters, unique in your organisation (case does not matter)","examples":["Home fibre standard"]},"planIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Plans this template is the default for (a JSON array in a string also works). A plan can be the default of one active template only","examples":[["9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a"]]},"productIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Products this template is the default for, for plans with no template of their own (a JSON array in a string also works). A product can be the default of one active template only","examples":[[]]},"status":{"type":"string","enum":["active","archived"],"description":"active (default) to offer it in the quote dialog, or archived","examples":["active"]}},"required":["lines","name"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","description":"active (offered in the quote dialog) or archived"},"planIds":{"type":"array","items":{"type":"string"},"description":"Plans this template is the default for; a plan is the default of one active template at most"},"productIds":{"type":"array","description":"Products this template is the default for; a product is the default of one active template at most"},"lines":{"type":"array","items":{"type":"object","properties":{"booksItemId":{"type":"string","description":"The Kazee Books item the line is (empty for a line of one of your products, when Books is not connected)"},"booksItemName":{"type":"string","description":"The item's name, as Books has it"},"productId":{},"productName":{},"description":{"type":"string"},"quantity":{"type":"integer"},"unitPrice":{"type":"integer","description":"A fixed price, or null to use the item's price at the time the quote is made"},"discountType":{"type":"string","description":"amount (discountValue is money off the line) or percent (0 to 100)"},"discountValue":{"type":"integer"},"treatment":{"type":"string","description":"What the line becomes when paid: invoice or retainer"}}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"7c6f5d8f-0a2b-4d9e-8b4c-8f9a0b1c2d3e","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Home fibre standard","description":"Installation and first month for home fibre","status":"active","planIds":["9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a"],"productIds":[],"lines":[{"booksItemId":"itm_3f9a2b7c1d4e5f60718293a4b5c6d7e8","booksItemName":"Router installation","productId":null,"productName":null,"description":"Router and fibre drop","quantity":1,"unitPrice":2000,"discountType":"amount","discountValue":0,"treatment":"invoice"}],"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}}}}},"400":{"description":"name or lines missing, a text too long, a line's number, discount or treatment not valid, a Books item missing, inactive or not sold, or a plan or product not of your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"Another template already has that name, or a plan or product you named is already the default of another active template (the message names it)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"description":"Quote templates are not enabled yet on this server","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.quotes.admin","crm.books.items.read"]}],"x-required-scopes":["crm.quotes.admin","crm.books.items.read"],"x-scope-mode":"all"}},"/books/taxes":{"get":{"tags":["Billing"],"summary":"List Books taxes","description":"Lists your organisation's Kazee Books taxes and tax groups as one flat, read-only list (for tax settings and quote editors). A group's rate is the rate Books really charges for it, and its members are the taxes in it. Inactive taxes are left out.\n\n**Permission:** `crm.billing.read` (View billing documents).","operationId":"getBooksTaxes","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"source":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"rate":{"type":"string","description":"Percent as text; for a group, the effective percent of its members together"},"kind":{"type":"string","description":"tax for a single tax, group for a tax group"},"members":{"type":"array","description":"The taxes in a group; empty for a single tax"}}}}}},"example":{"success":true,"source":"kazee-books","data":[{"id":"tax_8d9e0f1a2b3c4d4e5f6a7b8c9d0e1f2a","name":"VAT","rate":"16.0000","kind":"tax","members":[]},{"id":"txg_0f1a2b3c4d5e4f6a7b8c9d0e1f2a3b4c","name":"VAT + Excise","rate":"32.2400","kind":"group","members":[{"id":"tax_8d9e0f1a2b3c4d4e5f6a7b8c9d0e1f2a","name":"VAT","rate":"16.0000"},{"id":"tax_9e0f1a2b3c4d4e5f6a7b8c9d0e1f2a3b","name":"Excise duty","rate":"14.0000"}]}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Kazee Books is not connected to this CRM organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.billing.read"]}],"x-required-scopes":["crm.billing.read"]}},"/billing/books/change-requests/{recordId}":{"get":{"tags":["Subscriptions"],"summary":"Get a Books subscription change request","description":"Answers one Kazee Books plan-change, deposit top-up or trial request: where it stands, the money involved, and its history.\n\n**Permission:** `crm.billing.read` (View billing documents).","operationId":"readBooksChangeRequest","parameters":[{"name":"recordId","in":"path","required":true,"schema":{"type":"string"},"description":"The change request (an scr_… ID)","example":"scr_5a6b7c8d9e0f4a1b2c3d4e5f6a7b8c9d"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"source":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"subscription_id":{"type":"string"},"contact_id":{"type":"string"},"kind":{"type":"string","description":"change, deposit_topup or trial"},"status":{"type":"string","description":"draft, awaiting_funds, awaiting_approval, scheduled, applying_access, active, completed, cancelled, needs_attention or expired"},"direction":{"type":"string","description":"upgrade, downgrade, same or trial"},"from_item_id":{"type":"string"},"to_item_id":{"type":"string"},"from_charge":{"type":"string"},"to_charge":{"type":"string"},"effective_mode":{"type":"string"},"effective_date":{"type":"string"},"choices":{"type":"object","properties":{"downgrade_excess":{}}},"preview":{"type":"object","properties":{}},"money":{"type":"object","properties":{"currency_id":{"type":"string"},"topup_required":{"type":"string"},"topup_earmarked":{"type":"string"},"release":{"type":"string"},"service_now":{"type":"string"},"service_later":{"type":"string"},"allocations":{"type":"array"}}},"documents":{"type":"object","properties":{}},"approval":{"type":"object","properties":{"required":{"type":"boolean"},"decided":{"type":"boolean"},"mode":{"type":"string"}}},"access":{"type":"object","properties":{}},"trial":{"type":"object","properties":{}},"history":{"type":"array","items":{"type":"object","properties":{"at":{"type":"string","format":"date-time"},"action":{"type":"string"},"by":{"type":"object","properties":{"user_id":{"type":"string"},"name":{"type":"string"},"source":{"type":"string"}}},"detail":{"type":"object","properties":{"direction":{"type":"string"}}}}},"description":"What happened to the request, oldest first"},"requested_by":{"type":"string"},"requested_at":{"type":"string","format":"date-time"},"applied_at":{},"completed_at":{},"cancelled_at":{},"error":{},"version":{"type":"integer"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"source":"kazee-books","data":{"id":"scr_5a6b7c8d9e0f4a1b2c3d4e5f6a7b8c9d","subscription_id":"sub_4f5a6b7c8d9e4f0a1b2c3d4e5f6a7b8c","contact_id":"con_0b9e8c1e3f5a4c2d9a7b1e2f3a4b5c6d","kind":"change","status":"awaiting_funds","direction":"upgrade","from_item_id":"itm_6b7c8d9e0f1a4b2c3d4e5f6a7b8c9d0e","to_item_id":"itm_7c8d9e0f1a2b4c3d4e5f6a7b8c9d0e1f","from_charge":"3500.00","to_charge":"5500.00","effective_mode":"next_cycle","effective_date":"2026-11-01","choices":{"downgrade_excess":null},"preview":{},"money":{"currency_id":"c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f","topup_required":"2000.00","topup_earmarked":"0.00","release":"0.00","service_now":"0.00","service_later":"5500.00","allocations":[]},"documents":{},"approval":{"required":false,"decided":true,"mode":"respect_org_approvals"},"access":{},"trial":{},"history":[{"at":"2026-10-20T09:00:00.000Z","action":"requested","by":{"user_id":"65f1a2b3-c4d5-e6f7-a8b9-c0d100000000","name":"Jane Doe","source":"api"},"detail":{"direction":"upgrade"}}],"requested_by":"65f1a2b3-c4d5-e6f7-a8b9-c0d100000000","requested_at":"2026-10-20T09:00:00.000Z","applied_at":null,"completed_at":null,"cancelled_at":null,"error":null,"version":1,"created_at":"2026-10-20T09:00:00.000Z","updated_at":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"recordId is not a Books ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Kazee Books is not connected to this CRM organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such request, or you do not have the Books permission to read subscriptions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.billing.read"]}],"x-required-scopes":["crm.billing.read"]}},"/billing/books/capabilities":{"get":{"tags":["Billing"],"summary":"Get my Books billing permissions","description":"Says which Kazee Books collections you may read and which Books acts (record a payment, approve, email, ...) you may do, so an app shows only the tabs and buttons that will work. A collection you may not read is simply left out. For each act you may not do, it names the permissions your role lacks.\n\n**Permission:** `crm.billing.read` (View billing documents).","operationId":"getBooksBillingCapabilities","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"source":{"type":"string"},"canRead":{"type":"boolean","description":"False when you may read no Books collection at all"},"resources":{"type":"array","items":{"type":"string"},"description":"The Books collections you may read"},"scopes":{"type":"object","properties":{"estimates":{"type":"string"},"invoices":{"type":"string"},"subscription-invoices":{"type":"string"},"subscriptions":{"type":"string"}},"description":"The Books permission behind each collection you may read"},"actions":{"type":"object","properties":{"viewPdf":{"type":"boolean"},"recordPayment":{"type":"boolean"},"grantExtension":{"type":"boolean"},"submitApproval":{"type":"boolean"},"recallApproval":{"type":"boolean"},"submitEstimateApproval":{"type":"boolean"},"recallEstimateApproval":{"type":"boolean"},"decideApproval":{"type":"boolean"},"emailDocument":{"type":"boolean"},"markEstimateSent":{"type":"boolean"},"decideEstimate":{"type":"boolean"}},"description":"What you may do; emailDocument is also false while email sending is switched off for everyone"},"needs":{"type":"object","properties":{"recordPayment":{"type":"array","items":{"type":"object","properties":{"scope":{"type":"string"},"label":{"type":"string"},"app":{"type":"string"}}}},"decideApproval":{"type":"array","items":{"type":"object","properties":{"scope":{"type":"string"},"label":{"type":"string"},"app":{"type":"string"}}}}},"description":"For each act you may not do, the permissions you lack, by the name the role editor shows"}}},"example":{"success":true,"source":"kazee-books","canRead":true,"resources":["estimates","invoices","subscription-invoices","subscriptions"],"scopes":{"estimates":"books.sales.read","invoices":"books.sales.read","subscription-invoices":"books.sales.read","subscriptions":"books.sales.read"},"actions":{"viewPdf":true,"recordPayment":false,"grantExtension":false,"submitApproval":true,"recallApproval":true,"submitEstimateApproval":true,"recallEstimateApproval":true,"decideApproval":false,"emailDocument":false,"markEstimateSent":true,"decideEstimate":true},"needs":{"recordPayment":[{"scope":"crm.payments.manage","label":"Record payments and extensions","app":"CRM"},{"scope":"books.payments.create","label":"Record payments","app":"Books"}],"decideApproval":[{"scope":"books.approvals.decide","label":"Approve documents","app":"Books"}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.billing.read"]}],"x-required-scopes":["crm.billing.read"]}},"/billing/books/tax-catalogue":{"get":{"tags":["Billing"],"summary":"Get the Books tax catalogue","description":"Answers your organisation's Kazee Books taxes and tax groups in the form a quote editor needs to total its lines exactly as Books will while the person types. Rates only, no ledger data.\n\n**Permission:** `crm.billing.read` (View billing documents).","operationId":"getBooksTaxCatalogue","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"source":{"type":"string"},"data":{"type":"object","properties":{"taxes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"ratePercent":{"type":"string"},"type":{"type":"string","description":"plain, compound or withholding"},"isActive":{"type":"boolean"},"scope":{"type":"string","description":"sales, purchases or both, or null when Books does not say"}}}},"groups":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"memberIds":{"type":"array","items":{"type":"string"},"description":"IDs of the taxes in the group"},"effectivePercent":{"type":"string","description":"The group's combined percent, or null when Books has not worked it out"},"label":{"type":"string","description":"What to show, e.g. \"VAT + Excise (32.24%)\""}}}},"aliases":{"type":"object","properties":{"tax_0a1b2c3d4e5f4a6b7c8d9e0f1a2b3c4d":{"type":"string"}},"description":"Older tax IDs that were replaced, mapped to the tax that replaced them"}}}}},"example":{"success":true,"source":"kazee-books","data":{"taxes":[{"id":"tax_8d9e0f1a2b3c4d4e5f6a7b8c9d0e1f2a","name":"VAT","ratePercent":"16.0000","type":"plain","isActive":true,"scope":"both"},{"id":"tax_9e0f1a2b3c4d4e5f6a7b8c9d0e1f2a3b","name":"Excise duty","ratePercent":"14.0000","type":"plain","isActive":true,"scope":"sales"}],"groups":[{"id":"txg_0f1a2b3c4d5e4f6a7b8c9d0e1f2a3b4c","name":"VAT + Excise","memberIds":["tax_8d9e0f1a2b3c4d4e5f6a7b8c9d0e1f2a","tax_9e0f1a2b3c4d4e5f6a7b8c9d0e1f2a3b"],"effectivePercent":"32.2400","label":"VAT + Excise (32.2400%)"}],"aliases":{"tax_0a1b2c3d4e5f4a6b7c8d9e0f1a2b3c4d":"tax_8d9e0f1a2b3c4d4e5f6a7b8c9d0e1f2a"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Kazee Books is not connected to this CRM organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.billing.read"]}],"x-required-scopes":["crm.billing.read"]}},"/billing/books/list-fields/{resource}":{"get":{"tags":["Billing"],"summary":"Get a Books list's filter fields","description":"Says what one Kazee Books collection can be filtered and sorted by: its own fields and your organisation's custom fields, each with its type, the filter operators it takes and, for a choice, its options. Send conditions on these fields as `filters` to the list call. You need your own Books permission for that collection; without it the call answers 404.\n\n**Permission:** `crm.billing.read` (View billing documents).","operationId":"getBooksListFields","parameters":[{"name":"resource","in":"path","required":true,"schema":{"type":"string","enum":["estimates","invoices","credit-notes","items","recurring-invoices","subscription-invoices","payments-received","retainer-invoices","subscriptions"]},"description":"Which Kazee Books list: subscription-invoices are described by the invoices list. Payments made have no description","example":"subscriptions"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"source":{"type":"string"},"resource":{"type":"string"},"data":{"type":"object","properties":{"entity":{"type":"string"},"timezone":{"type":"string","description":"Day filters on date-time fields are cut at midnight in this zone"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"What a condition is keyed by in filters: a field's own key, or cf:<api_name> for a custom field"},"key":{"type":"string"},"label":{"type":"string"},"type":{"type":"string","description":"text, url, number, decimal, currency, date, datetime, select or checkbox"},"operators":{"type":"array","items":{"type":"string"},"description":"The operators a condition on it may use"},"origin":{"type":"string"},"sortable":{"type":"boolean"},"filterable":{"type":"boolean"},"column":{"type":"boolean","description":"False for a filter with no column of its own (a subscription's lifecycle)"},"listsValues":{"type":"boolean"},"options":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}}},"description":"A choice field's values and labels"}}}}}}}},"example":{"success":true,"source":"kazee-books","resource":"subscriptions","data":{"entity":"subscriptions","timezone":"Africa/Nairobi","fields":[{"id":"status_level","key":"status_level","label":"Level","type":"select","operators":["is","is_not","any_of","none_of","is_empty","is_not_empty"],"origin":"native","sortable":true,"filterable":true,"column":true,"listsValues":false,"options":[{"value":"NORMAL","label":"Normal"},{"value":"VIP","label":"VIP"}]},{"id":"monthly_rate","key":"monthly_rate","label":"Plan rate (ex-tax)","type":"currency","operators":["is","is_not","greater_than","less_than","at_least","at_most","between","is_empty","is_not_empty"],"origin":"native","sortable":true,"filterable":true,"column":true,"listsValues":false}]}}}}},"400":{"description":"resource is not one of the Books collections Books describes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Kazee Books is not connected to this CRM organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"You do not have the Books permission to read that collection","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.billing.read"]}],"x-required-scopes":["crm.billing.read"]}},"/billing/books/approvals/{documentType}/{recordId}":{"get":{"tags":["Billing"],"summary":"Get a Books document's approval state","description":"Says where one Kazee Books document's approval stands, who it is waiting on, and whether you may approve or recall it now. You may approve only if you hold the permission and are one of the approvers Books names for the current step; you may recall only if you submitted it or are a Books admin. If Books could not be asked, checked is false and the answers fall back to your permissions alone.\n\n**Permission:** `crm.billing.read` (View billing documents).","operationId":"getBooksApprovalState","parameters":[{"name":"documentType","in":"path","required":true,"schema":{"type":"string","enum":["invoice","estimate","credit_note","bill","purchase_order"]},"description":"What kind of Books document it is","example":"invoice"},{"name":"recordId","in":"path","required":true,"schema":{"type":"string"},"description":"The Books record's ID: a short type prefix and 32 hex digits, e.g. inv_… for an invoice","example":"inv_1c2d3e4f5a6b4c7d8e9f0a1b2c3d4e5f"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"source":{"type":"string"},"documentType":{"type":"string"},"data":{"type":"object","properties":{"status":{"type":"string","description":"pending, approved, rejected or recalled; null when it was never submitted"},"submittedByName":{"type":"string"},"submittedByYou":{"type":"boolean"},"waitingOn":{"type":"array","items":{"type":"string"},"description":"Names of the people who can sign the current step, while it is pending"},"onCurrentStep":{"type":"boolean","description":"True when you are one of those people"},"canDecide":{"type":"boolean","description":"True when you may approve or reject it now"},"canRecall":{"type":"boolean","description":"True when you may take the request back now"},"checked":{"type":"boolean","description":"False when Books could not be asked"}}}}},"example":{"success":true,"source":"kazee-books","documentType":"invoice","data":{"status":"pending","submittedByName":"Jane Doe","submittedByYou":false,"waitingOn":["Peter Otieno"],"onCurrentStep":true,"canDecide":true,"canRecall":false,"checked":true}}}}},"400":{"description":"documentType is not one Books can approve, or recordId is empty","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Kazee Books is not connected to this CRM organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.billing.read"]}],"x-required-scopes":["crm.billing.read"]}},"/billing/books/{resource}":{"get":{"tags":["Billing"],"summary":"List Books billing records","description":"Lists one Kazee Books collection (invoices, estimates, payments, subscriptions, ...) for your organisation, read-only, one page at a time. You need your own Books permission for that collection; without it the call answers 404, as if there were nothing. Filters a collection does not support are ignored; `filters` takes typed conditions on any field the collection's list-fields describes. Rows carry the customer's name beside contact_id.\n\n**Permission:** `crm.billing.read` (View billing documents).","operationId":"listBooksBillingRecords","parameters":[{"name":"resource","in":"path","required":true,"schema":{"type":"string","enum":["estimates","invoices","credit-notes","items","recurring-invoices","subscription-invoices","payments-received","payments-made","retainer-invoices","subscriptions"]},"description":"Which Kazee Books collection: subscription-invoices are the invoices a subscription's recurring profile made (CRM's Bills); invoices is the whole list","example":"invoices"},{"name":"contact_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only this Books customer's records (a con_… ID). Not used for items","example":"con_0b9e8c1e3f5a4c2d9a7b1e2f3a4b5c6d"},{"name":"filters","in":"query","required":false,"schema":{"type":"string"},"description":"JSON object of conditions keyed by field id (a custom field is cf:<api_name>), each { operator, value | values | from, to | min, max }, with the operators list-fields gives for that field, e.g. {\"status\":{\"operator\":\"any_of\",\"values\":[\"open\",\"paid\"]}}. subscription-invoices take the invoice fields","example":"{\"total\":{\"operator\":\"at_least\",\"value\":\"1000\"}}"},{"name":"page","in":"query","required":false,"schema":{"type":"object","properties":{"size":{"type":"integer"},"after":{"type":"string"},"number":{"type":"integer"}}},"description":"The same as page[size], page[after] and page[number]: a query string's page[size]=…&page[after]=… arrives in this form","example":{"size":20}},{"name":"page[after]","in":"query","required":false,"schema":{"type":"string"},"description":"The meta.next_cursor of the page before, to get the next page","example":"aW52XzFjMmQzZTRm"},{"name":"page[number]","in":"query","required":false,"schema":{"type":"integer"},"description":"Which page, from 1, of a sorted or numbered list (cannot be combined with page[after])","example":2},{"name":"page[size]","in":"query","required":false,"schema":{"type":"integer"},"description":"How many records per page (Kazee Books sets the default and the maximum)","example":20},{"name":"purchased","in":"query","required":false,"schema":{"type":"boolean"},"description":"Items only: only items you buy","example":false},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text to look for, e.g. a document number or reference; for subscriptions the name or PPPoE username. Not used for recurring and retainer invoices","example":"INV-0004"},{"name":"sold","in":"query","required":false,"schema":{"type":"boolean"},"description":"Items only: only items you sell","example":true},{"name":"sort","in":"query","required":false,"schema":{"type":"string"},"description":"A field id list-fields marks sortable. A sorted list pages by page[number], not page[after]","example":"date"},{"name":"sort_dir","in":"query","required":false,"schema":{"type":"string","enum":["asc","desc"]},"description":"asc (the default) or desc","example":"desc"},{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"Only records with this status, e.g. draft, open, partially_paid, paid or void for invoices; active, suspended or disconnected for subscriptions. Not used for payments","example":"open"},{"name":"status_level","in":"query","required":false,"schema":{"type":"string"},"description":"Subscriptions only: one of the service level codes the organisation set up in Kazee Books","example":"active"},{"name":"tracked","in":"query","required":false,"schema":{"type":"boolean"},"description":"Items only: only items whose stock is tracked","example":false},{"name":"type","in":"query","required":false,"schema":{"type":"string"},"description":"Items only: the kind of item, e.g. service or goods","example":"service"},{"name":"with_total","in":"query","required":false,"schema":{"type":"string","enum":["1"]},"description":"Invoices and subscriptions only: send 1 to also get meta.total, the number of matching records","example":"1"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"source":{"type":"string"},"readOnly":{"type":"boolean","description":"Always true: change these records in Kazee Books"},"resource":{"type":"string","description":"The collection asked for"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"number":{"type":"string"},"contact_id":{"type":"string"},"contact_name":{"type":"string"},"date":{"type":"string"},"due_date":{"type":"string"},"payment_terms":{"type":"object","properties":{"type":{"type":"string"},"days":{"type":"integer"}}},"currency":{"type":"string"},"exchange_rate":{"type":"string"},"status":{"type":"string"},"approval_status":{},"tax_treatment":{"type":"string"},"is_inclusive_tax":{"type":"boolean"},"sub_total":{"type":"string"},"tax_summary":{"type":"array","items":{"type":"object","properties":{"tax_id":{"type":"string"},"name":{"type":"string"},"rate_percent":{"type":"string"},"base":{"type":"string"},"amount":{"type":"string"}}}},"discount":{},"discount_total":{"type":"string"},"adjustment":{"type":"string"},"total":{"type":"string"},"amount_applied":{"type":"string"},"balance":{"type":"string"},"reference":{"type":"string"},"notes":{"type":"string"},"custom_fields":{"type":"object","properties":{}},"dimension_values":{"type":"object","properties":{}},"estimate_id":{},"so_id":{},"recurring_profile_id":{"type":"string"},"entry_id":{"type":"string"},"write_off":{},"issued_at":{"type":"string","format":"date-time"},"voided_at":{},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"description":"The records, in the shape Kazee Books gives them (here: invoices)"},"meta":{"type":"object","properties":{"next_cursor":{"type":"string","description":"Pass as page[after] for the next page; null on the last page"},"total":{"type":"integer","description":"Only when with_total=1 was asked: how many records match"}}}}},"example":{"success":true,"source":"kazee-books","readOnly":true,"resource":"invoices","data":[{"id":"inv_1c2d3e4f5a6b4c7d8e9f0a1b2c3d4e5f","number":"INV-000412","contact_id":"con_0b9e8c1e3f5a4c2d9a7b1e2f3a4b5c6d","contact_name":"Amani Wanjiru","date":"2026-10-01","due_date":"2026-10-08","payment_terms":{"type":"days","days":7},"currency":"KES","exchange_rate":"1","status":"partially_paid","approval_status":null,"tax_treatment":"vat_registered","is_inclusive_tax":true,"sub_total":"3017.24","tax_summary":[{"tax_id":"tax_8d9e0f1a2b3c4d4e5f6a7b8c9d0e1f2a","name":"VAT","rate_percent":"16.0000","base":"3017.24","amount":"482.76"}],"discount":null,"discount_total":"0.00","adjustment":"0.00","total":"3500.00","amount_applied":"1500.00","balance":"2000.00","reference":"SUB-0042 October","notes":"Thank you for your business.","custom_fields":{},"dimension_values":{},"estimate_id":null,"so_id":null,"recurring_profile_id":"rcp_4d5e6f7a8b9c4d0e1f2a3b4c5d6e7f8a","entry_id":"jrn_2b3c4d5e6f7a4b8c9d0e1f2a3b4c5d6e","write_off":null,"issued_at":"2026-10-01T06:00:00.000Z","voided_at":null,"created_at":"2026-10-01T06:00:00.000Z","updated_at":"2026-10-03T11:20:00.000Z"}],"meta":{"next_cursor":"aW52XzFjMmQzZTRm","total":128}}}}},"400":{"description":"resource is not one of the Books collections","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Kazee Books is not connected to this CRM organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"You do not have the Books permission to read that collection","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.billing.read"]}],"x-required-scopes":["crm.billing.read"]}},"/billing/books/{resource}/{recordId}":{"get":{"tags":["Billing"],"summary":"Get a Books billing record","description":"Answers one Kazee Books record (an invoice with its lines, a payment, a subscription, ...), read-only. You need your own Books permission for its collection; without it the call answers 404. The customer's name is added beside contact_id.\n\n**Permission:** `crm.billing.read` (View billing documents).","operationId":"getBooksBillingRecord","parameters":[{"name":"resource","in":"path","required":true,"schema":{"type":"string","enum":["estimates","invoices","credit-notes","items","recurring-invoices","subscription-invoices","payments-received","payments-made","retainer-invoices","subscriptions"]},"description":"Which Kazee Books collection: subscription-invoices are the invoices a subscription's recurring profile made (CRM's Bills); invoices is the whole list","example":"invoices"},{"name":"recordId","in":"path","required":true,"schema":{"type":"string"},"description":"The Books record's ID: a short type prefix and 32 hex digits, e.g. inv_… for an invoice","example":"inv_1c2d3e4f5a6b4c7d8e9f0a1b2c3d4e5f"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"source":{"type":"string"},"readOnly":{"type":"boolean","description":"Always true: change these records in Kazee Books"},"resource":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"number":{"type":"string"},"contact_id":{"type":"string"},"contact_name":{"type":"string"},"date":{"type":"string"},"due_date":{"type":"string"},"payment_terms":{"type":"object","properties":{"type":{"type":"string"},"days":{"type":"integer"}}},"currency":{"type":"string"},"exchange_rate":{"type":"string"},"status":{"type":"string"},"approval_status":{},"tax_treatment":{"type":"string"},"is_inclusive_tax":{"type":"boolean"},"sub_total":{"type":"string"},"tax_summary":{"type":"array","items":{"type":"object","properties":{"tax_id":{"type":"string"},"name":{"type":"string"},"rate_percent":{"type":"string"},"base":{"type":"string"},"amount":{"type":"string"}}}},"discount":{},"discount_total":{"type":"string"},"adjustment":{"type":"string"},"total":{"type":"string"},"amount_applied":{"type":"string"},"balance":{"type":"string"},"reference":{"type":"string"},"notes":{"type":"string"},"custom_fields":{"type":"object","properties":{}},"dimension_values":{"type":"object","properties":{}},"estimate_id":{},"so_id":{},"recurring_profile_id":{"type":"string"},"entry_id":{"type":"string"},"write_off":{},"issued_at":{"type":"string","format":"date-time"},"voided_at":{},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"line_items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"item_id":{"type":"string"},"description":{"type":"string"},"quantity":{"type":"string"},"rate":{"type":"string"},"account_id":{"type":"string"},"tax_id":{"type":"string"},"tax_group_id":{},"wht_tax_id":{},"line_taxes":{"type":"array","items":{"type":"object","properties":{"tax_id":{"type":"string"},"name":{"type":"string"},"rate_percent":{"type":"string"},"amount":{"type":"string"}}}},"base_amount":{"type":"string"},"dimension_values":{"type":"object","properties":{}},"warehouse_id":{}}}}},"description":"The record, in the shape Kazee Books gives it (here: an invoice)"}}},"example":{"success":true,"source":"kazee-books","readOnly":true,"resource":"invoices","data":{"id":"inv_1c2d3e4f5a6b4c7d8e9f0a1b2c3d4e5f","number":"INV-000412","contact_id":"con_0b9e8c1e3f5a4c2d9a7b1e2f3a4b5c6d","contact_name":"Amani Wanjiru","date":"2026-10-01","due_date":"2026-10-08","payment_terms":{"type":"days","days":7},"currency":"KES","exchange_rate":"1","status":"partially_paid","approval_status":null,"tax_treatment":"vat_registered","is_inclusive_tax":true,"sub_total":"3017.24","tax_summary":[{"tax_id":"tax_8d9e0f1a2b3c4d4e5f6a7b8c9d0e1f2a","name":"VAT","rate_percent":"16.0000","base":"3017.24","amount":"482.76"}],"discount":null,"discount_total":"0.00","adjustment":"0.00","total":"3500.00","amount_applied":"1500.00","balance":"2000.00","reference":"SUB-0042 October","notes":"Thank you for your business.","custom_fields":{},"dimension_values":{},"estimate_id":null,"so_id":null,"recurring_profile_id":"rcp_4d5e6f7a8b9c4d0e1f2a3b4c5d6e7f8a","entry_id":"jrn_2b3c4d5e6f7a4b8c9d0e1f2a3b4c5d6e","write_off":null,"issued_at":"2026-10-01T06:00:00.000Z","voided_at":null,"created_at":"2026-10-01T06:00:00.000Z","updated_at":"2026-10-03T11:20:00.000Z","line_items":[{"id":"dli_3c4d5e6f7a8b4c9d0e1f2a3b4c5d6e7f","item_id":"itm_6b7c8d9e0f1a4b2c3d4e5f6a7b8c9d0e","description":"Fibre 20 Mbps, October","quantity":"1","rate":"3500.00","account_id":"acc_1a2b3c4d5e6f4a7b8c9d0e1f2a3b4c5d","tax_id":"tax_8d9e0f1a2b3c4d4e5f6a7b8c9d0e1f2a","tax_group_id":null,"wht_tax_id":null,"line_taxes":[{"tax_id":"tax_8d9e0f1a2b3c4d4e5f6a7b8c9d0e1f2a","name":"VAT","rate_percent":"16.0000","amount":"482.76"}],"base_amount":"3017.24","dimension_values":{},"warehouse_id":null}]}}}}},"400":{"description":"resource is not one of the Books collections, or recordId is empty","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Kazee Books is not connected to this CRM organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record, or you do not have the Books permission to read that collection","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.billing.read"]}],"x-required-scopes":["crm.billing.read"]}},"/billing/books/{resource}/{recordId}/history":{"get":{"tags":["Billing"],"summary":"Get a Books record's history","description":"Answers the activity timeline of one Kazee Books record, newest first: when it was created, updated, sent or voided, the payments and credits applied to it, the invoices a recurring profile made, its email deliveries, and what was done to it from CRM (with who did it). If a secondary source could not be read, the timeline still comes back with partial true and the source named in missing.\n\n**Permission:** `crm.billing.read` (View billing documents).","operationId":"getBooksBillingRecordHistory","parameters":[{"name":"resource","in":"path","required":true,"schema":{"type":"string","enum":["estimates","invoices","credit-notes","items","recurring-invoices","subscription-invoices","payments-received","payments-made","retainer-invoices","subscriptions"]},"description":"Which Kazee Books collection: subscription-invoices are the invoices a subscription's recurring profile made (CRM's Bills); invoices is the whole list","example":"invoices"},{"name":"recordId","in":"path","required":true,"schema":{"type":"string"},"description":"The Books record's ID: a short type prefix and 32 hex digits, e.g. inv_… for an invoice","example":"inv_1c2d3e4f5a6b4c7d8e9f0a1b2c3d4e5f"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"source":{"type":"string"},"readOnly":{"type":"boolean"},"resource":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"event":{"type":"string","description":"A sentence to show for it"},"action":{"type":"string","description":"What happened, e.g. created, updated, sent, voided, payment, credit_note, email_queued, email_sent, email_failed, invoice_generated, or an act done from CRM such as books_approval_submit or books_send"},"at":{"type":"string","format":"date-time"},"by":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"description":"Who did it, for acts done from CRM; null for what Books did (Books cannot name the person)"},"details":{"type":"object","properties":{"documentType":{"type":"string"}}}}}},"partial":{"type":"boolean","description":"True when part of the history could not be read"},"missing":{"type":"array","description":"The parts that could not be read in full, e.g. sends, or sends:truncated when there were more than were read"}}},"example":{"success":true,"source":"kazee-books","readOnly":true,"resource":"invoices","data":[{"id":"crm:2f4e6a8c-1b3d-4f5a-9c7e-0d2b4f6a8c1e","event":"Submitted for approval from CRM","action":"books_approval_submit","at":"2026-10-01T06:05:00.000Z","by":{"id":"1b2c3d4e-5f6a-4b7c-8d9e-0f1a2b3c4d5e","name":"Jane Doe"},"details":{"documentType":"invoice"}},{"id":"inv_1c2d3e4f5a6b4c7d8e9f0a1b2c3d4e5f:created","event":"Invoice created · INV-000412","action":"created","at":"2026-10-01T06:00:00.000Z","by":null,"details":{"recordId":"inv_1c2d3e4f5a6b4c7d8e9f0a1b2c3d4e5f","number":"INV-000412","status":"partially_paid"}}],"partial":false,"missing":[]}}}},"400":{"description":"resource is not one of the Books collections, or recordId is empty","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Kazee Books is not connected to this CRM organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record, or you do not have the Books permission to read that collection","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.billing.read"]}],"x-required-scopes":["crm.billing.read"]}},"/billing/books/{resource}/{recordId}/pdf":{"get":{"tags":["Billing"],"summary":"Get a Books document as PDF","description":"Answers the printable PDF of one Kazee Books document (an invoice, estimate, credit note, retainer invoice or payment receipt) as base64 text inside JSON, to view or download. You need your own Books permissions to read the collection and to view documents.\n\n**Permission:** `crm.billing.read` (View billing documents) and `crm.books.comms.read`.","operationId":"getBooksDocumentPdf","parameters":[{"name":"resource","in":"path","required":true,"schema":{"type":"string","enum":["estimates","invoices","subscription-invoices","credit-notes","retainer-invoices","payments-received"]},"description":"Which Kazee Books collection the document is in; only these have a printable document","example":"invoices"},{"name":"recordId","in":"path","required":true,"schema":{"type":"string"},"description":"The Books record's ID: a short type prefix and 32 hex digits, e.g. inv_… for an invoice","example":"inv_1c2d3e4f5a6b4c7d8e9f0a1b2c3d4e5f"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"source":{"type":"string"},"resource":{"type":"string"},"recordId":{"type":"string"},"fileName":{"type":"string"},"contentType":{"type":"string","description":"Usually application/pdf"},"pdfContent":{"type":"string","description":"The PDF file, base64-encoded"}}},"example":{"success":true,"source":"kazee-books","resource":"invoices","recordId":"inv_1c2d3e4f5a6b4c7d8e9f0a1b2c3d4e5f","fileName":"INV-000412.pdf","contentType":"application/pdf","pdfContent":"JVBERi0xLjcKJcfsj6IKNSAwIG9iago8PC9MZW5ndGggNiAwIFI+PgpzdHJlYW0K"}}}},"400":{"description":"resource has no printable document (items, recurring invoices, subscriptions, payments made), or recordId is empty","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Kazee Books is not connected to this CRM organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such document, or you do not have the Books permission to read that collection","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.billing.read","crm.books.comms.read"]}],"x-required-scopes":["crm.billing.read","crm.books.comms.read"],"x-scope-mode":"all"}},"/billing/books/subscriptions/{recordId}/{verb}":{"get":{"tags":["Subscriptions"],"summary":"Read a Books subscription's plan-change details","description":"Reads one part of a Kazee Books subscription's plan-change flow, as Books works it out: the plans it may move to (plan-options), what a change would cost and when (plan-change-preview), its change requests, its deposit, or what a trial would do (trial-preview). Nothing changes. The preview's fingerprint is what you send back to confirm the change.\n\n**Permission:** `crm.billing.read` (View billing documents).","operationId":"readBooksSubscriptionVerb","parameters":[{"name":"recordId","in":"path","required":true,"schema":{"type":"string"},"description":"The Books subscription (a sub_… ID)","example":"sub_4f5a6b7c8d9e4f0a1b2c3d4e5f6a7b8c"},{"name":"verb","in":"path","required":true,"schema":{"type":"string","enum":["plan-options","plan-change-preview","change-requests","deposit","trial-preview"]},"description":"What to read","example":"plan-change-preview"},{"name":"days","in":"query","required":false,"schema":{"type":"integer"},"description":"trial-preview: how many days the trial lasts","example":7},{"name":"downgrade_excess","in":"query","required":false,"schema":{"type":"string","enum":["apply","keep"]},"description":"On a downgrade: apply the deposit no longer needed to the customer's bills, or keep it as credit","example":"apply"},{"name":"effective_date","in":"query","required":false,"schema":{"type":"string","format":"date"},"description":"With effective_mode date: the day it takes effect (YYYY-MM-DD)","example":"2026-11-01"},{"name":"effective_mode","in":"query","required":false,"schema":{"type":"string","enum":["now","next_cycle","date"]},"description":"When the change takes effect; the organisation's default when left out","example":"next_cycle"},{"name":"kind","in":"query","required":false,"schema":{"type":"string","enum":["change","deposit_topup"]},"description":"plan-change-preview: change for a plan change (the default), deposit_topup to raise the deposit only","example":"change"},{"name":"price","in":"query","required":false,"schema":{"type":"string"},"description":"A price for the new plan other than its list price, as a decimal","example":"5000.00"},{"name":"tier_item_id","in":"query","required":false,"schema":{"type":"string"},"description":"trial-preview: the Books plan (an itm_… item ID) to try","example":"itm_7c8d9e0f1a2b4c3d4e5f6a7b8c9d0e1f"},{"name":"to_item_id","in":"query","required":false,"schema":{"type":"string"},"description":"plan-change-preview: the Books plan (an itm_… item ID) to move to","example":"itm_7c8d9e0f1a2b4c3d4e5f6a7b8c9d0e1f"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"source":{"type":"string"},"verb":{"type":"string","description":"The verb asked for"},"data":{"type":"object","properties":{"allowed":{"type":"boolean","description":"False when the change may not be made; reason then says why"},"kind":{"type":"string"},"direction":{"type":"string"},"switches_plan":{"type":"boolean"},"today":{"type":"string"},"subscription_id":{"type":"string"},"status":{"type":"string"},"module_enabled":{"type":"boolean"},"from":{"type":"object","properties":{"item_id":{"type":"string"},"name":{"type":"string"},"charge":{"type":"string"}}},"to":{"type":"object","properties":{"item_id":{"type":"string"},"name":{"type":"string"},"charge":{"type":"string"},"tier":{"type":"integer"},"price_overridden":{"type":"boolean"}}},"effective":{"type":"object","properties":{"mode":{"type":"string"},"date":{"type":"string"},"executes_now":{"type":"boolean"}}},"billing_ownership":{"type":"string"},"service":{},"deposit":{"type":"object","properties":{"required_before":{"type":"string"},"required_after":{"type":"string"},"held":{"type":"string"},"pending_on_request":{"type":"string"},"topup":{"type":"string"},"release":{"type":"string"},"excess_choice":{}}},"credit":{"type":"object","properties":{"available":{"type":"string"},"can_fund_topup":{"type":"boolean"},"receipts":{"type":"array"}}},"collection":{"type":"object","properties":{"deposit_now":{"type":"string"},"service_now":{"type":"string"},"service_later":{"type":"string"},"service_due_date":{"type":"string"},"total_now":{"type":"string","description":"What the customer must pay now for the change"},"required_before_activation":{"type":"string"}}},"approval":{"type":"object","properties":{"required":{"type":"boolean"},"mode":{"type":"string"},"submitter_available":{"type":"boolean"}}},"permission":{"type":"object","properties":{"required":{},"granted":{"type":"boolean"}}},"arrears":{"type":"object","properties":{"blocked":{"type":"boolean"},"overdue_balance":{"type":"string"},"invoices":{"type":"array"}}},"enforcement":{"type":"object","properties":{"provider":{"type":"string"},"mode":{"type":"string"},"pending":{"type":"boolean"}}},"fingerprint":{"type":"string","description":"Send this back with the change request to confirm this exact preview"}},"description":"Kazee Books' answer for that verb, as is (here: plan-change-preview)"}}},"example":{"success":true,"source":"kazee-books","verb":"plan-change-preview","data":{"allowed":true,"kind":"change","direction":"upgrade","switches_plan":true,"today":"2026-10-20","subscription_id":"sub_4f5a6b7c8d9e4f0a1b2c3d4e5f6a7b8c","status":"active","module_enabled":true,"from":{"item_id":"itm_6b7c8d9e0f1a4b2c3d4e5f6a7b8c9d0e","name":"Fibre 20 Mbps","charge":"3500.00"},"to":{"item_id":"itm_7c8d9e0f1a2b4c3d4e5f6a7b8c9d0e1f","name":"Fibre 50 Mbps","charge":"5500.00","tier":2,"price_overridden":false},"effective":{"mode":"next_cycle","date":"2026-11-01","executes_now":false},"billing_ownership":"active","service":null,"deposit":{"required_before":"3500.00","required_after":"5500.00","held":"3500.00","pending_on_request":"0.00","topup":"2000.00","release":"0.00","excess_choice":null},"credit":{"available":"0.00","can_fund_topup":false,"receipts":[]},"collection":{"deposit_now":"2000.00","service_now":"0.00","service_later":"5500.00","service_due_date":"2026-11-01","total_now":"2000.00","required_before_activation":"2000.00"},"approval":{"required":false,"mode":"respect_org_approvals","submitter_available":true},"permission":{"required":null,"granted":true},"arrears":{"blocked":false,"overdue_balance":"0.00","invoices":[]},"enforcement":{"provider":"radius","mode":"live","pending":false},"fingerprint":"9f8e7d6c5b4a39281706f5e4d3c2b1a0"}}}}},"400":{"description":"verb is unknown, recordId is not a Books ID, or Books refused an input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Kazee Books is not connected to this CRM organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such subscription, or you do not have the Books permission to read subscriptions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.billing.read"]}],"x-required-scopes":["crm.billing.read"]},"post":{"tags":["Subscriptions"],"summary":"Change a Books subscription's plan or start a trial","description":"Confirms a previewed plan change or deposit top-up on a Kazee Books subscription (verb change-requests), or starts a trial of another plan (verb trials). Books records you as the person who asked. Send the fingerprint from the preview: Books refuses if anything changed since. Send an Idempotency-Key header so that a retry cannot make a second request.\n\n**Permission:** `crm.billing.manage` (Change subscription plans) and `crm.books.sales.update`.","operationId":"writeBooksSubscriptionVerb","parameters":[{"name":"recordId","in":"path","required":true,"schema":{"type":"string"},"description":"The Books subscription (a sub_… ID)","example":"sub_4f5a6b7c8d9e4f0a1b2c3d4e5f6a7b8c"},{"name":"verb","in":"path","required":true,"schema":{"type":"string","enum":["change-requests","trials"]},"description":"change-requests confirms a plan change or top-up; trials starts a trial","example":"change-requests"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"days":{"type":"integer","description":"trials: how many days the trial lasts","examples":[7]},"downgrade_excess":{"type":"string","enum":["apply","keep"],"description":"On a downgrade: apply the deposit no longer needed to the customer's bills, or keep it as credit","examples":["apply"]},"effective_date":{"type":"string","format":"date","description":"With effective_mode date: the day it takes effect (YYYY-MM-DD)","examples":["2026-11-01"]},"effective_mode":{"type":"string","enum":["now","next_cycle","date"],"description":"When the change takes effect; the organisation's default when left out","examples":["next_cycle"]},"fingerprint":{"type":"string","description":"change-requests: the fingerprint of the preview you are confirming (Books needs it)","examples":["9f8e7d6c5b4a39281706f5e4d3c2b1a0"]},"kind":{"type":"string","enum":["change","deposit_topup"],"description":"change for a plan change (the default), deposit_topup to raise the deposit only","examples":["change"]},"note":{"type":"string","description":"A note kept with the request","examples":["Agreed on the phone on 20 Oct"]},"price":{"type":"string","description":"A price for the new plan other than its list price, as a decimal","examples":["5000.00"]},"reason":{"type":"string","description":"change-requests: why the change is made","examples":["Customer asked for faster internet"]},"tier_item_id":{"type":"string","description":"trials: the Books plan (an itm_… item ID) to try","examples":["itm_7c8d9e0f1a2b4c3d4e5f6a7b8c9d0e1f"]},"to_item_id":{"type":"string","description":"The Books plan (an itm_… item ID) to move to","examples":["itm_7c8d9e0f1a2b4c3d4e5f6a7b8c9d0e1f"]},"use_available_credit":{"type":"boolean","description":"change-requests: pay the top-up from the customer's unused payments where the organisation allows it","examples":[true]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"source":{"type":"string"},"verb":{"type":"string"},"data":{"type":"object","properties":{"request":{"type":"object","properties":{"id":{"type":"string"},"subscription_id":{"type":"string"},"contact_id":{"type":"string"},"kind":{"type":"string"},"status":{"type":"string","description":"draft, awaiting_funds, awaiting_approval, scheduled, applying_access, active, completed, cancelled, needs_attention or expired"},"direction":{"type":"string"},"from_item_id":{"type":"string"},"to_item_id":{"type":"string"},"from_charge":{"type":"string"},"to_charge":{"type":"string"},"effective_mode":{"type":"string"},"effective_date":{"type":"string"},"choices":{"type":"object","properties":{"downgrade_excess":{}}},"preview":{"type":"object","properties":{}},"money":{"type":"object","properties":{"currency_id":{"type":"string"},"topup_required":{"type":"string"},"topup_earmarked":{"type":"string"},"release":{"type":"string"},"service_now":{"type":"string"},"service_later":{"type":"string"},"allocations":{"type":"array"}}},"documents":{"type":"object","properties":{}},"approval":{"type":"object","properties":{"required":{"type":"boolean"},"decided":{"type":"boolean"},"mode":{"type":"string"}}},"access":{"type":"object","properties":{}},"trial":{"type":"object","properties":{}},"history":{"type":"array","items":{"type":"object","properties":{"at":{"type":"string","format":"date-time"},"action":{"type":"string"},"by":{"type":"object","properties":{"user_id":{"type":"string"},"name":{"type":"string"},"source":{"type":"string"}}},"detail":{"type":"object","properties":{"direction":{"type":"string"}}}}}},"requested_by":{"type":"string"},"requested_at":{"type":"string","format":"date-time"},"applied_at":{},"completed_at":{},"cancelled_at":{},"error":{},"version":{"type":"integer"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"preview":{"type":"object","properties":{"allowed":{"type":"boolean"},"kind":{"type":"string"},"direction":{"type":"string"},"switches_plan":{"type":"boolean"},"today":{"type":"string"},"subscription_id":{"type":"string"},"status":{"type":"string"},"module_enabled":{"type":"boolean"},"from":{"type":"object","properties":{"item_id":{"type":"string"},"name":{"type":"string"},"charge":{"type":"string"}}},"to":{"type":"object","properties":{"item_id":{"type":"string"},"name":{"type":"string"},"charge":{"type":"string"},"tier":{"type":"integer"},"price_overridden":{"type":"boolean"}}},"effective":{"type":"object","properties":{"mode":{"type":"string"},"date":{"type":"string"},"executes_now":{"type":"boolean"}}},"billing_ownership":{"type":"string"},"service":{},"deposit":{"type":"object","properties":{"required_before":{"type":"string"},"required_after":{"type":"string"},"held":{"type":"string"},"pending_on_request":{"type":"string"},"topup":{"type":"string"},"release":{"type":"string"},"excess_choice":{}}},"credit":{"type":"object","properties":{"available":{"type":"string"},"can_fund_topup":{"type":"boolean"},"receipts":{"type":"array"}}},"collection":{"type":"object","properties":{"deposit_now":{"type":"string"},"service_now":{"type":"string"},"service_later":{"type":"string"},"service_due_date":{"type":"string"},"total_now":{"type":"string"},"required_before_activation":{"type":"string"}}},"approval":{"type":"object","properties":{"required":{"type":"boolean"},"mode":{"type":"string"},"submitter_available":{"type":"boolean"}}},"permission":{"type":"object","properties":{"required":{},"granted":{"type":"boolean"}}},"arrears":{"type":"object","properties":{"blocked":{"type":"boolean"},"overdue_balance":{"type":"string"},"invoices":{"type":"array"}}},"enforcement":{"type":"object","properties":{"provider":{"type":"string"},"mode":{"type":"string"},"pending":{"type":"boolean"}}},"fingerprint":{"type":"string"}}},"applied":{"type":"boolean","description":"True when the change took effect at once"}},"description":"Kazee Books' answer, as is. change-requests: the request, the preview it was made from, and applied; trials: the trial request"}}},"example":{"success":true,"source":"kazee-books","verb":"change-requests","data":{"request":{"id":"scr_5a6b7c8d9e0f4a1b2c3d4e5f6a7b8c9d","subscription_id":"sub_4f5a6b7c8d9e4f0a1b2c3d4e5f6a7b8c","contact_id":"con_0b9e8c1e3f5a4c2d9a7b1e2f3a4b5c6d","kind":"change","status":"awaiting_funds","direction":"upgrade","from_item_id":"itm_6b7c8d9e0f1a4b2c3d4e5f6a7b8c9d0e","to_item_id":"itm_7c8d9e0f1a2b4c3d4e5f6a7b8c9d0e1f","from_charge":"3500.00","to_charge":"5500.00","effective_mode":"next_cycle","effective_date":"2026-11-01","choices":{"downgrade_excess":null},"preview":{},"money":{"currency_id":"c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f","topup_required":"2000.00","topup_earmarked":"0.00","release":"0.00","service_now":"0.00","service_later":"5500.00","allocations":[]},"documents":{},"approval":{"required":false,"decided":true,"mode":"respect_org_approvals"},"access":{},"trial":{},"history":[{"at":"2026-10-20T09:00:00.000Z","action":"requested","by":{"user_id":"65f1a2b3-c4d5-e6f7-a8b9-c0d100000000","name":"Jane Doe","source":"api"},"detail":{"direction":"upgrade"}}],"requested_by":"65f1a2b3-c4d5-e6f7-a8b9-c0d100000000","requested_at":"2026-10-20T09:00:00.000Z","applied_at":null,"completed_at":null,"cancelled_at":null,"error":null,"version":1,"created_at":"2026-10-20T09:00:00.000Z","updated_at":"2026-10-20T09:00:00.000Z"},"preview":{"allowed":true,"kind":"change","direction":"upgrade","switches_plan":true,"today":"2026-10-20","subscription_id":"sub_4f5a6b7c8d9e4f0a1b2c3d4e5f6a7b8c","status":"active","module_enabled":true,"from":{"item_id":"itm_6b7c8d9e0f1a4b2c3d4e5f6a7b8c9d0e","name":"Fibre 20 Mbps","charge":"3500.00"},"to":{"item_id":"itm_7c8d9e0f1a2b4c3d4e5f6a7b8c9d0e1f","name":"Fibre 50 Mbps","charge":"5500.00","tier":2,"price_overridden":false},"effective":{"mode":"next_cycle","date":"2026-11-01","executes_now":false},"billing_ownership":"active","service":null,"deposit":{"required_before":"3500.00","required_after":"5500.00","held":"3500.00","pending_on_request":"0.00","topup":"2000.00","release":"0.00","excess_choice":null},"credit":{"available":"0.00","can_fund_topup":false,"receipts":[]},"collection":{"deposit_now":"2000.00","service_now":"0.00","service_later":"5500.00","service_due_date":"2026-11-01","total_now":"2000.00","required_before_activation":"2000.00"},"approval":{"required":false,"mode":"respect_org_approvals","submitter_available":true},"permission":{"required":null,"granted":true},"arrears":{"blocked":false,"overdue_balance":"0.00","invoices":[]},"enforcement":{"provider":"radius","mode":"live","pending":false},"fingerprint":"9f8e7d6c5b4a39281706f5e4d3c2b1a0"},"applied":false}}}}},"400":{"description":"verb is unknown, recordId is not a Books ID, or Books refused an input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You lack a permission this needs; a change that voids the cycle's invoice also needs that Books permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such subscription","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The change is not allowed now (Books says why), the preview changed since (fingerprint), or the Idempotency-Key was used for something else","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.billing.manage","crm.books.sales.update"]}],"x-required-scopes":["crm.billing.manage","crm.books.sales.update"],"x-scope-mode":"all"}},"/accounts/analytics":{"get":{"tags":["Accounts"],"summary":"Get account figures","description":"Figures over all your organisation's accounts: how many there are by status, how many were opened each month, and their total credit limit and balance. The figures cover every account, whatever the period.\n\n**Permission:** `crm.accounts.read` (View accounts).","operationId":"getAccountAnalytics","parameters":[{"name":"groupBy","in":"query","required":false,"schema":{"type":"string","default":"status"},"description":"Not used: the breakdown is always by status"},{"name":"period","in":"query","required":false,"schema":{"type":"string","default":"30"},"description":"Number of days, e.g. 30. Only labels the answer: the figures always cover every account","example":"30"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"overview":{"type":"object","properties":{"totalAccounts":{"type":"integer"},"activeAccounts":{"type":"integer"},"suspendedAccounts":{"type":"integer"},"terminatedAccounts":{"type":"integer"},"period":{"type":"string","description":"The period asked for, as words; it only labels the answer"}}},"statusBreakdown":{"type":"array","items":{"type":"object","properties":{"status":{"type":"string","description":"An account status"},"count":{"type":"integer"}}}},"financialMetrics":{"type":"object","properties":{"totalCreditLimit":{"type":"integer"},"totalCurrentBalance":{"type":"number"},"count":{"type":"integer"}}},"trends":{"type":"object","properties":{"monthlyTrends":{"type":"array","items":{"type":"object","properties":{"month":{"type":"string","description":"Month the accounts were opened, YYYY-MM"},"count":{"type":"integer"},"totalCreditLimit":{"type":"integer","description":"Sum of the credit limits of the accounts opened that month"}}}}}}}},"generatedAt":{"type":"string","format":"date-time","description":"When the figures were worked out"},"period":{"type":"string"}}},"example":{"success":true,"data":{"overview":{"totalAccounts":412,"activeAccounts":371,"suspendedAccounts":23,"terminatedAccounts":18,"period":"Last 30 days"},"statusBreakdown":[{"status":"active","count":371},{"status":"suspended","count":23},{"status":"terminated","count":18}],"financialMetrics":{"totalCreditLimit":2150000,"totalCurrentBalance":184500.5,"count":412},"trends":{"monthlyTrends":[{"month":"2026-09","count":31,"totalCreditLimit":155000},{"month":"2026-10","count":27,"totalCreditLimit":135000}]}},"generatedAt":"2026-10-21T09:00:00.000Z","period":"30 days"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.accounts.read"]}],"x-required-scopes":["crm.accounts.read"]}},"/accounts":{"get":{"tags":["Accounts"],"summary":"List accounts","description":"List your organisation's accounts, a page at a time, newest first, each with its owner, address and people. Terminated accounts are left out unless you ask for them or filter by status. A search written as a phone number or email also finds accounts through their people's phone or email in Kazee Contacts.\n\n**Permission:** `crm.accounts.read` (View accounts).","operationId":"getAccounts","parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Get just this account (as a list of one, or none); other filters are ignored","example":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"},{"name":"contactId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only accounts this contact is linked to","example":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"},{"name":"customFieldFilters","in":"query","required":false,"schema":{"type":"object"},"description":"Older form of filters for custom fields, keyed by field key (without cf:)","example":{"region":"Nairobi"}},{"name":"fieldFilters","in":"query","required":false,"schema":{"type":"object"},"description":"Older form of filters for built-in fields, keyed by field name","example":{"status":["active"]}},{"name":"filters","in":"query","required":false,"schema":{"type":"object"},"description":"Field conditions keyed by filter field ID (a native field name, or cf:<key> for your own fields; GET /filter-fields lists them), each { operator, value } or { operator, values }; a bare value means \"is\". A JSON object in a string also works. An invalid condition answers 400 naming the field","example":{"status":{"operator":"is","values":["active"]}}},{"name":"includeStats","in":"query","required":false,"schema":{"type":"boolean"},"description":"Also answer how many matching accounts there are by status","example":true},{"name":"includeTerminated","in":"query","required":false,"schema":{"type":"boolean","default":"false"},"description":"Also list terminated accounts (when status is not given)","example":false},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25},"description":"Accounts per page, at most 500","example":25},{"name":"ownerId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only accounts this person owns","example":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text to find in the name or account number; a phone number or email also matches the account's people","example":"Baraka"},{"name":"sort","in":"query","required":false,"schema":{"type":"object"},"description":"Order as { field, order }: field is name, updatedAt, currentBalance or createdAt (default), order asc or desc (default)","example":{"field":"name","order":"asc"}},{"name":"status","in":"query","required":false,"schema":{"type":"array","items":{"type":"string","enum":["active","inactive","suspended","terminated"]}},"description":"Only accounts with these statuses (one or several)","example":["active"]},{"name":"tags","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"}},"description":"Only accounts with any of these labels (a JSON array in a string, or one label, also works)","example":["retail"]}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the account is filed under one, else the account's own"},"accountNumber":{"type":"string","description":"Your organisation's account number, unique"},"status":{"type":"string","description":"active, inactive, suspended or terminated"},"description":{"type":"string"},"addressId":{"type":"string"},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string","description":"Credit limit, a decimal number in a string"},"currentBalance":{"type":"string","description":"Current balance, a decimal number in a string"},"originLeadId":{"type":"string","description":"The lead the account was converted from, if any"},"walletId":{},"billingContactId":{"type":"string","description":"The contact who gets the bills"},"serviceContactId":{"type":"string","description":"The contact for service matters"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"}}},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array","description":"IDs of this account in other systems, e.g. { source: zoho, externalId }"},"partyId":{"type":"string","description":"The person or company record in Kazee Contacts the account is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"phone":{"type":"string","description":"The phone of the account's reach person (its primary contact first)"},"email":{"type":"string","description":"The email of the account's reach person (its primary contact first)"},"owner":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"}}},"address":{"type":"object","properties":{"id":{"type":"string"},"street":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"}}},"primaryContact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}}},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{"type":"string"},"cacheDisplayName":{"type":"string"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string"},"fullName":{"type":"string"}},"description":"The primary contact, or null"},"contacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"roles":{"type":"array","items":{"type":"string"},"description":"What the person does for the account: admin, billing, technical, support, readonly or primary"},"isPrimary":{"type":"boolean","description":"True for the account's primary contact (one per account)"},"status":{"type":"string","description":"The link's status, usually active"},"preferences":{"type":"object","properties":{}},"notes":{"type":"string"},"source":{"type":"string","description":"crm when CRM made the link, contacts when it came from Kazee Contacts (another app linked the person)"},"createdBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"contact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}}},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{"type":"string"},"cacheDisplayName":{"type":"string"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string"},"fullName":{"type":"string"}},"description":"The linked contact"}}}}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}}},"stats":{"type":"array","items":{"type":"object","properties":{"status":{"type":"string"},"count":{"type":"integer"}}},"description":"Only with includeStats=true: how many accounts match, by status"},"search":{"type":"object","properties":{"source":{"type":"string"}},"description":"Only when you searched: where the matches came from (contacts or cache) and a notice when Kazee Contacts could not be asked"}}},"example":{"success":true,"data":[{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Baraka Traders Ltd","accountNumber":"ACC-000123","status":"active","description":"Hardware shop on Moi Avenue","addressId":"f4e13f6b-8c0d-4f7a-9d2e-6b7c8d9e0f1a","onboardingDate":"2026-10-01T08:30:00.000Z","terminationDate":null,"creditLimit":"50000.00","currentBalance":"0.00","originLeadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","walletId":null,"billingContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","serviceContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notificationPreferences":{"email":true,"sms":true},"tags":["retail"],"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Baraka Traders Ltd","cachePrimaryPhone":"+254700000101","cachePrimaryEmail":"info@baraka-traders.example","cachePartyRevision":3,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","phone":"+254700000102","email":"wanjiru.kamau@example.com","owner":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","firstName":"Amina","lastName":"Otieno","email":"amina.otieno@example.com"},"address":{"id":"f4e13f6b-8c0d-4f7a-9d2e-6b7c8d9e0f1a","street":"Moi Avenue 12","city":"Nairobi","country":"Kenya"},"primaryContact":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau"},"contacts":[{"id":"05f2406c-9d1e-4a8b-8e3f-7c8d9e0f1a2b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","roles":["admin","billing"],"isPrimary":true,"status":"active","preferences":{},"notes":"Linked during account creation","source":"crm","createdBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-01T08:30:00.000Z","contact":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau"}}]}],"pagination":{"page":1,"limit":25,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false},"stats":[{"status":"active","count":1}],"search":{"source":"contacts"}}}}},"400":{"description":"A filter names an unknown field or is not valid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.accounts.read"]}],"x-required-scopes":["crm.accounts.read"]},"post":{"tags":["Accounts"],"summary":"Create an account","description":"Create a customer account and link one or more existing contacts to it. Each contact gets the roles you give it (admin when none), and you become the owner unless you name one. Automations for new accounts run after it is made.\n\n**Permission:** `crm.accounts.create` (Create accounts).","operationId":"createAccount","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountNumber":{"type":"string","description":"Your account number, up to 40 characters, unique","examples":["ACC-000123"]},"address":{"type":"object","description":"Not used: the address is not saved by this call"},"billingContactId":{"type":"string","format":"uuid","description":"The contact who gets the bills","examples":["4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"]},"contactIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"IDs of the existing contacts to link: at least one (a JSON array in a string also works)","examples":[["4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"]]},"contactRoles":{"type":"object","description":"Roles per contact ID, e.g. { \"<contactId>\": [\"admin\", \"billing\"] }; a contact left out gets admin","examples":[{"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b":["admin","billing"]}]},"creditLimit":{"type":"number","description":"Credit limit; 0 when left out","examples":[50000]},"customFields":{"type":"object","description":"Your organisation's own fields, by field key","examples":[{"industry":"Hardware"}]},"description":{"type":"string","description":"Notes about the account","examples":["Hardware shop on Moi Avenue"]},"name":{"type":"string","description":"The account's name, e.g. the company or household","examples":["Baraka Traders Ltd"]},"notificationPreferences":{"type":"object","description":"How the account wants to hear from you: { email, sms, preferredContactMethod } (a JSON string also works)","examples":[{"email":true,"sms":true}]},"ownerId":{"type":"string","format":"uuid","description":"The person who owns the account; you when left out","examples":["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]},"primaryContactId":{"type":"string","format":"uuid","description":"Which of contactIds is the primary contact","examples":["4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"]},"serviceContactId":{"type":"string","format":"uuid","description":"The contact for service matters","examples":["4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"]},"tags":{"type":"array","items":{"type":"string"},"description":"Labels (a JSON array in a string also works)","examples":[["retail"]]}},"required":["contactIds","name"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the account is filed under one, else the account's own"},"accountNumber":{"type":"string","description":"Your organisation's account number, unique"},"status":{"type":"string","description":"active, inactive, suspended or terminated"},"description":{"type":"string"},"addressId":{"type":"string"},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string","description":"Credit limit, a decimal number in a string"},"currentBalance":{"type":"string","description":"Current balance, a decimal number in a string"},"originLeadId":{"type":"string","description":"The lead the account was converted from, if any"},"walletId":{},"billingContactId":{"type":"string","description":"The contact who gets the bills"},"serviceContactId":{"type":"string","description":"The contact for service matters"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"}}},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array","description":"IDs of this account in other systems, e.g. { source: zoho, externalId }"},"partyId":{"type":"string","description":"The person or company record in Kazee Contacts the account is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"contactsLinked":{"type":"object","properties":{"totalLinked":{"type":"integer"},"contacts":{"type":"array","items":{"type":"object","properties":{"contactId":{"type":"string"},"isPrimary":{"type":"boolean"},"roles":{"type":"array","items":{"type":"string"},"description":"The roles the contact got"}}}}}}}},"example":{"success":true,"data":{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Baraka Traders Ltd","accountNumber":"ACC-000123","status":"active","description":"Hardware shop on Moi Avenue","addressId":"f4e13f6b-8c0d-4f7a-9d2e-6b7c8d9e0f1a","onboardingDate":"2026-10-01T08:30:00.000Z","terminationDate":null,"creditLimit":"50000.00","currentBalance":"0.00","originLeadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","walletId":null,"billingContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","serviceContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notificationPreferences":{"email":true,"sms":true},"tags":["retail"],"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Baraka Traders Ltd","cachePrimaryPhone":"+254700000101","cachePrimaryEmail":"info@baraka-traders.example","cachePartyRevision":3,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"},"contactsLinked":{"totalLinked":1,"contacts":[{"contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","isPrimary":true,"roles":["admin","billing"]}]}}}}},"400":{"description":"contactIds is empty, a JSON field is not valid JSON, primaryContactId is not one of contactIds, or Kazee Contacts refused the new record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"One of contactIds is not a contact in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Another account already has that account number","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"description":"Kazee Contacts could not be reached to file the new account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.accounts.create"]}],"x-required-scopes":["crm.accounts.create"]}},"/accounts/{accountId}/with-contacts":{"get":{"tags":["Accounts"],"summary":"Get an account with its contacts","description":"Get an account with its contacts and the details of each link (roles, primary, status), plus a short count. Only active links unless you ask for all.\n\n**Permission:** `crm.accounts.read` (View accounts).","operationId":"getAccountWithContacts","parameters":[{"name":"accountId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The account's ID","example":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"},{"name":"includeInactive","in":"query","required":false,"schema":{"type":"string","enum":["true","false"],"default":"false"},"description":"\"true\" to include links that are no longer active","example":"false"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"account":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the account is filed under one, else the account's own"},"accountNumber":{"type":"string","description":"Your organisation's account number, unique"},"status":{"type":"string","description":"active, inactive, suspended or terminated"},"description":{"type":"string"},"addressId":{"type":"string"},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string","description":"Credit limit, a decimal number in a string"},"currentBalance":{"type":"string","description":"Current balance, a decimal number in a string"},"originLeadId":{"type":"string","description":"The lead the account was converted from, if any"},"walletId":{},"billingContactId":{"type":"string","description":"The contact who gets the bills"},"serviceContactId":{"type":"string","description":"The contact for service matters"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"}}},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array","description":"IDs of this account in other systems, e.g. { source: zoho, externalId }"},"partyId":{"type":"string","description":"The person or company record in Kazee Contacts the account is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"phone":{"type":"string","description":"The phone of the account's reach person (its primary contact first)"},"email":{"type":"string","description":"The email of the account's reach person (its primary contact first)"},"owner":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"}}},"address":{"type":"object","properties":{"id":{"type":"string"},"street":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"}}},"primaryContact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}}},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{"type":"string"},"cacheDisplayName":{"type":"string"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string"},"fullName":{"type":"string"}},"description":"The primary contact, or null"},"contacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"roles":{"type":"array","items":{"type":"string"},"description":"What the person does for the account: admin, billing, technical, support, readonly or primary"},"isPrimary":{"type":"boolean","description":"True for the account's primary contact (one per account)"},"status":{"type":"string","description":"The link's status, usually active"},"preferences":{"type":"object","properties":{}},"notes":{"type":"string"},"source":{"type":"string","description":"crm when CRM made the link, contacts when it came from Kazee Contacts (another app linked the person)"},"createdBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"contact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}}},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{"type":"string"},"cacheDisplayName":{"type":"string"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string"},"fullName":{"type":"string"}},"description":"The linked contact"}}}}}},"contacts":{"type":"object","properties":{"total":{"type":"integer"},"primary":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}}},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{"type":"string"},"cacheDisplayName":{"type":"string"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string"},"fullName":{"type":"string"}},"description":"The primary contact, or null"},"relationships":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"roles":{"type":"array","items":{"type":"string"},"description":"What the person does for the account: admin, billing, technical, support, readonly or primary"},"isPrimary":{"type":"boolean","description":"True for the account's primary contact (one per account)"},"status":{"type":"string","description":"The link's status, usually active"},"preferences":{"type":"object","properties":{}},"notes":{"type":"string"},"source":{"type":"string","description":"crm when CRM made the link, contacts when it came from Kazee Contacts (another app linked the person)"},"createdBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"contact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}}},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{"type":"string"},"cacheDisplayName":{"type":"string"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string"},"fullName":{"type":"string"}},"description":"The linked contact"}}}},"summary":{"type":"object","properties":{"totalContacts":{"type":"integer"},"activeContacts":{"type":"integer"},"primaryContactId":{"type":"string"}}}}}}}}},"example":{"success":true,"data":{"account":{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Baraka Traders Ltd","accountNumber":"ACC-000123","status":"active","description":"Hardware shop on Moi Avenue","addressId":"f4e13f6b-8c0d-4f7a-9d2e-6b7c8d9e0f1a","onboardingDate":"2026-10-01T08:30:00.000Z","terminationDate":null,"creditLimit":"50000.00","currentBalance":"0.00","originLeadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","walletId":null,"billingContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","serviceContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notificationPreferences":{"email":true,"sms":true},"tags":["retail"],"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Baraka Traders Ltd","cachePrimaryPhone":"+254700000101","cachePrimaryEmail":"info@baraka-traders.example","cachePartyRevision":3,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","phone":"+254700000102","email":"wanjiru.kamau@example.com","owner":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","firstName":"Amina","lastName":"Otieno","email":"amina.otieno@example.com"},"address":{"id":"f4e13f6b-8c0d-4f7a-9d2e-6b7c8d9e0f1a","street":"Moi Avenue 12","city":"Nairobi","country":"Kenya"},"primaryContact":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau"},"contacts":[{"id":"05f2406c-9d1e-4a8b-8e3f-7c8d9e0f1a2b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","roles":["admin","billing"],"isPrimary":true,"status":"active","preferences":{},"notes":"Linked during account creation","source":"crm","createdBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-01T08:30:00.000Z","contact":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau"}}]},"contacts":{"total":1,"primary":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau"},"relationships":[{"id":"05f2406c-9d1e-4a8b-8e3f-7c8d9e0f1a2b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","roles":["admin","billing"],"isPrimary":true,"status":"active","preferences":{},"notes":"Linked during account creation","source":"crm","createdBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-01T08:30:00.000Z","contact":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau"}}],"summary":{"totalContacts":1,"activeContacts":1,"primaryContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No account with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.accounts.read"]}],"x-required-scopes":["crm.accounts.read"]}},"/accounts/{accountId}/invoices":{"get":{"tags":["Billing"],"summary":"List an account's invoices","description":"List an account's invoices, read live from Kazee Books (CRM keeps no copy). When Books is not connected, or the account has no Books customer yet, it answers linked: false with an empty list rather than an error.\n\n**Permission:** `crm.billing.read` (View billing documents).","operationId":"getAccountInvoices","parameters":[{"name":"accountId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The account's ID","example":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"},{"name":"page[after]","in":"query","required":false,"schema":{"type":"string"},"description":"The meta.next_cursor of the previous page"},{"name":"page[size]","in":"query","required":false,"schema":{"type":"integer"},"description":"How many invoices per page (Books' default when left out)","example":20},{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"Only invoices with this Books status, e.g. overdue","example":"overdue"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"linked":{"type":"boolean","description":"False when the invoices could not be looked up: Books is not connected, or the account has no Books customer. The reply then also holds reason: books_not_configured or no_books_contact"},"booksContactId":{"type":"string","description":"The account's customer ID in Kazee Books"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"number":{"type":"string"},"status":{"type":"string"},"total":{"type":"string"},"amount_applied":{"type":"string"},"balance":{"type":"string"}}},"description":"The invoices as Kazee Books answers them"},"meta":{"type":"object","properties":{"next_cursor":{"description":"Pass as page[after] to get the next page; null on the last page"}}}}},"example":{"success":true,"linked":true,"booksContactId":"6a1d2c3b-4e5f-4a6b-8c7d-9e0f1a2b3c4d","data":[{"id":"inv_7f3a9c2e4b6d4e8fa1c3e5b7d9f1a3c5","number":"INV-001042","status":"overdue","total":"3480.00","amount_applied":"0.00","balance":"3480.00"}],"meta":{"next_cursor":null}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No account with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.billing.read"]}],"x-required-scopes":["crm.billing.read"]}},"/accounts/{accountId}":{"get":{"tags":["Accounts"],"summary":"Get an account","description":"Get one account with its owner, address, primary contact and every linked contact (active or not). Lookup custom fields come with the record they point at under `_expanded`.\n\n**Permission:** `crm.accounts.read` (View accounts).","operationId":"getAccount","parameters":[{"name":"accountId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The account's ID","example":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"},{"name":"includeAuditLog","in":"query","required":false,"schema":{"type":"string","enum":["true","false"],"default":"false"},"description":"Not used: the account's history is read from Activity"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the account is filed under one, else the account's own"},"accountNumber":{"type":"string","description":"Your organisation's account number, unique"},"status":{"type":"string","description":"active, inactive, suspended or terminated"},"description":{"type":"string"},"addressId":{"type":"string"},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string","description":"Credit limit, a decimal number in a string"},"currentBalance":{"type":"string","description":"Current balance, a decimal number in a string"},"originLeadId":{"type":"string","description":"The lead the account was converted from, if any"},"walletId":{},"billingContactId":{"type":"string","description":"The contact who gets the bills"},"serviceContactId":{"type":"string","description":"The contact for service matters"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"}}},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array","description":"IDs of this account in other systems, e.g. { source: zoho, externalId }"},"partyId":{"type":"string","description":"The person or company record in Kazee Contacts the account is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"phone":{"type":"string","description":"The phone of the account's reach person (its primary contact first)"},"email":{"type":"string","description":"The email of the account's reach person (its primary contact first)"},"owner":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"}}},"address":{"type":"object","properties":{"id":{"type":"string"},"street":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"}}},"primaryContact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}}},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{"type":"string"},"cacheDisplayName":{"type":"string"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string"},"fullName":{"type":"string"}},"description":"The primary contact, or null"},"contacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"roles":{"type":"array","items":{"type":"string"},"description":"What the person does for the account: admin, billing, technical, support, readonly or primary"},"isPrimary":{"type":"boolean","description":"True for the account's primary contact (one per account)"},"status":{"type":"string","description":"The link's status, usually active"},"preferences":{"type":"object","properties":{}},"notes":{"type":"string"},"source":{"type":"string","description":"crm when CRM made the link, contacts when it came from Kazee Contacts (another app linked the person)"},"createdBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"contact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}}},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{"type":"string"},"cacheDisplayName":{"type":"string"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string"},"fullName":{"type":"string"}},"description":"The linked contact"}}}}}}}},"example":{"success":true,"data":{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Baraka Traders Ltd","accountNumber":"ACC-000123","status":"active","description":"Hardware shop on Moi Avenue","addressId":"f4e13f6b-8c0d-4f7a-9d2e-6b7c8d9e0f1a","onboardingDate":"2026-10-01T08:30:00.000Z","terminationDate":null,"creditLimit":"50000.00","currentBalance":"0.00","originLeadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","walletId":null,"billingContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","serviceContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notificationPreferences":{"email":true,"sms":true},"tags":["retail"],"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Baraka Traders Ltd","cachePrimaryPhone":"+254700000101","cachePrimaryEmail":"info@baraka-traders.example","cachePartyRevision":3,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","phone":"+254700000102","email":"wanjiru.kamau@example.com","owner":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","firstName":"Amina","lastName":"Otieno","email":"amina.otieno@example.com"},"address":{"id":"f4e13f6b-8c0d-4f7a-9d2e-6b7c8d9e0f1a","street":"Moi Avenue 12","city":"Nairobi","country":"Kenya"},"primaryContact":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau"},"contacts":[{"id":"05f2406c-9d1e-4a8b-8e3f-7c8d9e0f1a2b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","roles":["admin","billing"],"isPrimary":true,"status":"active","preferences":{},"notes":"Linked during account creation","source":"crm","createdBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-01T08:30:00.000Z","contact":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau"}}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No account with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.accounts.read"]}],"x-required-scopes":["crm.accounts.read"]},"put":{"tags":["Accounts"],"summary":"Update an account","description":"Change an account's details and, in the same call, which contacts are linked to it. Send only what changes: a name equal to the one shown is no rename. Contacts can be added and removed one by one (addContactIds, removeContactIds) or replaced as a whole set (contactIds). Automations for updated accounts run after it.\n\n**Permission:** `crm.accounts.update` (Update accounts).","operationId":"updateAccount","parameters":[{"name":"accountId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The account's ID","example":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"addContactIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Contacts to link (a JSON array in a string also works)","examples":[["5a4d3b6d-8e0f-4b7c-8f2a-6d7e8f9a0b1c"]]},"address":{"type":"object","description":"Not used: the address is not saved by this call"},"contactIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"The whole set of contacts the account should have: those missing are linked, the others unlinked. Overrides addContactIds and removeContactIds"},"contactRoles":{"type":"object","description":"Roles per contact ID for the contacts being linked; a contact left out gets admin","examples":[{"5a4d3b6d-8e0f-4b7c-8f2a-6d7e8f9a0b1c":["technical"]}]},"customFields":{"type":"object","description":"Your organisation's own fields, by field key; replaces the stored set","examples":[{"industry":"Hardware"}]},"description":{"type":"string","description":"Notes about the account"},"name":{"type":"string","description":"New name","examples":["Baraka Traders Ltd"]},"originLeadId":{"type":"string","format":"uuid","description":"The lead the account came from; empty or null clears the link","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]},"primaryContactId":{"type":"string","format":"uuid","description":"Make this linked contact the primary one","examples":["4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"]},"removeContactIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Contacts to unlink (a JSON array in a string also works)"},"status":{"type":"string","enum":["active","inactive","suspended","terminated"],"description":"New status, set as given (the status call checks the allowed moves; this one does not)","examples":["active"]},"tags":{"type":"array","items":{"type":"string"},"description":"Labels, replacing the current ones (a JSON array in a string also works)","examples":[["retail","priority"]]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the account is filed under one, else the account's own"},"accountNumber":{"type":"string","description":"Your organisation's account number, unique"},"status":{"type":"string","description":"active, inactive, suspended or terminated"},"description":{"type":"string"},"addressId":{"type":"string"},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string","description":"Credit limit, a decimal number in a string"},"currentBalance":{"type":"string","description":"Current balance, a decimal number in a string"},"originLeadId":{"type":"string","description":"The lead the account was converted from, if any"},"walletId":{},"billingContactId":{"type":"string","description":"The contact who gets the bills"},"serviceContactId":{"type":"string","description":"The contact for service matters"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"}}},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array","description":"IDs of this account in other systems, e.g. { source: zoho, externalId }"},"partyId":{"type":"string","description":"The person or company record in Kazee Contacts the account is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"phone":{"type":"string","description":"The phone of the account's reach person (its primary contact first)"},"email":{"type":"string","description":"The email of the account's reach person (its primary contact first)"},"owner":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"}}},"primaryContact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}}},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{"type":"string"},"cacheDisplayName":{"type":"string"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string"},"fullName":{"type":"string"}},"description":"The primary contact, or null"},"contacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"roles":{"type":"array","items":{"type":"string"},"description":"What the person does for the account: admin, billing, technical, support, readonly or primary"},"isPrimary":{"type":"boolean","description":"True for the account's primary contact (one per account)"},"status":{"type":"string","description":"The link's status, usually active"},"preferences":{"type":"object","properties":{}},"notes":{"type":"string"},"source":{"type":"string","description":"crm when CRM made the link, contacts when it came from Kazee Contacts (another app linked the person)"},"createdBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"contact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}}},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{"type":"string"},"cacheDisplayName":{"type":"string"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string"},"fullName":{"type":"string"}},"description":"The linked contact"}}}}}},"contactChanges":{"type":"object","properties":{"changed":{"type":"boolean"},"changes":{"type":"array","items":{"type":"string"},"description":"What happened to the links, in words"}}}}},"example":{"success":true,"message":"Account updated successfully with contact changes","data":{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Baraka Traders Ltd","accountNumber":"ACC-000123","status":"active","description":"Hardware shop on Moi Avenue","addressId":"f4e13f6b-8c0d-4f7a-9d2e-6b7c8d9e0f1a","onboardingDate":"2026-10-01T08:30:00.000Z","terminationDate":null,"creditLimit":"50000.00","currentBalance":"0.00","originLeadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","walletId":null,"billingContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","serviceContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notificationPreferences":{"email":true,"sms":true},"tags":["retail"],"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Baraka Traders Ltd","cachePrimaryPhone":"+254700000101","cachePrimaryEmail":"info@baraka-traders.example","cachePartyRevision":3,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","phone":"+254700000102","email":"wanjiru.kamau@example.com","owner":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","firstName":"Amina","lastName":"Otieno","email":"amina.otieno@example.com"},"primaryContact":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau"},"contacts":[{"id":"05f2406c-9d1e-4a8b-8e3f-7c8d9e0f1a2b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","roles":["admin","billing"],"isPrimary":true,"status":"active","preferences":{},"notes":"Linked during account creation","source":"crm","createdBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-01T08:30:00.000Z","contact":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau"}}]},"contactChanges":{"changed":true,"changes":["Added contact Wanjiru Kamau"]}}}}},"400":{"description":"A JSON field is not valid JSON, or Kazee Contacts refused the rename","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No account with that ID, a contact to add does not exist, or primaryContactId is not linked to the account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"description":"Kazee Contacts could not be reached to save the rename","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.accounts.update"]}],"x-required-scopes":["crm.accounts.update"]}},"/contacts/analytics":{"get":{"tags":["Contacts"],"summary":"Get contact figures","description":"Figures over all your organisation's contacts: how many there are by status and how many were added each month. The figures cover every contact, whatever the period.\n\n**Permission:** `crm.contacts.read` (View contacts).","operationId":"getContactAnalytics","parameters":[{"name":"period","in":"query","required":false,"schema":{"type":"string","default":"30"},"description":"Number of days, e.g. 30. Only labels the answer: the figures always cover every contact","example":"30"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"overview":{"type":"object","properties":{"totalContacts":{"type":"integer"},"activeContacts":{"type":"integer"},"inactiveContacts":{"type":"integer"},"invitedContacts":{"type":"integer"},"suspendedContacts":{"type":"integer"}}},"statusBreakdown":{"type":"array","items":{"type":"object","properties":{"status":{"type":"string","description":"A contact status"},"count":{"type":"integer"}}}},"monthlyTrends":{"type":"array","items":{"type":"object","properties":{"month":{"type":"string","description":"Month the contacts were added, YYYY-MM"},"count":{"type":"integer"}}}}}},"generatedAt":{"type":"string","format":"date-time","description":"When the figures were worked out"},"period":{"type":"string","description":"The period asked for; it only labels the answer"}}},"example":{"success":true,"data":{"overview":{"totalContacts":958,"activeContacts":902,"inactiveContacts":41,"invitedContacts":6,"suspendedContacts":9},"statusBreakdown":[{"status":"active","count":902},{"status":"inactive","count":41},{"status":"suspended","count":9},{"status":"invited","count":6}],"monthlyTrends":[{"month":"2026-09","count":64},{"month":"2026-10","count":51}]},"generatedAt":"2026-10-21T09:00:00.000Z","period":"30 days"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.contacts.read"]}],"x-required-scopes":["crm.contacts.read"]}},"/contacts":{"get":{"tags":["Contacts"],"summary":"List contacts","description":"List your organisation's contacts, a page at a time, newest first, each with its owner, address and accounts. Inactive contacts are left out unless you ask for them or filter by status. A search written as a phone number or email also asks Kazee Contacts who matches. With accountId it answers all of that account's contacts in one page instead (only role and isPrimary narrow it further).\n\n**Permission:** `crm.contacts.read` (View contacts).","operationId":"getContacts","parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only the contacts actively linked to this account, all in one page","example":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"},{"name":"contactId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Get just this contact (as a list of one, or none); other filters are ignored","example":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"},{"name":"customFieldFilters","in":"query","required":false,"schema":{"type":"object"},"description":"Older form of filters for custom fields, keyed by field key (without cf:)","example":{"region":"Nairobi"}},{"name":"fieldFilters","in":"query","required":false,"schema":{"type":"object"},"description":"Older form of filters for built-in fields, keyed by field name","example":{"status":["active"]}},{"name":"filters","in":"query","required":false,"schema":{"type":"object"},"description":"Field conditions keyed by filter field ID (a native field name, or cf:<key> for your own fields; GET /filter-fields lists them), each { operator, value } or { operator, values }; a bare value means \"is\". A JSON object in a string also works. An invalid condition answers 400 naming the field","example":{"status":{"operator":"is","values":["active"]}}},{"name":"includeInactive","in":"query","required":false,"schema":{"type":"boolean","default":"false"},"description":"Also list inactive contacts (when status is not given)","example":false},{"name":"includeStats","in":"query","required":false,"schema":{"type":"boolean"},"description":"Also answer how many matching contacts there are by status","example":true},{"name":"isPrimary","in":"query","required":false,"schema":{"type":"boolean"},"description":"With accountId: only the primary contact (true) or only the others (false)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25},"description":"Contacts per page, at most 500","example":25},{"name":"ownerId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only contacts this person owns","example":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"role","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"}},"description":"With accountId: only contacts with any of these roles on the account, e.g. billing","example":["billing"]},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text to find in the name, email or phone; a phone number or email is also looked up in Kazee Contacts","example":"Kamau"},{"name":"sort","in":"query","required":false,"schema":{"type":"object"},"description":"Order as { field, order }: field is firstName, lastName, updatedAt or createdAt (default), order asc or desc (default)","example":{"field":"lastName","order":"asc"}},{"name":"status","in":"query","required":false,"schema":{"type":"array","items":{"type":"string","enum":["active","inactive","suspended"]}},"description":"Only contacts with these statuses (one or several)","example":["active"]},{"name":"subscriptionId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only the contacts of this subscription"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","description":"The email to show (the shared record's when filed under one)"},"phone":{"type":"string","description":"The phone to show (the shared record's when filed under one)"},"addressId":{},"status":{"type":"string","description":"active, invited, inactive or suspended"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}},"description":"Communication preferences: notifications, preferredContactMethod (email, phone or sms), timezone, language"},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array"},"partyId":{"type":"string","description":"The person record in Kazee Contacts the contact is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the contact is filed under one, else first and last name"},"fullName":{"type":"string","description":"Same as displayName"},"owner":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"}}},"address":{},"accounts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"isPrimary":{"type":"boolean"}}},"description":"The accounts it is linked to: { id, name, isPrimary }"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}}},"stats":{"type":"array","items":{"type":"object","properties":{"status":{"type":"string"},"count":{"type":"integer"}}},"description":"Only with includeStats=true: how many contacts match, by status"},"search":{"type":"object","properties":{"source":{"type":"string"}},"description":"Only when you searched: where the matches came from (contacts or cache) and a notice when Kazee Contacts could not be asked"}}},"example":{"success":true,"data":[{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau","owner":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","firstName":"Amina","lastName":"Otieno","email":"amina.otieno@example.com"},"address":null,"accounts":[{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","name":"Baraka Traders Ltd","isPrimary":true}]}],"pagination":{"page":1,"limit":25,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false},"stats":[{"status":"active","count":1}],"search":{"source":"contacts"}}}}},"400":{"description":"A filter names an unknown field or is not valid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.contacts.read"]}],"x-required-scopes":["crm.contacts.read"]},"post":{"tags":["Contacts"],"summary":"Create a contact","description":"Create a contact (a person you deal with), on its own or linked as admin to one or more accounts. The email must not belong to another contact. You become the owner unless you name one. Automations for new contacts run after it is made.\n\n**Permission:** `crm.contacts.create` (Create contacts).","operationId":"createContact","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid","description":"An account to link the contact to","examples":["2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"]},"accountIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Accounts to link the contact to (a JSON array in a string also works)","examples":[["2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"]]},"address":{"type":"object","description":"Not used: the address is not saved by this call"},"customFields":{"type":"object","description":"Your organisation's own fields, by field key","examples":[{"job_title":"Office manager"}]},"email":{"type":"string","format":"email","description":"Email address, saved in lower case; unique among contacts","examples":["wanjiru.kamau@example.com"]},"firstName":{"type":"string","description":"First name","examples":["Wanjiru"]},"lastName":{"type":"string","description":"Last name","examples":["Kamau"]},"notes":{"type":"string","description":"Notes about the person","examples":["Prefers calls after 2 pm"]},"ownerId":{"type":"string","format":"uuid","description":"The person who owns the contact; you when left out","examples":["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]},"phone":{"type":"string","description":"Phone number, up to 32 characters","examples":["+254700000102"]},"preferences":{"type":"object","description":"Communication preferences: { notifications, preferredContactMethod, timezone, language } (a JSON string also works)","examples":[{"preferredContactMethod":"email","language":"en"}]},"status":{"type":"string","enum":["active","invited","inactive","suspended"],"default":"active","description":"Starting status","examples":["active"]}},"required":["email","firstName","lastName"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"contact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","description":"The email to show (the shared record's when filed under one)"},"phone":{"type":"string","description":"The phone to show (the shared record's when filed under one)"},"addressId":{},"status":{"type":"string","description":"active, invited, inactive or suspended"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}},"description":"Communication preferences: notifications, preferredContactMethod (email, phone or sms), timezone, language"},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array"},"partyId":{"type":"string","description":"The person record in Kazee Contacts the contact is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the contact is filed under one, else first and last name"},"fullName":{"type":"string","description":"Same as displayName"}}},"accountRelationships":{"type":"array","items":{"type":"object","properties":{"accountId":{"type":"string"},"relationshipId":{"type":"string","description":"ID of the link between the contact and the account"}}}}}}}},"example":{"success":true,"message":"Contact Wanjiru Kamau created and linked to 1 account(s)","data":{"contact":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau"},"accountRelationships":[{"accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","relationshipId":"05f2406c-9d1e-4a8b-8e3f-7c8d9e0f1a2b"}]}}}}},"400":{"description":"preferences is not valid JSON, or Kazee Contacts refused the new record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"One of the accounts is not in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A contact with that email already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"description":"Kazee Contacts could not be reached to file the new contact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.contacts.create"]}],"x-required-scopes":["crm.contacts.create"]}},"/contacts/{contactId}/with-accounts":{"get":{"tags":["Contacts"],"summary":"Get a contact with its accounts","description":"Get a contact with the accounts it is linked to and the details of each link (roles, primary, status), plus a short count. Only active links unless you ask for all.\n\n**Permission:** `crm.contacts.read` (View contacts).","operationId":"getContactWithAccounts","parameters":[{"name":"contactId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The contact's ID","example":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"},{"name":"includeInactive","in":"query","required":false,"schema":{"type":"string","enum":["true","false"],"default":"false"},"description":"\"true\" to include links that are no longer active","example":"false"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"contact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","description":"The email to show (the shared record's when filed under one)"},"phone":{"type":"string","description":"The phone to show (the shared record's when filed under one)"},"addressId":{},"status":{"type":"string","description":"active, invited, inactive or suspended"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}},"description":"Communication preferences: notifications, preferredContactMethod (email, phone or sms), timezone, language"},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array"},"partyId":{"type":"string","description":"The person record in Kazee Contacts the contact is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the contact is filed under one, else first and last name"},"fullName":{"type":"string","description":"Same as displayName"},"owner":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"}}},"address":{},"accounts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"isPrimary":{"type":"boolean"}}}}}},"accounts":{"type":"object","properties":{"total":{"type":"integer"},"primary":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"accountNumber":{"type":"string"},"status":{"type":"string"},"description":{"type":"string"},"addressId":{"type":"string"},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string"},"currentBalance":{"type":"string"},"originLeadId":{"type":"string"},"walletId":{},"billingContactId":{"type":"string"},"serviceContactId":{"type":"string"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"}}},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{"type":"string"},"cacheDisplayName":{"type":"string"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"description":"The first account it is the primary contact of, or null"},"relationships":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"roles":{"type":"array","items":{"type":"string"},"description":"What the person does for the account: admin, billing, technical, support, readonly or primary"},"isPrimary":{"type":"boolean","description":"True for the account's primary contact (one per account)"},"status":{"type":"string","description":"The link's status, usually active"},"preferences":{"type":"object","properties":{}},"notes":{"type":"string"},"source":{"type":"string","description":"crm when CRM made the link, contacts when it came from Kazee Contacts (another app linked the person)"},"createdBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"account":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the account is filed under one, else the account's own"},"accountNumber":{"type":"string","description":"Your organisation's account number, unique"},"status":{"type":"string","description":"active, inactive, suspended or terminated"},"description":{"type":"string"},"addressId":{"type":"string"},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string","description":"Credit limit, a decimal number in a string"},"currentBalance":{"type":"string","description":"Current balance, a decimal number in a string"},"originLeadId":{"type":"string","description":"The lead the account was converted from, if any"},"walletId":{},"billingContactId":{"type":"string","description":"The contact who gets the bills"},"serviceContactId":{"type":"string","description":"The contact for service matters"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"}}},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array","description":"IDs of this account in other systems, e.g. { source: zoho, externalId }"},"partyId":{"type":"string","description":"The person or company record in Kazee Contacts the account is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}},"summary":{"type":"object","properties":{"totalAccounts":{"type":"integer"},"activeAccounts":{"type":"integer"},"isPrimaryForAnyAccount":{"type":"boolean"}}}}}}}}},"example":{"success":true,"data":{"contact":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau","owner":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","firstName":"Amina","lastName":"Otieno","email":"amina.otieno@example.com"},"address":null,"accounts":[{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","name":"Baraka Traders Ltd","isPrimary":true}]},"accounts":{"total":1,"primary":{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Baraka Traders Ltd","accountNumber":"ACC-000123","status":"active","description":"Hardware shop on Moi Avenue","addressId":"f4e13f6b-8c0d-4f7a-9d2e-6b7c8d9e0f1a","onboardingDate":"2026-10-01T08:30:00.000Z","terminationDate":null,"creditLimit":"50000.00","currentBalance":"0.00","originLeadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","walletId":null,"billingContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","serviceContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notificationPreferences":{"email":true,"sms":true},"tags":["retail"],"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Baraka Traders Ltd","cachePrimaryPhone":"+254700000101","cachePrimaryEmail":"info@baraka-traders.example","cachePartyRevision":3,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"},"relationships":[{"id":"05f2406c-9d1e-4a8b-8e3f-7c8d9e0f1a2b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","roles":["admin","billing"],"isPrimary":true,"status":"active","preferences":{},"notes":"Linked during account creation","source":"crm","createdBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-01T08:30:00.000Z","account":{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Baraka Traders Ltd","accountNumber":"ACC-000123","status":"active","description":"Hardware shop on Moi Avenue","addressId":"f4e13f6b-8c0d-4f7a-9d2e-6b7c8d9e0f1a","onboardingDate":"2026-10-01T08:30:00.000Z","terminationDate":null,"creditLimit":"50000.00","currentBalance":"0.00","originLeadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","walletId":null,"billingContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","serviceContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notificationPreferences":{"email":true,"sms":true},"tags":["retail"],"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Baraka Traders Ltd","cachePrimaryPhone":"+254700000101","cachePrimaryEmail":"info@baraka-traders.example","cachePartyRevision":3,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}}],"summary":{"totalAccounts":1,"activeAccounts":1,"isPrimaryForAnyAccount":true}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No contact with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.contacts.read"]}],"x-required-scopes":["crm.contacts.read"]}},"/contacts/{contactId}":{"get":{"tags":["Contacts"],"summary":"Get a contact","description":"Get one contact with its owner, address and the accounts it is linked to, with the details of each link (roles, primary, status). Lookup custom fields come with the record they point at under `_expanded`.\n\n**Permission:** `crm.contacts.read` (View contacts).","operationId":"getContact","parameters":[{"name":"contactId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The contact's ID","example":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"contact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","description":"The email to show (the shared record's when filed under one)"},"phone":{"type":"string","description":"The phone to show (the shared record's when filed under one)"},"addressId":{},"status":{"type":"string","description":"active, invited, inactive or suspended"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}},"description":"Communication preferences: notifications, preferredContactMethod (email, phone or sms), timezone, language"},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array"},"partyId":{"type":"string","description":"The person record in Kazee Contacts the contact is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the contact is filed under one, else first and last name"},"fullName":{"type":"string","description":"Same as displayName"},"owner":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"}}},"address":{},"accounts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"isPrimary":{"type":"boolean"}}},"description":"The accounts it is linked to, short: { id, name, isPrimary }"}}},"accountRelationships":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"roles":{"type":"array","items":{"type":"string"},"description":"What the person does for the account: admin, billing, technical, support, readonly or primary"},"isPrimary":{"type":"boolean","description":"True for the account's primary contact (one per account)"},"status":{"type":"string","description":"The link's status, usually active"},"preferences":{"type":"object","properties":{}},"notes":{"type":"string"},"source":{"type":"string","description":"crm when CRM made the link, contacts when it came from Kazee Contacts (another app linked the person)"},"createdBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"account":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the account is filed under one, else the account's own"},"accountNumber":{"type":"string","description":"Your organisation's account number, unique"},"status":{"type":"string","description":"active, inactive, suspended or terminated"},"description":{"type":"string"},"addressId":{"type":"string"},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string","description":"Credit limit, a decimal number in a string"},"currentBalance":{"type":"string","description":"Current balance, a decimal number in a string"},"originLeadId":{"type":"string","description":"The lead the account was converted from, if any"},"walletId":{},"billingContactId":{"type":"string","description":"The contact who gets the bills"},"serviceContactId":{"type":"string","description":"The contact for service matters"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"}}},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array","description":"IDs of this account in other systems, e.g. { source: zoho, externalId }"},"partyId":{"type":"string","description":"The person or company record in Kazee Contacts the account is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}}}},"example":{"success":true,"data":{"contact":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau","owner":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","firstName":"Amina","lastName":"Otieno","email":"amina.otieno@example.com"},"address":null,"accounts":[{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","name":"Baraka Traders Ltd","isPrimary":true}]},"accountRelationships":[{"id":"05f2406c-9d1e-4a8b-8e3f-7c8d9e0f1a2b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","roles":["admin","billing"],"isPrimary":true,"status":"active","preferences":{},"notes":"Linked during account creation","source":"crm","createdBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-01T08:30:00.000Z","account":{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Baraka Traders Ltd","accountNumber":"ACC-000123","status":"active","description":"Hardware shop on Moi Avenue","addressId":"f4e13f6b-8c0d-4f7a-9d2e-6b7c8d9e0f1a","onboardingDate":"2026-10-01T08:30:00.000Z","terminationDate":null,"creditLimit":"50000.00","currentBalance":"0.00","originLeadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","walletId":null,"billingContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","serviceContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notificationPreferences":{"email":true,"sms":true},"tags":["retail"],"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Baraka Traders Ltd","cachePrimaryPhone":"+254700000101","cachePrimaryEmail":"info@baraka-traders.example","cachePartyRevision":3,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No contact with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.contacts.read"]}],"x-required-scopes":["crm.contacts.read"]},"put":{"tags":["Contacts"],"summary":"Update a contact","description":"Change a contact's details and, in the same call, which accounts it is linked to. Send only what changes: an email or phone equal to the one shown is no change. The email and phone must not belong to another contact. Accounts can be added and removed one by one or replaced as a whole set (accountIds). Automations for updated contacts run after it.\n\n**Permission:** `crm.contacts.update` (Update contacts).","operationId":"updateContact","parameters":[{"name":"contactId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The contact's ID","example":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid","description":"One more account to link the contact to"},"accountIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"The whole set of accounts the contact should be linked to: missing ones are linked (as admin), the others unlinked. Overrides addAccountIds and removeAccountIds"},"addAccountIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Accounts to link the contact to, as admin","examples":[["3e2b1f4b-6c8d-4f5a-8d0e-4b5c6d7e8f9a"]]},"address":{"type":"object","description":"Not used: the address is not saved by this call"},"customFields":{"type":"object","description":"Your organisation's own fields, by field key; replaces the stored set","examples":[{"job_title":"Office manager"}]},"email":{"type":"string","format":"email","description":"Email address, saved in lower case","examples":["wanjiru.kamau@example.com"]},"firstName":{"type":"string","description":"First name","examples":["Wanjiru"]},"lastName":{"type":"string","description":"Last name","examples":["Kamau"]},"notes":{"type":"string","description":"Notes about the person"},"phone":{"type":"string","description":"Phone number, up to 32 characters","examples":["+254700000102"]},"preferences":{"type":"object","description":"Communication preferences, replacing the stored ones (a JSON string also works)","examples":[{"preferredContactMethod":"sms"}]},"primaryAccountId":{"type":"string","format":"uuid","description":"Make the contact the primary contact of this account (it must be linked to it)","examples":["2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"]},"removeAccountIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Accounts to unlink the contact from"},"status":{"type":"string","enum":["active","invited","inactive","suspended"],"description":"New status, set as given (the status call checks the allowed moves; this one does not)","examples":["active"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","description":"The email to show (the shared record's when filed under one)"},"phone":{"type":"string","description":"The phone to show (the shared record's when filed under one)"},"addressId":{},"status":{"type":"string","description":"active, invited, inactive or suspended"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}},"description":"Communication preferences: notifications, preferredContactMethod (email, phone or sms), timezone, language"},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array"},"partyId":{"type":"string","description":"The person record in Kazee Contacts the contact is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the contact is filed under one, else first and last name"},"fullName":{"type":"string","description":"Same as displayName"}}},"accountChanges":{"type":"object","properties":{"changed":{"type":"boolean"},"changes":{"type":"array","items":{"type":"string"},"description":"What happened to the account links, in words"}}}}},"example":{"success":true,"message":"Contact updated successfully with account relationship changes","data":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau"},"accountChanges":{"changed":true,"changes":["Added to account Baraka Traders Ltd"]}}}}},"400":{"description":"A JSON field is not valid JSON, or Kazee Contacts refused the change","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No contact with that ID, an account to add does not exist, or the contact is not linked to primaryAccountId","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Another contact already has that email or phone","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"description":"Kazee Contacts could not be reached to save the change","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.contacts.update"]}],"x-required-scopes":["crm.contacts.update"]},"delete":{"tags":["Contacts"],"summary":"Delete a contact","description":"Delete a contact: it is unlinked from every account (and stops being their billing or service contact) and no longer listed. It is kept, marked deleted, for the history.\n\n**Permission:** `crm.contacts.delete` (Delete contacts).","operationId":"deleteContact","parameters":[{"name":"contactId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The contact's ID","example":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"},{"name":"deletedBy","in":"query","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID of the person deleting it (for the history)","example":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"},{"name":"reason","in":"query","required":false,"schema":{"type":"string"},"description":"Why, kept in the history","example":"Duplicate of another contact"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"deletedAt":{"type":"string","format":"date-time","description":"When it was deleted"}}}}},"example":{"success":true,"message":"Contact Wanjiru Kamau deleted","data":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","deletedAt":"2026-10-21T09:00:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No contact with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.contacts.delete"]}],"x-required-scopes":["crm.contacts.delete"]}},"/subscriptions":{"get":{"tags":["Subscriptions"],"summary":"List subscriptions","description":"Lists your organisation's subscriptions (service lines), newest first, with each line's account, plan, owner and products. Passwords answer masked. billingCycle filters only the page already read, so a page can hold fewer lines than limit.\n\n**Permission:** `crm.subscriptions.read` (View subscriptions).","operationId":"listSubscriptions","parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this account's lines","example":"a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c"},{"name":"billingCycle","in":"query","required":false,"schema":{"type":"string","enum":["monthly","quarterly","annually"]},"description":"Only lines billed on this cycle (a list also works); applied to the page after it is read","example":"monthly"},{"name":"contactId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only lines this contact is linked to, directly or through their account","example":"e0b6a2d5-7f4c-4dbe-8a91-5f8c1d4e7a0b"},{"name":"includeStats","in":"query","required":false,"schema":{"type":"boolean"},"description":"Also answer counts per status in stats","example":true},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25},"description":"Lines per page, up to 500","example":25},{"name":"ownerId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only lines this person owns","example":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text found in the line's name or PPPoE username","example":"wanjiku"},{"name":"servicePlanId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only lines on this service plan","example":"5e8b1d3f-7a2c-4c6e-9b4d-8f0a1c3e5d7b"},{"name":"sort","in":"query","required":false,"schema":{"type":"object","properties":{"field":{"type":"string","enum":["createdAt","nextBillingAt","updatedAt"]},"order":{"type":"string","enum":["asc","desc"]}}},"description":"Order: field is createdAt (default), nextBillingAt or updatedAt; order is desc (default) or asc","example":{"field":"nextBillingAt","order":"asc"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["pending_installation","active","disconnected","terminated"]},"description":"Only lines in this status (a list also works)","example":"active"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"pppoe":{"type":"string"},"accountId":{"type":"string"},"servicePlanId":{"type":"string"},"status":{"type":"string","description":"pending_installation, active, disconnected or terminated"},"startDate":{"type":"string","format":"date-time"},"billingCycle":{"type":"string"},"lastBilledAt":{"type":"string","format":"date-time"},"nextBillingAt":{"type":"string","format":"date-time"},"billDate":{"type":"string","format":"date-time"},"renewalDate":{"type":"string","format":"date-time"},"billLeadDays":{"type":"integer"},"disconnectionDate":{},"terminationDate":{},"lastPaymentDate":{"type":"string","format":"date-time"},"ownerId":{"type":"string"},"password":{"type":"object","properties":{"secret":{"type":"boolean"},"set":{"type":"boolean"}},"description":"Masked as { \"secret\": true, \"set\": true } when a password is stored; see Reveal a subscription password"},"customFields":{"type":"object","properties":{"sm_password":{"type":"object","properties":{"secret":{"type":"boolean"},"set":{"type":"boolean"}}},"router_model":{"type":"string"}}},"externalIds":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"externalId":{"type":"string"}}}},"version":{"type":"integer"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"account":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"accountNumber":{"type":"string"}}},"servicePlan":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"productId":{"type":"string"}}},"owner":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"}}},"products":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"subscriptionId":{"type":"string"},"productId":{"type":"string"},"quantity":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"product":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"sku":{"type":"string"}}}}}},"validNextStatuses":{"type":"array","items":{"type":"string"},"description":"Statuses the line may move to next"},"previousStatuses":{"type":"array","items":{"type":"string"},"description":"Statuses the line may have come from"},"pipeline":{"type":"string"},"currentStatus":{"type":"string"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}}},"stats":{"type":"object","properties":{"totalCount":{"type":"integer"},"activeCount":{"type":"integer"},"terminatedCount":{"type":"integer"},"expiredCount":{"type":"integer"},"disconnectedCount":{"type":"integer"},"inactiveCount":{"type":"integer"}},"description":"Only with includeStats=true: counts per status over the same filter (inactiveCount counts pending_installation)"}}},"example":{"success":true,"data":[{"id":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","name":"Home fibre 20 Mbps","pppoe":"jane.wanjiku.home","accountId":"a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c","servicePlanId":"5e8b1d3f-7a2c-4c6e-9b4d-8f0a1c3e5d7b","status":"active","startDate":"2026-09-01T00:00:00.000Z","billingCycle":"monthly","lastBilledAt":"2026-10-01T00:00:00.000Z","nextBillingAt":"2026-11-01T00:00:00.000Z","billDate":"2026-10-22T00:00:00.000Z","renewalDate":"2026-11-01T00:00:00.000Z","billLeadDays":10,"disconnectionDate":null,"terminationDate":null,"lastPaymentDate":"2026-10-02T10:15:00.000Z","ownerId":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d","password":{"secret":true,"set":true},"customFields":{"sm_password":{"secret":true,"set":true},"router_model":"HG8145V5"},"externalIds":[{"source":"zoho","externalId":"4815000000123457"}],"version":3,"deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z","account":{"id":"a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c","name":"Jane Wanjiku","accountNumber":"ACC-00042"},"servicePlan":{"id":"5e8b1d3f-7a2c-4c6e-9b4d-8f0a1c3e5d7b","name":"Fibre 20 Mbps","productId":"b7e3d9a2-4c1f-4a8b-9d6e-2c5f8a1b4d7e"},"owner":{"id":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d","firstName":"Peter","lastName":"Otieno"},"products":[{"id":"d9a5f1c4-6e3b-4cad-9f80-4e7b0c3d6f9a","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","subscriptionId":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","productId":"b7e3d9a2-4c1f-4a8b-9d6e-2c5f8a1b4d7e","quantity":"1.00","createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z","product":{"id":"b7e3d9a2-4c1f-4a8b-9d6e-2c5f8a1b4d7e","name":"Fibre 20 Mbps","sku":"FIB-20"}}],"validNextStatuses":["disconnected","terminated"],"previousStatuses":["pending_installation","disconnected"],"pipeline":"subscription","currentStatus":"active"}],"pagination":{"page":1,"limit":25,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false},"stats":{"totalCount":1,"activeCount":1,"terminatedCount":0,"expiredCount":0,"disconnectedCount":0,"inactiveCount":0}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.subscriptions.read"]}],"x-required-scopes":["crm.subscriptions.read"]}},"/subscriptions/plans":{"get":{"tags":["Products & plans"],"summary":"List plans","description":"Lists your organisation's service plans in name order, a page at a time, each with the product it belongs to. Narrow it by a search on the name, status, service type, product, connection type or field conditions; includeStats adds counts by status and connection type.\n\n**Permission:** `crm.plans.read` (View plans).","operationId":"getPlans_2","parameters":[{"name":"connectionType","in":"query","required":false,"schema":{"type":"array","items":{"type":"string","enum":["Fiber","Wireless"]}},"description":"Only plans offered on any of these connection types (one value or a list)","example":["Fiber"]},{"name":"fieldFilters","in":"query","required":false,"schema":{"type":"object"},"description":"Older form: conditions on built-in fields, keyed by name","example":{"status":["active"]}},{"name":"filters","in":"query","required":false,"schema":{"type":"object"},"description":"Field conditions keyed by filter field ID (a native field name, or cf:<key> for your own fields; GET /filter-fields lists them), each { operator, value } or { operator, values }; a bare value means \"is\". A JSON object in a string also works. An invalid condition answers 400 naming the field","example":{"status":{"operator":"is","values":["active"]}}},{"name":"includeStats","in":"query","required":false,"schema":{"type":"boolean"},"description":"true to add stats: counts of the matching plans","example":true},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25},"description":"Plans per page, 1 to 500","example":25},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"product","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Older name for productId"},{"name":"productId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only plans of this product","example":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f"},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text to look for in the plan name","example":"Fibre"},{"name":"serviceType","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Older name for serviceTypeId"},{"name":"serviceTypeId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only plans of this service type","example":"e3d02e5a-7b9c-4e6f-9c1d-5a6b7c8d9e0f"},{"name":"sort","in":"query","required":false,"schema":{"type":"object"},"description":"Accepted but not used yet: plans always come in name order"},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["active","inactive","suspended"]},"description":"Only plans in this status","example":"active"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"limitStr":{"type":"string"},"burstMaxDown":{"description":"Burst settings (burstMax*, burstThresh*, burstTime*): the router's burst speed, threshold and time, or null"},"burstMaxUp":{},"burstThreshDown":{},"burstThreshUp":{},"burstTimeDown":{},"burstTimeUp":{},"connectionType":{"type":"array","items":{"type":"string"},"description":"Fiber, Wireless or both"},"installationFee":{"type":"string","description":"Installation fee, a decimal in a string"},"maxLimitDown":{"type":"integer","description":"Download speed limit, in kbit/s"},"maxLimitUp":{"type":"integer","description":"Upload speed limit, in kbit/s"},"rateLimit":{"type":"string","description":"The router's rate-limit text, e.g. 50M/25M"},"serviceTypeId":{"type":"string"},"statusLevelId":{},"status":{"type":"string","description":"active, inactive or suspended"},"productId":{"type":"string","description":"The product this plan is a speed option of"},"booksItemId":{"type":"string","description":"The Kazee Books item the plan is sold as (the first month of its quotes), or null"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"product":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"sku":{"type":"string"},"price":{"type":"string"}},"description":"The product the plan belongs to: ID, name, SKU and price"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}}},"stats":{"type":"object","properties":{"totalPlans":{"type":"integer"},"activePlans":{"type":"integer"},"inactivePlans":{"type":"integer"},"suspendedPlans":{"type":"integer"},"fiberPlans":{"type":"integer"},"wirelessPlans":{"type":"integer"}},"description":"Only with includeStats: how many matching plans there are, by status and connection type"}}},"example":{"success":true,"data":[{"id":"9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Home Fibre 50 Mbps","limitStr":"","burstMaxDown":null,"burstMaxUp":null,"burstThreshDown":null,"burstThreshUp":null,"burstTimeDown":null,"burstTimeUp":null,"connectionType":["Fiber"],"installationFee":"2000.00","maxLimitDown":51200,"maxLimitUp":25600,"rateLimit":"50M/25M","serviceTypeId":"e3d02e5a-7b9c-4e6f-9c1d-5a6b7c8d9e0f","statusLevelId":null,"status":"active","productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","booksItemId":"itm_8c1d4e5f60718293a4b5c6d7e8f9a0b1","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","product":{"id":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","name":"Home Fibre","sku":"HF-50","price":"4500.00"}}],"pagination":{"page":1,"limit":25,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false},"stats":{"totalPlans":1,"activePlans":1,"inactivePlans":0,"suspendedPlans":0,"fiberPlans":1,"wirelessPlans":0}}}}},"400":{"description":"A field condition is not valid (the message names the field)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.plans.read"]}],"x-required-scopes":["crm.plans.read"]}},"/subscriptions/products":{"get":{"tags":["Products & plans"],"summary":"List products for subscriptions","description":"Lists your organisation's products, by name A to Z unless you sort otherwise, to pick from when setting up a subscription. Needs the subscriptions read permission rather than the products one.\n\n**Permission:** `crm.subscriptions.read` (View subscriptions).","operationId":"get_products","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25},"description":"Products per page, up to 500","example":25},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text found in the product's name or SKU","example":"fibre"},{"name":"sort","in":"query","required":false,"schema":{"type":"object","properties":{"field":{"type":"string","enum":["name","price","createdAt"]},"order":{"type":"string","enum":["asc","desc"]}}},"description":"Order: field is name (default), price or createdAt; order is asc (default) or desc","example":{"field":"price","order":"desc"}},{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"Only products in this status, e.g. active (a list also works)","example":"active"},{"name":"taxGroupId","in":"query","required":false,"schema":{"type":"string"},"description":"Accepted but not applied yet: it does not filter the list","example":"tax_vat16"},{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["service","item"]},"description":"Only products of this type","example":"service"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","description":"service or item"},"price":{"type":"string","description":"Price as text with two decimals"},"sku":{"type":"string"},"status":{"type":"string"},"description":{"type":"string"},"taxId":{"type":"string"},"unit":{"type":"string"},"recurringPeriod":{"type":"string","description":"The period it bills per; null for a one-off"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}}}}},"example":{"success":true,"data":[{"id":"b7e3d9a2-4c1f-4a8b-9d6e-2c5f8a1b4d7e","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","name":"Fibre 20 Mbps","type":"service","price":"3500.00","sku":"FIB-20","status":"active","description":"Home fibre internet, 20 Mbps down and up","taxId":"tax_vat16","unit":"month","recurringPeriod":"monthly","customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}],"pagination":{"page":1,"limit":25,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.subscriptions.read"]}],"x-required-scopes":["crm.subscriptions.read"]}},"/subscriptions/network-commands":{"get":{"tags":["Subscriptions"],"summary":"List what was sent to the network","description":"Answers the disconnections and reconnections CRM put to the network, newest first: for one line, in one state (state=failed shows what the network has not taken), or both. Empty for an organisation whose network gateway was never on.\n\n**Permission:** `crm.subscriptions.read` (View subscriptions).","operationId":"listLineNetworkCommands","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer"},"description":"At most this many, up to 200. Default 50","example":50},{"name":"state","in":"query","required":false,"schema":{"type":"string","enum":["shadow","pending","applied","partial","failed","cancelled","held"]},"description":"Only commands in this state","example":"failed"},{"name":"subscriptionId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this line","example":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"commands":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"subscriptionId":{"type":"string"},"lineName":{"type":"string","description":"The name the line goes by on the network, else its own name"},"action":{"type":"string"},"mode":{"type":"string"},"state":{"type":"string","description":"held: a plan change kept until the line is back on, nothing called. shadow: recorded, nothing called. pending: still trying. applied: every target said yes. partial: a decisive target said yes, another did not. failed: the network does not have it. cancelled: overtaken before it was sent"},"networkApplied":{"type":"boolean","description":"Whether the network really has it, whatever the line's status says"},"source":{"type":"string"},"requestId":{"type":"string"},"reason":{"type":"string"},"attempts":{"type":"integer"},"nextAttemptAt":{},"lastError":{"type":"string"},"appliedAt":{},"requests":{"type":"array","items":{"type":"object","properties":{"target":{"type":"string"},"decisive":{"type":"boolean"},"method":{"type":"string"},"url":{"type":"string"},"headers":{"type":"object","properties":{"Content-Type":{"type":"string"}}},"body":{"type":"object","properties":{"name":{"type":"string"},"api_key":{"type":"string"}}}}},"description":"Each request exactly as sent or as it would be sent, with the credential masked"},"results":{"type":"object","properties":{"core":{"type":"object","properties":{"ok":{"type":"boolean"},"status":{"type":"integer"},"answer":{"type":"string"},"error":{"type":"string"},"at":{"type":"string","format":"date-time"},"durationMs":{"type":"integer"}}}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}},"example":{"success":true,"commands":[{"id":"0a9f3c1e-6b7d-4e2f-8a1b-2c3d4e5f6a7b","subscriptionId":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","lineName":"line0001234","action":"disconnect","mode":"live","state":"failed","networkApplied":false,"source":"approve_list","requestId":"1b2c3d4e-5f6a-4b7c-8d9e-0f1a2b3c4d5e","reason":"Non-payment","attempts":8,"nextAttemptAt":null,"lastError":"core: HTTP 503: unavailable","appliedAt":null,"requests":[{"target":"core","decisive":true,"method":"POST","url":"https://network.example.com/api/line/off","headers":{"Content-Type":"application/json"},"body":{"name":"line0001234","api_key":"***"}}],"results":{"core":{"ok":false,"status":503,"answer":"unavailable","error":"HTTP 503: unavailable","at":"2026-11-04T17:00:02.000Z","durationMs":412}},"createdAt":"2026-11-04T09:00:00.000Z","updatedAt":"2026-11-04T17:00:02.000Z"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.subscriptions.read"]}],"x-required-scopes":["crm.subscriptions.read"]}},"/subscriptions/provision-commands":{"get":{"tags":["Subscriptions"],"summary":"List the new lines sent to the network","description":"Answers the new lines CRM asked the network to make, newest first: for one line, one deal, in one state (state=failed shows what the network has not taken), or any of these together. Empty for an organisation whose line provisioning was never on.\n\n**Permission:** `crm.subscriptions.read` (View subscriptions).","operationId":"listLineProvisionCommands","parameters":[{"name":"dealId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this deal","example":"5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"},"description":"At most this many, up to 200. Default 50","example":50},{"name":"state","in":"query","required":false,"schema":{"type":"string","enum":["shadow","pending","applied","partial","failed","cancelled"]},"description":"Only commands in this state","example":"failed"},{"name":"subscriptionId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this line","example":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"commands":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"subscriptionId":{"type":"string"},"dealId":{"type":"string"},"lineName":{"type":"string","description":"The name the line goes by on the network, else its own name"},"mode":{"type":"string"},"state":{"type":"string","description":"shadow: recorded, nothing called. pending: still trying. applied: every target said yes. partial: a decisive target said yes, another did not. failed: the network does not have it. cancelled: overtaken before it was sent"},"networkApplied":{"type":"boolean","description":"Whether the network really has the line, whatever the line's status says"},"source":{"type":"string"},"attempts":{"type":"integer"},"nextAttemptAt":{},"lastError":{"type":"string"},"appliedAt":{},"requests":{"type":"array","items":{"type":"object","properties":{"target":{"type":"string"},"decisive":{"type":"boolean"},"method":{"type":"string"},"url":{"type":"string"},"headers":{"type":"object","properties":{"Content-Type":{"type":"string"}}},"body":{"type":"object","properties":{"name":{"type":"string"},"pw":{"type":"string"},"plan":{"type":"string"},"api_key":{"type":"string"}}}}},"description":"Each request exactly as sent or as it would be sent, with the credential and the password masked"},"results":{"type":"object","properties":{"core":{"type":"object","properties":{"ok":{"type":"boolean"},"status":{"type":"integer"},"answer":{"type":"string"},"error":{"type":"string"},"at":{"type":"string","format":"date-time"},"durationMs":{"type":"integer"}}}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}},"example":{"success":true,"commands":[{"id":"3c7d9e1f-2a4b-4c6d-8e0f-1a2b3c4d5e6f","subscriptionId":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","dealId":"5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9","lineName":"line0001234","mode":"live","state":"failed","networkApplied":false,"source":"automation","attempts":8,"nextAttemptAt":null,"lastError":"core: HTTP 503: unavailable","appliedAt":null,"requests":[{"target":"core","decisive":true,"method":"POST","url":"https://network.example.com/api/line","headers":{"Content-Type":"application/json"},"body":{"name":"line0001234","pw":"***","plan":"Home 10","api_key":"***"}}],"results":{"core":{"ok":false,"status":503,"answer":"unavailable","error":"HTTP 503: unavailable","at":"2026-11-04T17:00:02.000Z","durationMs":412}},"createdAt":"2026-11-04T09:00:00.000Z","updatedAt":"2026-11-04T17:00:02.000Z"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.subscriptions.read"]}],"x-required-scopes":["crm.subscriptions.read"]}},"/subscriptions/{subscriptionId}":{"get":{"tags":["Subscriptions"],"summary":"Get a subscription","description":"Answers one subscription (service line) with its account and the account's active people, its plan with the plan's product and service type, where it sits in the subscription pipeline, and its reminders from the last 30 days. Passwords answer masked. A line that does not exist still answers 200, with success false and error.code \"SUBSCRIPTION_DETAILS_ERROR\".\n\n**Permission:** `crm.subscriptions.read` (View subscriptions).","operationId":"getSubscriptionDetails","parameters":[{"name":"subscriptionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The subscription's ID","example":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"subscription":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"pppoe":{"type":"string"},"accountId":{"type":"string"},"servicePlanId":{"type":"string"},"status":{"type":"string"},"startDate":{"type":"string","format":"date-time"},"billingCycle":{"type":"string"},"lastBilledAt":{"type":"string","format":"date-time"},"nextBillingAt":{"type":"string","format":"date-time"},"billDate":{"type":"string","format":"date-time"},"renewalDate":{"type":"string","format":"date-time"},"billLeadDays":{"type":"integer"},"disconnectionDate":{},"terminationDate":{},"lastPaymentDate":{"type":"string","format":"date-time"},"ownerId":{"type":"string"},"password":{"type":"object","properties":{"secret":{"type":"boolean"},"set":{"type":"boolean"}},"description":"Masked as { \"secret\": true, \"set\": true } when a password is stored"},"customFields":{"type":"object","properties":{"sm_password":{"type":"object","properties":{"secret":{"type":"boolean"},"set":{"type":"boolean"}}},"router_model":{"type":"string"}}},"externalIds":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"externalId":{"type":"string"}}}},"version":{"type":"integer"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"account":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"accountNumber":{"type":"string"},"status":{"type":"string"},"description":{},"addressId":{},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string"},"currentBalance":{"type":"string"},"originLeadId":{},"walletId":{},"billingContactId":{"type":"string"},"serviceContactId":{"type":"string"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"}}},"tags":{"type":"array"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{},"cacheDisplayName":{},"cachePrimaryPhone":{},"cachePrimaryEmail":{},"cachePartyRevision":{},"cacheUpdatedAt":{},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"contactRelationships":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"roles":{"type":"array","items":{"type":"string"}},"isPrimary":{"type":"boolean"},"status":{"type":"string"},"preferences":{"type":"object","properties":{}},"notes":{},"source":{"type":"string"},"createdBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"contact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{}},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{},"cacheDisplayName":{},"cachePrimaryPhone":{},"cachePrimaryEmail":{},"cachePartyRevision":{},"cacheUpdatedAt":{},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}},"servicePlan":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"limitStr":{"type":"string"},"burstMaxDown":{},"burstMaxUp":{},"burstThreshDown":{},"burstThreshUp":{},"burstTimeDown":{},"burstTimeUp":{},"connectionType":{"type":"array","items":{"type":"string"}},"installationFee":{"type":"string"},"maxLimitDown":{"type":"integer"},"maxLimitUp":{"type":"integer"},"rateLimit":{"type":"string"},"serviceTypeId":{"type":"string"},"statusLevelId":{},"status":{"type":"string"},"productId":{"type":"string"},"booksItemId":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"product":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"price":{"type":"string"},"sku":{"type":"string"},"status":{"type":"string"},"description":{"type":"string"},"taxId":{"type":"string"},"unit":{"type":"string"},"recurringPeriod":{"type":"string"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"serviceType":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}}}}},"radiusStatus":{"type":"string","description":"Always unknown here; the RADIUS status is synced separately"},"validNextStatuses":{"type":"array","items":{"type":"string"}},"previousStatuses":{"type":"array","items":{"type":"string"}},"pipeline":{"type":"string"},"currentStatus":{"type":"string"}}},"recentReminders":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"subscriptionId":{"type":"string"},"reminderTypeId":{"type":"string"},"priority":{"type":"string"},"status":{"type":"string"},"title":{"type":"string"},"message":{"type":"string"},"templateData":{"type":"object","properties":{"accountName":{"type":"string"},"amount":{"type":"string"}}},"scheduledAt":{"type":"string","format":"date-time"},"sentAt":{"type":"string","format":"date-time"},"acknowledgedAt":{},"acknowledgedBy":{},"expiresAt":{},"metadata":{"type":"object","properties":{"channels":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"address":{"type":"string"},"status":{"type":"string"},"retryCount":{"type":"integer"}}}},"contactId":{"type":"string"},"preferredChannel":{"type":"string"}}},"escalationLevel":{"type":"integer"},"maxEscalations":{"type":"integer"},"escalationInterval":{"type":"integer"},"nextEscalationAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"description":"Up to 10 of the line's latest reminders, only those from the last 30 days"},"network":{"type":"object","properties":{"id":{"type":"string"},"subscriptionId":{"type":"string"},"lineName":{"type":"string","description":"The name the line goes by on the network, else its own name"},"action":{"type":"string"},"mode":{"type":"string"},"state":{"type":"string"},"networkApplied":{"type":"boolean"},"source":{"type":"string"},"requestId":{"type":"string"},"reason":{"type":"string"},"attempts":{"type":"integer"},"nextAttemptAt":{},"lastError":{"type":"string"},"appliedAt":{},"requests":{"type":"array","items":{"type":"object","properties":{"target":{"type":"string"},"decisive":{"type":"boolean"},"method":{"type":"string"},"url":{"type":"string"},"headers":{"type":"object","properties":{"Content-Type":{"type":"string"}}},"body":{"type":"object","properties":{"name":{"type":"string"},"api_key":{"type":"string"}}}}}},"results":{"type":"object","properties":{"core":{"type":"object","properties":{"ok":{"type":"boolean"},"status":{"type":"integer"},"answer":{"type":"string"},"error":{"type":"string"},"at":{"type":"string","format":"date-time"},"durationMs":{"type":"integer"}}}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"description":"The line's newest disconnection, reconnection or plan change on the network: state and networkApplied say whether the network really has what the status says. A plan change kept until the line is back (state held) answers only when the line has no other command. Null when CRM never put one to the network"}}}}},"example":{"success":true,"data":{"subscription":{"id":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","name":"Home fibre 20 Mbps","pppoe":"jane.wanjiku.home","accountId":"a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c","servicePlanId":"5e8b1d3f-7a2c-4c6e-9b4d-8f0a1c3e5d7b","status":"active","startDate":"2026-09-01T00:00:00.000Z","billingCycle":"monthly","lastBilledAt":"2026-10-01T00:00:00.000Z","nextBillingAt":"2026-11-01T00:00:00.000Z","billDate":"2026-10-22T00:00:00.000Z","renewalDate":"2026-11-01T00:00:00.000Z","billLeadDays":10,"disconnectionDate":null,"terminationDate":null,"lastPaymentDate":"2026-10-02T10:15:00.000Z","ownerId":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d","password":{"secret":true,"set":true},"customFields":{"sm_password":{"secret":true,"set":true},"router_model":"HG8145V5"},"externalIds":[{"source":"zoho","externalId":"4815000000123457"}],"version":3,"deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z","account":{"id":"a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","name":"Jane Wanjiku","accountNumber":"ACC-00042","status":"active","description":null,"addressId":null,"onboardingDate":"2026-09-01T08:00:00.000Z","terminationDate":null,"creditLimit":"0.00","currentBalance":"0.00","originLeadId":null,"walletId":null,"billingContactId":"e0b6a2d5-7f4c-4dbe-8a91-5f8c1d4e7a0b","serviceContactId":"e0b6a2d5-7f4c-4dbe-8a91-5f8c1d4e7a0b","ownerId":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d","notificationPreferences":{"email":true,"sms":true},"tags":[],"customFields":{},"externalIds":[],"partyId":null,"cacheDisplayName":null,"cachePrimaryPhone":null,"cachePrimaryEmail":null,"cachePartyRevision":null,"cacheUpdatedAt":null,"cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z","contactRelationships":[{"id":"f1c7b3e6-8a5d-4ecf-9ba2-6a9d2e5f8b1c","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","accountId":"a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c","contactId":"e0b6a2d5-7f4c-4dbe-8a91-5f8c1d4e7a0b","roles":["billing","technical"],"isPrimary":true,"status":"active","preferences":{},"notes":null,"source":"crm","createdBy":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d","createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z","contact":{"id":"e0b6a2d5-7f4c-4dbe-8a91-5f8c1d4e7a0b","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","firstName":"Jane","lastName":"Wanjiku","email":"jane.wanjiku@example.com","phone":"+254700000001","addressId":null,"status":"active","preferences":{},"ownerId":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d","notes":null,"customFields":{},"externalIds":[],"partyId":null,"cacheDisplayName":null,"cachePrimaryPhone":null,"cachePrimaryEmail":null,"cachePartyRevision":null,"cacheUpdatedAt":null,"cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}]},"servicePlan":{"id":"5e8b1d3f-7a2c-4c6e-9b4d-8f0a1c3e5d7b","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","name":"Fibre 20 Mbps","limitStr":"20M/20M","burstMaxDown":null,"burstMaxUp":null,"burstThreshDown":null,"burstThreshUp":null,"burstTimeDown":null,"burstTimeUp":null,"connectionType":["fibre"],"installationFee":"2000.00","maxLimitDown":20,"maxLimitUp":20,"rateLimit":"20M/20M","serviceTypeId":"c8f4e0b3-5d2a-4b9c-8e7f-3d6a9b2c5e8f","statusLevelId":null,"status":"active","productId":"b7e3d9a2-4c1f-4a8b-9d6e-2c5f8a1b4d7e","booksItemId":"itm_01JABCDEF0123456789XYZ","deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z","product":{"id":"b7e3d9a2-4c1f-4a8b-9d6e-2c5f8a1b4d7e","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","name":"Fibre 20 Mbps","type":"service","price":"3500.00","sku":"FIB-20","status":"active","description":"Home fibre internet, 20 Mbps down and up","taxId":"tax_vat16","unit":"month","recurringPeriod":"monthly","customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"},"serviceType":{"name":"Home fibre","description":"Fibre internet for homes"}},"radiusStatus":"unknown","validNextStatuses":["disconnected","terminated"],"previousStatuses":["pending_installation","disconnected"],"pipeline":"subscription","currentStatus":"active"},"recentReminders":[{"id":"4e6b2a8d-3f0c-4d5e-8af7-1f4c7d0e3a6b","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","subscriptionId":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","reminderTypeId":"3d5a1f7c-2e9b-4c4d-9fe6-0e3b6c9d2f5a","priority":"medium","status":"sent","title":"Payment due for Jane Wanjiku","message":"Hello Jane Wanjiku, your bill of KES 3,500 is due on 1 November.","templateData":{"accountName":"Jane Wanjiku","amount":"KES 3,500"},"scheduledAt":"2026-10-20T09:00:00.000Z","sentAt":"2026-10-20T09:00:05.000Z","acknowledgedAt":null,"acknowledgedBy":null,"expiresAt":null,"metadata":{"channels":[{"type":"email","address":"jane.wanjiku@example.com","status":"sent","retryCount":0}],"contactId":"e0b6a2d5-7f4c-4dbe-8a91-5f8c1d4e7a0b","preferredChannel":"email"},"escalationLevel":0,"maxEscalations":3,"escalationInterval":24,"nextEscalationAt":null,"deletedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:00:05.000Z"}],"network":{"id":"0a9f3c1e-6b7d-4e2f-8a1b-2c3d4e5f6a7b","subscriptionId":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","lineName":"line0001234","action":"disconnect","mode":"live","state":"failed","networkApplied":false,"source":"approve_list","requestId":"1b2c3d4e-5f6a-4b7c-8d9e-0f1a2b3c4d5e","reason":"Non-payment","attempts":8,"nextAttemptAt":null,"lastError":"core: HTTP 503: unavailable","appliedAt":null,"requests":[{"target":"core","decisive":true,"method":"POST","url":"https://network.example.com/api/line/off","headers":{"Content-Type":"application/json"},"body":{"name":"line0001234","api_key":"***"}}],"results":{"core":{"ok":false,"status":503,"answer":"unavailable","error":"HTTP 503: unavailable","at":"2026-11-04T17:00:02.000Z","durationMs":412}},"createdAt":"2026-11-04T09:00:00.000Z","updatedAt":"2026-11-04T17:00:02.000Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.subscriptions.read"]}],"x-required-scopes":["crm.subscriptions.read"]}},"/subscriptions/{subscriptionId}/reminders":{"get":{"tags":["Reminders"],"summary":"List a subscription's reminders","description":"Lists the reminders sent or due for one subscription, earliest scheduled first. Filters on a list of priorities and on category apply to the page after it is read, so a page can hold fewer reminders than limit.\n\n**Permission:** `crm.subscriptions.read` (View subscriptions).","operationId":"getRemindersPerSubscription","parameters":[{"name":"subscriptionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The subscription's ID","example":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e"},{"name":"category","in":"query","required":false,"schema":{"type":"string","enum":["payment_due","payment_overdue","low_balance","subscription_expiring","service_suspended","service_disconnected","bill_generated","payment_failed","custom","welcome","service_activated","service_reactivated","service_terminated","plan_change"]},"description":"Only reminders whose template is in this category (a list also works)","example":"payment_due"},{"name":"channelType","in":"query","required":false,"schema":{"type":"string","enum":["email","sms","push","in_app"]},"description":"Accepted but not applied yet: it does not filter the list","example":"email"},{"name":"includeStats","in":"query","required":false,"schema":{"type":"boolean"},"description":"Also answer counts per status in stats","example":true},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25},"description":"Reminders per page, up to 500","example":25},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"priority","in":"query","required":false,"schema":{"type":"string","enum":["low","medium","high","urgent"]},"description":"Only reminders of this priority (a list also works)","example":"high"},{"name":"reminderTypeId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only reminders made from this reminder template","example":"3d5a1f7c-2e9b-4c4d-9fe6-0e3b6c9d2f5a"},{"name":"sort","in":"query","required":false,"schema":{"type":"object"},"description":"Accepted but not applied: reminders always come earliest scheduled first","example":{"field":"scheduledAt","order":"asc"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["pending","scheduled","sent","delivered","failed","acknowledged","cancelled"]},"description":"Only reminders in this status (a list also works)","example":"sent"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"subscriptionId":{"type":"string"},"reminderTypeId":{"type":"string"},"priority":{"type":"string"},"status":{"type":"string","description":"pending, scheduled, sent, delivered, failed, acknowledged or cancelled"},"title":{"type":"string"},"message":{"type":"string"},"templateData":{"type":"object","properties":{"accountName":{"type":"string"},"amount":{"type":"string"}}},"scheduledAt":{"type":"string","format":"date-time"},"sentAt":{"type":"string","format":"date-time"},"acknowledgedAt":{},"acknowledgedBy":{},"expiresAt":{},"metadata":{"type":"object","properties":{"channels":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"address":{"type":"string"},"status":{"type":"string"},"retryCount":{"type":"integer"}}},"description":"Where the reminder was sent and how that went (email address, status)"},"contactId":{"type":"string"},"preferredChannel":{"type":"string"}}},"escalationLevel":{"type":"integer"},"maxEscalations":{"type":"integer"},"escalationInterval":{"type":"integer"},"nextEscalationAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}}},"stats":{"type":"object","properties":{"totalCount":{"type":"integer"},"pendingCount":{"type":"integer"},"sentCount":{"type":"integer"},"acknowledgedCount":{"type":"integer"},"failedCount":{"type":"integer"},"typeBreakdown":{"type":"array"}},"description":"Only with includeStats=true: counts over all the line's reminders matching status, priority and type (typeBreakdown is always empty)"}}},"example":{"success":true,"data":[{"id":"4e6b2a8d-3f0c-4d5e-8af7-1f4c7d0e3a6b","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","subscriptionId":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","reminderTypeId":"3d5a1f7c-2e9b-4c4d-9fe6-0e3b6c9d2f5a","priority":"medium","status":"sent","title":"Payment due for Jane Wanjiku","message":"Hello Jane Wanjiku, your bill of KES 3,500 is due on 1 November.","templateData":{"accountName":"Jane Wanjiku","amount":"KES 3,500"},"scheduledAt":"2026-10-20T09:00:00.000Z","sentAt":"2026-10-20T09:00:05.000Z","acknowledgedAt":null,"acknowledgedBy":null,"expiresAt":null,"metadata":{"channels":[{"type":"email","address":"jane.wanjiku@example.com","status":"sent","retryCount":0}],"contactId":"e0b6a2d5-7f4c-4dbe-8a91-5f8c1d4e7a0b","preferredChannel":"email"},"escalationLevel":0,"maxEscalations":3,"escalationInterval":24,"nextEscalationAt":null,"deletedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:00:05.000Z"}],"pagination":{"page":1,"limit":25,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false},"stats":{"totalCount":1,"pendingCount":0,"sentCount":1,"acknowledgedCount":0,"failedCount":0,"typeBreakdown":[]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.subscriptions.read"]}],"x-required-scopes":["crm.subscriptions.read"]}},"/reminder-types":{"get":{"tags":["Reminders"],"summary":"List reminder templates","description":"Lists your organisation's reminder templates (reminder types), by title A to Z: the built-in ones and those made with Create a reminder template.\n\n**Permission:** `crm.reminders.read` (View reminders).","operationId":"getReminderTypes","parameters":[{"name":"category","in":"query","required":false,"schema":{"type":"string"},"description":"Only templates in this category, e.g. payment_due (a list also works)","example":"payment_due"},{"name":"isActive","in":"query","required":false,"schema":{"type":"boolean"},"description":"true for active templates only, false for inactive only","example":true},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25},"description":"Templates per page","example":25},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"priority","in":"query","required":false,"schema":{"type":"string","enum":["low","medium","high","urgent"]},"description":"Only templates of this priority (a list also works)","example":"high"},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text found in the title, message, description or category (any case)","example":"payment"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"category":{"type":"string","description":"payment_due, payment_overdue, low_balance, subscription_expiring, service_suspended, service_disconnected, bill_generated, payment_failed, custom, welcome, service_activated, service_reactivated, service_terminated or plan_change"},"priority":{"type":"string"},"title":{"type":"string"},"message":{"type":"string"},"templateVariables":{"type":"array","items":{"type":"string"}},"isActive":{"type":"boolean"},"description":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}}}}},"example":{"success":true,"data":[{"id":"3d5a1f7c-2e9b-4c4d-9fe6-0e3b6c9d2f5a","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","category":"custom","priority":"medium","title":"Payment due for ${accountName}","message":"Hello ${accountName}, your bill of ${amount} is due on ${dueDate}.","templateVariables":["accountName","amount","dueDate"],"isActive":true,"description":"Sent a few days before a bill is due","deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}],"pagination":{"page":1,"limit":25,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.reminders.read"]}],"x-required-scopes":["crm.reminders.read"]}},"/comments/{targetType}/{targetId}":{"get":{"tags":["Comments"],"summary":"List a record's comments","description":"Lists the comments on one record, newest first, replies included, each with its author's name filled in.\n\n**Permission:** `crm.comments.read` (View comments).","operationId":"listComments","parameters":[{"name":"targetType","in":"path","required":true,"schema":{"type":"string"},"description":"The kind of record: lead, deal, account, contact, ticket, call, custom, …, or books:<resource> for a Kazee Books record (e.g. books:invoices)","example":"lead"},{"name":"targetId","in":"path","required":true,"schema":{"type":"string"},"description":"The record's ID; for a Books record its Books ID (e.g. inv_…) or UUID","example":"d37e0b8a-5c9f-4ace-8b7d-8f0a2c4e6b79"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50},"description":"Comments per page, at most 100","example":50},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"targetType":{"type":"string","description":"The kind of record the comment is on, e.g. lead, deal, account, contact, ticket, call, custom, or books:<resource> for a Kazee Books record"},"targetId":{"type":"string","description":"The record's ID (a Books record answers with its Books ID, e.g. inv_…)"},"authorId":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"profilePicture":{}},"description":"Who wrote it"},"content":{"type":"string","description":"The text; the web app saves it as a rich-text (TipTap) document in JSON"},"attachments":{"type":"array","description":"Files on the comment (empty: these calls never add any)"},"parentId":{"description":"The comment this one replies to, or null"},"replyCount":{"type":"integer"},"mentions":{"type":"array","items":{"type":"string"},"description":"IDs of the people tagged in it"},"reactions":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"emoji":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}}},"seenBy":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"seenAt":{"type":"string","format":"date-time"}}},"description":"Who has seen it, and when"},"actedBy":{"description":"The bot that wrote or last edited it, { id, name, ai: true }: a personal bot acting for the author, or a bot as itself (shown with the AI diamond); null when a person did"},"isDeleted":{"type":"boolean"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}}}}},"example":{"success":true,"data":[{"id":"2c8d5a3f-0b4e-4fbd-9acc-3e5f7b9dbace","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","targetType":"lead","targetId":"d37e0b8a-5c9f-4ace-8b7d-8f0a2c4e6b79","authorId":{"_id":"0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac","id":"0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac","firstName":"Peter","lastName":"Otieno","email":"peter.otieno@example.com","profilePicture":null},"content":"Called her this morning; she wants the 20 Mbps plan.","attachments":[],"parentId":null,"replyCount":0,"mentions":["1b7c4f2e-9a3d-4eac-8fbb-2d4e6a8cafbd"],"reactions":[{"userId":"1b7c4f2e-9a3d-4eac-8fbb-2d4e6a8cafbd","emoji":"👍","createdAt":"2026-10-20T10:15:00.000Z"}],"seenBy":[{"userId":"1b7c4f2e-9a3d-4eac-8fbb-2d4e6a8cafbd","seenAt":"2026-10-20T10:14:00.000Z"}],"actedBy":null,"isDeleted":false,"deletedAt":null,"createdAt":"2026-10-20T10:00:00.000Z","updatedAt":"2026-10-20T10:00:00.000Z"}],"pagination":{"page":1,"limit":50,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false}}}}},"400":{"description":"targetType or targetId is missing, or a Books targetType or ID is not valid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.comments.read"]}],"x-required-scopes":["crm.comments.read"]}},"/comments/{commentId}/replies":{"get":{"tags":["Comments"],"summary":"List a comment's replies","description":"Lists the replies to one comment, oldest first (at most 100), each with its author's name filled in. A comment with no replies, or an unknown ID, answers an empty list.\n\n**Permission:** `crm.comments.read` (View comments).","operationId":"getCommentReplies","parameters":[{"name":"commentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The comment's ID","example":"2c8d5a3f-0b4e-4fbd-9acc-3e5f7b9dbace"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"targetType":{"type":"string","description":"The kind of record the comment is on, e.g. lead, deal, account, contact, ticket, call, custom, or books:<resource> for a Kazee Books record"},"targetId":{"type":"string","description":"The record's ID (a Books record answers with its Books ID, e.g. inv_…)"},"authorId":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"profilePicture":{}},"description":"Who wrote it"},"content":{"type":"string","description":"The text; the web app saves it as a rich-text (TipTap) document in JSON"},"attachments":{"type":"array","description":"Files on the comment (empty: these calls never add any)"},"parentId":{"type":"string","description":"The comment this one replies to, or null"},"replyCount":{"type":"integer"},"mentions":{"type":"array","description":"IDs of the people tagged in it"},"reactions":{"type":"array"},"seenBy":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"seenAt":{"type":"string","format":"date-time"}}},"description":"Who has seen it, and when"},"actedBy":{"description":"The bot that wrote or last edited it, { id, name, ai: true }: a personal bot acting for the author, or a bot as itself (shown with the AI diamond); null when a person did"},"isDeleted":{"type":"boolean"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}},"example":{"success":true,"data":[{"id":"3d9e6b4a-1c5f-4ace-8bdd-4f6a8caecbdf","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","targetType":"lead","targetId":"d37e0b8a-5c9f-4ace-8b7d-8f0a2c4e6b79","authorId":{"_id":"0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac","id":"0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac","firstName":"Peter","lastName":"Otieno","email":"peter.otieno@example.com","profilePicture":null},"content":"Great, I will send her the quote.","attachments":[],"parentId":"2c8d5a3f-0b4e-4fbd-9acc-3e5f7b9dbace","replyCount":0,"mentions":[],"reactions":[],"seenBy":[{"userId":"1b7c4f2e-9a3d-4eac-8fbb-2d4e6a8cafbd","seenAt":"2026-10-20T10:14:00.000Z"}],"actedBy":null,"isDeleted":false,"deletedAt":null,"createdAt":"2026-10-20T10:00:00.000Z","updatedAt":"2026-10-20T10:00:00.000Z"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.comments.read"]}],"x-required-scopes":["crm.comments.read"]}},"/notifications/unread-count":{"get":{"tags":["Notifications"],"summary":"Count my unread notifications","description":"Answers how many notifications in your own feed are unread and not archived, for the bell's badge.\n\n**Permission:** `crm.notifications.read` (View notifications).","operationId":"getUnreadNotificationCount","parameters":[{"name":"userId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional: your own user ID. Any other person's ID is refused (403)","example":"1b3e9d5a-0c7f-4a2b-9dc4-8c1f4a7b0d3e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"count":{"type":"integer"}}}}},"example":{"success":true,"data":{"count":3}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"userId is not your own","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.notifications.read"]}],"x-required-scopes":["crm.notifications.read"]}},"/notifications":{"get":{"tags":["Notifications"],"summary":"List my notifications","description":"Lists the notifications in your own feed, newest first. It is always your own feed: there is no way to read a colleague's.\n\n**Permission:** `crm.notifications.read` (View notifications).","operationId":"getNotifications","parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Accepted but not applied: it does not filter the list"},{"name":"category","in":"query","required":false,"schema":{"type":"string"},"description":"Only notifications in this category, e.g. subscriptions","example":"subscriptions"},{"name":"contactId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Accepted but not applied: it does not filter the list"},{"name":"includeStats","in":"query","required":false,"schema":{"type":"boolean"},"description":"Also answer a total in stats","example":true},{"name":"isArchived","in":"query","required":false,"schema":{"type":"boolean"},"description":"true for archived notifications only, false for the rest","example":false},{"name":"isRead","in":"query","required":false,"schema":{"type":"boolean"},"description":"true for read notifications only, false for unread only","example":false},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25},"description":"Notifications per page, up to 500","example":25},{"name":"notificationType","in":"query","required":false,"schema":{"type":"string"},"description":"Only notifications of this type, e.g. create","example":"create"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"read","in":"query","required":false,"schema":{"type":"boolean"},"description":"Same as isRead, used when isRead is not sent","example":false},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Accepted but not applied: it does not filter the list"},{"name":"sent","in":"query","required":false,"schema":{"type":"boolean"},"description":"Accepted but not applied: it does not filter the list"},{"name":"sort","in":"query","required":false,"schema":{"type":"object"},"description":"Accepted but not applied: the list is always newest first"},{"name":"source","in":"query","required":false,"schema":{"type":"string"},"description":"Only notifications from this source, e.g. crm:subscriptions:subscription_created","example":"crm:subscriptions:subscription_created"},{"name":"userId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional: your own user ID. Any other person's ID is refused (403)","example":"1b3e9d5a-0c7f-4a2b-9dc4-8c1f4a7b0d3e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"userId":{"type":"string"},"actorId":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"profilePhoto":{},"profilePicture":{}},"description":"Who caused it: the person (id, name, email, photos) when known, else their ID or null"},"category":{"type":"string"},"notificationType":{"type":"string","description":"What happened, e.g. create, update or transition"},"title":{"type":"string"},"content":{"type":"string"},"source":{"type":"string","description":"Where it came from, e.g. crm:subscriptions:subscription_created"},"sourceEntityId":{"type":"string"},"data":{"type":"object","properties":{"accountId":{"type":"string"},"accountName":{"type":"string"},"subscriptionId":{"type":"string"},"module":{"type":"string"},"entity":{"type":"string"},"action":{"type":"string"},"outcome":{"type":"string"}}},"isRead":{"type":"boolean"},"readAt":{},"isArchived":{"type":"boolean"},"archivedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}}},"stats":{"type":"object","properties":{"total":{"type":"integer"},"byStatus":{"type":"object","properties":{}},"byEntity":{"type":"object","properties":{}},"byModule":{"type":"object","properties":{}}},"description":"Only with includeStats=true: total counts your notifications matching isRead and isArchived only; the breakdowns are always empty"}}},"example":{"success":true,"data":[{"id":"5f7c3b9e-4a1d-4e6f-9b08-2a5d8e1f4b7c","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","userId":"1b3e9d5a-0c7f-4a2b-9dc4-8c1f4a7b0d3e","actorId":{"id":"2c4f0e6b-1d8a-4b3c-8ed5-9d2a5b8c1e4f","firstName":"Peter","lastName":"Otieno","email":"peter.otieno@example.com","profilePhoto":null,"profilePicture":null},"category":"subscriptions","notificationType":"create","title":"subscription_created","content":"New subscription created for account \"Jane Wanjiku\"","source":"crm:subscriptions:subscription_created","sourceEntityId":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","data":{"accountId":"a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c","accountName":"Jane Wanjiku","subscriptionId":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","module":"subscriptions","entity":"subscription","action":"create","outcome":"success"},"isRead":false,"readAt":null,"isArchived":false,"archivedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}],"pagination":{"page":1,"limit":25,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false},"stats":{"total":1,"byStatus":{},"byEntity":{},"byModule":{}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"userId is not your own","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.notifications.read"]}],"x-required-scopes":["crm.notifications.read"]}},"/requests/analytics":{"get":{"tags":["Requests"],"summary":"Get request figures","description":"Counts the service requests opened in a period (default: the last 30 days): by status, by type and by assignee, per month, and the ten accounts with the most. A failure answers 200 with success false and the error.\n\n**Permission:** `crm.requests.read` (View requests).","operationId":"getRequestAnalytics","parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this account's requests","example":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b"},{"name":"assignedTo","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only requests handled by this person"},{"name":"endDate","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"End of the period; default now","example":"2026-10-21T00:00:00.000Z"},{"name":"requestType","in":"query","required":false,"schema":{"type":"string","enum":["relocation","cctv_sale","upgrade","downgrade","additional_service","product_installation","maintenance","configuration_change","billing_adjustment","service_suspension","service_reactivation","other"]},"description":"Only requests of this type"},{"name":"startDate","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Start of the period (by opening date); default 30 days ago","example":"2026-10-01T00:00:00.000Z"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"analytics":{"type":"object","properties":{"dateRange":{"type":"object","properties":{"startDate":{"type":"string","format":"date-time"},"endDate":{"type":"string","format":"date-time"}}},"totalRequests":{"type":"integer"},"statusDistribution":{"type":"array","items":{"type":"object","properties":{"_id":{"type":"string"},"count":{"type":"integer"}},"description":"_id is a status, count how many requests are in it"}},"typeDistribution":{"type":"array","items":{"type":"object","properties":{"_id":{"type":"string"},"count":{"type":"integer"}},"description":"_id is a request type"}},"teamDistribution":{"type":"array","items":{"type":"object","properties":{"_id":{"type":"string"},"count":{"type":"integer"}},"description":"_id is an assignee's user ID"}},"monthlyTrends":{"type":"array","items":{"type":"object","properties":{"year":{"type":"integer"},"month":{"type":"integer"},"count":{"type":"integer"}},"description":"Requests opened per calendar month"}},"topAccounts":{"type":"array","items":{"type":"object","properties":{"accountId":{"type":"string"},"count":{"type":"integer"}},"description":"The ten accounts with the most requests"}}}}}},"example":{"success":true,"analytics":{"dateRange":{"startDate":"2026-10-01T00:00:00.000Z","endDate":"2026-10-21T00:00:00.000Z"},"totalRequests":5,"statusDistribution":[{"_id":"new","count":2},{"_id":"completed","count":3}],"typeDistribution":[{"_id":"relocation","count":3},{"_id":"upgrade","count":2}],"teamDistribution":[{"_id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","count":3}],"monthlyTrends":[{"year":2026,"month":10,"count":5}],"topAccounts":[{"accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","count":2}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.requests.read"]}],"x-required-scopes":["crm.requests.read"]}},"/requests":{"get":{"tags":["Requests"],"summary":"List requests","description":"Lists your organisation's service requests, newest first, a page at a time. Expired requests are left out unless you filter by status or set includeExpired. Each row also says which statuses it can move to next, and lookup fields come with the record they point to.\n\n**Permission:** `crm.requests.read` (View requests).","operationId":"getRequests","parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this account's requests","example":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b"},{"name":"assignedTo","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only requests handled by this person","example":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"},{"name":"customFieldFilters","in":"query","required":false,"schema":{"type":"object"},"description":"Older form: conditions on the organisation's own fields, by field key, e.g. { \"region\": \"Nairobi\" } (an object or a JSON string)","example":{"region":"Nairobi"}},{"name":"fieldFilters","in":"query","required":false,"schema":{"type":"object"},"description":"Older form: conditions on built-in fields, by field name (an object or a JSON string)","example":{"status":["active"]}},{"name":"filters","in":"query","required":false,"schema":{"type":"object"},"description":"Field conditions keyed by filter field ID (a native field name, or cf:<key> for your own fields; GET /filter-fields lists them), each { operator, value } or { operator, values }; a bare value means \"is\". A JSON object in a string also works. An invalid condition answers 400 naming the field","example":{"status":{"operator":"is","values":["active"]}}},{"name":"includeExpired","in":"query","required":false,"schema":{"type":"boolean","default":"false"},"description":"true to also list expired requests when no status is given","example":false},{"name":"includeStats","in":"query","required":false,"schema":{"type":"boolean"},"description":"true to add stats: the count of matching requests per status","example":true},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25},"description":"Requests per page, at most 500","example":25},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"requestType","in":"query","required":false,"schema":{"type":"string","enum":["relocation","cctv_sale","upgrade","downgrade","additional_service","product_installation","maintenance","configuration_change","billing_adjustment","service_suspension","service_reactivation","other"]},"description":"Only requests of this type (one type)","example":"relocation"},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text to find in the title or description","example":"fibre"},{"name":"sort","in":"query","required":false,"schema":{"type":"string"},"description":"Not used yet: requests always come newest first"},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["new","assigned","in_progress","completed","closed","expired"]},"description":"Only requests in this status; repeat the parameter for several","example":"assigned"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"requestType":{"type":"string","description":"What is asked for, e.g. relocation, upgrade or maintenance"},"title":{"type":"string"},"description":{"type":"string"},"accountId":{"type":"string"},"subscriptionId":{"type":"string"},"quoteId":{},"status":{"type":"string","description":"new, assigned, in_progress, completed, closed or expired (rejected once rejected)"},"ownerId":{"type":"string"},"assignedTo":{"type":"string","description":"The person handling it"},"expiryAt":{"type":"string","format":"date-time","description":"When the request lapses if nobody finishes it"},"lastCustomerUpdate":{},"customerNotified":{"type":"boolean"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"pipeline":{"type":"string"},"currentStatus":{"type":"string","description":"The same as status"},"nextStages":{"type":"array","items":{"type":"string"},"description":"Statuses it may move to next"},"previousStages":{"type":"array","items":{"type":"string"},"description":"Statuses it may have come from"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer","description":"This page's number, from 1"},"limit":{"type":"integer","description":"Most items in a page (at most 500)"},"total":{"type":"integer","description":"How many items match in all"},"totalPages":{"type":"integer","description":"How many pages there are"},"hasNext":{"type":"boolean","description":"Whether another page follows"},"hasPrev":{"type":"boolean","description":"Whether a page comes before"}}},"stats":{"type":"array","items":{"type":"object","properties":{"status":{"type":"string"},"count":{"type":"integer"}},"description":"How many matching requests are in each status (only with includeStats)"}}}},"example":{"success":true,"data":[{"id":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","requestType":"relocation","title":"Relocation - Move the fibre line to the new house in Kileleshwa","description":"Move the fibre line to the new house in Kileleshwa from 1 November.","accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","subscriptionId":"b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d","quoteId":null,"status":"assigned","ownerId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","assignedTo":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","expiryAt":"2026-11-19T08:00:00.000Z","lastCustomerUpdate":null,"customerNotified":false,"customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-10-20T08:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z","pipeline":"request","currentStatus":"assigned","nextStages":["in_progress","closed","expired"],"previousStages":["new","in_progress"]}],"pagination":{"page":1,"limit":25,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false},"stats":[{"status":"assigned","count":1}]}}}},"400":{"description":"A field condition in filters, customFieldFilters or fieldFilters is not valid (the error names the field)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.requests.read"]}],"x-required-scopes":["crm.requests.read"]},"post":{"tags":["Requests"],"summary":"Create a request","description":"Opens a service request (a relocation, upgrade, installation and the like) on an account. Without a title one is made from the type and the start of the description; with assignedTo the request starts as \"assigned\", otherwise \"new\". Known problem: today the organisation is not passed on to the request pipeline, so this call answers 400 \"organizationId is required\".\n\n**Permission:** `crm.requests.create` (Create requests).","operationId":"createRequest","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid","description":"The customer account it is for","examples":["9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b"]},"assignedTo":{"type":"string","format":"uuid","description":"Person to hand it to; left out, it goes to you when your sign-in carries your user","examples":["1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"]},"customFields":{"type":"object","description":"The organisation's own request fields, by key. Currently dropped by validation and not stored","examples":[{}]},"description":{"type":"string","description":"What the customer wants, in full","examples":["Move the fibre line to the new house in Kileleshwa from 1 November."]},"expiryAt":{"type":"string","format":"date-time","description":"When the request lapses if unfinished; must be in the future","examples":["2026-11-19T08:00:00.000Z"]},"quoteId":{"type":"string","format":"uuid","description":"A quote it belongs to"},"requestType":{"type":"string","enum":["relocation","cctv_sale","upgrade","downgrade","additional_service","product_installation","maintenance","configuration_change","billing_adjustment","service_suspension","service_reactivation","other"],"description":"What is asked for","examples":["relocation"]},"subscriptionId":{"type":"string","format":"uuid","description":"The subscription it concerns","examples":["b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d"]},"title":{"type":"string","description":"Short title; left out, it becomes \"<Type> - <first 50 characters of the description>\"","examples":["Relocation - Move the fibre line to the new house in Kileleshwa"]}},"required":["accountId","description","requestType"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"request":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string"},"requestType":{"type":"string","description":"What is asked for, e.g. relocation, upgrade or maintenance"},"title":{"type":"string"},"description":{"type":"string"},"accountId":{"type":"string"},"subscriptionId":{"type":"string"},"quoteId":{},"status":{"type":"string","description":"new, assigned, in_progress, completed, closed or expired (rejected once rejected)"},"assignedTo":{"type":"string","description":"The person handling it"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"expiryAt":{"type":"string","format":"date-time","description":"When the request lapses if nobody finishes it"}}}}},"example":{"success":true,"message":"Request created successfully","request":{"_id":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e","id":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e","requestType":"relocation","title":"Relocation - Move the fibre line to the new house in Kileleshwa","description":"Move the fibre line to the new house in Kileleshwa from 1 November.","accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","subscriptionId":"b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d","quoteId":null,"status":"assigned","assignedTo":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","createdAt":"2026-10-20T08:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z","expiryAt":"2026-11-19T08:00:00.000Z"}}}}},"400":{"description":"expiryAt is not a date or is not in the future, requestType is unknown, a required field is empty, or the organisation is missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"The request could not be saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.requests.create"]}],"x-required-scopes":["crm.requests.create"]}},"/requests/{requestId}":{"get":{"tags":["Requests"],"summary":"Get a request","description":"Reads one service request with its account, subscription (passwords masked) and quote, how many days old it is, and whether it lapses within 7 days or already has.\n\n**Permission:** `crm.requests.read` (View requests).","operationId":"getRequestDetails","parameters":[{"name":"requestId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The request to read","example":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"request":{"type":"object","properties":{"id":{"type":"string"},"requestType":{"type":"string","description":"What is asked for, e.g. relocation, upgrade or maintenance"},"title":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","description":"new, assigned, in_progress, completed, closed or expired (rejected once rejected)"},"account":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"accountNumber":{"type":"string"},"status":{"type":"string"},"description":{},"addressId":{},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string"},"currentBalance":{"type":"string"},"originLeadId":{},"walletId":{},"billingContactId":{"type":"string"},"serviceContactId":{"type":"string"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{}},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{},"cacheDisplayName":{},"cachePrimaryPhone":{},"cachePrimaryEmail":{},"cachePartyRevision":{},"cacheUpdatedAt":{},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"description":"The account, or null"},"subscription":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"pppoe":{"type":"string"},"accountId":{"type":"string"},"servicePlanId":{"type":"string"},"status":{"type":"string"},"startDate":{"type":"string","format":"date-time"},"billingCycle":{"type":"string"},"lastBilledAt":{"type":"string","format":"date-time"},"nextBillingAt":{"type":"string","format":"date-time"},"billDate":{"type":"string","format":"date-time"},"renewalDate":{"type":"string","format":"date-time"},"billLeadDays":{"type":"integer"},"disconnectionDate":{},"terminationDate":{},"lastPaymentDate":{"type":"string","format":"date-time"},"ownerId":{"type":"string"},"password":{"type":"object","properties":{"secret":{"type":"boolean"},"set":{"type":"boolean"}}},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"version":{"type":"integer"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"description":"The subscription with its password shown only as { secret: true, set: true }, or null"},"quote":{"description":"The quote, or null"},"assignedTo":{"type":"string","description":"The person handling it"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"expiryAt":{"type":"string","format":"date-time","description":"When the request lapses if nobody finishes it"}}},"metrics":{"type":"object","properties":{"ageInDays":{"type":"integer"}}},"alerts":{"type":"object","properties":{"isExpiringSoon":{"type":"boolean","description":"It lapses within the next 7 days (or already has)"},"isOverdue":{"type":"boolean","description":"Its expiry date has passed and it is not completed, closed or expired"}}}}},"example":{"success":true,"request":{"id":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e","requestType":"relocation","title":"Relocation - Move the fibre line to the new house in Kileleshwa","description":"Move the fibre line to the new house in Kileleshwa from 1 November.","status":"assigned","account":{"id":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","name":"Wanjiru Household","accountNumber":"ACC-000318","status":"active","description":null,"addressId":null,"onboardingDate":"2026-03-02T00:00:00.000Z","terminationDate":null,"creditLimit":"0.00","currentBalance":"2500.00","originLeadId":null,"walletId":null,"billingContactId":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","serviceContactId":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","ownerId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","notificationPreferences":{},"tags":["home-fibre"],"customFields":{},"externalIds":[],"partyId":null,"cacheDisplayName":null,"cachePrimaryPhone":null,"cachePrimaryEmail":null,"cachePartyRevision":null,"cacheUpdatedAt":null,"cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-03-02T10:00:00.000Z","updatedAt":"2026-10-20T09:05:00.000Z"},"subscription":{"id":"b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","name":"Home Fibre 50 Mbps","pppoe":"wanjiru.home","accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","servicePlanId":"a2f3a4b5-c6d7-4e8f-9b0c-1d2e3f4a5b6c","status":"active","startDate":"2026-03-05T00:00:00.000Z","billingCycle":"monthly","lastBilledAt":"2026-10-05T00:00:00.000Z","nextBillingAt":"2026-11-05T00:00:00.000Z","billDate":"2026-10-26T00:00:00.000Z","renewalDate":"2026-11-05T00:00:00.000Z","billLeadDays":10,"disconnectionDate":null,"terminationDate":null,"lastPaymentDate":"2026-10-04T00:00:00.000Z","ownerId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","password":{"secret":true,"set":true},"customFields":{},"externalIds":[],"version":3,"deletedAt":null,"createdAt":"2026-03-02T10:00:00.000Z","updatedAt":"2026-10-05T00:00:00.000Z"},"quote":null,"assignedTo":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","createdAt":"2026-10-20T08:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z","expiryAt":"2026-11-19T08:00:00.000Z"},"metrics":{"ageInDays":1},"alerts":{"isExpiringSoon":false,"isOverdue":false}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No request with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"The request could not be read","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.requests.read"]}],"x-required-scopes":["crm.requests.read"]},"put":{"tags":["Requests"],"summary":"Update a request","description":"Changes a service request's details. Only the fields you send change; an empty value is ignored, and the account cannot be changed this way. A new status must follow the allowed steps: new → assigned, closed or expired; assigned → in_progress, closed or expired; in_progress → completed, assigned or closed; completed → closed; closed or expired → new.\n\n**Permission:** `crm.requests.update` (Update requests).","operationId":"updateRequest","parameters":[{"name":"requestId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The request to change","example":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid","description":"Accepted but not applied: a request keeps its account"},"assignedTo":{"type":"string","format":"uuid","description":"Person to hand it to","examples":["1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"]},"customFields":{"type":"object","description":"The organisation's own request fields, by key. Currently dropped by validation and not stored"},"description":{"type":"string","description":"New description"},"expiryAt":{"type":"string","format":"date-time","description":"New lapse date","examples":["2026-11-19T08:00:00.000Z"]},"quoteId":{"type":"string","format":"uuid","description":"A quote it belongs to"},"requestType":{"type":"string","enum":["relocation","cctv_sale","upgrade","downgrade","additional_service","product_installation","maintenance","configuration_change","billing_adjustment","service_suspension","service_reactivation","other"],"description":"New request type"},"status":{"type":"string","enum":["new","assigned","in_progress","completed","closed","expired"],"description":"New status; must be allowed after the current one","examples":["in_progress"]},"subscriptionId":{"type":"string","format":"uuid","description":"The subscription it concerns","examples":["b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d"]},"title":{"type":"string","description":"New title","examples":["Relocation - Move the fibre line to the new house in Kileleshwa"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"request":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string"},"requestType":{"type":"string","description":"What is asked for, e.g. relocation, upgrade or maintenance"},"title":{"type":"string"},"description":{"type":"string"},"accountId":{"type":"string"},"subscriptionId":{"type":"string"},"quoteId":{},"status":{"type":"string","description":"new, assigned, in_progress, completed, closed or expired (rejected once rejected)"},"assignedTo":{"type":"string","description":"The person handling it"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"expiryAt":{"type":"string","format":"date-time","description":"When the request lapses if nobody finishes it"}}}}},"example":{"success":true,"message":"Request updated successfully","request":{"_id":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e","id":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e","requestType":"relocation","title":"Relocation - Move the fibre line to the new house in Kileleshwa","description":"Move the fibre line to the new house in Kileleshwa from 1 November.","accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","subscriptionId":"b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d","quoteId":null,"status":"in_progress","assignedTo":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","createdAt":"2026-10-20T08:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z","expiryAt":"2026-11-19T08:00:00.000Z"}}}}},"400":{"description":"expiryAt is not a date, requestType or status is unknown, or the status may not follow the current one","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No request with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"The request could not be saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.requests.update"]}],"x-required-scopes":["crm.requests.update"]}},"/dashboard/landing-page":{"get":{"tags":["Dashboards"],"summary":"Get the landing page figures","description":"Answers the business figures for the landing page in one call: accounts, subscriptions, leads, quotes, service requests and products, with recent activity, alerts and daily trends over the last `period` days. It carries no money figures (those live in Kazee Books).\n\n**Sign-in:** not needed.","operationId":"getLandingPageData","parameters":[{"name":"includeAlerts","in":"query","required":false,"schema":{"type":"boolean","default":"true"},"description":"Send true to include alerts; anything else leaves them out (default true)","example":true},{"name":"includeTrends","in":"query","required":false,"schema":{"type":"boolean","default":"true"},"description":"Send true to include daily trends; anything else leaves them out (default true)","example":true},{"name":"period","in":"query","required":false,"schema":{"type":"integer","default":"30"},"description":"How many days back to count new records over (default 30)","example":30}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"overview":{"type":"object","properties":{"totalAccounts":{"type":"integer"},"activeAccounts":{"type":"integer"},"totalSubscriptions":{"type":"integer"},"activeSubscriptions":{"type":"integer"},"totalLeads":{"type":"integer"},"conversionRate":{"type":"string"},"totalQuotes":{"type":"integer"},"quoteAcceptanceRate":{"type":"string"},"openSupportTickets":{"type":"integer"},"averageResolutionTime":{"type":"number"}},"description":"The headline numbers. openSupportTickets counts open service requests (new, assigned or in progress), not help desk tickets"},"metrics":{"type":"object","properties":{"accounts":{"type":"object","properties":{"totalAccounts":{"type":"integer"},"activeAccounts":{"type":"integer"},"newAccounts":{"type":"integer"},"accountsByStatus":{"type":"object","properties":{"active":{"type":"integer"},"suspended":{"type":"integer"},"closed":{"type":"integer"}}},"activationRate":{"type":"string","description":"Share of accounts that are active, in percent, as text with one decimal"}}},"subscriptions":{"type":"object","properties":{"totalSubscriptions":{"type":"integer"},"activeSubscriptions":{"type":"integer"},"newSubscriptions":{"type":"integer"},"subscriptionsByStatus":{"type":"object","properties":{"active":{"type":"integer"},"suspended":{"type":"integer"},"cancelled":{"type":"integer"}}},"activationRate":{"type":"string"}}},"leads":{"type":"object","properties":{"totalLeads":{"type":"integer"},"newLeads":{"type":"integer"},"convertedLeads":{"type":"integer"},"conversionRate":{"type":"string","description":"Leads converted in the period as a share of all leads, in percent, as text"},"leadsByStatus":{"type":"object","properties":{"new":{"type":"integer"},"contacted":{"type":"integer"},"qualified":{"type":"integer"},"converted":{"type":"integer"}}},"opportunityValue":{"type":"integer","description":"Total of quotes sent in the period"},"avgTimeToConversion":{"type":"string","description":"Fixed placeholder, not measured"}}},"quotes":{"type":"object","properties":{"totalQuotes":{"type":"integer"},"newQuotes":{"type":"integer"},"acceptedQuotes":{"type":"integer"},"acceptanceRate":{"type":"string"},"quotesByStatus":{"type":"object","properties":{"draft":{"type":"integer"},"sent":{"type":"integer"},"accepted":{"type":"integer"}}},"avgTimeToClose":{"type":"number","description":"Fixed placeholder, not measured"}}},"support":{"type":"object","properties":{"totalTickets":{"type":"integer"},"openTickets":{"type":"integer"},"resolvedTickets":{"type":"integer"},"resolutionRate":{"type":"string"},"ticketsByStatus":{"type":"object","properties":{"new":{"type":"integer"},"assigned":{"type":"integer"},"in_progress":{"type":"integer"},"completed":{"type":"integer"}}},"avgResolutionTime":{"type":"number"},"avgResponseTime":{"type":"number"},"satisfactionScore":{"type":"number"}},"description":"Counts service requests. avgResolutionTime, avgResponseTime and satisfactionScore are fixed placeholders, not measured"},"products":{"type":"object","properties":{"totalProducts":{"type":"integer"},"activeProducts":{"type":"integer"},"topProducts":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"subscriptionCount":{"type":"integer"}}},"description":"Up to 5 service plans with the most subscriptions"}}}}},"insights":{"type":"object","properties":{"topPerformingProducts":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"subscriptionCount":{"type":"integer"}}}},"customerSatisfaction":{"type":"number"},"marketOpportunities":{"type":"integer"},"operationalEfficiency":{"type":"object","properties":{"averageResponseTime":{"type":"number"},"quoteToCloseTime":{"type":"number"},"leadToConversionTime":{"type":"string"}}}}},"recentActivity":{"type":"object","properties":{"recentAccounts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"},"created_at":{"type":"string","format":"date-time"}}}},"recentSubscriptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"},"created_at":{"type":"string","format":"date-time"}}}}},"description":"The 5 newest accounts and the 5 newest subscriptions"},"alerts":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"title":{"type":"string"},"message":{"type":"string"},"count":{"type":"integer"},"priority":{"type":"string"}}},"description":"Warnings, such as suspended subscriptions; empty when includeAlerts is false"},"trends":{"type":"object","properties":{"accounts":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string"},"count":{"type":"integer"}}}},"subscriptions":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string"},"count":{"type":"integer"}}}}},"description":"New accounts and subscriptions per day over the period; {} when includeTrends is false"},"generatedAt":{"type":"string","format":"date-time"},"period":{"type":"string"}}}}},"example":{"success":true,"data":{"overview":{"totalAccounts":1240,"activeAccounts":1102,"totalSubscriptions":1530,"activeSubscriptions":1388,"totalLeads":860,"conversionRate":"4.7","totalQuotes":412,"quoteAcceptanceRate":"9.2","openSupportTickets":37,"averageResolutionTime":24.5},"metrics":{"accounts":{"totalAccounts":1240,"activeAccounts":1102,"newAccounts":58,"accountsByStatus":{"active":1102,"suspended":96,"closed":42},"activationRate":"88.9"},"subscriptions":{"totalSubscriptions":1530,"activeSubscriptions":1388,"newSubscriptions":71,"subscriptionsByStatus":{"active":1388,"suspended":102,"cancelled":40},"activationRate":"90.7"},"leads":{"totalLeads":860,"newLeads":120,"convertedLeads":40,"conversionRate":"4.7","leadsByStatus":{"new":300,"contacted":260,"qualified":260,"converted":40},"opportunityValue":845000,"avgTimeToConversion":"15.2"},"quotes":{"totalQuotes":412,"newQuotes":64,"acceptedQuotes":38,"acceptanceRate":"9.2","quotesByStatus":{"draft":120,"sent":200,"accepted":92},"avgTimeToClose":7.5},"support":{"totalTickets":530,"openTickets":37,"resolvedTickets":88,"resolutionRate":"16.6","ticketsByStatus":{"new":12,"assigned":15,"in_progress":10,"completed":493},"avgResolutionTime":24.5,"avgResponseTime":2.5,"satisfactionScore":4.2},"products":{"totalProducts":24,"activeProducts":20,"topProducts":[{"name":"Home Fibre 50 Mbps","subscriptionCount":612}]}},"insights":{"topPerformingProducts":[{"name":"Home Fibre 50 Mbps","subscriptionCount":612}],"customerSatisfaction":4.2,"marketOpportunities":845000,"operationalEfficiency":{"averageResponseTime":2.5,"quoteToCloseTime":7.5,"leadToConversionTime":"15.2"}},"recentActivity":{"recentAccounts":[{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","name":"Acme Traders Ltd","status":"active","created_at":"2026-10-20T09:00:00.000Z"}],"recentSubscriptions":[{"id":"e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8a9b","name":"Home Fibre 50 Mbps","status":"active","created_at":"2026-10-20T09:00:00.000Z"}]},"alerts":[{"type":"warning","title":"Suspended Subscriptions","message":"102 subscriptions are suspended","count":102,"priority":"medium"}],"trends":{"accounts":[{"day":"2026-10-20","count":4}],"subscriptions":[{"day":"2026-10-20","count":6}]},"generatedAt":"2026-10-21T07:00:00.000Z","period":"30 days"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[]}},"/user/preferences/{userId}":{"get":{"tags":["Teams & people"],"summary":"Get my preferences","description":"Answers your display preferences: language, dark mode and currency. The first time, it saves the defaults for you (English, light mode, the organisation's currency).\n\n**Permission:** `crm.users.read` (View users).","operationId":"getUserPreferences","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Your own user ID; any other person's is refused (403)","example":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"preferences":{"type":"object","properties":{"language":{"type":"string","description":"en, es, fr, sw or ar"},"isDarkMode":{"type":"boolean"},"currency":{"type":"string","description":"Currency the person sees amounts in; may differ from the organisation's"}}},"organizationCurrency":{"type":"object","properties":{"code":{"type":"string"},"symbol":{"type":"string"},"minorUnits":{"type":"integer"},"country":{"type":"string"},"seededAt":{"type":"string","format":"date-time"},"source":{"type":"string"}},"description":"The currency the organisation prices in, or null when it is not known yet"},"auditLog":{"type":"array","description":"Always empty"}}},"example":{"success":true,"preferences":{"language":"en","isDarkMode":false,"currency":"KES"},"organizationCurrency":{"code":"KES","symbol":"KSh","minorUnits":2,"country":"KE","seededAt":"2026-10-01T08:30:00.000Z","source":"inet-central"},"auditLog":[]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"userId is not your own","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.users.read"]}],"x-required-scopes":["crm.users.read"]},"post":{"tags":["Teams & people"],"summary":"Update my preferences","description":"Changes your display preferences (language, dark mode, currency). Only the keys you send change; the rest stay as they are. Answers all your preferences after the change.\n\n**Permission:** `crm.users.update` (Update users).","operationId":"updateUserPreferences","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Your own user ID; any other person's is refused (403)","example":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"preferences":{"type":"object","properties":{"language":{"type":"string","enum":["en","es","fr","sw","ar"]},"isDarkMode":{"type":"boolean"},"currency":{"type":"string","enum":["USD","EUR","GBP","KES","UGX","TZS","RWF","ETB","BIF","SSP","ZMW","MWK","MZN","NGN","GHS","ZAR","XAF","XOF","EGP","MAD","AED","INR"]}},"description":"The preferences to change: language (en, es, fr, sw or ar), isDarkMode (true or false) and currency (a currency code such as KES, UGX or USD). Other keys are refused","examples":[{"isDarkMode":true}]}},"required":["preferences"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"preferences":{"type":"object","properties":{"language":{"type":"string"},"isDarkMode":{"type":"boolean"},"currency":{"type":"string"}}},"message":{"type":"string"},"updatedFields":{"type":"array","items":{"type":"string"},"description":"The preferences this call changed"}}},"example":{"success":true,"preferences":{"language":"en","isDarkMode":true,"currency":"KES"},"message":"User preferences updated successfully","updatedFields":["isDarkMode"]}}}},"400":{"description":"preferences is not an object, has an unknown key, an unknown currency or language, a dark mode that is not true or false, or nothing to change","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"userId is not your own","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.users.update"]}],"x-required-scopes":["crm.users.update"]}},"/organization":{"get":{"tags":["Settings"],"summary":"Get your organisation","description":"Answers your own organisation (ID, name, slug, status and its stored settings) together with the currency CRM shows money in. Any signed-in person who can read users may call it.\n\n**Permission:** `crm.users.read` (View users).","operationId":"getOrganizationSelf","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"organization":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"status":{"type":"string","description":"active unless the organisation is suspended"},"settings":{"type":"object","properties":{"currency":{"type":"object","properties":{"code":{"type":"string"},"symbol":{"type":"string"},"minorUnits":{"type":"integer"},"country":{"type":"string"},"seededAt":{"type":"string","format":"date-time"},"source":{"type":"string"}}},"onboarding":{"type":"object","properties":{"version":{"type":"integer"},"enabled":{"type":"boolean"},"pipelineIds":{"type":"array","items":{"type":"string"}},"requires":{"type":"object","properties":{"subscription":{"type":"boolean"},"installation":{"type":"boolean"},"ticket":{"type":"boolean"}}},"triggers":{"type":"object","properties":{"create":{"type":"object","properties":{"trigger":{"type":"string"},"transitionId":{"type":"string"}}},"activate":{"type":"object","properties":{"transitionId":{}}},"estimateSent":{"type":"object","properties":{"transitionId":{}}},"installationCompleted":{"type":"object","properties":{"transitionId":{}}},"paymentReceived":{"type":"object","properties":{"transitionId":{"type":"string"}}},"paymentUndone":{"type":"object","properties":{"transitionId":{}}}}},"eligibility":{"type":"object","properties":{"basis":{"type":"string"},"allowAllocatedCredits":{"type":"boolean"},"exceptionRoles":{"type":"array","items":{"type":"string"}}}},"quote":{"type":"object","properties":{"itemRoles":{"type":"array","items":{"type":"string"}},"prepaidFirstPeriod":{"type":"boolean"},"validityDays":{"type":"integer"},"contractTerms":{"type":"array","items":{"type":"string"}},"allowPriceOverride":{"type":"boolean"},"serviceTaxKind":{"type":"string"},"oneOffTaxKind":{"type":"string"},"planLine":{"type":"object","properties":{"description":{},"treatment":{"type":"string"},"discountAllowed":{"type":"boolean"}}},"estimateTerms":{"type":"string"},"estimateNotes":{},"askDealFields":{"type":"array"},"planLineRules":{"type":"array"}}},"billing":{"type":"object","properties":{"anchor":{"type":"string"},"prepaidDays":{"type":"integer"},"termsDays":{"type":"integer"},"currency":{"type":"string"},"timezone":{"type":"string"},"monthlyItemIds":{"type":"array"},"noInvoiceWhen":{"type":"array"},"startInBooks":{"type":"boolean"},"leaveProfilePaused":{"type":"boolean"},"booksLifecycle":{"type":"boolean"},"reconnectOnPaidOrders":{"type":"boolean"},"cutsWithoutEvidenceWhen":{"type":"array"},"cutsWithoutEvidenceDateField":{},"cutsWithoutEvidenceWithinDays":{},"allowSuspend":{"type":"boolean"}}},"payments":{"type":"object","properties":{"reference":{"type":"string"},"instructions":{"type":"string"}}},"fields":{"type":"object","properties":{"installersFieldId":{"type":"string"},"appointmentFieldId":{},"siteFieldId":{},"connectionMethodFieldId":{},"serviceIdentifierFieldId":{"type":"string"},"completionTimeFieldId":{},"complimentaryFieldId":{}}},"technicians":{"type":"object","properties":{"mode":{"type":"string"},"roles":{"type":"array"}}},"provisioning":{"type":"object","properties":{"mode":{"type":"string"},"requiredAt":{"type":"string"},"identifierLabel":{"type":"string"},"identifierPattern":{"type":"string"},"identifierHint":{"type":"string"},"identifierPrefix":{"type":"string"},"identifierDigits":{"type":"integer"},"counter":{}}},"zoho":{"type":"object","properties":{"stop":{"type":"string"},"booksServiceDetailFields":{"type":"object","properties":{"pppoePassword":{"type":"string"},"smIp":{"type":"string"},"smPassword":{"type":"string"}}},"subscription":{},"billing":{}}},"tickets":{"type":"object","properties":{"enabled":{"type":"boolean"},"purpose":{"type":"string"}}},"approvals":{"type":"object","properties":{"reviewRoles":{"type":"array","items":{"type":"string"}}}},"rollout":{"type":"object","properties":{"effectiveFrom":{"type":"string"}}},"lineEvents":{"type":"object","properties":{"extensionDateField":{}}},"service":{"type":"object","properties":{"fieldMap":{"type":"array"}}},"followUps":{"type":"object","properties":{"overdue":{"type":"object","properties":{"enabled":{"type":"boolean"},"moduleSlug":{},"createdBy":{},"followTestRuns":{"type":"boolean"},"steps":{"type":"array"},"clearTransition":{},"skipWhen":{"type":"array"}}},"retention":{"type":"object","properties":{"enabled":{"type":"boolean"},"moduleSlug":{},"createdBy":{},"since":{},"reconnectTransition":{},"skipWhen":{"type":"array"}}}}},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}}},"description":"The organisation's stored settings blocks (onboarding, quotes, taxes, locale, currency and so on), as stored. Leaves out network_gateway and line_provisioning, which answer only at /organization/network-gateway and /organization/line-provisioning"}}},"currency":{"type":"object","properties":{"code":{"type":"string"},"symbol":{"type":"string"},"minorUnits":{"type":"integer","description":"Decimal places the currency uses (0 for UGX, 2 for KES)"},"country":{"type":"string"},"seededAt":{"type":"string","format":"date-time"},"source":{"type":"string"}},"description":"The currency CRM prices money in, copied from Kazee sign-in; null when sign-in has not given one yet (show plain numbers then)"}}},"example":{"success":true,"organization":{"id":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","name":"Acme Fibre Ltd","slug":"acme-fibre","status":"active","settings":{"currency":{"code":"KES","symbol":"KSh","minorUnits":2,"country":"KE","seededAt":"2026-10-01T08:30:00.000Z","source":"inet-central"},"onboarding":{"version":7,"enabled":true,"pipelineIds":["e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8a9b"],"requires":{"subscription":true,"installation":true,"ticket":true},"triggers":{"create":{"trigger":"verified_payment","transitionId":"f6a7b8c9-d0e1-4f2a-9b3c-5d6e7f8a9b0c"},"activate":{"transitionId":null},"estimateSent":{"transitionId":null},"installationCompleted":{"transitionId":null},"paymentReceived":{"transitionId":"f6a7b8c9-d0e1-4f2a-9b3c-5d6e7f8a9b0c"},"paymentUndone":{"transitionId":null}},"eligibility":{"basis":"full_settlement","allowAllocatedCredits":false,"exceptionRoles":["finance_manager"]},"quote":{"itemRoles":["plan","installation"],"prepaidFirstPeriod":true,"validityDays":30,"contractTerms":["12 months"],"allowPriceOverride":false,"serviceTaxKind":"vat16","oneOffTaxKind":"vat16","planLine":{"description":null,"treatment":"invoice","discountAllowed":true},"estimateTerms":"Payment is due before installation.","estimateNotes":null,"askDealFields":[],"planLineRules":[]},"billing":{"anchor":"activation","prepaidDays":30,"termsDays":15,"currency":"KES","timezone":"Africa/Nairobi","monthlyItemIds":[],"noInvoiceWhen":[],"startInBooks":false,"leaveProfilePaused":false,"booksLifecycle":false,"reconnectOnPaidOrders":false,"cutsWithoutEvidenceWhen":[],"cutsWithoutEvidenceDateField":null,"cutsWithoutEvidenceWithinDays":null,"allowSuspend":false},"payments":{"reference":"123456","instructions":"Pay by M-Pesa to paybill {reference}, account: your name."},"fields":{"installersFieldId":"a7b8c9d0-e1f2-4a3b-8c4d-6e7f8a9b0c1d","appointmentFieldId":null,"siteFieldId":null,"connectionMethodFieldId":null,"serviceIdentifierFieldId":"b8c9d0e1-f2a3-4b4c-9d5e-7f8a9b0c1d2e","completionTimeFieldId":null,"complimentaryFieldId":null},"technicians":{"mode":"many","roles":[]},"provisioning":{"mode":"generated","requiredAt":"booking","identifierLabel":"PPPoE account","identifierPattern":"^acme\\d{7}$","identifierHint":"acme followed by 7 digits","identifierPrefix":"acme","identifierDigits":7,"counter":null},"zoho":{"stop":"off","booksServiceDetailFields":{"pppoePassword":"cf_pppoe_password","smIp":"cf_sm_ip","smPassword":"cf_sm_password"},"subscription":null,"billing":null},"tickets":{"enabled":true,"purpose":"installation"},"approvals":{"reviewRoles":["operations_manager"]},"rollout":{"effectiveFrom":"2026-10-01"},"lineEvents":{"extensionDateField":null},"service":{"fieldMap":[]},"followUps":{"overdue":{"enabled":false,"moduleSlug":null,"createdBy":null,"followTestRuns":false,"steps":[],"clearTransition":null,"skipWhen":[]},"retention":{"enabled":false,"moduleSlug":null,"createdBy":null,"since":null,"reconnectTransition":null,"skipWhen":[]}},"updatedAt":"2026-10-20T09:00:00.000Z","updatedBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"}}},"currency":{"code":"KES","symbol":"KSh","minorUnits":2,"country":"KE","seededAt":"2026-10-01T08:30:00.000Z","source":"inet-central"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Your organisation has no CRM record yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.users.read"]}],"x-required-scopes":["crm.users.read"]}},"/organization/onboarding-settings":{"get":{"tags":["Settings"],"summary":"Get the paid-onboarding settings","description":"Answers how your organisation turns a paid deal into a running service: the settings with defaults filled in (an organisation that never set them gets everything switched off, version 0), the problems that stop the configuration working (blockers), and the last generated service number used.\n\n**Permission:** `crm.onboarding.read` (View onboarding settings).","operationId":"getOnboardingSettings","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"settings":{"type":"object","properties":{"version":{"type":"integer","description":"Goes up by one with every save; send it back with the next save"},"enabled":{"type":"boolean","description":"Master switch: off means no onboarding automation runs at all"},"pipelineIds":{"type":"array","items":{"type":"string"},"description":"Deal pipelines that take part; empty means none"},"requires":{"type":"object","properties":{"subscription":{"type":"boolean"},"installation":{"type":"boolean"},"ticket":{"type":"boolean"}}},"triggers":{"type":"object","properties":{"create":{"type":"object","properties":{"trigger":{"type":"string","description":"When a service is created: verified_payment, approved_order or manual"},"transitionId":{"type":"string"}}},"activate":{"type":"object","properties":{"transitionId":{}}},"estimateSent":{"type":"object","properties":{"transitionId":{}}},"installationCompleted":{"type":"object","properties":{"transitionId":{}}},"paymentReceived":{"type":"object","properties":{"transitionId":{"type":"string"}}},"paymentUndone":{"type":"object","properties":{"transitionId":{}}}}},"eligibility":{"type":"object","properties":{"basis":{"type":"string","description":"What counts as paid enough: full_settlement or authorized_credit"},"allowAllocatedCredits":{"type":"boolean"},"exceptionRoles":{"type":"array","items":{"type":"string"}}}},"quote":{"type":"object","properties":{"itemRoles":{"type":"array","items":{"type":"string"}},"prepaidFirstPeriod":{"type":"boolean"},"validityDays":{"type":"integer"},"contractTerms":{"type":"array","items":{"type":"string"}},"allowPriceOverride":{"type":"boolean"},"serviceTaxKind":{"type":"string"},"oneOffTaxKind":{"type":"string"},"planLine":{"type":"object","properties":{"description":{},"treatment":{"type":"string"},"discountAllowed":{"type":"boolean"}}},"estimateTerms":{"type":"string"},"estimateNotes":{},"askDealFields":{"type":"array"},"planLineRules":{"type":"array"}}},"billing":{"type":"object","properties":{"anchor":{"type":"string","description":"Where the first billing cycle starts: activation, contract_date or approved_date"},"prepaidDays":{"type":"integer"},"termsDays":{"type":"integer"},"currency":{"type":"string"},"timezone":{"type":"string"},"monthlyItemIds":{"type":"array"},"noInvoiceWhen":{"type":"array"},"startInBooks":{"type":"boolean"},"leaveProfilePaused":{"type":"boolean"},"booksLifecycle":{"type":"boolean"},"reconnectOnPaidOrders":{"type":"boolean"},"cutsWithoutEvidenceWhen":{"type":"array"},"cutsWithoutEvidenceDateField":{},"cutsWithoutEvidenceWithinDays":{},"allowSuspend":{"type":"boolean"}}},"payments":{"type":"object","properties":{"reference":{"type":"string"},"instructions":{"type":"string"}}},"fields":{"type":"object","properties":{"installersFieldId":{"type":"string"},"appointmentFieldId":{},"siteFieldId":{},"connectionMethodFieldId":{},"serviceIdentifierFieldId":{"type":"string"},"completionTimeFieldId":{},"complimentaryFieldId":{}}},"technicians":{"type":"object","properties":{"mode":{"type":"string","description":"one or many technicians per job"},"roles":{"type":"array"}}},"provisioning":{"type":"object","properties":{"mode":{"type":"string","description":"How the service identifier (e.g. PPPoE account) is obtained: none, staff_entered, external_system, generated or zoho_generated"},"requiredAt":{"type":"string","description":"When the identifier must exist: booking or completion"},"identifierLabel":{"type":"string"},"identifierPattern":{"type":"string"},"identifierHint":{"type":"string"},"identifierPrefix":{"type":"string"},"identifierDigits":{"type":"integer"},"counter":{}}},"zoho":{"type":"object","properties":{"stop":{"type":"string","description":"off: as today. crm: nothing is read from or sent to Zoho CRM (the import, the Zoho sale steps and buttons). all: Zoho Desk stops too"},"booksServiceDetailFields":{"type":"object","properties":{"pppoePassword":{"type":"string"},"smIp":{"type":"string"},"smPassword":{"type":"string"}}},"subscription":{},"billing":{}}},"tickets":{"type":"object","properties":{"enabled":{"type":"boolean"},"purpose":{"type":"string"}}},"approvals":{"type":"object","properties":{"reviewRoles":{"type":"array","items":{"type":"string"}}}},"rollout":{"type":"object","properties":{"effectiveFrom":{"type":"string"}}},"lineEvents":{"type":"object","properties":{"extensionDateField":{}}},"service":{"type":"object","properties":{"fieldMap":{"type":"array"}}},"followUps":{"type":"object","properties":{"overdue":{"type":"object","properties":{"enabled":{"type":"boolean"},"moduleSlug":{},"createdBy":{},"followTestRuns":{"type":"boolean"},"steps":{"type":"array"},"clearTransition":{},"skipWhen":{"type":"array"}}},"retention":{"type":"object","properties":{"enabled":{"type":"boolean"},"moduleSlug":{},"createdBy":{},"since":{},"reconnectTransition":{},"skipWhen":{"type":"array"}}}}},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}},"blockers":{"type":"array","description":"Why the configuration cannot work as saved (e.g. a ticket required while tickets are off); empty when it can"},"serviceIdentifierCounter":{"type":"object","properties":{"lastUsed":{"type":"integer","description":"The last generated service number used; null until first set or used"},"updatedAt":{"type":"string","format":"date-time"},"source":{"type":"string","description":"manual (set by a person) or allocation"}}}}},"example":{"success":true,"settings":{"version":7,"enabled":true,"pipelineIds":["e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8a9b"],"requires":{"subscription":true,"installation":true,"ticket":true},"triggers":{"create":{"trigger":"verified_payment","transitionId":"f6a7b8c9-d0e1-4f2a-9b3c-5d6e7f8a9b0c"},"activate":{"transitionId":null},"estimateSent":{"transitionId":null},"installationCompleted":{"transitionId":null},"paymentReceived":{"transitionId":"f6a7b8c9-d0e1-4f2a-9b3c-5d6e7f8a9b0c"},"paymentUndone":{"transitionId":null}},"eligibility":{"basis":"full_settlement","allowAllocatedCredits":false,"exceptionRoles":["finance_manager"]},"quote":{"itemRoles":["plan","installation"],"prepaidFirstPeriod":true,"validityDays":30,"contractTerms":["12 months"],"allowPriceOverride":false,"serviceTaxKind":"vat16","oneOffTaxKind":"vat16","planLine":{"description":null,"treatment":"invoice","discountAllowed":true},"estimateTerms":"Payment is due before installation.","estimateNotes":null,"askDealFields":[],"planLineRules":[]},"billing":{"anchor":"activation","prepaidDays":30,"termsDays":15,"currency":"KES","timezone":"Africa/Nairobi","monthlyItemIds":[],"noInvoiceWhen":[],"startInBooks":false,"leaveProfilePaused":false,"booksLifecycle":false,"reconnectOnPaidOrders":false,"cutsWithoutEvidenceWhen":[],"cutsWithoutEvidenceDateField":null,"cutsWithoutEvidenceWithinDays":null,"allowSuspend":false},"payments":{"reference":"123456","instructions":"Pay by M-Pesa to paybill {reference}, account: your name."},"fields":{"installersFieldId":"a7b8c9d0-e1f2-4a3b-8c4d-6e7f8a9b0c1d","appointmentFieldId":null,"siteFieldId":null,"connectionMethodFieldId":null,"serviceIdentifierFieldId":"b8c9d0e1-f2a3-4b4c-9d5e-7f8a9b0c1d2e","completionTimeFieldId":null,"complimentaryFieldId":null},"technicians":{"mode":"many","roles":[]},"provisioning":{"mode":"generated","requiredAt":"booking","identifierLabel":"PPPoE account","identifierPattern":"^acme\\d{7}$","identifierHint":"acme followed by 7 digits","identifierPrefix":"acme","identifierDigits":7,"counter":null},"zoho":{"stop":"off","booksServiceDetailFields":{"pppoePassword":"cf_pppoe_password","smIp":"cf_sm_ip","smPassword":"cf_sm_password"},"subscription":null,"billing":null},"tickets":{"enabled":true,"purpose":"installation"},"approvals":{"reviewRoles":["operations_manager"]},"rollout":{"effectiveFrom":"2026-10-01"},"lineEvents":{"extensionDateField":null},"service":{"fieldMap":[]},"followUps":{"overdue":{"enabled":false,"moduleSlug":null,"createdBy":null,"followTestRuns":false,"steps":[],"clearTransition":null,"skipWhen":[]},"retention":{"enabled":false,"moduleSlug":null,"createdBy":null,"since":null,"reconnectTransition":null,"skipWhen":[]}},"updatedAt":"2026-10-20T09:00:00.000Z","updatedBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"},"blockers":[],"serviceIdentifierCounter":{"lastUsed":5537,"updatedAt":"2026-10-20T09:00:00.000Z","source":"manual"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.onboarding.read"]}],"x-required-scopes":["crm.onboarding.read"]},"put":{"tags":["Settings"],"summary":"Save the paid-onboarding settings","description":"Saves your organisation's paid-onboarding settings and records who changed what. Only the sections you send change. It still answers 200 when nothing is saved: then saved is false, settings holds the current values and blockers says why (someone else saved since your version, or the configuration would contradict itself, e.g. a ticket required while tickets are off).\n\n**Permission:** `crm.onboarding.update` (Manage onboarding settings).","operationId":"updateOnboardingSettings","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","description":"The sections to change, in the shape GET /organization/onboarding-settings answers (e.g. { \"billing\": {...} }). Sections left out stay as they are; a zoho section keeps the parts it does not name, and the billing switches (startInBooks, leaveProfilePaused, booksLifecycle, allowSuspend) change only when named","examples":[{"tickets":{"enabled":true,"purpose":"installation"}}]},"version":{"type":"integer","description":"The settings version from your last read. If someone has saved since, nothing is saved and saved comes back false","examples":[7]}},"required":["settings","version"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"saved":{"type":"boolean","description":"false when nothing was saved (see blockers)"},"settings":{"type":"object","properties":{"version":{"type":"integer","description":"Goes up by one with every save; send it back with the next save"},"enabled":{"type":"boolean","description":"Master switch: off means no onboarding automation runs at all"},"pipelineIds":{"type":"array","items":{"type":"string"},"description":"Deal pipelines that take part; empty means none"},"requires":{"type":"object","properties":{"subscription":{"type":"boolean"},"installation":{"type":"boolean"},"ticket":{"type":"boolean"}}},"triggers":{"type":"object","properties":{"create":{"type":"object","properties":{"trigger":{"type":"string","description":"When a service is created: verified_payment, approved_order or manual"},"transitionId":{"type":"string"}}},"activate":{"type":"object","properties":{"transitionId":{}}},"estimateSent":{"type":"object","properties":{"transitionId":{}}},"installationCompleted":{"type":"object","properties":{"transitionId":{}}},"paymentReceived":{"type":"object","properties":{"transitionId":{"type":"string"}}},"paymentUndone":{"type":"object","properties":{"transitionId":{}}}}},"eligibility":{"type":"object","properties":{"basis":{"type":"string","description":"What counts as paid enough: full_settlement or authorized_credit"},"allowAllocatedCredits":{"type":"boolean"},"exceptionRoles":{"type":"array","items":{"type":"string"}}}},"quote":{"type":"object","properties":{"itemRoles":{"type":"array","items":{"type":"string"}},"prepaidFirstPeriod":{"type":"boolean"},"validityDays":{"type":"integer"},"contractTerms":{"type":"array","items":{"type":"string"}},"allowPriceOverride":{"type":"boolean"},"serviceTaxKind":{"type":"string"},"oneOffTaxKind":{"type":"string"},"planLine":{"type":"object","properties":{"description":{},"treatment":{"type":"string"},"discountAllowed":{"type":"boolean"}}},"estimateTerms":{"type":"string"},"estimateNotes":{},"askDealFields":{"type":"array"},"planLineRules":{"type":"array"}}},"billing":{"type":"object","properties":{"anchor":{"type":"string","description":"Where the first billing cycle starts: activation, contract_date or approved_date"},"prepaidDays":{"type":"integer"},"termsDays":{"type":"integer"},"currency":{"type":"string"},"timezone":{"type":"string"},"monthlyItemIds":{"type":"array"},"noInvoiceWhen":{"type":"array"},"startInBooks":{"type":"boolean"},"leaveProfilePaused":{"type":"boolean"},"booksLifecycle":{"type":"boolean"},"reconnectOnPaidOrders":{"type":"boolean"},"cutsWithoutEvidenceWhen":{"type":"array"},"cutsWithoutEvidenceDateField":{},"cutsWithoutEvidenceWithinDays":{},"allowSuspend":{"type":"boolean"}}},"payments":{"type":"object","properties":{"reference":{"type":"string"},"instructions":{"type":"string"}}},"fields":{"type":"object","properties":{"installersFieldId":{"type":"string"},"appointmentFieldId":{},"siteFieldId":{},"connectionMethodFieldId":{},"serviceIdentifierFieldId":{"type":"string"},"completionTimeFieldId":{},"complimentaryFieldId":{}}},"technicians":{"type":"object","properties":{"mode":{"type":"string","description":"one or many technicians per job"},"roles":{"type":"array"}}},"provisioning":{"type":"object","properties":{"mode":{"type":"string","description":"How the service identifier (e.g. PPPoE account) is obtained: none, staff_entered, external_system, generated or zoho_generated"},"requiredAt":{"type":"string","description":"When the identifier must exist: booking or completion"},"identifierLabel":{"type":"string"},"identifierPattern":{"type":"string"},"identifierHint":{"type":"string"},"identifierPrefix":{"type":"string"},"identifierDigits":{"type":"integer"},"counter":{}}},"zoho":{"type":"object","properties":{"stop":{"type":"string","description":"off: as today. crm: nothing is read from or sent to Zoho CRM (the import, the Zoho sale steps and buttons). all: Zoho Desk stops too"},"booksServiceDetailFields":{"type":"object","properties":{"pppoePassword":{"type":"string"},"smIp":{"type":"string"},"smPassword":{"type":"string"}}},"subscription":{},"billing":{}}},"tickets":{"type":"object","properties":{"enabled":{"type":"boolean"},"purpose":{"type":"string"}}},"approvals":{"type":"object","properties":{"reviewRoles":{"type":"array","items":{"type":"string"}}}},"rollout":{"type":"object","properties":{"effectiveFrom":{"type":"string"}}},"lineEvents":{"type":"object","properties":{"extensionDateField":{}}},"service":{"type":"object","properties":{"fieldMap":{"type":"array"}}},"followUps":{"type":"object","properties":{"overdue":{"type":"object","properties":{"enabled":{"type":"boolean"},"moduleSlug":{},"createdBy":{},"followTestRuns":{"type":"boolean"},"steps":{"type":"array"},"clearTransition":{},"skipWhen":{"type":"array"}}},"retention":{"type":"object","properties":{"enabled":{"type":"boolean"},"moduleSlug":{},"createdBy":{},"since":{},"reconnectTransition":{},"skipWhen":{"type":"array"}}}}},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}},"blockers":{"type":"array","description":"Why the configuration cannot work as saved (e.g. a ticket required while tickets are off); empty when it can"}}},"example":{"success":true,"saved":true,"settings":{"version":8,"enabled":true,"pipelineIds":["e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8a9b"],"requires":{"subscription":true,"installation":true,"ticket":true},"triggers":{"create":{"trigger":"verified_payment","transitionId":"f6a7b8c9-d0e1-4f2a-9b3c-5d6e7f8a9b0c"},"activate":{"transitionId":null},"estimateSent":{"transitionId":null},"installationCompleted":{"transitionId":null},"paymentReceived":{"transitionId":"f6a7b8c9-d0e1-4f2a-9b3c-5d6e7f8a9b0c"},"paymentUndone":{"transitionId":null}},"eligibility":{"basis":"full_settlement","allowAllocatedCredits":false,"exceptionRoles":["finance_manager"]},"quote":{"itemRoles":["plan","installation"],"prepaidFirstPeriod":true,"validityDays":30,"contractTerms":["12 months"],"allowPriceOverride":false,"serviceTaxKind":"vat16","oneOffTaxKind":"vat16","planLine":{"description":null,"treatment":"invoice","discountAllowed":true},"estimateTerms":"Payment is due before installation.","estimateNotes":null,"askDealFields":[],"planLineRules":[]},"billing":{"anchor":"activation","prepaidDays":30,"termsDays":15,"currency":"KES","timezone":"Africa/Nairobi","monthlyItemIds":[],"noInvoiceWhen":[],"startInBooks":false,"leaveProfilePaused":false,"booksLifecycle":false,"reconnectOnPaidOrders":false,"cutsWithoutEvidenceWhen":[],"cutsWithoutEvidenceDateField":null,"cutsWithoutEvidenceWithinDays":null,"allowSuspend":false},"payments":{"reference":"123456","instructions":"Pay by M-Pesa to paybill {reference}, account: your name."},"fields":{"installersFieldId":"a7b8c9d0-e1f2-4a3b-8c4d-6e7f8a9b0c1d","appointmentFieldId":null,"siteFieldId":null,"connectionMethodFieldId":null,"serviceIdentifierFieldId":"b8c9d0e1-f2a3-4b4c-9d5e-7f8a9b0c1d2e","completionTimeFieldId":null,"complimentaryFieldId":null},"technicians":{"mode":"many","roles":[]},"provisioning":{"mode":"generated","requiredAt":"booking","identifierLabel":"PPPoE account","identifierPattern":"^acme\\d{7}$","identifierHint":"acme followed by 7 digits","identifierPrefix":"acme","identifierDigits":7,"counter":null},"zoho":{"stop":"off","booksServiceDetailFields":{"pppoePassword":"cf_pppoe_password","smIp":"cf_sm_ip","smPassword":"cf_sm_password"},"subscription":null,"billing":null},"tickets":{"enabled":true,"purpose":"installation"},"approvals":{"reviewRoles":["operations_manager"]},"rollout":{"effectiveFrom":"2026-10-01"},"lineEvents":{"extensionDateField":null},"service":{"fieldMap":[]},"followUps":{"overdue":{"enabled":false,"moduleSlug":null,"createdBy":null,"followTestRuns":false,"steps":[],"clearTransition":null,"skipWhen":[]},"retention":{"enabled":false,"moduleSlug":null,"createdBy":null,"since":null,"reconnectTransition":null,"skipWhen":[]}},"updatedAt":"2026-10-20T09:00:00.000Z","updatedBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"},"blockers":[]}}}},"400":{"description":"settings is not an object, version is not a whole number, or a value is not allowed (an unknown option, a billing rule naming a field or Books item that does not exist, text too long); every problem is listed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.onboarding.update"]}],"x-required-scopes":["crm.onboarding.update"]}},"/organization/network-gateway":{"get":{"tags":["Settings"],"summary":"Get the network gateway settings","description":"Answers whether CRM drives your network when a line is disconnected or reconnected, and where it calls. An organisation that never set this up gets mode off and no targets, version 0. blockers lists what stops the settings working.\n\n**Permission:** `crm.networkGateway.admin` (Manage network gateway settings).","operationId":"getNetworkGatewaySettings","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"settings":{"type":"object","properties":{"version":{"type":"integer"},"mode":{"type":"string","description":"off: CRM touches no network. shadow: it records each request it would send and calls nothing. live: it makes the calls"},"provider":{"type":"string"},"targets":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"disconnectPath":{"type":"string"},"connectPath":{"type":"string"},"planChangePath":{},"decisive":{"type":"boolean"},"answer":{"type":"string"},"credentialName":{"type":"string"},"credentialIn":{"type":"string"},"hasCredential":{"type":"boolean","description":"Whether a credential is stored. The credential itself never answers"},"baseUrl":{"type":"string","description":"Null when the stored address cannot be read on this server"}}}},"scheduledCutHours":{"type":"array","items":{"type":"integer"}},"autoReconnectOnPayment":{"type":"boolean"},"planChanges":{"type":"boolean"},"offSignal":{},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}},"blockers":{"type":"array","items":{"type":"string"}}}},"example":{"success":true,"settings":{"version":3,"mode":"shadow","provider":"http_calls","targets":[{"key":"core","label":"Core network","disconnectPath":"/api/line/off","connectPath":"/api/line/on","planChangePath":null,"decisive":true,"answer":"json_ok","credentialName":"api_key","credentialIn":"body","hasCredential":true,"baseUrl":"https://network.example.com"}],"scheduledCutHours":[17,18],"autoReconnectOnPayment":false,"planChanges":false,"offSignal":null,"updatedAt":"2026-11-04T09:00:00.000Z","updatedBy":"Example Admin"},"blockers":["Add at least one network target."]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.networkGateway.admin"]}],"x-required-scopes":["crm.networkGateway.admin"]},"put":{"tags":["Settings"],"summary":"Save the network gateway settings","description":"Saves whether CRM drives your network when a line is disconnected or reconnected, and where it calls. Only what you send changes. It still answers 200 when nothing is saved: then saved is false, settings holds the current values and blockers says why (someone else saved since your version, or the mode cannot work with these targets). Switching to live means CRM really cuts and reconnects customers' lines.\n\n**Permission:** `crm.networkGateway.admin` (Manage network gateway settings).","operationId":"updateNetworkGatewaySettings","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","description":"What to change: mode (off, shadow or live), scheduledCutHours (hours of the day, in your billing time zone, when scheduled disconnections are carried out; empty keeps the daily morning run), autoReconnectOnPayment (true or false), planChanges (true or false: a plan change goes to the network too; a line that is off waits for its reconnection), offSignal (null, or { field, values }: the custom field, and its values, that say a line is off when CRM has sent no command for it yet), targets. targets replaces the list, each matched by its key: key, label, baseUrl, disconnectPath, connectPath (null when the target takes no part), planChangePath (null to use connectPath), decisive, answer (json_ok, text_ok or http_2xx), credentialName, credentialIn (body or header), credential. A target leaving out baseUrl or credential keeps the stored one; credential null clears it","examples":[{"mode":"shadow"}]},"version":{"type":"integer","description":"The settings version from your last read. If someone has saved since, nothing is saved and saved comes back false","examples":[3]}},"required":["settings","version"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"saved":{"type":"boolean","description":"false when nothing was saved (see blockers)"},"settings":{"type":"object","properties":{"version":{"type":"integer"},"mode":{"type":"string"},"provider":{"type":"string"},"targets":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"disconnectPath":{"type":"string"},"connectPath":{"type":"string"},"planChangePath":{},"decisive":{"type":"boolean"},"answer":{"type":"string"},"credentialName":{"type":"string"},"credentialIn":{"type":"string"},"hasCredential":{"type":"boolean"},"baseUrl":{"type":"string"}}}},"scheduledCutHours":{"type":"array","items":{"type":"integer"}},"autoReconnectOnPayment":{"type":"boolean"},"planChanges":{"type":"boolean"},"offSignal":{},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}},"blockers":{"type":"array","items":{"type":"string"}}}},"example":{"success":true,"saved":true,"settings":{"version":4,"mode":"shadow","provider":"http_calls","targets":[{"key":"core","label":"Core network","disconnectPath":"/api/line/off","connectPath":"/api/line/on","planChangePath":null,"decisive":true,"answer":"json_ok","credentialName":"api_key","credentialIn":"body","hasCredential":true,"baseUrl":"https://network.example.com"}],"scheduledCutHours":[17,18],"autoReconnectOnPayment":false,"planChanges":false,"offSignal":null,"updatedAt":"2026-11-04T09:00:00.000Z","updatedBy":"Example Admin"},"blockers":["Add at least one network target."]}}}},"400":{"description":"settings is not an object, version is not a whole number, a value is not allowed, or this server has no encryption key to store addresses and credentials under; every problem is listed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.networkGateway.admin"]}],"x-required-scopes":["crm.networkGateway.admin"]}},"/organization/line-provisioning":{"get":{"tags":["Settings"],"summary":"Get the line provisioning settings","description":"Answers whether CRM makes new customers' lines and where it calls. An organisation that never set this up gets mode off and no targets, version 0. blockers lists what stops the settings working.\n\n**Permission:** `crm.lineProvisioning.admin` (Manage line provisioning).","operationId":"getLineProvisioningSettings","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"settings":{"type":"object","properties":{"version":{"type":"integer"},"mode":{"type":"string","description":"off: the Create the line step does nothing. shadow: it records each request it would send and calls nothing. live: it makes the line's details and the calls"},"targets":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"createPath":{"type":"string"},"decisive":{"type":"boolean"},"answer":{"type":"string"},"credentialName":{"type":"string"},"credentialIn":{"type":"string"},"hasCredential":{"type":"boolean","description":"Whether a credential is stored. The credential itself never answers"},"body":{"type":"object","properties":{"name":{"type":"string"},"pw":{"type":"string"},"plan":{"type":"string"}}},"baseUrl":{"type":"string","description":"Null when the stored address cannot be read on this server"}}}},"pppoePassword":{"type":"object","properties":{"length":{"type":"integer"},"suffix":{"type":"string"}}},"sm":{"type":"object","properties":{"poolFirst":{"type":"string"},"poolLast":{"type":"string"},"passwordLength":{"type":"integer"},"connectionField":{"type":"string"},"connectionMatches":{"type":"array","items":{"type":"string"}},"existingCustomerField":{"type":"string"},"existingCustomerValues":{"type":"array","items":{"type":"string"}}}},"noLineWhen":{"type":"object","properties":{"dealField":{"type":"string"},"values":{"type":"array","items":{"type":"string"}}}},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}},"blockers":{"type":"array","items":{"type":"string"}}}},"example":{"success":true,"settings":{"version":3,"mode":"shadow","targets":[{"key":"core","label":"Core network","createPath":"/api/line","decisive":true,"answer":"json_ok","credentialName":"api_key","credentialIn":"body","hasCredential":true,"body":{"name":"pppoe","pw":"pppoePassword","plan":"planName"},"baseUrl":"https://network.example.com"}],"pppoePassword":{"length":12,"suffix":""},"sm":{"poolFirst":"192.0.2.10","poolLast":"192.0.2.250","passwordLength":13,"connectionField":"connection_method","connectionMatches":["wireless"],"existingCustomerField":"existing_clients","existingCustomerValues":["Yes"]},"noLineWhen":{"dealField":"status_level","values":["Wholesale"]},"updatedAt":"2026-11-04T09:00:00.000Z","updatedBy":"Example Admin"},"blockers":["Add at least one network target."]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.lineProvisioning.admin"]}],"x-required-scopes":["crm.lineProvisioning.admin"]},"put":{"tags":["Settings"],"summary":"Save the line provisioning settings","description":"Saves whether CRM makes new customers' lines, and where it calls. Only what you send changes. It still answers 200 when nothing is saved: then saved is false, settings holds the current values and blockers says why (someone else saved since your version, or the mode cannot work with these settings). Switching to live means the Create the line step really creates accounts on your network.\n\n**Permission:** `crm.lineProvisioning.admin` (Manage line provisioning).","operationId":"updateLineProvisioningSettings","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","description":"What to change: mode (off, shadow or live), pppoePassword (length, suffix), sm (poolFirst, poolLast, passwordLength, connectionField, connectionMatches, existingCustomerField, existingCustomerValues; null for no radio details), noLineWhen (dealField, values; null for none), targets. targets replaces the list, each matched by its key: key, label, baseUrl, createPath, decisive, answer (json_ok, text_ok or http_2xx), credentialName, credentialIn (body or header), credential, body (each name the request carries and which value it holds: pppoe, pppoePassword, accountName, planName or subscriptionId). A target leaving out baseUrl or credential keeps the stored one; credential null clears it","examples":[{"mode":"shadow"}]},"version":{"type":"integer","description":"The settings version from your last read. If someone has saved since, nothing is saved and saved comes back false","examples":[3]}},"required":["settings","version"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"saved":{"type":"boolean","description":"false when nothing was saved (see blockers)"},"settings":{"type":"object","properties":{"version":{"type":"integer"},"mode":{"type":"string"},"targets":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"createPath":{"type":"string"},"decisive":{"type":"boolean"},"answer":{"type":"string"},"credentialName":{"type":"string"},"credentialIn":{"type":"string"},"hasCredential":{"type":"boolean"},"body":{"type":"object","properties":{"name":{"type":"string"},"pw":{"type":"string"},"plan":{"type":"string"}}},"baseUrl":{"type":"string"}}}},"pppoePassword":{"type":"object","properties":{"length":{"type":"integer"},"suffix":{"type":"string"}}},"sm":{"type":"object","properties":{"poolFirst":{"type":"string"},"poolLast":{"type":"string"},"passwordLength":{"type":"integer"},"connectionField":{"type":"string"},"connectionMatches":{"type":"array","items":{"type":"string"}},"existingCustomerField":{"type":"string"},"existingCustomerValues":{"type":"array","items":{"type":"string"}}}},"noLineWhen":{"type":"object","properties":{"dealField":{"type":"string"},"values":{"type":"array","items":{"type":"string"}}}},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}},"blockers":{"type":"array","items":{"type":"string"}}}},"example":{"success":true,"saved":true,"settings":{"version":4,"mode":"shadow","targets":[{"key":"core","label":"Core network","createPath":"/api/line","decisive":true,"answer":"json_ok","credentialName":"api_key","credentialIn":"body","hasCredential":true,"body":{"name":"pppoe","pw":"pppoePassword","plan":"planName"},"baseUrl":"https://network.example.com"}],"pppoePassword":{"length":12,"suffix":""},"sm":{"poolFirst":"192.0.2.10","poolLast":"192.0.2.250","passwordLength":13,"connectionField":"connection_method","connectionMatches":["wireless"],"existingCustomerField":"existing_clients","existingCustomerValues":["Yes"]},"noLineWhen":{"dealField":"status_level","values":["Wholesale"]},"updatedAt":"2026-11-04T09:00:00.000Z","updatedBy":"Example Admin"},"blockers":["Add at least one network target."]}}}},"400":{"description":"settings is not an object, version is not a whole number, a value is not allowed, or this server has no encryption key to store addresses and credentials under; every problem is listed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.lineProvisioning.admin"]}],"x-required-scopes":["crm.lineProvisioning.admin"]}},"/organization/line-lifecycle":{"get":{"tags":["Settings"],"summary":"Get what a cut or reconnection does","description":"Answers what a cut or reconnection does on a line besides its status and the network. An organisation that never set this up gets everything off, version 0.\n\n**Permission:** `crm.lineLifecycle.admin` (Manage what a cut or reconnection does).","operationId":"getLineLifecycleSettings","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"settings":{"type":"object","properties":{"version":{"type":"integer"},"cutDates":{"type":"object","properties":{"enabled":{"type":"boolean"},"clearOnCut":{"type":"array","items":{"type":"string"}},"clearOnCutSources":{"type":"array","items":{"type":"string"}},"keepIfTodayIs":{"type":"string"},"followUps":{"type":"array","items":{"type":"object","properties":{"reason":{"type":"string"},"field":{"type":"string"},"months":{"type":"integer"}}}},"holdingFee":{"type":"object","properties":{"field":{"type":"string"},"levelField":{"type":"string"},"levels":{"type":"array","items":{"type":"string"}},"migratedField":{"type":"string"},"minPaidBills":{"type":"integer"},"longMonths":{"type":"integer"},"shortMonths":{"type":"integer"}}},"clearOnReconnect":{"type":"array","items":{"type":"string"}}},"description":"The dates a cut sets or clears on the line, and those a reconnection clears. Only with enabled true"},"customerMessages":{"type":"object","properties":{"onCut":{"type":"boolean"},"onReconnect":{"type":"boolean"},"cutSources":{"type":"array","items":{"type":"string"}},"reconnectSources":{"type":"array"}},"description":"Whether the customer is emailed the service_disconnected or service_reactivated template. The sources lists narrow it to some kinds of cut or reconnection"},"reconnectWithoutCycle":{"type":"boolean","description":"Whether a line with no billing cycle can be reconnected. It also can whenever the network gateway is on"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}}}},"example":{"success":true,"settings":{"version":2,"cutDates":{"enabled":true,"clearOnCut":["disconnectionDate","grace_end_date"],"clearOnCutSources":["approve_list"],"keepIfTodayIs":"grace_end_date","followUps":[{"reason":"Abroad","field":"follow_up_date","months":2}],"holdingFee":{"field":"hold_fee_start","levelField":"line_level","levels":["Standard",""],"migratedField":"moved_on","minPaidBills":6,"longMonths":3,"shortMonths":1},"clearOnReconnect":["hold_fee_start","hold_fee_end"]},"customerMessages":{"onCut":true,"onReconnect":true,"cutSources":["approve_list"],"reconnectSources":[]},"reconnectWithoutCycle":true,"updatedAt":"2026-11-04T09:00:00.000Z","updatedBy":"Example Admin"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.lineLifecycle.admin"]}],"x-required-scopes":["crm.lineLifecycle.admin"]},"put":{"tags":["Settings"],"summary":"Save what a cut or reconnection does","description":"Saves the dates a cut or reconnection keeps on a line and whether the customer gets a message. Only what you send changes. It still answers 200 when nothing is saved: then saved is false, settings holds the current values and blockers says why (someone else saved since your version). Switching a message on means CRM really emails customers.\n\n**Permission:** `crm.lineLifecycle.admin` (Manage what a cut or reconnection does).","operationId":"updateLineLifecycleSettings","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","description":"What to change. cutDates: enabled, clearOnCut (disconnectionDate or custom fields), clearOnCutSources, keepIfTodayIs (a custom date field), followUps ({ reason, field, months }), holdingFee (null or { field, levelField, levels, migratedField, minPaidBills, longMonths, shortMonths }), clearOnReconnect. customerMessages: onCut, onReconnect, cutSources, reconnectSources. reconnectWithoutCycle: true or false","examples":[{"customerMessages":{"onReconnect":true}}]},"version":{"type":"integer","description":"The settings version from your last read. If someone has saved since, nothing is saved and saved comes back false","examples":[2]}},"required":["settings","version"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"saved":{"type":"boolean","description":"false when nothing was saved (see blockers)"},"settings":{"type":"object","properties":{"version":{"type":"integer"},"cutDates":{"type":"object","properties":{"enabled":{"type":"boolean"},"clearOnCut":{"type":"array","items":{"type":"string"}},"clearOnCutSources":{"type":"array","items":{"type":"string"}},"keepIfTodayIs":{"type":"string"},"followUps":{"type":"array","items":{"type":"object","properties":{"reason":{"type":"string"},"field":{"type":"string"},"months":{"type":"integer"}}}},"holdingFee":{"type":"object","properties":{"field":{"type":"string"},"levelField":{"type":"string"},"levels":{"type":"array","items":{"type":"string"}},"migratedField":{"type":"string"},"minPaidBills":{"type":"integer"},"longMonths":{"type":"integer"},"shortMonths":{"type":"integer"}}},"clearOnReconnect":{"type":"array","items":{"type":"string"}}}},"customerMessages":{"type":"object","properties":{"onCut":{"type":"boolean"},"onReconnect":{"type":"boolean"},"cutSources":{"type":"array","items":{"type":"string"}},"reconnectSources":{"type":"array"}}},"reconnectWithoutCycle":{"type":"boolean"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}},"blockers":{"type":"array","items":{"type":"string"}}}},"example":{"success":true,"saved":true,"settings":{"version":3,"cutDates":{"enabled":true,"clearOnCut":["disconnectionDate","grace_end_date"],"clearOnCutSources":["approve_list"],"keepIfTodayIs":"grace_end_date","followUps":[{"reason":"Abroad","field":"follow_up_date","months":2}],"holdingFee":{"field":"hold_fee_start","levelField":"line_level","levels":["Standard",""],"migratedField":"moved_on","minPaidBills":6,"longMonths":3,"shortMonths":1},"clearOnReconnect":["hold_fee_start","hold_fee_end"]},"customerMessages":{"onCut":true,"onReconnect":true,"cutSources":["approve_list"],"reconnectSources":[]},"reconnectWithoutCycle":true,"updatedAt":"2026-11-04T09:00:00.000Z","updatedBy":"Example Admin"},"blockers":["Someone else saved these settings while you were editing. Review the current values and try again."]}}}},"400":{"description":"settings is not an object, version is not a whole number, or a value is not allowed; every problem is listed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.lineLifecycle.admin"]}],"x-required-scopes":["crm.lineLifecycle.admin"]}},"/organization/document-settings":{"get":{"tags":["Settings"],"summary":"Get the document settings","description":"Answers what your quote PDFs print about your organisation beside its name: the tagline, with the version a save should send back.\n\n**Permission:** `crm.onboarding.read` (View onboarding settings).","operationId":"getDocumentSettings","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"settings":{"type":"object","properties":{"tagline":{"type":"string","description":"Printed under the organisation's name on quote PDFs; null means no tagline"},"version":{"type":"integer","description":"Send it back with the next save"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}}}},"example":{"success":true,"settings":{"tagline":"Fast, reliable fibre for your home","version":3,"updatedAt":"2026-10-20T09:00:00.000Z","updatedBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.onboarding.read"]}],"x-required-scopes":["crm.onboarding.read"]},"put":{"tags":["Settings"],"summary":"Save the document settings","description":"Sets or clears the tagline your quote PDFs print under your organisation's name. Only what you send changes, and the change is recorded with who made it.\n\n**Permission:** `crm.onboarding.update` (Manage onboarding settings).","operationId":"updateDocumentSettings","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"tagline":{"type":"string","maxLength":120,"description":"One line of up to 120 characters; null or an empty text removes the tagline; leave out to keep it","examples":["Fast, reliable fibre for your home"]},"version":{"type":"integer","description":"The version from your last read. When sent and someone has saved since, nothing is saved and the call answers 409; leave it out to save over whatever is there","examples":[3]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"settings":{"type":"object","properties":{"tagline":{"type":"string"},"version":{"type":"integer"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}}}},"example":{"success":true,"settings":{"tagline":"Fast, reliable fibre for your home","version":4,"updatedAt":"2026-10-20T09:00:00.000Z","updatedBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"}}}}},"400":{"description":"tagline is not text, is over 120 characters or is more than one line; or version is not a whole number","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"Someone saved the settings after the version you sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.onboarding.update"]}],"x-required-scopes":["crm.onboarding.update"]}},"/organization/quote-settings":{"get":{"tags":["Settings"],"summary":"Get the quote settings","description":"Answers how your organisation quotes: its saved preferences (with the version a save should send back), and the quote engine and deal value rule actually in force.\n\n**Permission:** `crm.onboarding.read` (View onboarding settings).","operationId":"getQuoteSettings","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"settings":{"type":"object","properties":{"engine":{"type":"string","description":"Saved choice: v2 (the Kazee Books-linked engine) or v3 (the general one); null when not chosen"},"validityDays":{"type":"integer"},"terms":{"type":"string"},"allowPriceOverride":{"type":"boolean"},"confirmFields":{"type":"array","items":{"type":"string"}},"estimateMirror":{"type":"string","description":"auto: a quote also becomes an estimate in Kazee Books when connected; off: quotes stay in CRM"},"wording":{"type":"object","properties":{"fulfilmentStep":{"type":"string"},"billingStep":{"type":"string"}}},"valueRule":{},"version":{"type":"integer","description":"Send it back with the next save"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}},"engine":{"type":"string","description":"The engine in force: the saved one, else v2 for an organisation with paid-onboarding settings and v3 for every other"},"valueRule":{"type":"string","description":"What a deal's value is, in force: recurring_incl_tax (monthly total of recurring lines), all_lines (every line) or manual (typed by a person)"}}},"example":{"success":true,"settings":{"engine":"v3","validityDays":30,"terms":"Prices include VAT. Valid for 30 days.","allowPriceOverride":true,"confirmFields":["installationAddress"],"estimateMirror":"auto","wording":{"fulfilmentStep":"Installation","billingStep":"Start billing"},"valueRule":null,"version":2,"updatedAt":"2026-10-20T09:00:00.000Z","updatedBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"},"engine":"v3","valueRule":"all_lines"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.onboarding.read"]}],"x-required-scopes":["crm.onboarding.read"]},"put":{"tags":["Settings"],"summary":"Save the quote settings","description":"Changes how your organisation quotes. Only the fields you send change; the change is recorded with who made it. Answers the saved settings with the engine and value rule now in force.\n\n**Permission:** `crm.onboarding.update` (Manage onboarding settings).","operationId":"updateQuoteSettings","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"allowPriceOverride":{"type":"boolean","description":"Whether people may change a catalogue price on a quote line","examples":[true]},"confirmFields":{"type":"array","items":{"type":"string"},"description":"Up to 10 deal field keys the quote dialog asks a person to confirm before creating the quote","examples":[["installationAddress"]]},"engine":{"type":"string","enum":["v2","v3",null],"description":"Quote engine: v2 (every line a Kazee Books item, as paid onboarding uses) or v3 (lines from the deal's products, priced from the CRM catalogue or Books); null goes back to the automatic choice","examples":["v3"]},"estimateMirror":{"type":"string","enum":["auto","off"],"description":"auto: a quote also becomes an estimate in Kazee Books when connected; off: quotes stay in CRM","examples":["auto"]},"terms":{"type":"string","maxLength":2000,"description":"Terms printed with the quote; null or empty removes them","examples":["Prices include VAT. Valid for 30 days."]},"validityDays":{"type":"integer","minimum":1,"maximum":365,"description":"How many days a quote stays valid","examples":[30]},"valueRule":{"type":"string","enum":["recurring_incl_tax","all_lines","manual",null],"description":"What a deal's value is: recurring_incl_tax (monthly total of recurring lines, tax included), all_lines (every line's total) or manual (typed by a person); null goes back to the engine's default","examples":["all_lines"]},"version":{"type":"integer","description":"The version from your last read. When sent and someone has saved since, nothing is saved and the call answers 409; leave it out to save over whatever is there","examples":[2]},"wording":{"type":"object","description":"Your own words for the deal billing card's steps, keyed fulfilmentStep, fulfilmentWaiting, fulfilmentDone, billingStep or billingStartsOn: one line of up to 80 characters each; null or empty removes a key","examples":[{"fulfilmentStep":"Installation","billingStep":"Start billing"}]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"settings":{"type":"object","properties":{"engine":{"type":"string","description":"Saved choice: v2 (the Kazee Books-linked engine) or v3 (the general one); null when not chosen"},"validityDays":{"type":"integer"},"terms":{"type":"string"},"allowPriceOverride":{"type":"boolean"},"confirmFields":{"type":"array","items":{"type":"string"}},"estimateMirror":{"type":"string","description":"auto: a quote also becomes an estimate in Kazee Books when connected; off: quotes stay in CRM"},"wording":{"type":"object","properties":{"fulfilmentStep":{"type":"string"},"billingStep":{"type":"string"}}},"valueRule":{},"version":{"type":"integer","description":"Send it back with the next save"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}},"engine":{"type":"string","description":"The engine in force: the saved one, else v2 for an organisation with paid-onboarding settings and v3 for every other"},"valueRule":{"type":"string","description":"What a deal's value is, in force: recurring_incl_tax (monthly total of recurring lines), all_lines (every line) or manual (typed by a person)"}}},"example":{"success":true,"settings":{"engine":"v3","validityDays":30,"terms":"Prices include VAT. Valid for 30 days.","allowPriceOverride":true,"confirmFields":["installationAddress"],"estimateMirror":"auto","wording":{"fulfilmentStep":"Installation","billingStep":"Start billing"},"valueRule":null,"version":3,"updatedAt":"2026-10-20T09:00:00.000Z","updatedBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"},"engine":"v3","valueRule":"all_lines"}}}},"400":{"description":"A value is not allowed: an unknown engine, estimateMirror or valueRule, validityDays outside 1 to 365, terms over 2,000 characters, more than 10 confirmFields or one that is not a field key, an unknown wording key or wording over 80 characters or more than one line","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"Someone saved the settings after the version you sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.onboarding.update"]}],"x-required-scopes":["crm.onboarding.update"]}},"/organization/link-buttons":{"get":{"tags":["Settings"],"summary":"List the link buttons","description":"Answers your organisation's link buttons: the name, the kind of record each sits on and the web address it opens, with {{field}} blanks filled from the record. Anyone signed in may read them.\n\n**Permission:** any signed-in member.","operationId":"getLinkButtons","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"settings":{"type":"object","properties":{"buttons":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"record":{"type":"string"},"url":{"type":"string"}}},"description":"Each button: id, label, record (subscription, contact, account, deal or ticket) and url, whose {{field}} blanks the screen fills from that record"},"version":{"type":"integer","description":"Send it back with the next save"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}}}},"example":{"success":true,"settings":{"buttons":[{"id":"troubleshoot","label":"Troubleshoot","record":"subscription","url":"https://ops.example.com/dashboard.html#usage/{{name}}"}],"version":3,"updatedAt":"2026-10-29T11:00:00.000Z","updatedBy":"00000000-0000-4000-8000-000000000001"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":[]}]},"put":{"tags":["Settings"],"summary":"Save the link buttons","description":"Replaces your organisation's link buttons with the list you send. A button without an id gets one. The change is recorded with who made it.\n\n**Permission:** `crm.linkButtons.admin` (Manage link buttons).","operationId":"updateLinkButtons","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"buttons":{"type":"array","items":{"type":"object"},"description":"Up to 30 buttons, each { id?, label, record, url }: label up to 40 characters; record subscription, contact, account, deal or ticket; url an https address whose {{field}} blanks (path, query or fragment only) are filled from the record","examples":[[{"id":"troubleshoot","label":"Troubleshoot","record":"subscription","url":"https://ops.example.com/dashboard.html#usage/{{name}}"}]]},"version":{"type":"integer","description":"The version from your last read. When sent and someone has saved since, nothing is saved and the call answers 409","examples":[3]}},"required":["buttons"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"settings":{"type":"object","properties":{"buttons":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"record":{"type":"string"},"url":{"type":"string"}}},"description":"The buttons now saved, each with its id"},"version":{"type":"integer","description":"Send it back with the next save"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}}}},"example":{"success":true,"settings":{"buttons":[{"id":"troubleshoot","label":"Troubleshoot","record":"subscription","url":"https://ops.example.com/dashboard.html#usage/{{name}}"}],"version":4,"updatedAt":"2026-10-29T11:00:00.000Z","updatedBy":"00000000-0000-4000-8000-000000000001"}}}}},"400":{"description":"A button is not allowed: more than 30 buttons, a label missing or over 40 characters, a record other than subscription, contact, account, deal or ticket, or an address that is not https, has a blank in its site, or a blank that is not a field name","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"Someone saved the buttons after the version you sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.linkButtons.admin"]}],"x-required-scopes":["crm.linkButtons.admin"]}},"/organization/taxes":{"get":{"tags":["Settings"],"summary":"List your organisation's taxes","description":"Answers your organisation's own tax list, used to tax quotes when Kazee Books is not connected (with Books, Books' own taxes apply and this list is not read).\n\n**Permission:** `crm.onboarding.read` (View onboarding settings).","operationId":"getOrganizationTaxes","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"settings":{"type":"object","properties":{"taxes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"ratePercent":{"type":"string","description":"Rate in percent with four decimals, e.g. 16.0000"},"active":{"type":"boolean","description":"false: switched off; a saved tax is never removed because products and quotes point at it"}}}},"defaultTaxId":{"type":"string","description":"The tax a new product starts with; null means none"},"version":{"type":"integer","description":"Send it back with the next save"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}}}},"example":{"success":true,"settings":{"taxes":[{"id":"c9d0e1f2-a3b4-4c5d-8e6f-8a9b0c1d2e3f","name":"VAT","ratePercent":"16.0000","active":true},{"id":"d0e1f2a3-b4c5-4d6e-9f7a-9b0c1d2e3f4a","name":"Excise duty","ratePercent":"15.0000","active":false}],"defaultTaxId":"c9d0e1f2-a3b4-4c5d-8e6f-8a9b0c1d2e3f","version":4,"updatedAt":"2026-10-20T09:00:00.000Z","updatedBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.onboarding.read"]}],"x-required-scopes":["crm.onboarding.read"]},"put":{"tags":["Settings"],"summary":"Save your organisation's taxes","description":"Saves your organisation's own tax list, used for quotes when Kazee Books is not connected. Send the whole list: saved taxes with their ID, new ones without. A saved tax cannot be removed, only switched off. A new rate applies to quotes made from then on. Recorded with who changed it.\n\n**Permission:** `crm.onboarding.update` (Manage onboarding settings).","operationId":"updateOrganizationTaxes","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"defaultTaxId":{"type":"string","format":"uuid","description":"The active tax a new product starts with; null or empty for none; leave out to keep it","examples":["c9d0e1f2-a3b4-4c5d-8e6f-8a9b0c1d2e3f"]},"taxes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"A saved tax's ID; leave out for a new tax"},"name":{"type":"string","description":"Up to 60 characters, one line"},"ratePercent":{"type":"string","description":"Percent from 0 to 100, at most 4 decimals, e.g. 16"},"active":{"type":"boolean","description":"false switches the tax off; default true"}}},"description":"The whole tax list, at most 30 (a JSON list in a string also works); leave out to keep the list as it is","examples":[[{"id":"c9d0e1f2-a3b4-4c5d-8e6f-8a9b0c1d2e3f","name":"VAT","ratePercent":"16"},{"name":"Excise duty","ratePercent":"15","active":false}]]},"version":{"type":"integer","description":"The version from your last read. When sent and someone has saved since, nothing is saved and the call answers 409; leave it out to save over whatever is there","examples":[4]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"settings":{"type":"object","properties":{"taxes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"ratePercent":{"type":"string","description":"Rate in percent with four decimals, e.g. 16.0000"},"active":{"type":"boolean","description":"false: switched off; a saved tax is never removed because products and quotes point at it"}}}},"defaultTaxId":{"type":"string","description":"The tax a new product starts with; null means none"},"version":{"type":"integer","description":"Send it back with the next save"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}}}},"example":{"success":true,"settings":{"taxes":[{"id":"c9d0e1f2-a3b4-4c5d-8e6f-8a9b0c1d2e3f","name":"VAT","ratePercent":"16.0000","active":true},{"id":"d0e1f2a3-b4c5-4d6e-9f7a-9b0c1d2e3f4a","name":"Excise duty","ratePercent":"15.0000","active":false}],"defaultTaxId":"c9d0e1f2-a3b4-4c5d-8e6f-8a9b0c1d2e3f","version":5,"updatedAt":"2026-10-20T09:00:00.000Z","updatedBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"}}}}},"400":{"description":"taxes is not a list or has over 30 taxes, a tax has an unknown ID, is listed twice, has no name or one over 60 characters, or a rate outside 0 to 100; a saved tax is left out; two taxes share a name; or defaultTaxId is not an active tax in the list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"Someone saved the taxes after the version you sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.onboarding.update"]}],"x-required-scopes":["crm.onboarding.update"]}},"/organization/flags":{"get":{"tags":["Settings"],"summary":"Get the organisation's billing flags","description":"Says whether Kazee Books is connected to your organisation, whether Books runs its subscriptions, and which quote engine its deals use. Any signed-in person of the organisation may ask. \"unknown\" means Books could not be asked just now: keep showing what was shown before.\n\n**Permission:** `crm.users.read` (View users).","operationId":"getOrganizationFlags","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"flags":{"type":"object","properties":{"books":{"type":"string","description":"on, off or unknown: whether Kazee Books is connected to this organisation"},"subscriptions":{"type":"string","description":"on, off or unknown: whether Kazee Books runs subscriptions for it"},"quoteEngine":{"type":"string","description":"v2 (the Books-linked quote engine) or v3 (the general one)"}}}}},"example":{"success":true,"flags":{"books":"on","subscriptions":"on","quoteEngine":"v2"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.users.read"]}],"x-required-scopes":["crm.users.read"]}},"/organization/locale":{"get":{"tags":["Settings"],"summary":"Get your organisation's country and time zone","description":"Answers your organisation's saved country and time zone (with the version a save should send back), and what they decide: the time zone in force, the dialling code phone numbers are read with, and what a lead's tax number is called and how it must look. Any signed-in person who can read users may call it.\n\n**Permission:** `crm.users.read` (View users).","operationId":"getOrganizationLocale","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"settings":{"type":"object","properties":{"country":{"type":"string","description":"Saved ISO country code (e.g. KE); null when not chosen"},"timeZone":{"description":"Saved IANA time zone; null means the country's own zone"},"version":{"type":"integer","description":"Send it back with the next save"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}},"effective":{"type":"object","properties":{"country":{"type":"string"},"countryName":{"type":"string"},"countrySource":{"type":"string","description":"Where the country came from: locale (saved here), calls (the calls settings) or null"},"timeZone":{"type":"string"},"timeZoneSource":{"type":"string","description":"Where the time zone in force came from: locale, country or default (UTC)"},"dialCode":{"type":"string","description":"Dialling code a phone number written without one is read with"},"taxNumber":{"type":"object","properties":{"label":{"type":"string"},"pattern":{"type":"string","description":"Pattern a lead's tax number must match; null means any value"},"schemes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"pattern":{"type":"string"},"example":{"type":"string"}}}}}}}}}},"example":{"success":true,"settings":{"country":"KE","timeZone":null,"version":1,"updatedAt":"2026-10-20T09:00:00.000Z","updatedBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"},"effective":{"country":"KE","countryName":"Kenya","countrySource":"locale","timeZone":"Africa/Nairobi","timeZoneSource":"country","dialCode":"254","taxNumber":{"label":"KRA PIN","pattern":"^[A-Z][0-9]{9}[A-Z]$","schemes":[{"id":"kra_pin","label":"KRA PIN","pattern":"^[A-Z][0-9]{9}[A-Z]$","example":"A123456789B"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.users.read"]}],"x-required-scopes":["crm.users.read"]},"put":{"tags":["Settings"],"summary":"Save your organisation's country and time zone","description":"Saves your organisation's country and time zone, and answers them with what they now decide (time zone in force, dialling code, tax number rules). Only what you send changes; recorded with who changed it.\n\n**Permission:** `crm.onboarding.update` (Manage onboarding settings).","operationId":"updateOrganizationLocale","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"country":{"type":"string","description":"ISO 3166-1 two-letter country code; null or empty clears it; leave out to keep it","examples":["KE"]},"timeZone":{"type":"string","description":"IANA time zone; null or empty means the country's own zone; leave out to keep it","examples":["Africa/Nairobi"]},"version":{"type":"integer","description":"The version from your last read. When sent and someone has saved since, nothing is saved and the call answers 409; leave it out to save over whatever is there","examples":[1]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"settings":{"type":"object","properties":{"country":{"type":"string","description":"Saved ISO country code (e.g. KE); null when not chosen"},"timeZone":{"description":"Saved IANA time zone; null means the country's own zone"},"version":{"type":"integer","description":"Send it back with the next save"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}},"effective":{"type":"object","properties":{"country":{"type":"string"},"countryName":{"type":"string"},"countrySource":{"type":"string","description":"Where the country came from: locale (saved here), calls (the calls settings) or null"},"timeZone":{"type":"string"},"timeZoneSource":{"type":"string","description":"Where the time zone in force came from: locale, country or default (UTC)"},"dialCode":{"type":"string","description":"Dialling code a phone number written without one is read with"},"taxNumber":{"type":"object","properties":{"label":{"type":"string"},"pattern":{"type":"string","description":"Pattern a lead's tax number must match; null means any value"},"schemes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"pattern":{"type":"string"},"example":{"type":"string"}}}}}}}}}},"example":{"success":true,"settings":{"country":"KE","timeZone":null,"version":2,"updatedAt":"2026-10-20T09:00:00.000Z","updatedBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"},"effective":{"country":"KE","countryName":"Kenya","countrySource":"locale","timeZone":"Africa/Nairobi","timeZoneSource":"country","dialCode":"254","taxNumber":{"label":"KRA PIN","pattern":"^[A-Z][0-9]{9}[A-Z]$","schemes":[{"id":"kra_pin","label":"KRA PIN","pattern":"^[A-Z][0-9]{9}[A-Z]$","example":"A123456789B"}]}}}}}},"400":{"description":"country is not an ISO country code, timeZone is not a time zone like Africa/Kampala, or version is not a whole number","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"Someone saved the settings after the version you sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.onboarding.update"]}],"x-required-scopes":["crm.onboarding.update"]}},"/quote-pricing":{"get":{"tags":["Quotes"],"summary":"Price products as quote lines","description":"Prices products as quote lines the way your organisation prices them right now (its own catalogue, or the connected accounting app such as Kazee Books): name, price, tax, unit, and why a product cannot be quoted. Used when adding lines in the quote dialog. Changes nothing.\n\n**Permission:** `crm.products.read` (View products).","operationId":"getQuotePricing","parameters":[{"name":"dealId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"The deal the quote is for; it must exist","example":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e"},{"name":"productIds","in":"query","required":true,"schema":{"type":"array","items":{"type":"string","format":"uuid"}},"description":"The products to price, 1 to 50 IDs (a list, or one string with the IDs separated by commas)","example":["8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f"]}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"pricing":{"type":"string","description":"Where prices come from: catalogue (your products) or connected (the accounting app)"},"currency":{"type":"object","properties":{"code":{"type":"string"},"minorUnits":{"type":"integer","description":"Decimals the figures are rounded to"}}},"lines":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string"},"itemRef":{"description":"The accounting app's item for the product, when prices come from it"},"name":{"type":"string"},"description":{"type":"string"},"unit":{"type":"string"},"recurringPeriod":{"type":"string"},"quantity":{"type":"integer"},"unitPrice":{"type":"integer"},"listPrice":{"type":"integer","description":"The product's own price now"},"discount":{"type":"integer","description":"The discount as an amount (0 for a new line)"},"discountType":{"type":"string"},"discountPercent":{},"treatment":{"type":"string"},"tax":{"type":"object","properties":{"kind":{"type":"string"},"id":{"type":"string"},"label":{"type":"string"},"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"ratePercent":{"type":"string"},"type":{"type":"string"}}}}}},"locked":{"type":"boolean"},"priceLocked":{"type":"boolean","description":"True when your quote settings do not allow changing the price"},"discountLocked":{"type":"boolean"},"descriptionLocked":{"type":"boolean"},"treatmentLocked":{"type":"boolean"},"hint":{},"issue":{"description":"Why the product cannot be quoted (e.g. no longer exists), or null"}}}}}}}},"example":{"success":true,"data":{"pricing":"catalogue","currency":{"code":"KES","minorUnits":2},"lines":[{"productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","itemRef":null,"name":"Home Fibre 50 Mbps","description":"Unlimited home fibre, 50 Mbps","unit":"month","recurringPeriod":"monthly","quantity":1,"unitPrice":4500,"listPrice":4500,"discount":0,"discountType":"amount","discountPercent":null,"treatment":"invoice","tax":{"kind":"tax","id":"a7b8c9d0-e1f2-4a3b-8c4d-5e6f7a8b9c0d","label":"VAT (16%)","members":[{"id":"a7b8c9d0-e1f2-4a3b-8c4d-5e6f7a8b9c0d","name":"VAT","ratePercent":"16.00","type":"plain"}]},"locked":false,"priceLocked":true,"discountLocked":false,"descriptionLocked":false,"treatmentLocked":false,"hint":null,"issue":null}]}}}}},"400":{"description":"productIds is missing, has more than 50 IDs, or holds something that is not an ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"dealId names no deal in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.products.read"]}],"x-required-scopes":["crm.products.read"]}},"/organization/billing-rules/fields":{"get":{"tags":["Settings"],"summary":"List the deal fields a billing rule may test","description":"Lists the deal fields a paid-onboarding billing rule may test: the deal's own fields and its active custom fields, with each field's type and choices.\n\n**Permission:** `crm.onboarding.read` (View onboarding settings).","operationId":"getBillingRuleFields","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"fields":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"What a condition's fieldPath names: a deal column, or customFields.<key> for a custom field"},"label":{"type":"string"},"type":{"type":"string","description":"string, number, boolean, array or date"},"options":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}}},"description":"The choices, for a select or multi-select field"}}}}}},"example":{"success":true,"fields":[{"path":"customFields.connection_type","label":"Connection type","type":"string","options":[{"value":"Layer 2","label":"Layer 2"},{"value":"Fibre","label":"Fibre"}]},{"path":"value","label":"Value","type":"number"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.onboarding.read"]}],"x-required-scopes":["crm.onboarding.read"]}},"/user/notification-settings/{userId}":{"get":{"tags":["Notifications"],"summary":"Get my notification settings","description":"Answers your notification choices for email, push, in-app and business events. Before you first save them it answers the defaults (everything on) without saving anything. The user in the path must be you.\n\n**Permission:** `crm.notifications.read` (View notifications).","operationId":"getNotificationSettings","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Your own user ID; any other person's is refused (403)","example":"1b3e9d5a-0c7f-4a2b-9dc4-8c1f4a7b0d3e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"settings":{"type":"object","properties":{"email":{"type":"object","properties":{"systemUpdates":{"type":"boolean"},"securityAlerts":{"type":"boolean"},"accountActivity":{"type":"boolean"}}},"push":{"type":"object","properties":{"enabled":{"type":"boolean"},"soundAlerts":{"type":"boolean"},"vibration":{"type":"boolean"}}},"inApp":{"type":"object","properties":{"showBadges":{"type":"boolean"},"toastNotifications":{"type":"boolean"},"notificationCenter":{"type":"boolean"}}},"business":{"type":"object","properties":{"newLeads":{"type":"boolean"},"paymentReminders":{"type":"boolean"},"subscriptionUpdates":{"type":"boolean"},"inventoryUpdates":{"type":"boolean"}}}},"description":"Each group (email, push, inApp, business) holds on/off switches; all on when nothing is saved"},"persisted":{"type":"boolean","description":"false when you have never saved settings and the defaults are answered"},"auditLog":{"type":"array","description":"Always empty"}}},"example":{"success":true,"settings":{"email":{"systemUpdates":true,"securityAlerts":true,"accountActivity":true},"push":{"enabled":true,"soundAlerts":true,"vibration":true},"inApp":{"showBadges":true,"toastNotifications":true,"notificationCenter":true},"business":{"newLeads":true,"paymentReminders":true,"subscriptionUpdates":true,"inventoryUpdates":true}},"persisted":true,"auditLog":[]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"userId is not your own","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.notifications.read"]}],"x-required-scopes":["crm.notifications.read"]},"post":{"tags":["Notifications"],"summary":"Update my notification settings","description":"Saves your notification choices. Each group you send (email, push, inApp, business) replaces that whole group as saved, so send every switch of a group you change; groups you leave out keep what was saved. Answers the settings as saved. The user in the path must be you.\n\n**Permission:** `crm.notifications.update` (Update notifications).","operationId":"updateNotificationSettings","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Your own user ID; any other person's is refused (403)","example":"1b3e9d5a-0c7f-4a2b-9dc4-8c1f4a7b0d3e"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"email":{"type":"object","properties":{"systemUpdates":{"type":"boolean"},"securityAlerts":{"type":"boolean"},"accountActivity":{"type":"boolean"}}},"push":{"type":"object","properties":{"enabled":{"type":"boolean"},"soundAlerts":{"type":"boolean"},"vibration":{"type":"boolean"}}},"inApp":{"type":"object","properties":{"showBadges":{"type":"boolean"},"toastNotifications":{"type":"boolean"},"notificationCenter":{"type":"boolean"}}},"business":{"type":"object","properties":{"newLeads":{"type":"boolean"},"paymentReminders":{"type":"boolean"},"subscriptionUpdates":{"type":"boolean"},"inventoryUpdates":{"type":"boolean"}}}},"description":"The groups to save, each with true/false switches: email (systemUpdates, securityAlerts, accountActivity), push (enabled, soundAlerts, vibration), inApp (showBadges, toastNotifications, notificationCenter), business (newLeads, paymentReminders, subscriptionUpdates, inventoryUpdates)","examples":[{"email":{"systemUpdates":false,"securityAlerts":true,"accountActivity":true}}]}},"required":["settings"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"settings":{"type":"object","properties":{"email":{"type":"object","properties":{"systemUpdates":{"type":"boolean"},"securityAlerts":{"type":"boolean"},"accountActivity":{"type":"boolean"}}},"push":{"type":"object","properties":{"enabled":{"type":"boolean"},"soundAlerts":{"type":"boolean"},"vibration":{"type":"boolean"}}},"inApp":{"type":"object","properties":{"showBadges":{"type":"boolean"},"toastNotifications":{"type":"boolean"},"notificationCenter":{"type":"boolean"}}},"business":{"type":"object","properties":{"newLeads":{"type":"boolean"},"paymentReminders":{"type":"boolean"},"subscriptionUpdates":{"type":"boolean"},"inventoryUpdates":{"type":"boolean"}}}}},"message":{"type":"string"}}},"example":{"success":true,"settings":{"email":{"systemUpdates":true,"securityAlerts":true,"accountActivity":true},"push":{"enabled":true,"soundAlerts":true,"vibration":true},"inApp":{"showBadges":true,"toastNotifications":true,"notificationCenter":true},"business":{"newLeads":true,"paymentReminders":true,"subscriptionUpdates":true,"inventoryUpdates":true}},"message":"Notification settings updated successfully"}}}},"400":{"description":"settings is not an object, names an unknown group or switch, or a switch is not true or false","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"userId is not your own","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.notifications.update"]}],"x-required-scopes":["crm.notifications.update"]}},"/export/data":{"get":{"tags":["Exports"],"summary":"Export records as CSV","description":"Exports leads, quotes, contacts, accounts, service requests or products as CSV text in the reply (up to 5,000 records, newest first): the records you name, or every record matching the filters. Fields hidden from you are masked. A failure (such as an unknown entityType) still answers 200, with success false and an error.\n\n**Permission:** `crm.exports.read` (View data exports).","operationId":"exportData","parameters":[{"name":"assignedTo","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only records assigned to this person (their CRM user ID)","example":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"},{"name":"dateFrom","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Accepted, but not applied today: no record list narrows by date","example":"2026-10-01T00:00:00.000Z"},{"name":"dateTo","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Accepted, but not applied today: no record list narrows by date","example":"2026-10-21T23:59:59.000Z"},{"name":"entityIds","in":"query","required":false,"schema":{"type":"array","items":{"type":"string","format":"uuid"}},"description":"Export just these records (a list or comma-separated). Leave out to export every record matching the filters below","example":["6e7f8a9b-0c1d-4e2f-8a3b-4c5d6e7f8a9b"]},{"name":"entityType","in":"query","required":true,"schema":{"type":"string","enum":["leads","quotes","contacts","accounts","requests","products"]},"description":"Which records to export","example":"leads"},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Only records matching this text","example":"wanjiku"},{"name":"source","in":"query","required":false,"schema":{"type":"string"},"description":"Only records from this source (leads)","example":"website"},{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"Only records with this status","example":"new"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"csvContent":{"type":"string","description":"The CSV file as text: a header row, then one line per record (columns depend on entityType)"},"exportType":{"type":"string","description":"selected when entityIds was sent, filtered otherwise"},"entityCount":{"type":"integer","description":"How many records are in the file"},"entityType":{"type":"string"}}},"example":{"success":true,"csvContent":"ID,Name,Source,Status,Email,Phone,Assigned To,Assigned Email,Notes,Created Date,Updated Date,Converted Account ID\n,Grace Wanjiku,website,new,grace.wanjiku@example.com,+254700000123,,,Wants 50 Mbps home fibre,2026-10-01,2026-10-20,","exportType":"selected","entityCount":1,"entityType":"leads"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.exports.read"]}],"x-required-scopes":["crm.exports.read"]}},"/export/pdf":{"get":{"tags":["Exports"],"summary":"Export records as a PDF report","description":"Builds a PDF report (title, summary figures and a table) of leads, quotes, contacts, accounts, service requests, subscriptions, reminders, products or plans and answers it base64-encoded in JSON: the records you name, or every record matching the filters. A failure (such as an unknown entityType) still answers 200, with success false and an error.\n\n**Permission:** `crm.exports.read` (View data exports).","operationId":"exportDataPDFV2","parameters":[{"name":"assignedTo","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only records assigned to this person (their CRM user ID)","example":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"},{"name":"dateFrom","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Accepted, but not applied today: the report is not narrowed by date","example":"2026-10-01T00:00:00.000Z"},{"name":"dateTo","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Accepted, but not applied today: the report is not narrowed by date","example":"2026-10-21T23:59:59.000Z"},{"name":"entityIds","in":"query","required":false,"schema":{"type":"array","items":{"type":"string","format":"uuid"}},"description":"Export just these records (a list or comma-separated). Leave out to export every record matching the filters below","example":["6e7f8a9b-0c1d-4e2f-8a3b-4c5d6e7f8a9b"]},{"name":"entityType","in":"query","required":true,"schema":{"type":"string","enum":["leads","quotes","contacts","accounts","requests","subscriptions","reminders","products","plans"]},"description":"Which records to export","example":"leads"},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Only records matching this text","example":"wanjiku"},{"name":"source","in":"query","required":false,"schema":{"type":"string"},"description":"Only records from this source (leads)","example":"website"},{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"Only records with this status","example":"new"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"pdfContent":{"type":"string","description":"The PDF report, base64-encoded"},"entityType":{"type":"string"},"exportType":{"type":"string","description":"selected when entityIds was sent, filtered otherwise"},"contentType":{"type":"string","description":"Always application/pdf"}}},"example":{"success":true,"pdfContent":"JVBERi0xLjMKJf////8KOCAwIG9iago8PCAvVHlwZSAvRXh0R1N0YXRlIC4uLg==","entityType":"leads","exportType":"filtered","contentType":"application/pdf"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.exports.read"]}],"x-required-scopes":["crm.exports.read"]}},"/export/entities":{"get":{"tags":["Exports"],"summary":"List exportable record types","description":"Lists the record types a data copy (Kazee Data Sync copying CRM into a warehouse) can read, for export contract v1. For app logins and people with crm.export.read; the organisation is the token's.\n\n**Permission:** `crm.export.read` (Read every record for a data copy (read only)).","operationId":"listExportEntities","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"version":{"type":"integer","description":"The export contract version (1)"},"entities":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"The record type's name, its table name, e.g. leads"},"primaryKey":{"type":"string","description":"Its key column(s), comma-separated when there are several"},"parent":{"description":"For a child type (quote lines, deal products, ...): the parent type and the column holding the parent's ID; null otherwise"},"appendOnly":{"type":"boolean","description":"True when rows are only ever added, never changed"}}}}}},"example":{"version":1,"entities":[{"name":"leads","primaryKey":"id","parent":null,"appendOnly":false},{"name":"quote_line_items","primaryKey":"id","parent":{"entity":"quotes","foreignKey":"quote_id"},"appendOnly":false}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Not an app login or signed-in person, crm.export.read is missing, or the token names another organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.export.read"]}],"x-required-scopes":["crm.export.read"]}},"/export/entities/{name}":{"get":{"tags":["Exports"],"summary":"Read a page of exported rows","description":"Answers one page of a record type's rows for a data copy (export contract v1), deleted rows included, oldest change first. For app logins and people with crm.export.read; a person also needs the record type's own read permission. Child types (such as quote lines) page by parent and never split one parent's lines; such a page also lists emptyParents, the parents in it that have no lines now.\n\n**Permission:** `crm.export.read` (Read every record for a data copy (read only)).","operationId":"getExportEntityRows","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"},"description":"The record type, as listExportEntities names it (e.g. leads, quote_line_items)","example":"leads"},{"name":"after","in":"query","required":false,"schema":{"type":"string"},"description":"The `next` cursor of the previous page, to carry on after it","example":"eyJ2IjoxLCJlIjoibGVhZHMiLCJ0IjoiMjAyNi0xMC0yMFQwOTowMDowMC4wMDAwMDBaIiwiayI6WyIuLi4iXX0"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1},"description":"Rows per page (for a child type: parents per page), default 500; more than 1000 counts as 1000","example":500},{"name":"since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only rows changed at or after this time (ISO 8601 with a zone)","example":"2026-10-18T00:00:00.000Z"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"entity":{"type":"string","description":"The record type read"},"rows":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organization_id":{"type":"string"},"first_name":{"type":"string"},"last_name":{"type":"string"},"source":{"type":"string"},"contact_info":{"type":"object","properties":{"email":{"type":"string"},"phone":{"type":"string"}}},"status":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"deleted_at":{},"_change_ts":{"type":"string","format":"date-time"}}},"description":"The rows: every exported column under its database name, deleted rows included. Plus _change_ts (when the row last changed), _pk for a key of several columns, and for a child type _parent_id"},"next":{"type":"string","description":"Send this as `after` to get the next page; null when there are no more"},"serverTime":{"type":"string","format":"date-time","description":"The server's time when the page was read"}}},"example":{"entity":"leads","rows":[{"id":"6e7f8a9b-0c1d-4e2f-8a3b-4c5d6e7f8a9b","organization_id":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","first_name":"Grace","last_name":"Wanjiku","source":"website","contact_info":{"email":"grace.wanjiku@example.com","phone":"+254700000123"},"status":"new","created_at":"2026-10-01T08:30:00.000Z","updated_at":"2026-10-20T09:00:00.000Z","deleted_at":null,"_change_ts":"2026-10-20T09:00:00.000Z"}],"next":"eyJ2IjoxLCJlIjoibGVhZHMiLCJ0IjoiMjAyNi0xMC0yMFQwOTowMDowMC4wMDAwMDBaIiwiayI6WyIuLi4iXX0","serverTime":"2026-10-21T07:00:00.000Z"}}}},"400":{"description":"since is not an ISO 8601 date-time with a zone, after is not a cursor of this record type, or limit is not a whole number from 1","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Not an app login or signed-in person, crm.export.read or the record type's own read permission is missing, the token names another organisation, or (for ticket data) some tickets are in departments you are not in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No exportable record type with that name","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.export.read"]}],"x-required-scopes":["crm.export.read"]}},"/pipelines":{"get":{"tags":["Pipelines"],"summary":"List pipelines","description":"Lists your organisation's pipelines (not deleted ones), sorted by name, at most 200. Each pipeline comes without its stages and transitions: get those with the pipeline configuration call.\n\n**Permission:** `crm.pipelines.read` (View pipelines).","operationId":"getPipelines","parameters":[{"name":"customModuleSlug","in":"query","required":false,"schema":{"type":"string"},"description":"Only pipelines of this custom module (its slug)","example":"site-surveys"},{"name":"isActive","in":"query","required":false,"schema":{"type":"boolean"},"description":"true for active pipelines only, false for inactive ones only; leave out for both","example":true},{"name":"moduleType","in":"query","required":false,"schema":{"type":"string"},"description":"Only pipelines for this record type: leads, deals, subscriptions, tickets, requests, accounts, contacts or custom","example":"leads"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"pipelines":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"moduleType":{"type":"string","description":"Record type the pipeline moves: leads, deals, subscriptions, tickets, requests, accounts, contacts or custom"},"customModuleSlug":{"description":"For a custom module's pipeline, the module's slug; otherwise null"},"isDefault":{"type":"boolean","description":"The pipeline new records of that type start in (one per record type, or per custom module)"},"isActive":{"type":"boolean"},"cardDateField":{"type":"string","description":"Key of the date or date-time field the board shows on every card and orders each column by (earliest first, records without it last); null: no card date, columns by last update"},"createdBy":{"type":"string"},"updatedBy":{"type":"string"},"deletedAt":{"description":"Always null here: deleted pipelines are left out"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}},"example":{"success":true,"pipelines":[{"id":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","organizationId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","name":"Home fibre sales","description":"Leads for home fibre, from first contact to qualified","moduleType":"leads","customModuleSlug":null,"isDefault":true,"isActive":true,"cardDateField":"follow_up_date","createdBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","updatedBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","deletedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.pipelines.read"]}],"x-required-scopes":["crm.pipelines.read"]},"post":{"tags":["Pipelines"],"summary":"Create a pipeline","description":"Creates an empty, active pipeline (no stages yet) for a record type. Making it the default takes the default mark off the record type's other pipeline (for a custom module, only that module's).\n\n**Permission:** `crm.pipelines.create` (Create pipelines).","operationId":"createPipeline","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"customModuleSlug":{"type":"string","description":"For moduleType custom: the custom module's slug","examples":["site-surveys"]},"description":{"type":"string","description":"What the pipeline is for","examples":["Leads for home fibre, from first contact to qualified"]},"isDefault":{"type":"boolean","description":"Make it the record type's default pipeline","examples":[false]},"moduleType":{"type":"string","description":"Record type it moves: leads, deals, subscriptions, tickets, requests, accounts, contacts or custom (up to 16 characters)","examples":["leads"]},"name":{"type":"string","description":"Name of the pipeline","examples":["Home fibre sales"]}},"required":["moduleType","name"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"pipeline":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"moduleType":{"type":"string","description":"Record type the pipeline moves: leads, deals, subscriptions, tickets, requests, accounts, contacts or custom"},"customModuleSlug":{"description":"For a custom module's pipeline, the module's slug; otherwise null"},"isDefault":{"type":"boolean","description":"The pipeline new records of that type start in (one per record type, or per custom module)"},"isActive":{"type":"boolean"},"cardDateField":{"description":"Key of the date or date-time field the board shows on every card and orders each column by (earliest first, records without it last); null: no card date, columns by last update"},"createdBy":{"type":"string"},"updatedBy":{"type":"string"},"deletedAt":{"description":"Always null here: deleted pipelines are left out"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"pipeline":{"id":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","organizationId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","name":"Home fibre sales","description":"Leads for home fibre, from first contact to qualified","moduleType":"leads","customModuleSlug":null,"isDefault":false,"isActive":true,"cardDateField":null,"createdBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","updatedBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","deletedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"name is empty or moduleType is over 16 characters (the message lists the problem)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.pipelines.create"]}],"x-required-scopes":["crm.pipelines.create"]}},"/pipelines/capabilities":{"get":{"tags":["Automations"],"summary":"Get workflow capabilities","description":"Lists what transitions and automation rules can use for a record type: the fields conditions may test, comparison operators, triggers, action types (and which really run), template placeholders, and whether custom code can run on this server.\n\n**Permission:** `crm.workflow.read` (View record transitions).","operationId":"getWorkflowCapabilities","parameters":[{"name":"moduleType","in":"query","required":false,"schema":{"type":"string"},"description":"Record type: leads, deals, subscriptions, tickets, requests, accounts, contacts or custom (the default)","example":"deals"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"capabilities":{"type":"object","properties":{"automationRuleScoping":{"type":"boolean"},"automationDispatch":{"type":"boolean"},"scheduledAutomations":{"type":"boolean"},"scheduledAutomationIntervalMinutes":{"type":"integer"},"transitionActions":{"type":"boolean"},"sandboxAvailable":{"type":"boolean","description":"Whether custom code can run on this server"},"customCodeTestRun":{"type":"boolean"},"customCodeExecution":{"type":"boolean"},"operators":{"type":"array","items":{"type":"string"},"description":"Comparisons a condition may use"},"triggers":{"type":"array","items":{"type":"string"},"description":"Events an automation rule may start on; deal pipelines also get the Books estimate-paid triggers"},"triggerDescriptions":{"type":"object","properties":{"books_estimate_paid":{"type":"string"}}},"actions":{"type":"array","items":{"type":"string"},"description":"Action types offered for this record type"},"moduleFields":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string"},"operators":{"type":"array","items":{"type":"string"}},"valueType":{"type":"string"}}},"description":"Fields conditions may test, with the comparisons each allows (a custom module's own fields first)"},"templateMacros":{"type":"array","items":{"type":"object","properties":{"macro":{"type":"string"},"description":{"type":"string"}}},"description":"Placeholders an action's text may use, such as {{record.name}}"},"customCode":{"type":"object","properties":{"testRunEndpoint":{"type":"string"},"runtimeExecutionEnabled":{"type":"boolean"},"testRunEnabled":{"type":"boolean"},"sandboxAvailable":{"type":"boolean"},"reason":{"type":"string"}}},"actionExecution":{"type":"object","properties":{"executable":{"type":"array","items":{"type":"string"}},"conditional":{"type":"object","properties":{"add_tag":{"type":"string"}}},"unsupported":{"type":"object","properties":{}}},"description":"Which action types really run, which only under conditions, and which are accepted but do nothing"},"aliases":{"type":"object","properties":{"assign":{"type":"string"},"notify":{"type":"string"},"create_task":{"type":"string"}}}}}}},"example":{"success":true,"capabilities":{"automationRuleScoping":true,"automationDispatch":true,"scheduledAutomations":true,"scheduledAutomationIntervalMinutes":5,"transitionActions":true,"sandboxAvailable":true,"customCodeTestRun":true,"customCodeExecution":true,"operators":["equals","not_equals","contains","is_empty","in"],"triggers":["record_created","record_updated","stage_changed","field_changed","scheduled","manual"],"triggerDescriptions":{"books_estimate_paid":"Books estimate paid: the deal's estimate was paid in full and Books made the retainer (deposit) and the invoice."},"actions":["email","webhook","notification","update_field","convert_lead"],"moduleFields":[{"field":"status","label":"Status","operators":["equals","not_equals","in","not_in"],"valueType":"string"}],"templateMacros":[{"macro":"{{record.name}}","description":"The record's name."}],"customCode":{"testRunEndpoint":"/workflow/code/test","runtimeExecutionEnabled":true,"testRunEnabled":true,"sandboxAvailable":true,"reason":"CRM custom-code test runs and runtime custom actions execute in isolated-vm with dry-run side-effect queues by default."},"actionExecution":{"executable":["email","webhook","notification","update_field"],"conditional":{"add_tag":"Executable only for CRM modules with a persisted tags field: accounts and tickets."},"unsupported":{}},"aliases":{"assign":"assign_user","notify":"notification","create_task":"schedule_task"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.workflow.read"]}],"x-required-scopes":["crm.workflow.read"]}},"/workflow/capabilities":{"get":{"tags":["Automations"],"summary":"Get workflow capabilities","description":"Lists what transitions and automation rules can use for a record type: the fields conditions may test, comparison operators, triggers, action types (and which really run), template placeholders, and whether custom code can run on this server.\n\n**Permission:** `crm.workflow.read` (View record transitions).","operationId":"getWorkflowCapabilities_2","parameters":[{"name":"moduleType","in":"query","required":false,"schema":{"type":"string"},"description":"Record type: leads, deals, subscriptions, tickets, requests, accounts, contacts or custom (the default)","example":"deals"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"capabilities":{"type":"object","properties":{"automationRuleScoping":{"type":"boolean"},"automationDispatch":{"type":"boolean"},"scheduledAutomations":{"type":"boolean"},"scheduledAutomationIntervalMinutes":{"type":"integer"},"transitionActions":{"type":"boolean"},"sandboxAvailable":{"type":"boolean","description":"Whether custom code can run on this server"},"customCodeTestRun":{"type":"boolean"},"customCodeExecution":{"type":"boolean"},"operators":{"type":"array","items":{"type":"string"},"description":"Comparisons a condition may use"},"triggers":{"type":"array","items":{"type":"string"},"description":"Events an automation rule may start on; deal pipelines also get the Books estimate-paid triggers"},"triggerDescriptions":{"type":"object","properties":{"books_estimate_paid":{"type":"string"}}},"actions":{"type":"array","items":{"type":"string"},"description":"Action types offered for this record type"},"moduleFields":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string"},"operators":{"type":"array","items":{"type":"string"}},"valueType":{"type":"string"}}},"description":"Fields conditions may test, with the comparisons each allows (a custom module's own fields first)"},"templateMacros":{"type":"array","items":{"type":"object","properties":{"macro":{"type":"string"},"description":{"type":"string"}}},"description":"Placeholders an action's text may use, such as {{record.name}}"},"customCode":{"type":"object","properties":{"testRunEndpoint":{"type":"string"},"runtimeExecutionEnabled":{"type":"boolean"},"testRunEnabled":{"type":"boolean"},"sandboxAvailable":{"type":"boolean"},"reason":{"type":"string"}}},"actionExecution":{"type":"object","properties":{"executable":{"type":"array","items":{"type":"string"}},"conditional":{"type":"object","properties":{"add_tag":{"type":"string"}}},"unsupported":{"type":"object","properties":{}}},"description":"Which action types really run, which only under conditions, and which are accepted but do nothing"},"aliases":{"type":"object","properties":{"assign":{"type":"string"},"notify":{"type":"string"},"create_task":{"type":"string"}}}}}}},"example":{"success":true,"capabilities":{"automationRuleScoping":true,"automationDispatch":true,"scheduledAutomations":true,"scheduledAutomationIntervalMinutes":5,"transitionActions":true,"sandboxAvailable":true,"customCodeTestRun":true,"customCodeExecution":true,"operators":["equals","not_equals","contains","is_empty","in"],"triggers":["record_created","record_updated","stage_changed","field_changed","scheduled","manual"],"triggerDescriptions":{"books_estimate_paid":"Books estimate paid: the deal's estimate was paid in full and Books made the retainer (deposit) and the invoice."},"actions":["email","webhook","notification","update_field","convert_lead"],"moduleFields":[{"field":"status","label":"Status","operators":["equals","not_equals","in","not_in"],"valueType":"string"}],"templateMacros":[{"macro":"{{record.name}}","description":"The record's name."}],"customCode":{"testRunEndpoint":"/workflow/code/test","runtimeExecutionEnabled":true,"testRunEnabled":true,"sandboxAvailable":true,"reason":"CRM custom-code test runs and runtime custom actions execute in isolated-vm with dry-run side-effect queues by default."},"actionExecution":{"executable":["email","webhook","notification","update_field"],"conditional":{"add_tag":"Executable only for CRM modules with a persisted tags field: accounts and tickets."},"unsupported":{}},"aliases":{"assign":"assign_user","notify":"notification","create_task":"schedule_task"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.workflow.read"]}],"x-required-scopes":["crm.workflow.read"]}},"/pipelines/schema":{"get":{"tags":["Pipelines"],"summary":"Get the pipeline blueprint schema","description":"Lists everything a pipeline blueprint may contain: field types, action types and the settings each takes, condition operators, automation triggers, rollup functions, custom-code functions, template variables and record types. The same for every organisation; the AI pipeline assistant reads it to know what is valid.\n\n**Permission:** `crm.pipelines.read` (View pipelines).","operationId":"getPipelineSchema","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"schema":{"type":"object","properties":{"fieldTypes":{"type":"array","items":{"type":"string"}},"transitionFieldTypes":{"type":"array","items":{"type":"string"}},"actionTypes":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"description":{"type":"string"},"configSchema":{"type":"object","properties":{"actionType":{"type":"string"},"description":{"type":"string"},"requiredKeys":{"type":"array","items":{"type":"string"}},"optionalKeys":{"type":"array","items":{"type":"string"}},"configShape":{"type":"object","properties":{"url":{"type":"string"},"method":{"type":"string"},"body":{"type":"string"}}}}}}},"description":"Each action type, what it does, and its settings (configSchema)"},"conditionOperators":{"type":"array","items":{"type":"string"},"description":"Comparisons a condition may use"},"automationTriggers":{"type":"array","items":{"type":"object","properties":{"event":{"type":"string"},"description":{"type":"string"}}},"description":"Events an automation may start on"},"rollupFunctions":{"type":"array","items":{"type":"string"}},"sandboxApiFunctions":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"signature":{"type":"string"},"description":{"type":"string"}}},"description":"Functions custom code may call; empty when this server cannot run custom code"},"templateVariables":{"type":"array","items":{"type":"string"},"description":"Placeholders an action's text may use"},"moduleTypes":{"type":"array","items":{"type":"string"}},"actionAliases":{"type":"object","properties":{"assign":{"type":"string"},"notify":{"type":"string"},"create_task":{"type":"string"}},"description":"Old action names and the action each now means"},"stageMigrationModes":{"type":"array","items":{"type":"object","properties":{"mode":{"type":"string"},"description":{"type":"string"}}}}}}}},"example":{"success":true,"schema":{"fieldTypes":["text","number","date","select","checkbox","currency"],"transitionFieldTypes":["text","textarea","number","date","select"],"actionTypes":[{"type":"webhook","description":"POST/GET an HTTP webhook with the record payload.","configSchema":{"actionType":"webhook","description":"POST/GET an HTTP webhook with the record payload.","requiredKeys":["url"],"optionalKeys":["method","headers","body"],"configShape":{"url":"string","method":"string","body":"string|object"}}}],"conditionOperators":["eq","neq","gt","contains","isEmpty","in"],"automationTriggers":[{"event":"record_created","description":"A new record was created."}],"rollupFunctions":["count","sum","avg","min","max"],"sandboxApiFunctions":[{"name":"setField","signature":"setField(fieldName, value)","description":"Queue a field update on the current record."}],"templateVariables":["record.<field>","actor.email","date.today"],"moduleTypes":["leads","deals","subscriptions","tickets","requests","accounts","contacts","custom"],"actionAliases":{"assign":"assign_user","notify":"notification","create_task":"schedule_task"},"stageMigrationModes":[{"mode":"move","description":"Move records from a removed stage to another existing/new stage (callers must map stage→status values for the target module)."}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.pipelines.read"]}],"x-required-scopes":["crm.pipelines.read"]}},"/workflow/schema":{"get":{"tags":["Pipelines"],"summary":"Get the pipeline blueprint schema","description":"Lists everything a pipeline blueprint may contain: field types, action types and the settings each takes, condition operators, automation triggers, rollup functions, custom-code functions, template variables and record types. The same for every organisation; the AI pipeline assistant reads it to know what is valid.\n\n**Permission:** `crm.pipelines.read` (View pipelines).","operationId":"getPipelineSchema_2","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"schema":{"type":"object","properties":{"fieldTypes":{"type":"array","items":{"type":"string"}},"transitionFieldTypes":{"type":"array","items":{"type":"string"}},"actionTypes":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"description":{"type":"string"},"configSchema":{"type":"object","properties":{"actionType":{"type":"string"},"description":{"type":"string"},"requiredKeys":{"type":"array","items":{"type":"string"}},"optionalKeys":{"type":"array","items":{"type":"string"}},"configShape":{"type":"object","properties":{"url":{"type":"string"},"method":{"type":"string"},"body":{"type":"string"}}}}}}},"description":"Each action type, what it does, and its settings (configSchema)"},"conditionOperators":{"type":"array","items":{"type":"string"},"description":"Comparisons a condition may use"},"automationTriggers":{"type":"array","items":{"type":"object","properties":{"event":{"type":"string"},"description":{"type":"string"}}},"description":"Events an automation may start on"},"rollupFunctions":{"type":"array","items":{"type":"string"}},"sandboxApiFunctions":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"signature":{"type":"string"},"description":{"type":"string"}}},"description":"Functions custom code may call; empty when this server cannot run custom code"},"templateVariables":{"type":"array","items":{"type":"string"},"description":"Placeholders an action's text may use"},"moduleTypes":{"type":"array","items":{"type":"string"}},"actionAliases":{"type":"object","properties":{"assign":{"type":"string"},"notify":{"type":"string"},"create_task":{"type":"string"}},"description":"Old action names and the action each now means"},"stageMigrationModes":{"type":"array","items":{"type":"object","properties":{"mode":{"type":"string"},"description":{"type":"string"}}}}}}}},"example":{"success":true,"schema":{"fieldTypes":["text","number","date","select","checkbox","currency"],"transitionFieldTypes":["text","textarea","number","date","select"],"actionTypes":[{"type":"webhook","description":"POST/GET an HTTP webhook with the record payload.","configSchema":{"actionType":"webhook","description":"POST/GET an HTTP webhook with the record payload.","requiredKeys":["url"],"optionalKeys":["method","headers","body"],"configShape":{"url":"string","method":"string","body":"string|object"}}}],"conditionOperators":["eq","neq","gt","contains","isEmpty","in"],"automationTriggers":[{"event":"record_created","description":"A new record was created."}],"rollupFunctions":["count","sum","avg","min","max"],"sandboxApiFunctions":[{"name":"setField","signature":"setField(fieldName, value)","description":"Queue a field update on the current record."}],"templateVariables":["record.<field>","actor.email","date.today"],"moduleTypes":["leads","deals","subscriptions","tickets","requests","accounts","contacts","custom"],"actionAliases":{"assign":"assign_user","notify":"notification","create_task":"schedule_task"},"stageMigrationModes":[{"mode":"move","description":"Move records from a removed stage to another existing/new stage (callers must map stage→status values for the target module)."}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.pipelines.read"]}],"x-required-scopes":["crm.pipelines.read"]}},"/pipelines/{pipelineId}/blueprint":{"get":{"tags":["Pipelines"],"summary":"Export a pipeline as a blueprint","description":"Answers a whole pipeline as one blueprint document: its stages, transitions (with their conditions and actions) and the automation rules tied to it. Stage and transition IDs in it are the real ones. Send it back to create or update a pipeline from a blueprint, e.g. to copy a pipeline.\n\n**Permission:** `crm.pipelines.read` (View pipelines).","operationId":"exportPipelineBlueprint","parameters":[{"name":"pipelineId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The pipeline","example":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"blueprint":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"moduleType":{"type":"string","description":"Record type: leads, deals, subscriptions, tickets, requests, accounts, contacts or custom (custom also needs customModuleSlug)"},"stages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The stage's ID inside the blueprint; transitions point at it"},"name":{"type":"string"},"color":{"type":"string"},"orderNum":{"type":"integer"},"isDefault":{"type":"boolean"},"positionX":{"type":"integer"},"positionY":{"type":"integer"}}}},"transitions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"fromStageId":{"type":"string"},"toStageId":{"type":"string"},"isGlobal":{"type":"boolean"},"fromStageIds":{"type":"array"},"isActive":{"type":"boolean"},"requiresConfirmation":{"type":"boolean"},"allowedRoles":{"type":"array"},"allowedUsers":{"type":"array"},"conditions":{"type":"array","items":{"type":"object","properties":{"fieldPath":{"type":"string"},"operator":{"type":"string"},"logicOperator":{"type":"string"}}},"description":"Conditions the record must meet: fieldPath, operator (eq, neq, gt, contains, isEmpty, isNotEmpty, in...), value"},"fields":{"type":"array"},"requiredFieldIds":{"type":"array","items":{"type":"string"}},"requireComment":{"type":"boolean"},"actions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"actionType":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"},"config":{"type":"object","properties":{"to":{"type":"string"},"message":{"type":"string"}}},"orderNum":{"type":"integer"}}}}}}},"automations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"},"trigger":{"type":"object","properties":{"event":{"type":"string","description":"What starts the rule: record_created, record_updated, field_changed, stage_entered, stage_exited, stage_changed, transition_executed, scheduled, manual..."},"config":{"type":"object","properties":{}}}},"conditions":{"type":"array"},"actions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"actionType":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"},"config":{"type":"object","properties":{"to":{"type":"string"},"subject":{"type":"string"},"body":{"type":"string"}}},"orderNum":{"type":"integer"}}}},"orderNum":{"type":"integer"}}}}}}}},"example":{"success":true,"blueprint":{"name":"Home fibre sales","description":"Leads for home fibre, from first contact to qualified","moduleType":"leads","stages":[{"id":"new","name":"New","color":"#3b82f6","orderNum":0,"isDefault":true,"positionX":0,"positionY":100},{"id":"qualified","name":"Qualified","color":"#10b981","orderNum":1,"isDefault":false,"positionX":200,"positionY":100}],"transitions":[{"id":"new-to-qualified","name":"Qualify","color":"#22c55e","fromStageId":"new","toStageId":"qualified","isGlobal":false,"fromStageIds":[],"isActive":true,"requiresConfirmation":false,"allowedRoles":[],"allowedUsers":[],"conditions":[{"fieldPath":"phone","operator":"isNotEmpty","logicOperator":"AND"}],"fields":[],"requiredFieldIds":["coverage_confirmed"],"requireComment":false,"actions":[{"id":"new-to-qualified-action-0","actionType":"notification","name":"Tell the owner","isActive":true,"config":{"to":"owner","message":"{{record.name}} is qualified"},"orderNum":0}]}],"automations":[{"id":"welcome","name":"Welcome new leads","isActive":true,"trigger":{"event":"record_created","config":{}},"conditions":[],"actions":[{"id":"welcome-action-0","actionType":"email","name":"Welcome email","isActive":true,"config":{"to":"{{record.email}}","subject":"Thanks for your interest","body":"We will call you soon."},"orderNum":0}],"orderNum":0}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"No pipeline with that ID in your organisation (the message says it was not found)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.pipelines.read"]}],"x-required-scopes":["crm.pipelines.read"]},"put":{"tags":["Pipelines"],"summary":"Update a pipeline from a blueprint","description":"Rewrites a pipeline to match a blueprint: its name, description and record type take the blueprint's, stages are matched by name (changed, added, or removed when missing from it), and the pipeline's transitions and automation rules are replaced by the blueprint's. A stageMigrationPlan is recorded and answered back, but it does not move or archive any records itself (every entry answers affected 0).\n\n**Permission:** `crm.pipelines.update` (Update pipelines).","operationId":"updatePipelineFromBlueprint","parameters":[{"name":"pipelineId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The pipeline to change","example":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"blueprint":{"type":"object","description":"The whole pipeline: name, moduleType (and customModuleSlug for a custom module), stages, and optionally transitions, automations and config. Get the allowed values from the pipeline blueprint schema. A JSON string also works","examples":[{"name":"Home fibre sales","description":"Leads for home fibre, from first contact to qualified","moduleType":"leads","stages":[{"id":"new","name":"New","color":"#3b82f6","orderNum":0,"isDefault":true,"positionX":0,"positionY":100},{"id":"qualified","name":"Qualified","color":"#10b981","orderNum":1,"isDefault":false,"positionX":200,"positionY":100}],"transitions":[{"id":"new-to-qualified","name":"Qualify","color":"#22c55e","fromStageId":"new","toStageId":"qualified","isGlobal":false,"fromStageIds":[],"isActive":true,"requiresConfirmation":false,"allowedRoles":[],"allowedUsers":[],"conditions":[{"fieldPath":"phone","operator":"isNotEmpty","logicOperator":"AND"}],"fields":[],"requiredFieldIds":["coverage_confirmed"],"requireComment":false,"actions":[{"id":"new-to-qualified-action-0","actionType":"notification","name":"Tell the owner","isActive":true,"config":{"to":"owner","message":"{{record.name}} is qualified"},"orderNum":0}]}],"automations":[{"id":"welcome","name":"Welcome new leads","isActive":true,"trigger":{"event":"record_created","config":{}},"conditions":[],"actions":[{"id":"welcome-action-0","actionType":"email","name":"Welcome email","isActive":true,"config":{"to":"{{record.email}}","subject":"Thanks for your interest","body":"We will call you soon."},"orderNum":0}],"orderNum":0}]}]},"stageMigrationPlan":{"type":"array","items":{"type":"object","properties":{"fromStageId":{"type":"string"},"toStageId":{"type":"string"},"mode":{"type":"string","enum":["move","archive"]}}},"description":"What should happen to records on stages the blueprint removes: for each stage, move (to toStageId) or archive. A JSON string also works","examples":[[{"fromStageId":"a1b2c3d4-0001-4e5f-8a9b-0c1d2e3f4a5b","toStageId":"a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b","mode":"move"}]]}},"required":["blueprint"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"pipelineId":{"type":"string"},"stageMigration":{"type":"object","properties":{"moved":{"type":"integer"},"archived":{"type":"integer"},"entries":{"type":"array","items":{"type":"object","properties":{"fromStageId":{"type":"string"},"toStageId":{"type":"string"},"mode":{"type":"string"},"affected":{"type":"integer"}}}}},"description":"Only when a stageMigrationPlan was sent: one entry per planned stage, with the records affected (always 0: records are not moved here)"}}},"example":{"success":true,"pipelineId":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","stageMigration":{"moved":0,"archived":0,"entries":[{"fromStageId":"a1b2c3d4-0001-4e5f-8a9b-0c1d2e3f4a5b","toStageId":"a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b","mode":"move","affected":0}]}}}}},"400":{"description":"blueprint or stageMigrationPlan is not valid JSON, or blueprint is missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No pipeline with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"The blueprint does not pass validation (the message lists every problem)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.pipelines.update"]}],"x-required-scopes":["crm.pipelines.update"]}},"/pipelines/{moduleType}/config":{"get":{"tags":["Pipelines"],"summary":"Get a pipeline's configuration","description":"Answers a record type's pipeline in full: the pipeline, its stages (each with the active transitions leaving and reaching it), its active transitions, and what its rules may use. Without pipelineId it is the default pipeline, else the first active one. When the record type has no pipeline yet, a default one is made and answered, so this call can create data. For tickets it is the Desk ticket blueprint: made with Desk's statuses and moves when there is none, and the untouched 4-stage pipeline of before upgraded to them in place (any other ticket pipeline is left as it is).\n\n**Permission:** `crm.pipelines.read` (View pipelines).","operationId":"getPipelineConfig","parameters":[{"name":"moduleType","in":"path","required":true,"schema":{"type":"string"},"description":"Record type: leads, deals, subscriptions, tickets, requests or custom","example":"leads"},{"name":"customModuleSlug","in":"query","required":false,"schema":{"type":"string"},"description":"For moduleType custom (and then needed): the custom module's slug","example":"site-surveys"},{"name":"pipelineId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"A particular pipeline. If no pipeline has this ID, a new default pipeline is made instead","example":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"pipeline":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"moduleType":{"type":"string","description":"Record type the pipeline moves: leads, deals, subscriptions, tickets, requests, accounts, contacts or custom"},"customModuleSlug":{"description":"For a custom module's pipeline, the module's slug; otherwise null"},"isDefault":{"type":"boolean","description":"The pipeline new records of that type start in (one per record type, or per custom module)"},"isActive":{"type":"boolean"},"cardDateField":{"type":"string","description":"Key of the date or date-time field the board shows on every card and orders each column by (earliest first, records without it last); null: no card date, columns by last update"},"createdBy":{"type":"string"},"updatedBy":{"type":"string"},"deletedAt":{"description":"Always null here: deleted pipelines are left out"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"stages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"pipelineId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"color":{"type":"string","description":"Colour of the stage, a hex code like #8b5cf6"},"orderNum":{"type":"integer","description":"Position of the stage on the board, from 0"},"stageType":{"type":"string","description":"Kind of stage: active, won or lost (free text; these are the ones CRM sets up)"},"isDefault":{"type":"boolean","description":"New records start in this stage"},"isTerminal":{"type":"boolean","description":"Records in this stage are finished"},"positionX":{"type":"integer","description":"Where the stage sits on the workflow editor's canvas, left to right"},"positionY":{"type":"integer","description":"Where the stage sits on the workflow editor's canvas, top to bottom"},"statusKey":{"description":"Ticket pipelines: the ticket status this stage stands for, set when the stage is made and kept through a rename; null elsewhere (the status is then the name in lowercase with underscores)"},"createdBy":{"type":"string"},"updatedBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"transitionsFrom":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"pipelineId":{"type":"string"},"fromStageId":{"type":"string"},"toStageId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"color":{"type":"string"},"icon":{"type":"string"},"isActive":{"type":"boolean"},"requiresConfirmation":{"type":"boolean"},"confirmationMessage":{"type":"string"},"isGlobal":{"type":"boolean"},"fromStageIds":{"type":"array","items":{"type":"string"}},"criteria":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"fieldPath":{"type":"string"},"fieldLabel":{"type":"string"},"operator":{"type":"string"},"logicOperator":{"type":"string"}}}},"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"type":{"type":"string"},"required":{"type":"boolean"}}}},"requiredFieldIds":{"type":"array","items":{"type":"string"}},"requireComment":{"type":"boolean"},"commentPrompt":{"type":"string"},"actions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"actionType":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"},"config":{"type":"object","properties":{"to":{"type":"string"},"title":{"type":"string"},"message":{"type":"string"}}},"orderNum":{"type":"integer"}}}},"allowedRoles":{"type":"array","items":{"type":"string"}},"allowedUsers":{"type":"array","items":{"type":"string"}},"createdBy":{"type":"string"},"updatedBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"description":"Active transitions that can be run from this stage (a global transition shows under each of its stages)"},"transitionsTo":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"fromStageId":{"type":"string"}}},"description":"Active transitions that lead into this stage"}}}},"transitions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"pipelineId":{"type":"string"},"fromStageId":{"type":"string","description":"Stage the record leaves. For a global transition, the first of fromStageIds"},"toStageId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"color":{"type":"string"},"icon":{"type":"string"},"isActive":{"type":"boolean"},"requiresConfirmation":{"type":"boolean"},"confirmationMessage":{"type":"string"},"isGlobal":{"type":"boolean","description":"Can be run from any stage in fromStageIds, not just fromStageId"},"fromStageIds":{"type":"array","items":{"type":"string"},"description":"Stages a global transition can be run from (the one source stage otherwise)"},"criteria":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"fieldPath":{"type":"string"},"fieldLabel":{"type":"string"},"operator":{"type":"string"},"logicOperator":{"type":"string"}}},"description":"Conditions the record must meet for the transition to show and run"},"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"type":{"type":"string"},"required":{"type":"boolean"}}},"description":"Old-style form fields. Retired: new and edited transitions keep this as it is and never use it"},"requiredFieldIds":{"type":"array","items":{"type":"string"},"description":"Record fields (custom field keys) that must be filled to run it; values entered are saved on the record"},"requireComment":{"type":"boolean","description":"A comment (notes) is needed to run it"},"commentPrompt":{"type":"string"},"actions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"actionType":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"},"config":{"type":"object","properties":{"to":{"type":"string"},"title":{"type":"string"},"message":{"type":"string"}}},"orderNum":{"type":"integer"}}},"description":"Steps run when it is run: actionType (email, webhook, notification, update_field, assign_user, create_ticket, start_billing...), name, config and order"},"allowedRoles":{"type":"array","items":{"type":"string"},"description":"Roles that may run it. With allowedUsers both empty, anyone may"},"allowedUsers":{"type":"array","items":{"type":"string"},"description":"People (user IDs) that may run it, besides allowedRoles"},"createdBy":{"type":"string"},"updatedBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"capabilities":{"type":"object","properties":{"automationRuleScoping":{"type":"boolean"},"automationDispatch":{"type":"boolean"},"scheduledAutomations":{"type":"boolean"},"scheduledAutomationIntervalMinutes":{"type":"integer"},"transitionActions":{"type":"boolean"},"sandboxAvailable":{"type":"boolean","description":"Whether custom code can run on this server"},"customCodeTestRun":{"type":"boolean"},"customCodeExecution":{"type":"boolean"},"operators":{"type":"array","items":{"type":"string"},"description":"Comparisons a condition may use"},"triggers":{"type":"array","items":{"type":"string"},"description":"Events an automation rule may start on; deal pipelines also get the Books estimate-paid triggers"},"triggerDescriptions":{"type":"object","properties":{"books_estimate_paid":{"type":"string"}}},"actions":{"type":"array","items":{"type":"string"},"description":"Action types offered for this record type"},"moduleFields":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string"},"operators":{"type":"array","items":{"type":"string"}},"valueType":{"type":"string"}}},"description":"Fields conditions may test, with the comparisons each allows (a custom module's own fields first)"},"templateMacros":{"type":"array","items":{"type":"object","properties":{"macro":{"type":"string"},"description":{"type":"string"}}},"description":"Placeholders an action's text may use, such as {{record.name}}"},"customCode":{"type":"object","properties":{"testRunEndpoint":{"type":"string"},"runtimeExecutionEnabled":{"type":"boolean"},"testRunEnabled":{"type":"boolean"},"sandboxAvailable":{"type":"boolean"},"reason":{"type":"string"}}},"actionExecution":{"type":"object","properties":{"executable":{"type":"array","items":{"type":"string"}},"conditional":{"type":"object","properties":{"add_tag":{"type":"string"}}},"unsupported":{"type":"object","properties":{}}},"description":"Which action types really run, which only under conditions, and which are accepted but do nothing"},"aliases":{"type":"object","properties":{"assign":{"type":"string"},"notify":{"type":"string"},"create_task":{"type":"string"}}}}}}},"example":{"success":true,"pipeline":{"id":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","organizationId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","name":"Home fibre sales","description":"Leads for home fibre, from first contact to qualified","moduleType":"leads","customModuleSlug":null,"isDefault":true,"isActive":true,"cardDateField":"follow_up_date","createdBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","updatedBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","deletedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z"},"stages":[{"id":"a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b","organizationId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","pipelineId":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","name":"Contacted","description":"We have spoken to them","color":"#8b5cf6","orderNum":1,"stageType":"active","isDefault":false,"isTerminal":false,"positionX":200,"positionY":100,"statusKey":null,"createdBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","updatedBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","deletedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z","transitionsFrom":[{"id":"b2c3d4e5-0001-4f60-9a7b-1c2d3e4f5a6b","organizationId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","pipelineId":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","fromStageId":"a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b","toStageId":"a1b2c3d4-0003-4e5f-8a9b-0c1d2e3f4a5b","name":"Qualify","description":"They want fibre and we can reach them","color":"#22c55e","icon":"check-circle","isActive":true,"requiresConfirmation":true,"confirmationMessage":"Mark this lead as qualified?","isGlobal":false,"fromStageIds":["a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b"],"criteria":[{"id":"cond-0","fieldPath":"phone","fieldLabel":"Phone","operator":"isNotEmpty","logicOperator":"AND"}],"fields":[{"key":"survey_date","label":"Survey date","type":"date","required":true}],"requiredFieldIds":["coverage_confirmed"],"requireComment":true,"commentPrompt":"What did they say?","actions":[{"id":"qualify-action-0","actionType":"notification","name":"Tell the owner","isActive":true,"config":{"to":"owner","title":"Lead qualified","message":"{{record.name}} moved to {{stage.to}}"},"orderNum":0}],"allowedRoles":["sales"],"allowedUsers":["f6a7b8c9-3d4e-4f5a-9b6c-7d8e9f0a1b2c"],"createdBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","updatedBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","deletedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z"}],"transitionsTo":[{"id":"b2c3d4e5-0000-4f60-9a7b-1c2d3e4f5a6b","name":"Make contact","fromStageId":"a1b2c3d4-0001-4e5f-8a9b-0c1d2e3f4a5b"}]}],"transitions":[{"id":"b2c3d4e5-0001-4f60-9a7b-1c2d3e4f5a6b","organizationId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","pipelineId":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","fromStageId":"a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b","toStageId":"a1b2c3d4-0003-4e5f-8a9b-0c1d2e3f4a5b","name":"Qualify","description":"They want fibre and we can reach them","color":"#22c55e","icon":"check-circle","isActive":true,"requiresConfirmation":true,"confirmationMessage":"Mark this lead as qualified?","isGlobal":false,"fromStageIds":["a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b"],"criteria":[{"id":"cond-0","fieldPath":"phone","fieldLabel":"Phone","operator":"isNotEmpty","logicOperator":"AND"}],"fields":[{"key":"survey_date","label":"Survey date","type":"date","required":true}],"requiredFieldIds":["coverage_confirmed"],"requireComment":true,"commentPrompt":"What did they say?","actions":[{"id":"qualify-action-0","actionType":"notification","name":"Tell the owner","isActive":true,"config":{"to":"owner","title":"Lead qualified","message":"{{record.name}} moved to {{stage.to}}"},"orderNum":0}],"allowedRoles":["sales"],"allowedUsers":["f6a7b8c9-3d4e-4f5a-9b6c-7d8e9f0a1b2c"],"createdBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","updatedBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","deletedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z"}],"capabilities":{"automationRuleScoping":true,"automationDispatch":true,"scheduledAutomations":true,"scheduledAutomationIntervalMinutes":5,"transitionActions":true,"sandboxAvailable":true,"customCodeTestRun":true,"customCodeExecution":true,"operators":["equals","not_equals","contains","is_empty","in"],"triggers":["record_created","record_updated","stage_changed","field_changed","scheduled","manual"],"triggerDescriptions":{"books_estimate_paid":"Books estimate paid: the deal's estimate was paid in full and Books made the retainer (deposit) and the invoice."},"actions":["email","webhook","notification","update_field","convert_lead"],"moduleFields":[{"field":"status","label":"Status","operators":["equals","not_equals","in","not_in"],"valueType":"string"}],"templateMacros":[{"macro":"{{record.name}}","description":"The record's name."}],"customCode":{"testRunEndpoint":"/workflow/code/test","runtimeExecutionEnabled":true,"testRunEnabled":true,"sandboxAvailable":true,"reason":"CRM custom-code test runs and runtime custom actions execute in isolated-vm with dry-run side-effect queues by default."},"actionExecution":{"executable":["email","webhook","notification","update_field"],"conditional":{"add_tag":"Executable only for CRM modules with a persisted tags field: accounts and tickets."},"unsupported":{}},"aliases":{"assign":"assign_user","notify":"notification","create_task":"schedule_task"}}}}}},"400":{"description":"moduleType is custom but customModuleSlug is missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.pipelines.read"]}],"x-required-scopes":["crm.pipelines.read"]}},"/pipelines/history/{recordId}":{"get":{"tags":["Pipelines"],"summary":"List transition history","description":"Lists the transitions run on one record, or in one pipeline, newest first: who moved what from which stage to which, the form values and notes given, and how each of the transition's actions went. Filters combine; with none, it lists the whole organisation's history.\n\n**Permission:** `crm.workflow.read` (View record transitions).","operationId":"getTransitionHistory","parameters":[{"name":"recordId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Only this record's history (part of the address in /pipelines/history/:recordId)","example":"d4e5f6a7-1b2c-4d3e-9f4a-5b6c7d8e9f0a"},{"name":"fromDate","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only transitions run at or after this moment","example":"2026-10-01T00:00:00.000Z"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50},"description":"Entries per page","example":50},{"name":"moduleType","in":"query","required":false,"schema":{"type":"string"},"description":"Only records of this type: leads, deals, subscriptions, tickets, requests or custom","example":"leads"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page to answer, from 1","example":1},{"name":"performedBy","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only transitions run by this person (user ID)","example":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b"},{"name":"pipelineId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this pipeline's history (part of the address in /pipelines/:pipelineId/history)","example":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7"},{"name":"toDate","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only transitions run at or before this moment","example":"2026-10-21T00:00:00.000Z"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"logs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"moduleType":{"type":"string"},"recordId":{"type":"string"},"recordType":{"type":"string","description":"Lead, Deal, Subscription, Ticket, Request or CustomRecord"},"pipelineId":{"type":"string"},"transitionId":{"type":"string","description":"The transition run; null once it was deleted"},"transitionName":{"type":"string"},"fromStageId":{"type":"string"},"toStageId":{"type":"string"},"fromStageName":{"type":"string"},"toStageName":{"type":"string"},"performedBy":{"type":"string"},"performedByEmail":{"type":"string"},"fieldValues":{"type":"object","properties":{"coverage_confirmed":{"type":"boolean"}},"description":"Values entered on the transition's form"},"notes":{"type":"string","description":"The comment given when it was run"},"actionResults":{"type":"array","items":{"type":"object","properties":{"actionType":{"type":"string"},"actionName":{"type":"string"},"success":{"type":"boolean"},"message":{"type":"string"},"executedAt":{"type":"string","format":"date-time"}}},"description":"Each action the transition ran, whether it worked, and its message"},"createdAt":{"type":"string","format":"date-time"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"}}}}},"example":{"success":true,"logs":[{"id":"c3d4e5f6-0001-4a7b-8c9d-2e3f4a5b6c7d","organizationId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","moduleType":"leads","recordId":"d4e5f6a7-1b2c-4d3e-9f4a-5b6c7d8e9f0a","recordType":"Lead","pipelineId":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","transitionId":"b2c3d4e5-0001-4f60-9a7b-1c2d3e4f5a6b","transitionName":"Qualify","fromStageId":"a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b","toStageId":"a1b2c3d4-0003-4e5f-8a9b-0c1d2e3f4a5b","fromStageName":"Contacted","toStageName":"Qualified","performedBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","performedByEmail":"jane.doe@example.com","fieldValues":{"coverage_confirmed":true},"notes":"Wants the 50 Mbps plan","actionResults":[{"actionType":"notification","actionName":"Tell the owner","success":true,"message":"Notification sent","executedAt":"2026-10-20T10:15:02.000Z"}],"createdAt":"2026-10-20T10:15:00.000Z"}],"pagination":{"page":1,"limit":50,"total":1,"totalPages":1}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.workflow.read"]}],"x-required-scopes":["crm.workflow.read"]}},"/pipelines/{pipelineId}/history":{"get":{"tags":["Pipelines"],"summary":"List transition history","description":"Lists the transitions run on one record, or in one pipeline, newest first: who moved what from which stage to which, the form values and notes given, and how each of the transition's actions went. Filters combine; with none, it lists the whole organisation's history.\n\n**Permission:** `crm.workflow.read` (View record transitions).","operationId":"getTransitionHistory_2","parameters":[{"name":"pipelineId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Only this pipeline's history (part of the address in /pipelines/:pipelineId/history)","example":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7"},{"name":"fromDate","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only transitions run at or after this moment","example":"2026-10-01T00:00:00.000Z"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50},"description":"Entries per page","example":50},{"name":"moduleType","in":"query","required":false,"schema":{"type":"string"},"description":"Only records of this type: leads, deals, subscriptions, tickets, requests or custom","example":"leads"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page to answer, from 1","example":1},{"name":"performedBy","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only transitions run by this person (user ID)","example":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b"},{"name":"recordId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this record's history (part of the address in /pipelines/history/:recordId)","example":"d4e5f6a7-1b2c-4d3e-9f4a-5b6c7d8e9f0a"},{"name":"toDate","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only transitions run at or before this moment","example":"2026-10-21T00:00:00.000Z"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"logs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"moduleType":{"type":"string"},"recordId":{"type":"string"},"recordType":{"type":"string","description":"Lead, Deal, Subscription, Ticket, Request or CustomRecord"},"pipelineId":{"type":"string"},"transitionId":{"type":"string","description":"The transition run; null once it was deleted"},"transitionName":{"type":"string"},"fromStageId":{"type":"string"},"toStageId":{"type":"string"},"fromStageName":{"type":"string"},"toStageName":{"type":"string"},"performedBy":{"type":"string"},"performedByEmail":{"type":"string"},"fieldValues":{"type":"object","properties":{"coverage_confirmed":{"type":"boolean"}},"description":"Values entered on the transition's form"},"notes":{"type":"string","description":"The comment given when it was run"},"actionResults":{"type":"array","items":{"type":"object","properties":{"actionType":{"type":"string"},"actionName":{"type":"string"},"success":{"type":"boolean"},"message":{"type":"string"},"executedAt":{"type":"string","format":"date-time"}}},"description":"Each action the transition ran, whether it worked, and its message"},"createdAt":{"type":"string","format":"date-time"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"}}}}},"example":{"success":true,"logs":[{"id":"c3d4e5f6-0001-4a7b-8c9d-2e3f4a5b6c7d","organizationId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","moduleType":"leads","recordId":"d4e5f6a7-1b2c-4d3e-9f4a-5b6c7d8e9f0a","recordType":"Lead","pipelineId":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","transitionId":"b2c3d4e5-0001-4f60-9a7b-1c2d3e4f5a6b","transitionName":"Qualify","fromStageId":"a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b","toStageId":"a1b2c3d4-0003-4e5f-8a9b-0c1d2e3f4a5b","fromStageName":"Contacted","toStageName":"Qualified","performedBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","performedByEmail":"jane.doe@example.com","fieldValues":{"coverage_confirmed":true},"notes":"Wants the 50 Mbps plan","actionResults":[{"actionType":"notification","actionName":"Tell the owner","success":true,"message":"Notification sent","executedAt":"2026-10-20T10:15:02.000Z"}],"createdAt":"2026-10-20T10:15:00.000Z"}],"pagination":{"page":1,"limit":50,"total":1,"totalPages":1}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.workflow.read"]}],"x-required-scopes":["crm.workflow.read"]}},"/pipeline-agent/sessions":{"get":{"tags":["Automations"],"summary":"List your pipeline assistant sessions","description":"Lists your own recent AI pipeline assistant sessions, as short summaries without the conversation, e.g. to pick one to carry on.\n\n**Permission:** `crm.pipelineAgent.read` (View pipeline agent sessions).","operationId":"listPipelineAgentSessions","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer"},"description":"How many to answer: 10 by default, at most 50","example":10},{"name":"mode","in":"query","required":false,"schema":{"type":"string","enum":["create","edit"]},"description":"Only sessions in this mode; any other value is ignored","example":"create"},{"name":"pipelineId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only sessions editing this pipeline","example":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"sessions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"mode":{"type":"string","description":"create (make a new pipeline) or edit (change pipelineId's pipeline)"},"provider":{"type":"string"},"model":{"type":"string"},"status":{"type":"string","description":"idle, running, cancelled, applied or failed"},"pipelineId":{},"moduleType":{"type":"string"},"customModuleSlug":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"appliedPipelineId":{},"appliedAt":{},"draftIsValid":{"type":"boolean"},"hasDraft":{"type":"boolean","description":"The assistant has drafted a pipeline"},"messageCount":{"type":"integer"},"lastUserMessage":{"type":"string","description":"Your last message, cut to 120 characters"}}}}}},"example":{"sessions":[{"id":"28b9c0d1-5f6a-4b7c-9d8e-9f0a1b2c3d4e","mode":"create","provider":"anthropic","model":"claude-sonnet-4-20250514","status":"idle","pipelineId":null,"moduleType":"leads","customModuleSlug":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:01:09.000Z","appliedPipelineId":null,"appliedAt":null,"draftIsValid":true,"hasDraft":true,"messageCount":4,"lastUserMessage":"Make a pipeline for home fibre leads: New, Contacted, Qualified"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.pipelineAgent.read"]}],"x-required-scopes":["crm.pipelineAgent.read"]},"post":{"tags":["Automations"],"summary":"Start a pipeline assistant session","description":"Starts a chat with the AI pipeline assistant, to draft a new pipeline (create mode) or change an existing one (edit mode). Uses your organisation's AI provider (or the one you name). Send messages to the session next; nothing changes in CRM until the draft is applied.\n\n**Permission:** `crm.pipelineAgent.create` (Create pipeline agent sessions).","operationId":"startPipelineAgentSession","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"customModuleSlug":{"type":"string","description":"For a custom module: its slug","examples":["site-surveys"]},"mode":{"type":"string","enum":["create","edit"],"default":"create","description":"create a new pipeline, or edit pipelineId's","examples":["create"]},"model":{"type":"string","description":"AI model; default: the provider's default model","examples":["claude-sonnet-4-20250514"]},"moduleType":{"type":"string","description":"Record type the pipeline is for: leads, deals, subscriptions, tickets, requests, accounts, contacts or custom. In edit mode, the pipeline's by default","examples":["leads"]},"pipelineId":{"type":"string","format":"uuid","description":"In edit mode (and then needed): the pipeline to change","examples":["6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7"]},"provider":{"type":"string","enum":["anthropic","openai"],"description":"AI provider; default: your organisation's","examples":["anthropic"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"pipelineId":{},"moduleType":{"type":"string"},"customModuleSlug":{},"mode":{"type":"string","description":"create (make a new pipeline) or edit (change pipelineId's pipeline)"},"provider":{"type":"string","description":"AI provider: anthropic or openai"},"model":{"type":"string"},"status":{"type":"string","description":"idle, running (a turn is working), cancelled, applied or failed"},"lastError":{},"messages":{"type":"array","description":"The conversation: role (user, assistant, tool or system), content, and the assistant's tool calls and their results"},"draftBlueprint":{"description":"The pipeline the assistant has drafted so far, as a blueprint (null before its first draft)"},"draftIsValid":{"type":"boolean"},"draftValidationErrors":{"type":"array"},"stageMigrations":{"type":"array","description":"In edit mode, what happens to records on stages the draft removes: move them to another stage, or archive them"},"appliedPipelineId":{"description":"The pipeline made or changed once the draft was applied"},"appliedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"availableProviders":{"type":"array","items":{"type":"string"},"description":"AI providers your organisation has set up"}}},"example":{"session":{"id":"28b9c0d1-5f6a-4b7c-9d8e-9f0a1b2c3d4e","userId":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","pipelineId":null,"moduleType":"leads","customModuleSlug":null,"mode":"create","provider":"anthropic","model":"claude-sonnet-4-20250514","status":"idle","lastError":null,"messages":[],"draftBlueprint":null,"draftIsValid":false,"draftValidationErrors":[],"stageMigrations":[],"appliedPipelineId":null,"appliedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:01:09.000Z"},"availableProviders":["anthropic","openai"]}}}},"400":{"description":"Edit mode without pipelineId, or the AI provider is not set up for your organisation (code AI_NOT_CONFIGURED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No pipeline with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.pipelineAgent.create"]}],"x-required-scopes":["crm.pipelineAgent.create"]}},"/pipeline-agent/sessions/{sessionId}":{"get":{"tags":["Automations"],"summary":"Get a pipeline assistant session","description":"Answers one of your AI pipeline assistant sessions in full: the whole conversation, the draft pipeline (a blueprint), whether it is valid, and any planned stage moves. Only the person who started a session may read it.\n\n**Permission:** `crm.pipelineAgent.read` (View pipeline agent sessions).","operationId":"getPipelineAgentSession","parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The assistant session","example":"28b9c0d1-5f6a-4b7c-9d8e-9f0a1b2c3d4e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"pipelineId":{},"moduleType":{"type":"string"},"customModuleSlug":{},"mode":{"type":"string","description":"create (make a new pipeline) or edit (change pipelineId's pipeline)"},"provider":{"type":"string","description":"AI provider: anthropic or openai"},"model":{"type":"string"},"status":{"type":"string","description":"idle, running (a turn is working), cancelled, applied or failed"},"lastError":{},"messages":{"type":"array","items":{"type":"object","properties":{"role":{"type":"string"},"content":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}},"description":"The conversation: role (user, assistant, tool or system), content, and the assistant's tool calls and their results"},"draftBlueprint":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"moduleType":{"type":"string"},"stages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"orderNum":{"type":"integer"},"isDefault":{"type":"boolean"},"positionX":{"type":"integer"},"positionY":{"type":"integer"}}}},"transitions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"fromStageId":{"type":"string"},"toStageId":{"type":"string"},"isGlobal":{"type":"boolean"},"fromStageIds":{"type":"array"},"isActive":{"type":"boolean"},"requiresConfirmation":{"type":"boolean"},"allowedRoles":{"type":"array"},"allowedUsers":{"type":"array"},"conditions":{"type":"array","items":{"type":"object","properties":{"fieldPath":{"type":"string"},"operator":{"type":"string"},"logicOperator":{"type":"string"}}}},"fields":{"type":"array"},"requiredFieldIds":{"type":"array","items":{"type":"string"}},"requireComment":{"type":"boolean"},"actions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"actionType":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"},"config":{"type":"object","properties":{"to":{"type":"string"},"message":{"type":"string"}}},"orderNum":{"type":"integer"}}}}}}},"automations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"},"trigger":{"type":"object","properties":{"event":{"type":"string"},"config":{"type":"object","properties":{}}}},"conditions":{"type":"array"},"actions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"actionType":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"},"config":{"type":"object","properties":{"to":{"type":"string"},"subject":{"type":"string"},"body":{"type":"string"}}},"orderNum":{"type":"integer"}}}},"orderNum":{"type":"integer"}}}}},"description":"The pipeline the assistant has drafted so far, as a blueprint (null before its first draft)"},"draftIsValid":{"type":"boolean"},"draftValidationErrors":{"type":"array"},"stageMigrations":{"type":"array","items":{"type":"object","properties":{"fromStageId":{"type":"string"},"fromStageName":{"type":"string"},"toStageId":{"type":"string"},"toStageName":{"type":"string"},"mode":{"type":"string"},"recordCountAtPlanning":{"type":"integer"},"note":{"type":"string"}}},"description":"In edit mode, what happens to records on stages the draft removes: move them to another stage, or archive them"},"appliedPipelineId":{"description":"The pipeline made or changed once the draft was applied"},"appliedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"session":{"id":"28b9c0d1-5f6a-4b7c-9d8e-9f0a1b2c3d4e","userId":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","pipelineId":null,"moduleType":"leads","customModuleSlug":null,"mode":"create","provider":"anthropic","model":"claude-sonnet-4-20250514","status":"idle","lastError":null,"messages":[{"role":"user","content":"Make a pipeline for home fibre leads: New, Contacted, Qualified","createdAt":"2026-10-20T09:01:00.000Z"},{"role":"assistant","content":null,"toolCalls":[{"id":"call_1","name":"propose_blueprint","arguments":{"blueprint":{"name":"Home fibre sales"}}}],"createdAt":"2026-10-20T09:01:05.000Z"},{"role":"tool","content":null,"toolResults":[{"toolCallId":"call_1","name":"propose_blueprint","result":{"valid":true},"isError":false}],"createdAt":"2026-10-20T09:01:06.000Z"},{"role":"assistant","content":"Here is a draft with three stages. Press Apply to create it.","createdAt":"2026-10-20T09:01:09.000Z"}],"draftBlueprint":{"name":"Home fibre sales","description":"Leads for home fibre, from first contact to qualified","moduleType":"leads","stages":[{"id":"new","name":"New","color":"#3b82f6","orderNum":0,"isDefault":true,"positionX":0,"positionY":100},{"id":"qualified","name":"Qualified","color":"#10b981","orderNum":1,"isDefault":false,"positionX":200,"positionY":100}],"transitions":[{"id":"new-to-qualified","name":"Qualify","color":"#22c55e","fromStageId":"new","toStageId":"qualified","isGlobal":false,"fromStageIds":[],"isActive":true,"requiresConfirmation":false,"allowedRoles":[],"allowedUsers":[],"conditions":[{"fieldPath":"phone","operator":"isNotEmpty","logicOperator":"AND"}],"fields":[],"requiredFieldIds":["coverage_confirmed"],"requireComment":false,"actions":[{"id":"new-to-qualified-action-0","actionType":"notification","name":"Tell the owner","isActive":true,"config":{"to":"owner","message":"{{record.name}} is qualified"},"orderNum":0}]}],"automations":[{"id":"welcome","name":"Welcome new leads","isActive":true,"trigger":{"event":"record_created","config":{}},"conditions":[],"actions":[{"id":"welcome-action-0","actionType":"email","name":"Welcome email","isActive":true,"config":{"to":"{{record.email}}","subject":"Thanks for your interest","body":"We will call you soon."},"orderNum":0}],"orderNum":0}]},"draftIsValid":true,"draftValidationErrors":[],"stageMigrations":[{"fromStageId":"a1b2c3d4-0001-4e5f-8a9b-0c1d2e3f4a5b","fromStageName":"New","toStageId":"a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b","toStageName":"Contacted","mode":"move","recordCountAtPlanning":12,"note":"New is merged into Contacted"}],"appliedPipelineId":null,"appliedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:01:09.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The session is someone else's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No assistant session with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.pipelineAgent.read"]}],"x-required-scopes":["crm.pipelineAgent.read"]}},"/deals/analytics":{"get":{"tags":["Deals"],"summary":"Get deal figures","description":"Deal figures for your organisation: totals, value weighted by probability, average size, win rate, the six default pipeline stages and deals created per month. dateFrom and dateTo filter by the deals' EXPECTED close date. velocity, topDeals and lostReasons are not worked out yet and always come back empty.\n\n**Permission:** `crm.deals.read` (View deals).","operationId":"getDealAnalytics","parameters":[{"name":"dateFrom","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only deals expected to close on or after this","example":"2026-10-01T00:00:00.000Z"},{"name":"dateTo","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only deals expected to close on or before this","example":"2026-12-31T23:59:59.000Z"},{"name":"ownerId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this person's deals","example":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"overview":{"type":"object","properties":{"totalDeals":{"type":"integer"},"totalValue":{"type":"integer"},"weightedValue":{"type":"integer","description":"Sum of value × probability / 100"},"averageDealSize":{"type":"integer"},"winRate":{"type":"number","description":"closed_won as a percent of closed_won plus closed_lost deals"}}},"pipeline":{"type":"array","items":{"type":"object","properties":{"stage":{"type":"string"},"label":{"type":"string"},"count":{"type":"integer"},"value":{"type":"integer"},"weightedValue":{"type":"integer"}}},"description":"The six default stages, each with its count, total and weighted value (deals at other stages count only in overview)"},"velocity":{"type":"object","properties":{"averageDaysToClose":{"type":"integer"},"averageDaysPerStage":{"type":"object","properties":{}}}},"trends":{"type":"array","items":{"type":"object","properties":{"period":{"type":"string","description":"Month the deals were created, YYYY-MM"},"dealsCreated":{"type":"integer"},"dealsWon":{"type":"integer","description":"Of those, how many are at closed_won now"},"dealsLost":{"type":"integer","description":"Not worked out yet: always 0"},"valueWon":{"type":"integer","description":"Not worked out yet: always 0"}}}},"topDeals":{"type":"array","description":"Not worked out yet: always an empty list"},"lostReasons":{"type":"array","description":"Not worked out yet: always an empty list"}}},"example":{"overview":{"totalDeals":12,"totalValue":54000,"weightedValue":23400,"averageDealSize":4500,"winRate":66.67},"pipeline":[{"stage":"proposal","label":"Proposal","count":4,"value":18000,"weightedValue":9000}],"velocity":{"averageDaysToClose":0,"averageDaysPerStage":{}},"trends":[{"period":"2026-10","dealsCreated":5,"dealsWon":2,"dealsLost":0,"valueWon":0}],"topDeals":[],"lostReasons":[]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.deals.read"]}],"x-required-scopes":["crm.deals.read"]}},"/deals":{"get":{"tags":["Deals"],"summary":"List deals","description":"List your organisation's deals, a page at a time, with each stage's count and total value for the same filters. With perStageLimit it answers the board instead: the newest-updated deals of every stage, up to that many each. Each deal carries its contact and lead with the phone and email its Call and Email reach, only if you may read contacts / leads.\n\n**Permission:** `crm.deals.read` (View deals).","operationId":"getDeals","parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only deals of this account","example":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"},{"name":"customFieldFilters","in":"query","required":false,"schema":{"type":"object"},"description":"Older form of filters for custom fields, keyed by field key without cf:","example":{"contract_term_length":"12 months"}},{"name":"fieldFilters","in":"query","required":false,"schema":{"type":"object"},"description":"Older form of filters for built-in fields, keyed by name","example":{"priority":"high"}},{"name":"filters","in":"query","required":false,"schema":{"type":"object"},"description":"Field conditions keyed by filter field ID (a native field name, or cf:<key> for your own fields; GET /filter-fields lists them), each { operator, value } or { operator, values }; a bare value means \"is\". A JSON object in a string also works. An invalid condition answers 400 naming the field","example":{"status":{"operator":"is","values":["active"]}}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20},"description":"Deals per page, up to 500","example":20},{"name":"ownerId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only deals owned by this person","example":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"perStageLimit","in":"query","required":false,"schema":{"type":"integer"},"description":"Board mode: answer up to this many deals per stage (newest updated first) instead of a page; pagination then covers just what came back","example":25},{"name":"priority","in":"query","required":false,"schema":{"type":"string","enum":["low","medium","high"]},"description":"Only deals with this priority","example":"high"},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text to find in the deal's name or description (a pasted Kazee Tickets ID also finds the deal it came from)","example":"Acacia"},{"name":"sortBy","in":"query","required":false,"schema":{"type":"string","enum":["createdAt","updatedAt","value","expectedCloseDate"],"default":"createdAt"},"description":"Field to sort by (anything else sorts by createdAt)","example":"createdAt"},{"name":"sortOrder","in":"query","required":false,"schema":{"type":"string","enum":["asc","desc"],"default":"desc"},"description":"Sort direction","example":"desc"},{"name":"source","in":"query","required":false,"schema":{"type":"string"},"description":"Only deals from this source","example":"website"},{"name":"stage","in":"query","required":false,"schema":{"type":"string"},"description":"Only deals at this stage key (a list of stages also works in a JSON body)","example":"proposal"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"leadId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"value":{"type":"string","description":"Deal value, a decimal string in the deal's currency (two decimals)"},"currency":{"type":"string","description":"ISO 4217 code; the organisation's currency unless set"},"recurringValue":{"type":"string"},"stage":{"type":"string","description":"Stage key on the deals pipeline, e.g. qualification, discovery, proposal, negotiation, closed_won, closed_lost (organisations may use their own)"},"probability":{"type":"integer","description":"Chance of winning, 0 to 100"},"expectedCloseDate":{"type":"string","format":"date-time"},"actualCloseDate":{},"ownerId":{"type":"string"},"teamId":{},"priority":{"type":"string","description":"low, medium or high"},"source":{"type":"string"},"dealType":{"type":"string"},"lostReason":{},"lostReasonDetails":{},"competitorId":{},"lastActivityDate":{"type":"string","format":"date-time"},"nextActivityDate":{},"nextActivityType":{},"notes":{"type":"string"},"customFields":{"type":"object","properties":{"contract_term_length":{"type":"string"}},"description":"The organisation's own fields, by field key. Fields hidden from you come back masked"},"externalIds":{"type":"array"},"createdBy":{"type":"string"},"updatedBy":{"type":"string"},"version":{"type":"integer","description":"Goes up by one on every change"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"owner":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"}},"description":"The deal's owner (not in board mode, with perStageLimit)"},"contact":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"name":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"}},"description":"The deal's contact with phone and email; only with contacts.read"},"lead":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"}},"description":"The deal's lead with phone and email; only with leads.read"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer","description":"Deals matching the filters, on all pages"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}}},"summary":{"type":"object","properties":{"totalValue":{"type":"integer","description":"Sum of the matching deals' values"},"byStage":{"type":"object","properties":{"proposal":{"type":"object","properties":{"count":{"type":"integer"},"value":{"type":"integer"}}}},"description":"Each stage's count and total value for the same filters"}}}}},"example":{"data":[{"id":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Acacia Bakery: 50 Mbps fibre","description":"Fibre for the bakery's main branch","leadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","value":"4500.00","currency":"KES","recurringValue":"4500.00","stage":"proposal","probability":50,"expectedCloseDate":"2026-11-01T00:00:00.000Z","actualCloseDate":null,"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","teamId":null,"priority":"high","source":"website","dealType":"new_business","lostReason":null,"lostReasonDetails":null,"competitorId":null,"lastActivityDate":"2026-10-15T14:05:00.000Z","nextActivityDate":null,"nextActivityType":null,"notes":"","customFields":{"contract_term_length":"12 months"},"externalIds":[],"createdBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","updatedBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","version":3,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","owner":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","firstName":"Brian","lastName":"Mwangi"},"contact":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","firstName":"Amina","lastName":"Otieno","name":"Amina Otieno","phone":"+254700000001","email":"amina@example.com"},"lead":{"id":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","name":"Acacia Bakery Ltd","phone":"+254700000002","email":"orders@example.com"}}],"pagination":{"page":1,"limit":20,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false},"summary":{"totalValue":4500,"byStage":{"proposal":{"count":1,"value":4500}}}}}}},"400":{"description":"A filter is not valid (the message names the field)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.deals.read"]}],"x-required-scopes":["crm.deals.read"]},"post":{"tags":["Deals"],"summary":"Create a deal","description":"Create a deal for an account, or from a lead that has been converted to an account (the deal then takes the lead's account, contact and source, and the lead is marked converted). Without a probability, the stage sets it (qualification 10, discovery 25, proposal 50, negotiation 75, closed_won 100, closed_lost 0). Your organisation's \"record created\" automations run on it.\n\n**Permission:** `crm.deals.create` (Create deals).","operationId":"createDeal","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid","description":"The account the deal is for. Send this or leadId (a converted lead's account wins)","examples":["2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"]},"contactId":{"type":"string","format":"uuid","description":"The contact person for the deal","examples":["4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"]},"currency":{"type":"string","description":"ISO 4217 currency code; defaults to your organisation's currency","examples":["KES"]},"customFields":{"type":"object","description":"Values for your organisation's own deal fields, by field key","examples":[{"contract_term_length":"12 months"}]},"dealType":{"type":"string","description":"Your organisation's kind of deal","examples":["new_business"]},"description":{"type":"string","description":"Longer description","examples":["Fibre for the bakery's main branch"]},"expectedCloseDate":{"type":"string","format":"date-time","description":"When you expect to close it","examples":["2026-11-01T00:00:00.000Z"]},"leadId":{"type":"string","format":"uuid","description":"The converted lead the deal comes from. Send this or accountId","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]},"name":{"type":"string","description":"The deal's name","examples":["Acacia Bakery: 50 Mbps fibre"]},"notes":{"type":"string","description":"Free-text notes","examples":["Wants installation before month end"]},"ownerId":{"type":"string","format":"uuid","description":"Who owns the deal","examples":["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]},"priority":{"type":"string","enum":["low","medium","high"],"default":"medium","description":"How urgent it is","examples":["high"]},"probability":{"type":"integer","description":"Chance of winning, 0 to 100; defaults from the stage","examples":[10]},"products":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","format":"uuid"},"planId":{"type":"string","format":"uuid"},"name":{"type":"string"},"quantity":{"type":"number"},"unitPrice":{"type":"number"},"discount":{"type":"number","description":"Percent off, 0 to 100"}},"required":["productId","name","quantity","unitPrice"]},"description":"Products to add as they are sent, not priced by the catalogue (each discount is a percent). To price them, use PUT /deals/:dealId/products","examples":[[{"productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","name":"50 Mbps Fibre","quantity":1,"unitPrice":4500,"discount":0}]]},"recurringValue":{"type":"number","description":"What it brings in each billing period","examples":[4500]},"source":{"type":"string","description":"Where the deal came from; defaults to the lead's source","examples":["website"]},"stage":{"type":"string","default":"qualification","description":"Stage key on the deals pipeline","examples":["qualification"]},"value":{"type":"number","description":"What the deal is worth, in its currency","examples":[4500]}},"required":["name","value"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"deal":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"leadId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"value":{"type":"string","description":"Deal value, a decimal string in the deal's currency (two decimals)"},"currency":{"type":"string","description":"ISO 4217 code; the organisation's currency unless set"},"recurringValue":{"type":"string"},"stage":{"type":"string","description":"Stage key on the deals pipeline, e.g. qualification, discovery, proposal, negotiation, closed_won, closed_lost (organisations may use their own)"},"probability":{"type":"integer","description":"Chance of winning, 0 to 100"},"expectedCloseDate":{"type":"string","format":"date-time"},"actualCloseDate":{},"ownerId":{"type":"string"},"teamId":{},"priority":{"type":"string","description":"low, medium or high"},"source":{"type":"string"},"dealType":{"type":"string"},"lostReason":{},"lostReasonDetails":{},"competitorId":{},"lastActivityDate":{},"nextActivityDate":{},"nextActivityType":{},"notes":{"type":"string"},"customFields":{"type":"object","properties":{"contract_term_length":{"type":"string"}},"description":"The organisation's own fields, by field key. Fields hidden from you come back masked"},"externalIds":{"type":"array"},"createdBy":{"type":"string"},"updatedBy":{"type":"string"},"version":{"type":"integer","description":"Goes up by one on every change"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"message":{"type":"string","description":"\"Deal created\", or \"Deal created from lead\""}}},"example":{"deal":{"id":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Acacia Bakery: 50 Mbps fibre","description":"Fibre for the bakery's main branch","leadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","value":"4500.00","currency":"KES","recurringValue":"4500.00","stage":"proposal","probability":50,"expectedCloseDate":"2026-11-01T00:00:00.000Z","actualCloseDate":null,"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","teamId":null,"priority":"high","source":"website","dealType":"new_business","lostReason":null,"lostReasonDetails":null,"competitorId":null,"lastActivityDate":null,"nextActivityDate":null,"nextActivityType":null,"notes":"","customFields":{"contract_term_length":"12 months"},"externalIds":[],"createdBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","updatedBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","version":0,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"},"message":"Deal created from lead"}}}},"400":{"description":"Neither leadId nor accountId was sent, or the lead has not been converted to an account yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"The lead, account or contact does not exist in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.deals.create"]}],"x-required-scopes":["crm.deals.create"]}},"/deals/{dealId}":{"get":{"tags":["Deals"],"summary":"Get a deal","description":"Get one deal as its record page opens it: the deal with its owner, account, contact, products and latest 50 activities. The contact and lead come with the phone and email the deal's Call and Email reach, only if you may read contacts / leads (otherwise the contact keeps just its name and the lead is left out).\n\n**Permission:** `crm.deals.read` (View deals).","operationId":"getDeal","parameters":[{"name":"dealId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The deal's ID","example":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"deal":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"leadId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"value":{"type":"string","description":"Deal value, a decimal string in the deal's currency (two decimals)"},"currency":{"type":"string","description":"ISO 4217 code; the organisation's currency unless set"},"recurringValue":{"type":"string"},"stage":{"type":"string","description":"Stage key on the deals pipeline, e.g. qualification, discovery, proposal, negotiation, closed_won, closed_lost (organisations may use their own)"},"probability":{"type":"integer","description":"Chance of winning, 0 to 100"},"expectedCloseDate":{"type":"string","format":"date-time"},"actualCloseDate":{},"ownerId":{"type":"string"},"teamId":{},"priority":{"type":"string","description":"low, medium or high"},"source":{"type":"string"},"dealType":{"type":"string"},"lostReason":{},"lostReasonDetails":{},"competitorId":{},"lastActivityDate":{"type":"string","format":"date-time"},"nextActivityDate":{},"nextActivityType":{},"notes":{"type":"string"},"customFields":{"type":"object","properties":{"contract_term_length":{"type":"string"}},"description":"The organisation's own fields, by field key. Fields hidden from you come back masked"},"externalIds":{"type":"array"},"createdBy":{"type":"string"},"updatedBy":{"type":"string"},"version":{"type":"integer","description":"Goes up by one on every change"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"owner":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"}},"description":"The deal's owner, or null"},"account":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"description":"The deal's account (its shared name), or null"},"contact":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"name":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"}},"description":"The deal's contact; phone and email only with contacts.read"},"products":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"dealId":{"type":"string"},"productId":{"type":"string"},"planId":{},"name":{"type":"string"},"quantity":{"type":"string"},"unitPrice":{"type":"string"},"discount":{"type":"string"},"total":{"type":"string"},"metadata":{"type":"object","properties":{"sortOrder":{"type":"integer"},"discountType":{"type":"string"},"discountPercent":{},"tax":{"type":"object","properties":{"kind":{"type":"string"},"id":{"type":"string"},"label":{"type":"string"},"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"ratePercent":{"type":"string"},"type":{"type":"string"}}}}}},"minorUnits":{"type":"integer"},"pricing":{"type":"string"},"unit":{"type":"string"},"recurringPeriod":{"type":"string"}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"description":"The deal's product lines as stored"},"activities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"dealId":{"type":"string"},"type":{"type":"string"},"subject":{"type":"string"},"body":{"type":"string"},"occurredAt":{"type":"string","format":"date-time"},"actorId":{"type":"string"},"metadata":{"type":"object","properties":{"previousStage":{"type":"string"},"newStage":{"type":"string"}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"description":"The latest 50 activities, newest first; metadata.actedBy { id, name, ai: true } names the bot when a bot did it (shown with the AI diamond)"},"lead":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"}},"description":"The lead the deal came from, with its phone and email; only with leads.read"},"_expanded":{"type":"object","properties":{"accountId":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"module":{"type":"string"}}}},"description":"Names for the IDs the deal holds (accountId, custom lookup fields), by field; only for records you may read"}}}}},"example":{"deal":{"id":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Acacia Bakery: 50 Mbps fibre","description":"Fibre for the bakery's main branch","leadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","value":"4500.00","currency":"KES","recurringValue":"4500.00","stage":"proposal","probability":50,"expectedCloseDate":"2026-11-01T00:00:00.000Z","actualCloseDate":null,"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","teamId":null,"priority":"high","source":"website","dealType":"new_business","lostReason":null,"lostReasonDetails":null,"competitorId":null,"lastActivityDate":"2026-10-15T14:05:00.000Z","nextActivityDate":null,"nextActivityType":null,"notes":"","customFields":{"contract_term_length":"12 months"},"externalIds":[],"createdBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","updatedBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","version":3,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","owner":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","firstName":"Brian","lastName":"Mwangi"},"account":{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","name":"Acacia Bakery Ltd"},"contact":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","firstName":"Amina","lastName":"Otieno","name":"Amina Otieno","phone":"+254700000001","email":"amina@example.com"},"products":[{"id":"f4e13e6b-8c0d-4e7f-9d2e-6b7c8d9e0f1a","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","dealId":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","planId":null,"name":"50 Mbps Fibre","quantity":"1.00","unitPrice":"4500.00","discount":"0.00","total":"4500.00","metadata":{"sortOrder":0,"discountType":"amount","discountPercent":null,"tax":{"kind":"tax","id":"460000000038029","label":"VAT (16%)","members":[{"id":"460000000038029","name":"VAT","ratePercent":"16","type":"plain"}]},"minorUnits":2,"pricing":"catalogue","unit":"month","recurringPeriod":"month"},"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}],"activities":[{"id":"a5f24f7c-9d1e-4f8a-8e3f-7c8d9e0f1a2b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","dealId":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","type":"stage_change","subject":"Stage changed: discovery → proposal","body":"Sent the proposal by email","occurredAt":"2026-10-15T14:05:00.000Z","actorId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","metadata":{"previousStage":"discovery","newStage":"proposal"},"createdAt":"2026-10-15T14:05:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}],"lead":{"id":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","name":"Acacia Bakery Ltd","phone":"+254700000002","email":"orders@example.com"},"_expanded":{"accountId":{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","label":"Acacia Bakery Ltd","module":"accounts"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No deal with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.deals.read"]}],"x-required-scopes":["crm.deals.read"]},"put":{"tags":["Deals"],"summary":"Update a deal","description":"Change some of a deal's fields; only the fields you send change. Changing the service plan (in customFields) supersedes an open quote priced from the old plan, and the deal's value then follows the new plan unless you send a value yourself (a typed value is kept from then on). Send `expected` to refuse the save when someone else changed the same fields since you read them. Your organisation's \"record updated\" automations run on it.\n\n**Permission:** `crm.deals.update` (Update deals).","operationId":"updateDeal","parameters":[{"name":"dealId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The deal's ID","example":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid","description":"Move the deal to another account (cannot be emptied)","examples":["3e2b1f4b-6c8d-4f5a-8d0e-4b5c6d7e8f9a"]},"contactId":{"type":"string","format":"uuid","description":"The contact person for the deal","examples":["5a4d3b6d-8e0f-4b7c-8f2a-6d7e8f9a0b1c"]},"currency":{"type":"string","description":"ISO 4217 currency code","examples":["KES"]},"customFields":{"type":"object","description":"ALL your organisation's own fields for the deal, by field key: replaces the whole set, so a key you leave out is deleted. To change a few, use customFieldsPatch","examples":[{"contract_term_length":"24 months"}]},"customFieldsPatch":{"type":"object","description":"Custom field values to change, by field key; every other key is left as it is","examples":[{"contract_term_length":"24 months"}]},"dealType":{"type":"string","description":"Your organisation's kind of deal","examples":["new_business"]},"description":{"type":"string","description":"Longer description","examples":["Fibre for the bakery's main branch"]},"expected":{"type":"object","description":"What you saw for each field this save changes (null for empty), e.g. { name: \"…\", customFields: { pppoe: null } }. If one no longer matches, the save is refused with 409. Leave out for no check","examples":[{"name":"Acacia Bakery: 50 Mbps fibre"}]},"expectedCloseDate":{"type":"string","format":"date-time","description":"When you expect to close it; empty clears it","examples":["2026-11-01T00:00:00.000Z"]},"name":{"type":"string","description":"The deal's name","examples":["Acacia Bakery: 50 Mbps fibre"]},"nextActivityDate":{"type":"string","format":"date-time","description":"When the next activity is due; empty clears it","examples":["2026-11-01T00:00:00.000Z"]},"nextActivityType":{"type":"string","description":"What the next activity is","examples":["call"]},"notes":{"type":"string","description":"Free-text notes","examples":["Customer asked for a site survey first"]},"ownerId":{"type":"string","format":"uuid","description":"Who owns the deal","examples":["b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e"]},"priority":{"type":"string","enum":["low","medium","high"],"description":"How urgent it is","examples":["high"]},"products":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","format":"uuid"},"planId":{"type":"string","format":"uuid"},"name":{"type":"string"},"quantity":{"type":"number"},"unitPrice":{"type":"number"},"discount":{"type":"number","description":"Percent off, 0 to 100"}},"required":["productId","name","quantity","unitPrice"]},"description":"Replace the deal's products with these, stored as sent (each discount is a percent). To price them from the catalogue, use PUT /deals/:dealId/products","examples":[[{"productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","name":"50 Mbps Fibre","quantity":1,"unitPrice":4500,"discount":0}]]},"recurringValue":{"type":"number","description":"What it brings in each billing period","examples":[4500]},"value":{"type":"number","description":"What the deal is worth. A value different from the stored one counts as typed by hand and is kept when the plan changes","examples":[5000]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"deal":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"leadId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"value":{"type":"string","description":"Deal value, a decimal string in the deal's currency (two decimals)"},"currency":{"type":"string","description":"ISO 4217 code; the organisation's currency unless set"},"recurringValue":{"type":"string"},"stage":{"type":"string","description":"Stage key on the deals pipeline, e.g. qualification, discovery, proposal, negotiation, closed_won, closed_lost (organisations may use their own)"},"probability":{"type":"integer","description":"Chance of winning, 0 to 100"},"expectedCloseDate":{"type":"string","format":"date-time"},"actualCloseDate":{},"ownerId":{"type":"string"},"teamId":{},"priority":{"type":"string","description":"low, medium or high"},"source":{"type":"string"},"dealType":{"type":"string"},"lostReason":{},"lostReasonDetails":{},"competitorId":{},"lastActivityDate":{"type":"string","format":"date-time"},"nextActivityDate":{},"nextActivityType":{},"notes":{"type":"string"},"customFields":{"type":"object","properties":{"contract_term_length":{"type":"string"}},"description":"The organisation's own fields, by field key. Fields hidden from you come back masked"},"externalIds":{"type":"array"},"createdBy":{"type":"string"},"updatedBy":{"type":"string"},"version":{"type":"integer","description":"Goes up by one on every change"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"message":{"type":"string","description":"\"Deal updated successfully\""}}},"example":{"deal":{"id":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Acacia Bakery: 50 Mbps fibre","description":"Fibre for the bakery's main branch","leadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","value":"4500.00","currency":"KES","recurringValue":"4500.00","stage":"proposal","probability":50,"expectedCloseDate":"2026-11-01T00:00:00.000Z","actualCloseDate":null,"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","teamId":null,"priority":"high","source":"website","dealType":"new_business","lostReason":null,"lostReasonDetails":null,"competitorId":null,"lastActivityDate":"2026-10-15T14:05:00.000Z","nextActivityDate":null,"nextActivityType":null,"notes":"","customFields":{"contract_term_length":"12 months"},"externalIds":[],"createdBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","updatedBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","version":4,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"},"message":"Deal updated successfully"}}}},"400":{"description":"accountId was sent empty (a deal's account can be changed, not removed), or customFieldsPatch or expected is not an object","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No deal with that ID, or the account sent does not exist in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A field named in expected was changed by someone else since you read it (FIELD_CONFLICT; meta.fields lists them). Nothing was saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.deals.update"]}],"x-required-scopes":["crm.deals.update"]}},"/deals/{dealId}/billing":{"get":{"tags":["Deals"],"summary":"Get a deal's billing summary","description":"What the deal's billing card shows: whether a quote can be made, the deal's main quote with its Kazee Books estimate and invoice and how much is paid, the deal's extra quotes, earlier quotes, and the service (subscription) it started. The money (totals, payments, lines, Books references) is included only if you also have billing.read; financialsVisible says whether it is. Reads CRM only, so it is fast; it never calls Books.\n\n**Permission:** `crm.deals.read` (View deals).","operationId":"getDealBilling","parameters":[{"name":"dealId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The deal's ID","example":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"dealId":{"type":"string"},"canQuote":{"type":"boolean","description":"Deprecated (older web): no quote yet, nothing blocking and the plan could be priced"},"quoteReady":{"type":"boolean","description":"A new main quote can be made: no open one (an expired or declined one does not count) and nothing blocking"},"blockers":{"type":"array","description":"Why the deal cannot be billed yet, in words"},"deal":{"type":"object","properties":{"value":{"type":"string"},"currency":{"type":"string"},"valueSource":{"type":"string","description":"plan when the value follows the plan, manual when someone typed it"},"planId":{"type":"string"},"specialPlanPrice":{}}},"contractTermLength":{"type":"string"},"askFields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"type":{"type":"string"},"options":{"type":"array","items":{"type":"string"}},"value":{"type":"string"},"missing":{"type":"boolean"}}},"description":"Your organisation's questions to answer before a quote, with this deal's answers; missing true holds the quote back"},"planLineRule":{"description":"The billing rule the deal's plan line follows, or null"},"noInvoice":{"type":"object","properties":{"matched":{"type":"boolean"},"reason":{}},"description":"Whether the deal matches your organisation's no-monthly-invoice rule, and why"},"pricingPreview":{"description":"Deprecated (older web): the plan priced by CRM, or null"},"financialsVisible":{"type":"boolean","description":"You have billing.read: money fields are included. Without it they are left out, not blanked"},"quote":{"type":"object","properties":{"id":{"type":"string"},"quoteNumber":{"type":"string"},"status":{"type":"string"},"source":{},"validityEnd":{"type":"string","format":"date-time"},"sentAt":{"type":"string","format":"date-time"},"open":{"type":"boolean"},"lineVersion":{"type":"integer","description":"2 for the Create quote dialog's lines, 1 for older lines, null for neither"},"total":{"type":"string"},"sourcePaymentStatus":{},"paidAmount":{"type":"string"},"balance":{"type":"string"},"paidAt":{},"paymentState":{"type":"string","description":"paid, partially_paid or unpaid"},"lineItems":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"quoteId":{"type":"string"},"productId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"quantity":{"type":"string"},"unitPrice":{"type":"string"},"discount":{"type":"string"},"taxAmount":{"type":"string"},"subtotal":{"type":"string"},"total":{"type":"string"},"sortOrder":{"type":"integer"},"metadata":{"type":"object","properties":{"tax":{"type":"object","properties":{"kind":{"type":"string"},"id":{"type":"string"},"label":{"type":"string"},"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"ratePercent":{"type":"string"},"type":{"type":"string"}}}}}},"treatment":{"type":"string"}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"treatment":{"type":"string"},"discountType":{"type":"string"},"discountPercent":{},"locked":{"type":"boolean"},"discountLocked":{"type":"boolean"},"descriptionLocked":{"type":"boolean"}}}},"taxTotal":{"type":"string"},"taxSummary":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"rate":{"type":"string"},"base":{"type":"string"},"amount":{"type":"string"}}}},"booksTotalMismatch":{}},"description":"The deal's main quote (a paid quote raised in Zoho leads when there are several), or null. A quote raised in Zoho also carries zoho (its payments and invoices as Zoho has them) and sourceAdjustment"},"estimate":{"type":"object","properties":{"id":{"type":"string"},"number":{"type":"string"},"accountRef":{"type":"string"},"paybill":{"type":"string"}},"description":"The Kazee Books estimate: its number and the account reference customers pay to (needs billing.read)"},"invoice":{},"booksContactId":{"type":"string"},"paidFirst":{"description":"Set when Books ran the paid-in-full rule: deposit, invoice, and how far installation and billing have got. profileLeftPausedAt: when the monthly invoice was left paused, so nothing is invoiced from it (null when it was not)"},"subscription":{"description":"The service this deal started, when exactly one points at it; recurringProfileId needs billing.read"},"extraQuoteReady":{"type":"boolean","description":"An extra quote can be added (the deal has an account)"},"extraQuotes":{"type":"array","description":"Extra quotes beside the main one, newest first, each with its own estimate and invoice"},"history":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"quoteNumber":{"type":"string"},"status":{"type":"string"},"total":{"type":"string"}}},"description":"The deal's main quotes, old and new"}}}}},"example":{"success":true,"data":{"dealId":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","canQuote":false,"quoteReady":false,"blockers":[],"deal":{"value":"5220.00","currency":"KES","valueSource":"plan","planId":"9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a","specialPlanPrice":null},"contractTermLength":"12 months","askFields":[{"key":"installation_type","label":"Installation type","type":"select","options":["Standard","Premium"],"value":"Standard","missing":false}],"planLineRule":null,"noInvoice":{"matched":false,"reason":null},"pricingPreview":null,"financialsVisible":true,"quote":{"id":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","quoteNumber":"QT-000123","status":"sent","source":null,"validityEnd":"2026-10-29T14:05:00.000Z","sentAt":"2026-10-15T14:05:00.000Z","open":true,"lineVersion":2,"total":"5220.00","sourcePaymentStatus":null,"paidAmount":"2000.00","balance":"3220.00","paidAt":null,"paymentState":"partially_paid","lineItems":[{"id":"b6a35a8d-0e2f-4a9b-9f4a-8d9e0f1a2b3c","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","quoteId":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","name":"50 Mbps Fibre","description":"Monthly fibre internet, 50 Mbps","quantity":"1.00","unitPrice":"4500.00","discount":"0.00","taxAmount":"720.00","subtotal":"4500.00","total":"5220.00","sortOrder":0,"metadata":{"tax":{"kind":"tax","id":"460000000038029","label":"VAT (16%)","members":[{"id":"460000000038029","name":"VAT","ratePercent":"16","type":"plain"}]},"treatment":"invoice"},"createdAt":"2026-10-15T14:05:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","treatment":"invoice","discountType":"amount","discountPercent":null,"locked":true,"discountLocked":false,"descriptionLocked":true}],"taxTotal":"720.00","taxSummary":[{"name":"VAT","rate":"16","base":"4500.00","amount":"720.00"}],"booksTotalMismatch":null},"estimate":{"id":"460000000123456","number":"EST-000123","accountRef":"EST000123","paybill":"123456"},"invoice":null,"booksContactId":"460000000654321","paidFirst":null,"subscription":null,"extraQuoteReady":true,"extraQuotes":[],"history":[{"id":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","quoteNumber":"QT-000123","status":"sent","total":"5220.00"}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No deal with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.deals.read"]}],"x-required-scopes":["crm.deals.read"]}},"/deals/{dealId}/buttons":{"get":{"tags":["Automations"],"summary":"List a deal's buttons","description":"Lists the buttons that apply to this sale (switched-on automation rules on deals with the manual trigger, whose conditions the deal passes), and whether you may press each. A button whose rule is not set up right is listed with allowed false and the reason in error.\n\n**Permission:** `crm.deals.read` (View deals).","operationId":"listDealButtons","parameters":[{"name":"dealId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The sale (deal)","example":"5be2f3a4-8c9d-4e0f-9a1b-2c3d4e5f6a7b"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The button's automation rule"},"label":{"type":"string"},"description":{},"allowed":{"type":"boolean","description":"You are one of the people the button names"},"error":{"type":"string","description":"Only when the button is not set up right: what is wrong (it cannot be pressed)"}}}}}},"example":{"success":true,"data":[{"id":"4ad1e2f3-7b8c-4d9e-8f0a-1b2c3d4e5f6b","label":"Push to Books","description":null,"allowed":false,"error":"triggerConfig.button.label: is 1 to 60 characters"},{"id":"39c0d1e2-6a7b-4c8d-9e0f-0a1b2c3d4e5f","label":"Send to Zoho","description":"Creates the customer and the deal in Zoho CRM","allowed":true}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No deal with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.deals.read"]}],"x-required-scopes":["crm.deals.read"]}},"/deals/{dealId}/quote-draft":{"get":{"tags":["Deals"],"summary":"Get a deal's quote draft","description":"What the Create / Edit quote dialog opens with for a deal: the starting lines priced now, the quote templates to pick from, and the options. With quoteId, that quote's own lines (to edit it); with templateId or kitId, that template's or kit's lines; otherwise the deal's products. The reply shown is the generic quote engine's (engine v3). Organisations on the plan-based engine (and quotes it made) get its draft instead: plan, planLineRule, template, templates, lines, catalogue (Books taxes) and notices, with the plan line linked to the plan's Kazee Books item.\n\n**Permission:** `crm.deals.read` (View deals) and `crm.books.items.read`.","operationId":"getDealQuoteDraft","parameters":[{"name":"dealId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The deal's ID","example":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e"},{"name":"kind","in":"query","required":false,"schema":{"type":"string","enum":["main","extra"]},"description":"main (default), or extra for a new extra quote beside the main one: no plan line and nothing from the deal's plan or products","example":"main"},{"name":"kitId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Start from this kit's parts (a product made of other products; generic engine only)","example":"c7b46b9e-1f3a-4bac-8a5b-9e0f1a2b3c4d"},{"name":"quoteId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"A quote of this deal to edit: the draft is its own lines","example":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d"},{"name":"templateId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Start from this quote template's lines","example":"7c6f5d8f-0a2b-4d9e-8b4c-8f9a0b1c2d3e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"engine":{"type":"string","description":"v3: the generic quote engine (absent on the plan-based draft)"},"pricing":{"type":"string","description":"catalogue (your CRM prices) or connected (Kazee Books prices)"},"currency":{"type":"object","properties":{"code":{"type":"string"},"minorUnits":{"type":"integer"}}},"dealId":{"type":"string"},"quoteId":{},"extra":{"type":"boolean","description":"A draft for an extra quote beside the main one"},"template":{},"templates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"isDefaultForPlan":{"type":"boolean","description":"The default template for the deal's plan"}}}},"kit":{},"lines":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string"},"itemRef":{},"name":{"type":"string"},"description":{"type":"string"},"unit":{"type":"string"},"recurringPeriod":{"type":"string"},"quantity":{"type":"integer"},"unitPrice":{"type":"integer"},"listPrice":{"type":"integer"},"discount":{"type":"integer"},"discountType":{"type":"string"},"discountPercent":{},"treatment":{"type":"string"},"tax":{"type":"object","properties":{"kind":{"type":"string"},"id":{"type":"string"},"label":{"type":"string"},"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"ratePercent":{"type":"string"},"type":{"type":"string"}}}}}},"locked":{"type":"boolean","description":"Product and quantity cannot be changed"},"priceLocked":{"type":"boolean","description":"The price cannot be changed (your quote preferences do not allow it)"},"discountLocked":{"type":"boolean"},"descriptionLocked":{"type":"boolean"},"treatmentLocked":{"type":"boolean"},"hint":{},"issue":{"description":"Why the line cannot go on a quote now, or null"}}}},"quoteDiscount":{"description":"A discount on the whole quote (amount or percent), or null"},"confirmFields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"type":{"type":"string"},"options":{"type":"array","items":{"type":"string"}},"value":{"type":"string"},"missing":{"type":"boolean"}}},"description":"Deal fields to confirm before creating the quote, with their current answers"},"options":{"type":"object","properties":{"allowPriceOverride":{"type":"boolean"},"validityDays":{"type":"integer"},"contractTerms":{"type":"array","items":{"type":"string"}},"mirrorsQuotes":{"type":"boolean","description":"A created quote also becomes a Kazee Books estimate"}}},"notices":{"type":"array","description":"Plain notes for the person, e.g. a price that moved"}}}}},"example":{"success":true,"data":{"engine":"v3","pricing":"catalogue","currency":{"code":"KES","minorUnits":2},"dealId":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","quoteId":null,"extra":false,"template":null,"templates":[{"id":"7c6f5d8f-0a2b-4d9e-8b4c-8f9a0b1c2d3e","name":"Home fibre","isDefaultForPlan":true}],"kit":null,"lines":[{"productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","itemRef":null,"name":"50 Mbps Fibre","description":"Monthly fibre internet, 50 Mbps","unit":"month","recurringPeriod":"month","quantity":1,"unitPrice":4500,"listPrice":4500,"discount":0,"discountType":"amount","discountPercent":null,"treatment":"invoice","tax":{"kind":"tax","id":"460000000038029","label":"VAT (16%)","members":[{"id":"460000000038029","name":"VAT","ratePercent":"16","type":"plain"}]},"locked":false,"priceLocked":true,"discountLocked":false,"descriptionLocked":false,"treatmentLocked":false,"hint":null,"issue":null}],"quoteDiscount":null,"confirmFields":[{"key":"installation_type","label":"Installation type","type":"select","options":["Standard","Premium"],"value":"Standard","missing":false}],"options":{"allowPriceOverride":false,"validityDays":14,"contractTerms":["12 months","24 months"],"mirrorsQuotes":false},"notices":[]}}}}},"400":{"description":"kind is not main or extra, the quote belongs to another deal, or the kit has no parts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No deal, quote, template or kit with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.deals.read","crm.books.items.read"]}],"x-required-scopes":["crm.deals.read","crm.books.items.read"],"x-scope-mode":"all"}},"/deals/{dealId}/products":{"get":{"tags":["Deals"],"summary":"List a deal's products","description":"List the products on a deal as saved: each line's product, quantity, price, discount and the tax it was priced with, the totals, and the version to send back when you save the list (PUT /deals/:dealId/products).\n\n**Permission:** `crm.deals.read` (View deals).","operationId":"getDealProducts","parameters":[{"name":"dealId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The deal's ID","example":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"dealId":{"type":"string"},"lines":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"productId":{"type":"string"},"name":{"type":"string"},"quantity":{"type":"integer"},"unitPrice":{"type":"integer"},"discountType":{"type":"string","description":"amount or percent: how the line's discount was entered"},"discount":{"type":"integer","description":"The amount off the line, before tax"},"discountPercent":{"description":"The percent entered, for a percent line"},"total":{"type":"integer","description":"quantity × unitPrice − discount, before tax"},"tax":{"type":"object","properties":{"kind":{"type":"string"},"id":{"type":"string"},"label":{"type":"string"},"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"ratePercent":{"type":"string"},"type":{"type":"string"}}}}},"description":"The tax the line was priced with"},"unit":{"type":"string"},"recurringPeriod":{"type":"string","description":"How often the line bills (month, year …); null for a one-off"}}}},"version":{"type":"string","description":"Send this back when saving; a save from an older list is refused (409)"},"totals":{"type":"object","properties":{"subtotal":{"type":"string"},"tax":{"type":"string"},"total":{"type":"string"}},"description":"Subtotal, tax and total as decimal strings; null when a line was saved before taxes were stored"}}}}},"example":{"success":true,"data":{"dealId":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","lines":[{"id":"f4e13e6b-8c0d-4e7f-9d2e-6b7c8d9e0f1a","productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","name":"50 Mbps Fibre","quantity":1,"unitPrice":4500,"discountType":"amount","discount":0,"discountPercent":null,"total":4500,"tax":{"kind":"tax","id":"460000000038029","label":"VAT (16%)","members":[{"id":"460000000038029","name":"VAT","ratePercent":"16","type":"plain"}]},"unit":"month","recurringPeriod":"month"}],"version":"3f9a1c2b7d4e5f60","totals":{"subtotal":"4500.00","tax":"720.00","total":"5220.00"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No deal with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.deals.read"]}],"x-required-scopes":["crm.deals.read"]},"put":{"tags":["Deals"],"summary":"Save a deal's products","description":"Replace all the products on a deal with the list sent (at most 100 lines), each priced now from your catalogue or Kazee Books: its name and tax come from the product, and a price may differ from the catalogue only where your quote preferences allow it. The deal's value then follows your organisation's value rule, unless someone typed the value by hand. Send the version you read to avoid overwriting someone else's change.\n\n**Permission:** `crm.deals.update` (Update deals).","operationId":"updateDealProducts","parameters":[{"name":"dealId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The deal's ID","example":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"products":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","format":"uuid"},"quantity":{"type":"number","description":"Default 1; above 0, at most 2 decimals"},"unitPrice":{"type":"number","description":"Leave out for the catalogue price"},"discountType":{"type":"string","enum":["amount","percent"]},"discount":{"type":"number","description":"Amount off the line, for discountType amount"},"discountPercent":{"type":"number","description":"0 to 100, for discountType percent"}},"required":["productId"]},"description":"The deal's full list of products, in order; an empty list removes them all (a JSON array in a string also works)","examples":[[{"productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","quantity":1,"discountType":"amount","discount":0}]]},"version":{"type":"string","description":"The version from the last read. Leave out to save without the check","examples":["3f9a1c2b7d4e5f60"]}},"required":["products"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"dealId":{"type":"string"},"lines":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"productId":{"type":"string"},"name":{"type":"string"},"quantity":{"type":"integer"},"unitPrice":{"type":"integer"},"discountType":{"type":"string"},"discount":{"type":"integer"},"discountPercent":{},"total":{"type":"integer"},"tax":{"type":"object","properties":{"kind":{"type":"string"},"id":{"type":"string"},"label":{"type":"string"},"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"ratePercent":{"type":"string"},"type":{"type":"string"}}}}}},"unit":{"type":"string"},"recurringPeriod":{"type":"string"}}}},"version":{"type":"string","description":"The new version, to send with the next save"},"totals":{"type":"object","properties":{"subtotal":{"type":"string"},"tax":{"type":"string"},"total":{"type":"string"}}},"dealValue":{"type":"object","properties":{"updated":{"type":"boolean"},"value":{"type":"string"},"rule":{"type":"string"}},"description":"What the value rule did: updated (true when the deal's value changed), value, rule (recurring_incl_tax, all_lines or manual) and, when nothing changed, skipped (manual, no_value, unchanged or not_found). Null when the rule could not run"}}}}},"example":{"success":true,"data":{"dealId":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","lines":[{"id":"f4e13e6b-8c0d-4e7f-9d2e-6b7c8d9e0f1a","productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","name":"50 Mbps Fibre","quantity":1,"unitPrice":4500,"discountType":"amount","discount":0,"discountPercent":null,"total":4500,"tax":{"kind":"tax","id":"460000000038029","label":"VAT (16%)","members":[{"id":"460000000038029","name":"VAT","ratePercent":"16","type":"plain"}]},"unit":"month","recurringPeriod":"month"}],"version":"8b1d2e3f4a5c6d7e","totals":{"subtotal":"4500.00","tax":"720.00","total":"5220.00"},"dealValue":{"updated":true,"value":"5220.00","rule":"recurring_incl_tax"}}}}}},"400":{"description":"products is not a list, has over 100 lines, or a line is not valid (no product, a product that no longer exists or cannot be quoted, a bad quantity, price or discount, a price change your organisation does not allow, or a discount larger than the line); the message names the line","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No deal with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The deal's products were changed by someone else since the version you sent; reload and save again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.deals.update"]}],"x-required-scopes":["crm.deals.update"]}},"/deals/{dealId}/kit-lines":{"get":{"tags":["Deals"],"summary":"Price a kit's parts for a deal's quote","description":"Price a kit's parts as quote lines for a deal, the way your organisation prices quotes now (its own catalogue, or Kazee Books when connected), ready to add in the quote dialog. A part that cannot be priced is kept with the reason in `issue`.\n\n**Permission:** `crm.deals.read` (View deals) and `crm.products.read` (View products).","operationId":"getDealKitLines","parameters":[{"name":"dealId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The deal's ID","example":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e"},{"name":"kitId","in":"query","required":true,"schema":{"type":"string","format":"uuid"},"description":"The kit (a product made of other products) to add","example":"c7b46b9e-1f3a-4bac-8a5b-9e0f1a2b3c4d"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"kit":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}},"lines":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string"},"itemRef":{"description":"The connected catalogue's (Kazee Books) item for the line, when there is one"},"name":{"type":"string"},"description":{"type":"string"},"unit":{"type":"string"},"recurringPeriod":{"type":"string"},"quantity":{"type":"integer"},"unitPrice":{"type":"integer"},"listPrice":{"type":"integer","description":"The product's own price now"},"discount":{"type":"integer"},"discountType":{"type":"string"},"discountPercent":{},"treatment":{"type":"string","description":"invoice or retainer"},"tax":{"type":"object","properties":{"kind":{"type":"string"},"id":{"type":"string"},"label":{"type":"string"},"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"ratePercent":{"type":"string"},"type":{"type":"string"}}}}}},"locked":{"type":"boolean"},"priceLocked":{"type":"boolean","description":"The price cannot be changed (your quote preferences do not allow it)"},"discountLocked":{"type":"boolean"},"descriptionLocked":{"type":"boolean"},"treatmentLocked":{"type":"boolean"},"hint":{},"issue":{"description":"Why the line cannot go on a quote now, or null"}}}},"pricing":{"type":"string","description":"catalogue (your CRM prices) or connected (Kazee Books prices)"},"currency":{"type":"object","properties":{"code":{"type":"string"},"minorUnits":{"type":"integer","description":"Decimals the figures are rounded to"}}}}}}},"example":{"success":true,"data":{"kit":{"id":"c7b46b9e-1f3a-4bac-8a5b-9e0f1a2b3c4d","name":"Home fibre starter kit"},"lines":[{"productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","itemRef":null,"name":"50 Mbps Fibre","description":"Monthly fibre internet, 50 Mbps","unit":"month","recurringPeriod":"month","quantity":1,"unitPrice":4500,"listPrice":4500,"discount":0,"discountType":"amount","discountPercent":null,"treatment":"invoice","tax":{"kind":"tax","id":"460000000038029","label":"VAT (16%)","members":[{"id":"460000000038029","name":"VAT","ratePercent":"16","type":"plain"}]},"locked":false,"priceLocked":true,"discountLocked":false,"descriptionLocked":false,"treatmentLocked":false,"hint":null,"issue":null}],"pricing":"catalogue","currency":{"code":"KES","minorUnits":2}}}}}},"400":{"description":"The kit has no parts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No kit (product) or deal with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.deals.read","crm.products.read"]}],"x-required-scopes":["crm.deals.read","crm.products.read"],"x-scope-mode":"all"}},"/products/{productId}/kit":{"get":{"tags":["Products & plans"],"summary":"List a kit's parts","description":"List the parts of a kit (a product made of other products), with their quantities. A product with no parts answers an empty list.\n\n**Permission:** `crm.products.read` (View products).","operationId":"getProductKit","parameters":[{"name":"productId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The kit's product ID","example":"c7b46b9e-1f3a-4bac-8a5b-9e0f1a2b3c4d"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"productId":{"type":"string"},"name":{"type":"string"},"parts":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string"},"name":{"type":"string"},"quantity":{"type":"integer"},"price":{"type":"string","description":"The part's own price before tax, a decimal string"},"status":{"type":"string","description":"active, inactive or discontinued"},"unit":{"type":"string"}}}}}}}},"example":{"success":true,"data":{"productId":"c7b46b9e-1f3a-4bac-8a5b-9e0f1a2b3c4d","name":"Home fibre starter kit","parts":[{"productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","name":"50 Mbps Fibre","quantity":1,"price":"4500.00","status":"active","unit":"month"},{"productId":"d8c57c0f-2a4b-4cbd-9b6c-0f1a2b3c4d5e","name":"Wi-Fi router","quantity":1,"price":"3500.00","status":"active","unit":"piece"}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No product with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.products.read"]}],"x-required-scopes":["crm.products.read"]},"put":{"tags":["Products & plans"],"summary":"Save a kit's parts","description":"Replace the parts of a kit with the list sent (at most 50). A part must be an active product, not the kit itself and not another kit, and each product only once. An empty list makes it a plain product again. Answers the kit as saved.\n\n**Permission:** `crm.products.update` (Update products).","operationId":"updateProductKit","parameters":[{"name":"productId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The kit's product ID","example":"c7b46b9e-1f3a-4bac-8a5b-9e0f1a2b3c4d"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"parts":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","format":"uuid"},"quantity":{"type":"number","description":"Default 1; above 0, at most 2 decimals"}},"required":["productId"]},"description":"The kit's full list of parts (a JSON array in a string also works)","examples":[[{"productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","quantity":1}]]}},"required":["parts"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"productId":{"type":"string"},"name":{"type":"string"},"parts":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string"},"name":{"type":"string"},"quantity":{"type":"integer"},"price":{"type":"string"},"status":{"type":"string"},"unit":{"type":"string"}}}}}}}},"example":{"success":true,"data":{"productId":"c7b46b9e-1f3a-4bac-8a5b-9e0f1a2b3c4d","name":"Home fibre starter kit","parts":[{"productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","name":"50 Mbps Fibre","quantity":1,"price":"4500.00","status":"active","unit":"month"},{"productId":"d8c57c0f-2a4b-4cbd-9b6c-0f1a2b3c4d5e","name":"Wi-Fi router","quantity":1,"price":"3500.00","status":"active","unit":"piece"}]}}}}},"400":{"description":"parts is not a list or has over 50, or a part is not valid (no product, the kit itself, listed twice, a bad quantity, not found, not active, or itself a kit); the message names the part","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No product with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.products.update"]}],"x-required-scopes":["crm.products.update"]}},"/tickets/analytics":{"get":{"tags":["Tickets"],"summary":"Get ticket analytics","description":"Ticket figures for a dashboard, over the tickets you may see that were opened in a period (the last 30 days unless dateRange says otherwise): totals, counts by status and priority, tickets per month, how many kept their resolution deadline, and who holds the most tickets.\n\n**Permission:** `crm.tickets.read` (View tickets).","operationId":"getTicketAnalytics","parameters":[{"name":"dateRange","in":"query","required":false,"schema":{"type":"object","properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"}}},"description":"Only tickets opened in this period, as an object { from, to }; either end may be left out. Without it: the last 30 days","example":{"from":"2026-10-01T00:00:00.000Z","to":"2026-10-21T00:00:00.000Z"}}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"summary":{"type":"object","properties":{"totalTickets":{"type":"integer"},"slaComplianceRate":{"type":"number","description":"Percent of tickets with a decided resolution clock that kept it (100 when none is decided yet)"}}},"distributions":{"type":"object","properties":{"status":{"type":"array","items":{"type":"object","properties":{"status":{"type":"string"},"count":{"type":"integer"}}}},"priority":{"type":"array","items":{"type":"object","properties":{"priority":{"type":"string"},"count":{"type":"integer"}}}}}},"trends":{"type":"object","properties":{"monthly":{"type":"array","items":{"type":"object","properties":{"month":{"type":"string","description":"Month opened, as YYYY-MM"},"count":{"type":"integer"},"closed":{"type":"integer","description":"Of those, how many are now resolved or closed"}}}}}},"sla":{"type":"object","properties":{"total":{"type":"integer"},"breached":{"type":"integer","description":"Tickets that missed their resolution deadline"},"withinSla":{"type":"integer","description":"Tickets resolved within their resolution deadline"},"pending":{"type":"integer","description":"Tickets whose resolution clock is not decided yet"}}},"topAssignees":{"type":"array","items":{"type":"object","properties":{"assignedTo":{"type":"string"},"count":{"type":"integer"}},"description":"People by how many of the tickets they hold, most first"}}}},"generatedAt":{"type":"string","format":"date-time"}}},"example":{"success":true,"data":{"summary":{"totalTickets":42,"slaComplianceRate":87.5},"distributions":{"status":[{"status":"open","count":12},{"status":"resolved","count":30}],"priority":[{"priority":"high","count":9},{"priority":"medium","count":33}]},"trends":{"monthly":[{"month":"2026-10","count":42,"closed":30}]},"sla":{"total":42,"breached":4,"withinSla":28,"pending":10},"topAssignees":[{"assignedTo":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","count":18},{"assignedTo":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","count":11}]},"generatedAt":"2026-10-21T09:00:00.000Z"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.read"]}],"x-required-scopes":["crm.tickets.read"]}},"/tickets/queue-counts":{"get":{"tags":["Tickets"],"summary":"Count tickets per queue filter","description":"Counts the tickets you may see in each filter of the Tickets queue: my open (assigned to you and open, pending or in progress; with includeUnassigned also unassigned unfinished tickets in no department or one of yours), needs reply (unfinished, the customer wrote last), all open (not resolved or closed), on hold (waiting on the customer) and all tickets. Each number is what GET /tickets answers for that filter.\n\n**Permission:** `crm.tickets.read` (View tickets).","operationId":"getTicketQueueCounts","parameters":[{"name":"includeUnassigned","in":"query","required":false,"schema":{"type":"boolean"},"description":"true: my open also counts unassigned unfinished tickets in no department or one of yours (Desk's Show unassigned)","example":true}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"myOpen":{"type":"integer","description":"Assigned to you and open, pending or in progress (plus unassigned unfinished ones in your departments with includeUnassigned); view=my_open"},"needsReply":{"type":"integer","description":"Not resolved or closed, and the customer wrote last; needsReply=true with the unfinished statuses"},"allOpen":{"type":"integer","description":"Not resolved or closed"},"onHold":{"type":"integer","description":"Waiting on the customer; onHold=true"},"all":{"type":"integer","description":"Every ticket you may see"}}}}},"example":{"success":true,"data":{"myOpen":7,"needsReply":4,"allOpen":38,"onHold":5,"all":1204}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.read"]}],"x-required-scopes":["crm.tickets.read"]}},"/tickets/statuses":{"get":{"tags":["Tickets"],"summary":"List ticket statuses","description":"Lists the ticket statuses of the organisation's ticket blueprint (its ticket pipeline), in board order: each status's key (the value a ticket's status holds), its name, colour and kind. The first call for an organisation without a ticket blueprint makes Desk's default one (and upgrades the untouched old 4-stage one), so this call can create data.\n\n**Permission:** `crm.tickets.read` (View tickets).","operationId":"getTicketStatuses","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"statuses":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"The value a ticket's status holds"},"label":{"type":"string","description":"The status's name, as the blueprint calls it"},"color":{"type":"string","description":"Its colour, a hex code"},"stageType":{"type":"string","description":"Its kind: active, won (resolved) or archived (closed), or another the blueprint uses"},"orderNum":{"type":"integer","description":"Its place on the board, from 0"}}}}}},"example":{"success":true,"statuses":[{"key":"open","label":"Open","color":"#3b82f6","stageType":"active","orderNum":0},{"key":"pending","label":"Pending","color":"#a855f7","stageType":"active","orderNum":1},{"key":"in_progress","label":"In Progress","color":"#f59e0b","stageType":"active","orderNum":2},{"key":"waiting_on_customer","label":"Waiting on customer","color":"#06b6d4","stageType":"active","orderNum":3},{"key":"waiting_on_third_party","label":"Waiting on third party","color":"#64748b","stageType":"active","orderNum":4},{"key":"resolved","label":"Resolved","color":"#10b981","stageType":"won","orderNum":5},{"key":"closed","label":"Closed","color":"#6b7280","stageType":"archived","orderNum":6}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.read"]}],"x-required-scopes":["crm.tickets.read"]}},"/tickets":{"get":{"tags":["Tickets"],"summary":"List tickets","description":"Lists the tickets you may see, newest first, a page at a time, with each one's account, contact, assignee, department and newest message the customer can see. Filters combine (all must match); a filter that takes several values matches any of them.\n\n**Permission:** `crm.tickets.read` (View tickets).","operationId":"getTickets","parameters":[{"name":"accountId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this account's tickets","example":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b"},{"name":"assignedTo","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only tickets assigned to this person","example":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"},{"name":"category","in":"query","required":false,"schema":{"type":"string"},"description":"Only tickets in this category (ignoring case), or none for tickets without one","example":"Billing"},{"name":"channel","in":"query","required":false,"schema":{"type":"string"},"description":"Only tickets that came in on this channel, e.g. whatsapp (repeat the key for several)","example":"whatsapp"},{"name":"contactId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this contact's tickets","example":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c"},{"name":"dealId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only tickets about this deal","example":"d3c4d5e6-f7a8-4b9c-8e1f-2a3b4c5d6e7f"},{"name":"includeStats","in":"query","required":false,"schema":{"type":"boolean"},"description":"true: also count the matching tickets by status, priority and channel (over the first 500 matches only)","example":true},{"name":"includeUnassigned","in":"query","required":false,"schema":{"type":"boolean"},"description":"With view=my_open, true: also unassigned tickets that are not resolved or closed, in no department or one of yours (Desk's Show unassigned)","example":true},{"name":"leadId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only tickets about this lead","example":"c2b3c4d5-e6f7-4a8b-9d0e-1f2a3b4c5d6e"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25},"description":"Tickets per page, 1 to 500","example":25},{"name":"needsReply","in":"query","required":false,"schema":{"type":"boolean"},"description":"true: only tickets where the customer wrote last (Desk's Needs reply)","example":true},{"name":"onHold","in":"query","required":false,"schema":{"type":"boolean"},"description":"true: only tickets on hold (waiting on the customer), soonest back first unless sort says otherwise","example":true},{"name":"ownerId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only tickets this person filed","example":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"priority","in":"query","required":false,"schema":{"type":"string","enum":["low","medium","high","urgent"]},"description":"Only tickets with this priority (repeat the key for several)","example":"high"},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text found in the subject, description, ticket number, or the requester's name or email","example":"router"},{"name":"senderPhone","in":"query","required":false,"schema":{"type":"string"},"description":"Only tickets from this chat number (WhatsApp), for chats no contact is linked to","example":"254711000001"},{"name":"sort","in":"query","required":false,"schema":{"type":"object"},"description":"Order as an object { field, order }: field is createdAt (default), updatedAt, priority, status, queue or holdUntil; order is asc or desc (default). The plain value queue (sort=queue) is the work queue's order: back from hold first (the newest return first), then tickets where the customer wrote last, then the newest activity. holdUntil: soonest back first (the default with onHold). Any other plain query-string value is ignored, so the list stays newest first","example":{"field":"updatedAt","order":"desc"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["open","pending","in_progress","waiting_on_customer","waiting_on_third_party","resolved","closed"]},"description":"Only tickets with this status (repeat the key for several)","example":"open"},{"name":"subscriptionId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only tickets about this subscription","example":"b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d"},{"name":"tags","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"}},"description":"Only tickets that carry all of these labels","example":["outage"]},{"name":"teamId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only tickets in this department","example":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"},{"name":"unassigned","in":"query","required":false,"schema":{"type":"boolean"},"description":"true: only tickets nobody is assigned to","example":true},{"name":"view","in":"query","required":false,"schema":{"type":"string","enum":["my_open"]},"description":"my_open: Desk's My open, the tickets assigned to you that are open, pending or in progress (tickets on hold are not in it). Other values are ignored","example":"my_open"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"ticketNumber":{"type":"string","description":"The number people quote, e.g. TKT-001042"},"subject":{"type":"string"},"description":{"type":"string"},"channel":{"type":"string","description":"Where it came in: web, email, whatsapp, phone, chat, telegram and the like"},"status":{"type":"string","description":"open, pending, in_progress, waiting_on_customer, waiting_on_third_party, resolved or closed"},"priority":{"type":"string","description":"low, medium, high or urgent"},"type":{"type":"string"},"category":{"type":"string","description":"One of the organisation's Desk ticket categories"},"accountId":{"type":"string"},"contactId":{"type":"string"},"subscriptionId":{"type":"string"},"leadId":{},"dealId":{},"requesterEmail":{"type":"string"},"requesterName":{"type":"string"},"ownerId":{"type":"string","description":"The person who filed it"},"assignedTo":{"type":"string","description":"The person working on it"},"teamId":{"type":"string","description":"The department it sits with"},"tags":{"type":"array","items":{"type":"string"}},"slaPolicyId":{"type":"string"},"slaStatus":{"type":"object","properties":{"firstResponseDue":{"type":"string","format":"date-time"},"resolutionDue":{"type":"string","format":"date-time"},"firstResponseAt":{"type":"string","format":"date-time"}},"description":"The service-level clocks: when the first reply and the resolution are due, and when they happened"},"customFields":{"type":"object","properties":{}},"mergedInto":{},"relatedTickets":{"type":"array"},"satisfaction":{"type":"object","properties":{}},"firstResponseTime":{"type":"integer","description":"Minutes from opening to the first agent reply"},"resolutionTime":{"description":"Minutes from opening to resolution"},"reopenCount":{"type":"integer"},"holdUntil":{"description":"On hold: when it comes back by itself (null when not on hold)"},"holdMode":{"description":"On hold: timer (for a time) or reply (until the customer replies, back after the organisation's no-reply days at most)"},"holdStartedAt":{"description":"On hold: since when"},"holdBy":{"description":"On hold: who put it on hold"},"holdPreviousStatus":{"description":"On hold: the status before, which Undo restores"},"backFromHoldAt":{"description":"When a hold ended by itself; the ticket tops its assignee's My open until an agent replies, holds or resolves it"},"backFromHoldReason":{"description":"timer (no reply in time) or customer_reply"},"version":{"type":"integer","description":"Goes up by one on every change"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"account":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}},"contact":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"}}},"assignee":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"}}},"team":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}},"lastMessage":{"type":"object","properties":{"preview":{"type":"string"},"direction":{"type":"string"},"hasAttachments":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}},"description":"The newest message the customer can see, as a one-line preview (null when there is none)"},"lastMessagePreview":{"type":"string","description":"The same newest message's one-line preview (null when there is none)"},"lastMessageAt":{"type":"string","format":"date-time","description":"When that message was written"},"lastMessageFrom":{"type":"string","description":"customer or agent: who wrote that message (null when there is none)"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer","description":"This page's number, from 1"},"limit":{"type":"integer","description":"Most tickets per page (at most 500)"},"total":{"type":"integer","description":"How many tickets match in all"},"totalPages":{"type":"integer","description":"How many pages there are"},"hasNext":{"type":"boolean","description":"Whether a later page follows"},"hasPrev":{"type":"boolean","description":"Whether an earlier page exists"}}},"stats":{"type":"object","properties":{"byStatus":{"type":"object","properties":{"open":{"type":"integer"},"waiting_on_customer":{"type":"integer"}}},"byPriority":{"type":"object","properties":{"high":{"type":"integer"},"medium":{"type":"integer"}}},"byChannel":{"type":"object","properties":{"whatsapp":{"type":"integer"},"email":{"type":"integer"}}}},"description":"Only with includeStats: how many matching tickets have each status, priority and channel"}}},"example":{"success":true,"data":[{"id":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","ticketNumber":"TKT-001042","subject":"Internet down since this morning","description":"The router shows a red light and nothing loads since about 7am.","channel":"whatsapp","status":"open","priority":"high","type":"incident","category":"Connectivity","accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","contactId":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","subscriptionId":"b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d","leadId":null,"dealId":null,"requesterEmail":"amina.wanjiru@example.com","requesterName":"Amina Wanjiru","ownerId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","assignedTo":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","teamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","tags":["outage"],"slaPolicyId":"e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a","slaStatus":{"firstResponseDue":"2026-10-20T10:15:00.000Z","resolutionDue":"2026-10-21T08:15:00.000Z","firstResponseAt":"2026-10-20T08:42:00.000Z"},"customFields":{},"mergedInto":null,"relatedTickets":[],"satisfaction":{},"firstResponseTime":27,"resolutionTime":null,"reopenCount":0,"holdUntil":null,"holdMode":null,"holdStartedAt":null,"holdBy":null,"holdPreviousStatus":null,"backFromHoldAt":null,"backFromHoldReason":null,"version":2,"deletedAt":null,"createdAt":"2026-10-20T08:15:00.000Z","updatedAt":"2026-10-20T09:05:00.000Z","account":{"id":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","name":"Wanjiru Household"},"contact":{"id":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","firstName":"Amina","lastName":"Wanjiru","email":"amina.wanjiru@example.com","phone":"+254700000001"},"assignee":{"id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","firstName":"Brian","lastName":"Otieno"},"team":{"id":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","name":"Technical Support"},"lastMessage":{"preview":"Sorry about that. I have restarted your line; please check now.","direction":"outbound","hasAttachments":false,"createdAt":"2026-10-20T08:42:00.000Z"},"lastMessagePreview":"Sorry about that. I have restarted your line; please check now.","lastMessageAt":"2026-10-20T08:42:00.000Z","lastMessageFrom":"agent"},{"id":"4f5a6b7c-8d9e-4f0a-9b2c-3d4e5f6a7b8c","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","ticketNumber":"TKT-001041","subject":"Question about my invoice","description":"My October invoice shows two charges for the same month.","channel":"email","status":"waiting_on_customer","priority":"medium","type":"question","category":"Billing","accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","contactId":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","subscriptionId":"b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d","leadId":null,"dealId":null,"requesterEmail":"amina.wanjiru@example.com","requesterName":"Amina Wanjiru","ownerId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","assignedTo":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","teamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","tags":[],"slaPolicyId":"e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a","slaStatus":{"firstResponseDue":"2026-10-20T10:15:00.000Z","resolutionDue":"2026-10-21T08:15:00.000Z","firstResponseAt":"2026-10-20T08:42:00.000Z"},"customFields":{},"mergedInto":null,"relatedTickets":[],"satisfaction":{},"firstResponseTime":64,"resolutionTime":null,"reopenCount":0,"holdUntil":"2026-10-22T14:24:00.000Z","holdMode":"reply","holdStartedAt":"2026-10-19T14:24:00.000Z","holdBy":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","holdPreviousStatus":"in_progress","backFromHoldAt":null,"backFromHoldReason":null,"version":2,"deletedAt":null,"createdAt":"2026-10-19T13:20:00.000Z","updatedAt":"2026-10-19T14:24:00.000Z","account":{"id":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","name":"Wanjiru Household"},"contact":{"id":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","firstName":"Amina","lastName":"Wanjiru","email":"amina.wanjiru@example.com","phone":"+254700000001"},"assignee":{"id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","firstName":"Brian","lastName":"Otieno"},"team":{"id":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","name":"Technical Support"},"lastMessage":{"preview":"Could you send a photo of the invoice?","direction":"outbound","hasAttachments":false,"createdAt":"2026-10-19T14:24:00.000Z"},"lastMessagePreview":"Could you send a photo of the invoice?","lastMessageAt":"2026-10-19T14:24:00.000Z","lastMessageFrom":"agent"}],"pagination":{"page":1,"limit":25,"total":2,"totalPages":1,"hasNext":false,"hasPrev":false},"stats":{"byStatus":{"open":1,"waiting_on_customer":1},"byPriority":{"high":1,"medium":1},"byChannel":{"whatsapp":1,"email":1}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.read"]}],"x-required-scopes":["crm.tickets.read"]},"post":{"tags":["Tickets"],"summary":"Create a ticket","description":"Opens a help desk ticket with the next ticket number and status open, and starts its service-level clocks. The assignee gets a Desk notification; a ticket filed unassigned into a department that takes turns goes to that department's next member. Ticket-created automations run afterwards.\n\n**Permission:** `crm.tickets.create` (Create tickets).","operationId":"createTicket","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid","description":"The customer account it is about","examples":["9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b"]},"assignedTo":{"type":"string","format":"uuid","description":"The person to work on it","examples":["1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"]},"category":{"type":"string","description":"One of the organisation's Desk ticket categories (matched ignoring case; stored in the list's spelling)","examples":["Technical Support"]},"channel":{"type":"string","default":"web","description":"Where it came in, up to 20 characters: web, email, whatsapp, phone, chat and the like","examples":["phone"]},"contactId":{"type":"string","format":"uuid","description":"The contact who asked; must be linked to the account and on the subscription when those are given","examples":["a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c"]},"customFields":{"type":"object","description":"Values for the organisation's own ticket fields, by key","examples":[{"routerModel":"TP-Link Archer C6"}]},"dealId":{"type":"string","format":"uuid","description":"A deal it is about","examples":["d3c4d5e6-f7a8-4b9c-8e1f-2a3b4c5d6e7f"]},"description":{"type":"string","description":"The full problem, as the customer told it","examples":["The router shows a red light and nothing loads since about 7am."]},"leadId":{"type":"string","format":"uuid","description":"A lead it is about","examples":["c2b3c4d5-e6f7-4a8b-9d0e-1f2a3b4c5d6e"]},"ownerId":{"type":"string","format":"uuid","description":"Who files it (the ticket's owner)","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]},"priority":{"type":"string","enum":["low","medium","high","urgent"],"default":"medium","description":"How urgent it is","examples":["high"]},"requesterEmail":{"type":"string","format":"email","description":"Email of the person who asked, when no contact is linked","examples":["amina.wanjiru@example.com"]},"requesterName":{"type":"string","description":"Name of the person who asked","examples":["Amina Wanjiru"]},"slaPolicy":{"type":"string","format":"uuid","description":"Service-level policy to apply; without it the policy that fits the ticket is picked","examples":["e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a"]},"subject":{"type":"string","description":"One line saying what is wrong","examples":["Internet down since this morning"]},"subscriptionId":{"type":"string","format":"uuid","description":"The service it is about; must belong to the account when one is given","examples":["b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d"]},"tags":{"type":"array","items":{"type":"string"},"description":"Labels (a single string becomes a one-label list)","examples":[["outage"]]},"teamId":{"type":"string","format":"uuid","description":"The department it goes to","examples":["2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"]},"type":{"type":"string","description":"Kind of ticket, up to 20 characters, e.g. incident or question","examples":["incident"]},"userId":{"type":"string","format":"uuid","description":"Who files it, when ownerId is not given","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]}},"required":["description","subject"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"ticketNumber":{"type":"string","description":"The number people quote, e.g. TKT-001042"},"subject":{"type":"string"},"description":{"type":"string"},"channel":{"type":"string","description":"Where it came in: web, email, whatsapp, phone, chat, telegram and the like"},"status":{"type":"string","description":"open, pending, in_progress, waiting_on_customer, waiting_on_third_party, resolved or closed"},"priority":{"type":"string","description":"low, medium, high or urgent"},"type":{"type":"string"},"category":{"type":"string","description":"One of the organisation's Desk ticket categories"},"accountId":{"type":"string"},"contactId":{"type":"string"},"subscriptionId":{"type":"string"},"leadId":{},"dealId":{},"requesterEmail":{"type":"string"},"requesterName":{"type":"string"},"ownerId":{"type":"string","description":"The person who filed it"},"assignedTo":{"type":"string","description":"The person working on it"},"teamId":{"type":"string","description":"The department it sits with"},"tags":{"type":"array","items":{"type":"string"}},"slaPolicyId":{"type":"string"},"slaStatus":{"type":"object","properties":{"firstResponseDue":{"type":"string","format":"date-time"},"resolutionDue":{"type":"string","format":"date-time"}},"description":"The service-level clocks: when the first reply and the resolution are due, and when they happened"},"customFields":{"type":"object","properties":{}},"mergedInto":{},"relatedTickets":{"type":"array"},"satisfaction":{"type":"object","properties":{}},"firstResponseTime":{"description":"Minutes from opening to the first agent reply"},"resolutionTime":{"description":"Minutes from opening to resolution"},"reopenCount":{"type":"integer"},"holdUntil":{"description":"On hold: when it comes back by itself (null when not on hold)"},"holdMode":{"description":"On hold: timer (for a time) or reply (until the customer replies, back after the organisation's no-reply days at most)"},"holdStartedAt":{"description":"On hold: since when"},"holdBy":{"description":"On hold: who put it on hold"},"holdPreviousStatus":{"description":"On hold: the status before, which Undo restores"},"backFromHoldAt":{"description":"When a hold ended by itself; the ticket tops its assignee's My open until an agent replies, holds or resolves it"},"backFromHoldReason":{"description":"timer (no reply in time) or customer_reply"},"version":{"type":"integer","description":"Goes up by one on every change"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"account":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}},"contact":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"}}},"assignee":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"}}},"team":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}}}}}},"example":{"success":true,"message":"Ticket created successfully","data":{"id":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","ticketNumber":"TKT-001042","subject":"Internet down since this morning","description":"The router shows a red light and nothing loads since about 7am.","channel":"whatsapp","status":"open","priority":"high","type":"incident","category":"Connectivity","accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","contactId":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","subscriptionId":"b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d","leadId":null,"dealId":null,"requesterEmail":"amina.wanjiru@example.com","requesterName":"Amina Wanjiru","ownerId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","assignedTo":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","teamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","tags":["outage"],"slaPolicyId":"e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a","slaStatus":{"firstResponseDue":"2026-10-20T10:15:00.000Z","resolutionDue":"2026-10-21T08:15:00.000Z"},"customFields":{},"mergedInto":null,"relatedTickets":[],"satisfaction":{},"firstResponseTime":null,"resolutionTime":null,"reopenCount":0,"holdUntil":null,"holdMode":null,"holdStartedAt":null,"holdBy":null,"holdPreviousStatus":null,"backFromHoldAt":null,"backFromHoldReason":null,"version":0,"deletedAt":null,"createdAt":"2026-10-20T08:15:00.000Z","updatedAt":"2026-10-20T08:15:00.000Z","account":{"id":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","name":"Wanjiru Household"},"contact":{"id":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","firstName":"Amina","lastName":"Wanjiru","email":"amina.wanjiru@example.com","phone":"+254700000001"},"assignee":{"id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","firstName":"Brian","lastName":"Otieno"},"team":{"id":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","name":"Technical Support"}}}}}},"400":{"description":"The category is not one of the organisation's ticket categories, or the account, contact and subscription do not belong together","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.create"]}],"x-required-scopes":["crm.tickets.create"]}},"/tickets/{ticketId}/moves":{"get":{"tags":["Tickets"],"summary":"List a ticket's moves","description":"Lists the moves you may make on a ticket from its current status: the active transitions of the organisation's ticket blueprint (its ticket pipeline) leaving that status that your role or name may run. Each says the status it leads to, its name and colour, whether it needs a comment, and the fields it needs filled with the ticket's current value and whether it is still missing. The first call for an organisation without a ticket blueprint makes Desk's default one (and upgrades the untouched old 4-stage one), so this call can create data. A ticket you may not see answers 404.\n\n**Permission:** `crm.tickets.read` (View tickets).","operationId":"getTicketMoves","parameters":[{"name":"ticketId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The ticket","example":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status":{"type":"string","description":"The ticket's status now"},"moves":{"type":"array","items":{"type":"object","properties":{"transitionId":{"type":"string","description":"The ticket pipeline transition this move runs (POST /pipelines/execute-transition runs the same)"},"name":{"type":"string","description":"The move's name, for its button"},"toStatus":{"type":"string","description":"The status the ticket gets: send it as status to PUT /tickets/:id/status"},"toLabel":{"type":"string","description":"The target status's name, as the blueprint calls it"},"color":{"type":"string","description":"The target status's colour, a hex code"},"requireComment":{"type":"boolean","description":"The move needs a comment (comment)"},"commentPrompt":{"type":"string","description":"What the comment should say, when the blueprint asks; or null"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The field's id: the key to send in fieldValues"},"label":{"type":"string"},"type":{"type":"string","description":"text, textarea, number, currency, date, datetime, select, multiselect, checkbox, email, phone, url, or another catalog type"},"options":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}}},"description":"For a select or multiselect: its choices"},"value":{"description":"The ticket's value now; null when empty"},"missing":{"type":"boolean","description":"Still empty: the move is refused until it is filled"}},"description":"Fields the move needs filled, shown ones only (a dependent field while its parent asks for it)"}}}}}}},"example":{"success":true,"status":"in_progress","moves":[{"transitionId":"6f1d2c3b-4a59-4e68-9d7c-8b9a0f1e2d3c","name":"Resolve","toStatus":"resolved","toLabel":"Resolved","color":"#10b981","requireComment":true,"commentPrompt":"How was it fixed?","fields":[{"id":"category","label":"Category","type":"select","options":[{"value":"Billing","label":"Billing"},{"value":"Technical Support","label":"Technical Support"}],"value":null,"missing":true}]}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No ticket with that ID that you may see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.read"]}],"x-required-scopes":["crm.tickets.read"]}},"/tickets/{ticketId}":{"get":{"tags":["Tickets"],"summary":"Get a ticket","description":"Reads one ticket with its account, contact, assignee, department, conversation (the newest 500 messages, oldest first), files and history (up to 100 events, newest first), plus its age and whether its service-level clocks are overdue. A ticket you may not see answers 404, like a missing one.\n\n**Permission:** `crm.tickets.read` (View tickets).","operationId":"getTicket","parameters":[{"name":"ticketId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The ticket to read","example":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"ticket":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"ticketNumber":{"type":"string","description":"The number people quote, e.g. TKT-001042"},"subject":{"type":"string"},"description":{"type":"string"},"channel":{"type":"string","description":"Where it came in: web, email, whatsapp, phone, chat, telegram and the like"},"status":{"type":"string","description":"open, pending, in_progress, waiting_on_customer, waiting_on_third_party, resolved or closed"},"priority":{"type":"string","description":"low, medium, high or urgent"},"type":{"type":"string"},"category":{"type":"string","description":"One of the organisation's Desk ticket categories"},"accountId":{"type":"string"},"contactId":{"type":"string"},"subscriptionId":{"type":"string"},"leadId":{},"dealId":{},"requesterEmail":{"type":"string"},"requesterName":{"type":"string"},"ownerId":{"type":"string","description":"The person who filed it"},"assignedTo":{"type":"string","description":"The person working on it"},"teamId":{"type":"string","description":"The department it sits with"},"tags":{"type":"array","items":{"type":"string"}},"slaPolicyId":{"type":"string"},"slaStatus":{"type":"object","properties":{"firstResponseDue":{"type":"string","format":"date-time"},"resolutionDue":{"type":"string","format":"date-time"},"firstResponseAt":{"type":"string","format":"date-time"}},"description":"The service-level clocks: when the first reply and the resolution are due, and when they happened"},"customFields":{"type":"object","properties":{}},"mergedInto":{},"relatedTickets":{"type":"array"},"satisfaction":{"type":"object","properties":{}},"firstResponseTime":{"type":"integer","description":"Minutes from opening to the first agent reply"},"resolutionTime":{"description":"Minutes from opening to resolution"},"reopenCount":{"type":"integer"},"holdUntil":{"description":"On hold: when it comes back by itself (null when not on hold)"},"holdMode":{"description":"On hold: timer (for a time) or reply (until the customer replies, back after the organisation's no-reply days at most)"},"holdStartedAt":{"description":"On hold: since when"},"holdBy":{"description":"On hold: who put it on hold"},"holdPreviousStatus":{"description":"On hold: the status before, which Undo restores"},"backFromHoldAt":{"description":"When a hold ended by itself; the ticket tops its assignee's My open until an agent replies, holds or resolves it"},"backFromHoldReason":{"description":"timer (no reply in time) or customer_reply"},"version":{"type":"integer","description":"Goes up by one on every change"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"account":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}},"contact":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"}}},"assignee":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"}}},"team":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}},"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"ticketId":{"type":"string"},"direction":{"type":"string","description":"inbound (from the customer) or outbound (to the customer)"},"authorType":{"type":"string","description":"agent, customer or system"},"authorId":{},"authorName":{"type":"string"},"authorEmail":{},"body":{"type":"string"},"bodyType":{"type":"string"},"isPrivate":{"type":"boolean","description":"An internal note the customer never sees"},"channel":{"type":"string"},"externalMessageId":{"type":"string"},"metadata":{"type":"object","properties":{}},"sentAt":{"type":"string","format":"date-time"},"deliveryState":{"type":"string","description":"stored, internal, queued, sent, delivered, read or failed"},"deliveryError":{},"deliveredAt":{},"deliveryAttempts":{"type":"integer"},"clientMessageId":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"attachments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"ticketId":{"type":"string"},"messageId":{"type":"string"},"documentId":{"type":"string"},"fileName":{"type":"string"},"originalName":{"type":"string"},"fileUrl":{"type":"string"},"mimeType":{"type":"string"},"fileSize":{"type":"integer"},"uploadedBy":{},"metadata":{"type":"object","properties":{}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"ticketId":{"type":"string"},"eventType":{"type":"string"},"actorId":{"type":"string"},"actorType":{"type":"string"},"fromValue":{"type":"string"},"toValue":{"type":"string"},"note":{},"metadata":{"type":"object","properties":{}},"occurredAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}}},"requesterPhone":{"type":"string","description":"The number to call the customer back on: their newest inbound message's, else the one the chat opened with"}}},"metrics":{"type":"object","properties":{"ageInHours":{"type":"number"},"ageInDays":{"type":"integer"},"timeInCurrentStatusHours":{"type":"number","description":"Hours since the status last changed (or since opening)"},"messageCount":{"type":"integer"},"eventCount":{"type":"integer"}}},"slaCompliance":{"type":"object","properties":{"firstResponseBreached":{"type":"boolean","description":"True when the first reply is overdue and none was sent yet"},"resolutionBreached":{"type":"boolean","description":"True when the ticket is still not resolved or closed after its resolution deadline"},"firstResponseDue":{"type":"string","format":"date-time"},"resolutionDue":{"type":"string","format":"date-time"},"firstResponseAt":{"type":"string","format":"date-time"},"resolvedAt":{}}}}}}},"example":{"success":true,"data":{"ticket":{"id":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","ticketNumber":"TKT-001042","subject":"Internet down since this morning","description":"The router shows a red light and nothing loads since about 7am.","channel":"whatsapp","status":"open","priority":"high","type":"incident","category":"Connectivity","accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","contactId":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","subscriptionId":"b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d","leadId":null,"dealId":null,"requesterEmail":"amina.wanjiru@example.com","requesterName":"Amina Wanjiru","ownerId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","assignedTo":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","teamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","tags":["outage"],"slaPolicyId":"e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a","slaStatus":{"firstResponseDue":"2026-10-20T10:15:00.000Z","resolutionDue":"2026-10-21T08:15:00.000Z","firstResponseAt":"2026-10-20T08:42:00.000Z"},"customFields":{},"mergedInto":null,"relatedTickets":[],"satisfaction":{},"firstResponseTime":27,"resolutionTime":null,"reopenCount":0,"holdUntil":null,"holdMode":null,"holdStartedAt":null,"holdBy":null,"holdPreviousStatus":null,"backFromHoldAt":null,"backFromHoldReason":null,"version":2,"deletedAt":null,"createdAt":"2026-10-20T08:15:00.000Z","updatedAt":"2026-10-20T09:05:00.000Z","account":{"id":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","name":"Wanjiru Household"},"contact":{"id":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","firstName":"Amina","lastName":"Wanjiru","email":"amina.wanjiru@example.com","phone":"+254700000001"},"assignee":{"id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","firstName":"Brian","lastName":"Otieno"},"team":{"id":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","name":"Technical Support"},"messages":[{"id":"6b7c8d9e-0f1a-4b2c-9d4e-5f6a7b8c9d0e","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","ticketId":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","direction":"inbound","authorType":"customer","authorId":null,"authorName":"Amina Wanjiru","authorEmail":null,"body":"Hi, my internet has been down since 7am.","bodyType":"text","isPrivate":false,"channel":"whatsapp","externalMessageId":"wamid.HBgMMjU0NzAwMDAwMDAwFQIAEhgUM0E5","metadata":{},"sentAt":"2026-10-20T08:15:00.000Z","deliveryState":"stored","deliveryError":null,"deliveredAt":null,"deliveryAttempts":0,"clientMessageId":null,"createdAt":"2026-10-20T08:15:00.000Z","updatedAt":"2026-10-20T08:15:00.000Z"},{"id":"5a6b7c8d-9e0f-4a1b-8c3d-4e5f6a7b8c9d","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","ticketId":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","direction":"outbound","authorType":"agent","authorId":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","authorName":"Brian Otieno","authorEmail":"brian.otieno@example.com","body":"Sorry about that. I have restarted your line; please check now.","bodyType":"text","isPrivate":false,"channel":"whatsapp","externalMessageId":"wamid.HBgMMjU0NzAwMDAwMDAwFQIAERgSQzY3","metadata":{},"sentAt":"2026-10-20T08:42:00.000Z","deliveryState":"delivered","deliveryError":null,"deliveredAt":"2026-10-20T08:42:03.000Z","deliveryAttempts":1,"clientMessageId":"c-1729413720000","createdAt":"2026-10-20T08:42:00.000Z","updatedAt":"2026-10-20T08:42:03.000Z"}],"attachments":[{"id":"7c8d9e0f-1a2b-4c3d-8e5f-6a7b8c9d0e1f","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","ticketId":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","messageId":"6b7c8d9e-0f1a-4b2c-9d4e-5f6a7b8c9d0e","documentId":"f5e6f7a8-b9c0-4d1e-8a3b-4c5d6e7f8a9b","fileName":"router-photo.jpg","originalName":"router-photo.jpg","fileUrl":"tickets/3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b/attachments/1792484100000-router-photo.jpg","mimeType":"image/jpeg","fileSize":245760,"uploadedBy":null,"metadata":{},"createdAt":"2026-10-20T08:15:00.000Z","updatedAt":"2026-10-20T08:15:00.000Z"}],"events":[{"id":"8d9e0f1a-2b3c-4d4e-9f6a-7b8c9d0e1f2a","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","ticketId":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","eventType":"status_changed","actorId":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","actorType":"agent","fromValue":"open","toValue":"in_progress","note":null,"metadata":{},"occurredAt":"2026-10-20T09:05:00.000Z","createdAt":"2026-10-20T09:05:00.000Z"}],"requesterPhone":"+254700000001"},"metrics":{"ageInHours":0.83,"ageInDays":0,"timeInCurrentStatusHours":0.17,"messageCount":2,"eventCount":1},"slaCompliance":{"firstResponseBreached":false,"resolutionBreached":false,"firstResponseDue":"2026-10-20T10:15:00.000Z","resolutionDue":"2026-10-21T08:15:00.000Z","firstResponseAt":"2026-10-20T08:42:00.000Z","resolvedAt":null}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No ticket with that ID in your organisation that you may see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.read"]}],"x-required-scopes":["crm.tickets.read"]},"put":{"tags":["Tickets"],"summary":"Update a ticket","description":"Changes a ticket's own fields: only those sent, and only when they differ from what is stored (a form that sends every field back changes nothing else). An empty string clears a field. Each change is noted in the ticket's history; a new assignee gets a Desk notification, and a ticket moved unassigned into a department that takes turns goes to its next member. Status has its own call. Ticket-updated automations run afterwards.\n\n**Permission:** `crm.tickets.update` (Update tickets).","operationId":"updateTicket","parameters":[{"name":"ticketId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The ticket to change","example":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid","description":"The customer account; empty unlinks. Must agree with the contact and subscription","examples":["9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b"]},"assignedTo":{"type":"string","format":"uuid","description":"The person to work on it; empty unassigns","examples":["1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"]},"category":{"type":"string","description":"One of the organisation's Desk ticket categories (matched ignoring case); empty clears it","examples":["Technical Support"]},"contactId":{"type":"string","format":"uuid","description":"The contact who asked; empty unlinks. Must be linked to the account and on the subscription","examples":["a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c"]},"customFields":{"type":"object","description":"Values for the organisation's own ticket fields, by key (a JSON string also works). Replaces the stored set","examples":[{"routerModel":"TP-Link Archer C6"}]},"dealId":{"type":"string","format":"uuid","description":"A deal it is about; empty unlinks","examples":["d3c4d5e6-f7a8-4b9c-8e1f-2a3b4c5d6e7f"]},"description":{"type":"string","description":"New full description","examples":["The router shows a red light. A fibre cut was reported on the street."]},"leadId":{"type":"string","format":"uuid","description":"A lead it is about; empty unlinks","examples":["c2b3c4d5-e6f7-4a8b-9d0e-1f2a3b4c5d6e"]},"ownerId":{"type":"string","format":"uuid","description":"New owner (the person the ticket belongs to)","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]},"priority":{"type":"string","enum":["low","medium","high","urgent"],"description":"How urgent it is","examples":["urgent"]},"requesterEmail":{"type":"string","format":"email","description":"The customer's email; empty clears it","examples":["amina.wanjiru@example.com"]},"requesterName":{"type":"string","description":"The customer's name, up to 200 characters; empty clears it","examples":["Amina Wanjiru"]},"slaPolicy":{"type":"string","format":"uuid","description":"Service-level policy to apply","examples":["e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a"]},"subject":{"type":"string","description":"New title: one line, 1 to 200 characters","examples":["Internet down since this morning (fibre cut)"]},"subscriptionId":{"type":"string","format":"uuid","description":"The service it is about; empty unlinks. Must belong to the account","examples":["b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d"]},"tags":{"type":"array","items":{"type":"string"},"description":"The full new list of labels (replaces the old one)","examples":[["outage","fibre"]]},"teamId":{"type":"string","format":"uuid","description":"The department it sits with; empty clears it","examples":["2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"]},"type":{"type":"string","description":"Kind of ticket, up to 20 characters","examples":["incident"]},"userId":{"type":"string","format":"uuid","description":"Who makes the change, when the sign-in names nobody","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"ticketNumber":{"type":"string","description":"The number people quote, e.g. TKT-001042"},"subject":{"type":"string"},"description":{"type":"string"},"channel":{"type":"string","description":"Where it came in: web, email, whatsapp, phone, chat, telegram and the like"},"status":{"type":"string","description":"open, pending, in_progress, waiting_on_customer, waiting_on_third_party, resolved or closed"},"priority":{"type":"string","description":"low, medium, high or urgent"},"type":{"type":"string"},"category":{"type":"string","description":"One of the organisation's Desk ticket categories"},"accountId":{"type":"string"},"contactId":{"type":"string"},"subscriptionId":{"type":"string"},"leadId":{},"dealId":{},"requesterEmail":{"type":"string"},"requesterName":{"type":"string"},"ownerId":{"type":"string","description":"The person who filed it"},"assignedTo":{"type":"string","description":"The person working on it"},"teamId":{"type":"string","description":"The department it sits with"},"tags":{"type":"array","items":{"type":"string"}},"slaPolicyId":{"type":"string"},"slaStatus":{"type":"object","properties":{"firstResponseDue":{"type":"string","format":"date-time"},"resolutionDue":{"type":"string","format":"date-time"},"firstResponseAt":{"type":"string","format":"date-time"}},"description":"The service-level clocks: when the first reply and the resolution are due, and when they happened"},"customFields":{"type":"object","properties":{}},"mergedInto":{},"relatedTickets":{"type":"array"},"satisfaction":{"type":"object","properties":{}},"firstResponseTime":{"type":"integer","description":"Minutes from opening to the first agent reply"},"resolutionTime":{"description":"Minutes from opening to resolution"},"reopenCount":{"type":"integer"},"holdUntil":{"description":"On hold: when it comes back by itself (null when not on hold)"},"holdMode":{"description":"On hold: timer (for a time) or reply (until the customer replies, back after the organisation's no-reply days at most)"},"holdStartedAt":{"description":"On hold: since when"},"holdBy":{"description":"On hold: who put it on hold"},"holdPreviousStatus":{"description":"On hold: the status before, which Undo restores"},"backFromHoldAt":{"description":"When a hold ended by itself; the ticket tops its assignee's My open until an agent replies, holds or resolves it"},"backFromHoldReason":{"description":"timer (no reply in time) or customer_reply"},"version":{"type":"integer","description":"Goes up by one on every change"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"account":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}},"contact":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"}}},"assignee":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"}}},"team":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}}}},"changes":{"type":"array","items":{"type":"string"},"description":"What changed, in words, as noted in the ticket's history"}}},"example":{"success":true,"message":"Ticket updated successfully","data":{"id":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","ticketNumber":"TKT-001042","subject":"Internet down since this morning","description":"The router shows a red light and nothing loads since about 7am.","channel":"whatsapp","status":"open","priority":"urgent","type":"incident","category":"Connectivity","accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","contactId":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","subscriptionId":"b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d","leadId":null,"dealId":null,"requesterEmail":"amina.wanjiru@example.com","requesterName":"Amina Wanjiru","ownerId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","assignedTo":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","teamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","tags":["outage","fibre"],"slaPolicyId":"e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a","slaStatus":{"firstResponseDue":"2026-10-20T10:15:00.000Z","resolutionDue":"2026-10-21T08:15:00.000Z","firstResponseAt":"2026-10-20T08:42:00.000Z"},"customFields":{},"mergedInto":null,"relatedTickets":[],"satisfaction":{},"firstResponseTime":27,"resolutionTime":null,"reopenCount":0,"holdUntil":null,"holdMode":null,"holdStartedAt":null,"holdBy":null,"holdPreviousStatus":null,"backFromHoldAt":null,"backFromHoldReason":null,"version":3,"deletedAt":null,"createdAt":"2026-10-20T08:15:00.000Z","updatedAt":"2026-10-20T09:05:00.000Z","account":{"id":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","name":"Wanjiru Household"},"contact":{"id":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","firstName":"Amina","lastName":"Wanjiru","email":"amina.wanjiru@example.com","phone":"+254700000001"},"assignee":{"id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","firstName":"Brian","lastName":"Otieno"},"team":{"id":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","name":"Technical Support"}},"changes":["Priority changed","Tags updated"]}}}},"400":{"description":"The title is empty or over 200 characters, the name is over 200 characters, the email is not valid, the category is not on the organisation's list, a linked account, contact or subscription is not found, or they do not belong together","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No ticket with that ID that you may see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.update"]}],"x-required-scopes":["crm.tickets.update"]}},"/sla-policies":{"get":{"tags":["Help desk setup"],"summary":"List SLA policies","description":"Lists your organisation's service-level (SLA) policies by name, a page at a time (without their business hours joined in). With includeStats, also counts every policy by priority and by active or not.\n\n**Permission:** `crm.sla.read` (View SLA policies).","operationId":"getSLAPolicies","parameters":[{"name":"includeStats","in":"query","required":false,"schema":{"type":"boolean"},"description":"true to add stats over all policies","example":true},{"name":"isActive","in":"query","required":false,"schema":{"type":"boolean"},"description":"true for only active policies, false for only inactive ones"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25},"description":"Items per page, at most 500","example":25},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"priority","in":"query","required":false,"schema":{"type":"string","enum":["any","urgent","high","medium","low"]},"description":"Only policies for this priority"},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text to find in the name","example":"high"},{"name":"sort","in":"query","required":false,"schema":{"type":"string"},"description":"Not used yet: policies come by name"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"isActive":{"type":"boolean"},"priority":{"type":"string","description":"The ticket priority it covers: any, urgent, high, medium or low"},"targets":{"type":"object","properties":{"firstResponse":{"type":"object","properties":{"hours":{"type":"integer"}}},"resolution":{"type":"object","properties":{"hours":{"type":"integer"}}}},"description":"Deadlines: firstResponse, resolution and nextResponse, each { hours, minutes }"},"businessHoursId":{"type":"string"},"escalationRules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"clock":{"type":"string"},"afterMinutes":{"type":"integer"},"setPriority":{"type":"string"},"notifyAssignee":{"type":"boolean"},"notifyTeamLeads":{"type":"boolean"}}},"description":"What happens when a clock runs late: { id, clock (first_response or resolution), afterMinutes, setPriority?, assignTo?, notifyAssignee?, notifyTeamLeads?, notifyUsers? }"},"conditions":{"type":"object","properties":{"channels":{"type":"array","items":{"type":"string"}},"teamIds":{"type":"array","items":{"type":"string"}}},"description":"Which tickets it covers: channels, ticketTypes, tags and teamIds (departments); empty means all"},"settings":{"type":"object","properties":{"pauseStatuses":{"type":"array","items":{"type":"string"}},"warnPercent":{"type":"integer"}},"description":"pauseStatuses (statuses that pause the resolution clock; default waiting_on_customer) and warnPercent (share of a deadline after which a clock shows amber; default 80)"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer","description":"This page's number, from 1"},"limit":{"type":"integer","description":"Most items in a page (at most 500)"},"total":{"type":"integer","description":"How many items match in all"},"totalPages":{"type":"integer","description":"How many pages there are"},"hasNext":{"type":"boolean","description":"Whether another page follows"},"hasPrev":{"type":"boolean","description":"Whether a page comes before"}}},"stats":{"type":"object","properties":{"byPriority":{"type":"object","properties":{"high":{"type":"integer"}},"description":"How many policies cover each priority (only with includeStats)"},"active":{"type":"integer"},"inactive":{"type":"integer"},"total":{"type":"integer"}}}}},"example":{"success":true,"data":[{"id":"e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","name":"High priority","description":"Reply within an hour, fix within a day","isActive":true,"priority":"high","targets":{"firstResponse":{"hours":1},"resolution":{"hours":24}},"businessHoursId":"c0b1c2d3-e4f5-4a6b-9d8e-9f0a1b2c3d4e","escalationRules":[{"id":"fr-1","clock":"first_response","afterMinutes":30,"setPriority":"urgent","notifyAssignee":true,"notifyTeamLeads":true}],"conditions":{"channels":["whatsapp","email"],"teamIds":["2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"]},"settings":{"pauseStatuses":["waiting_on_customer"],"warnPercent":80},"deletedAt":null,"createdAt":"2026-06-01T08:00:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}],"pagination":{"page":1,"limit":25,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false},"stats":{"byPriority":{"high":1},"active":1,"inactive":0,"total":1}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.sla.read"]}],"x-required-scopes":["crm.sla.read"]},"post":{"tags":["Help desk setup"],"summary":"Create an SLA policy","description":"Adds a service-level (SLA) policy: how fast tickets of a priority must get a first reply and be resolved, which tickets it covers, in which business hours its clocks run and what happens when they run late. Answers with status 201.\n\n**Permission:** `crm.sla.create` (Create SLA policies).","operationId":"createSLAPolicy","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessHoursId":{"type":"string","format":"uuid","description":"Business hours its clocks count in; left out, round the clock","examples":["c0b1c2d3-e4f5-4a6b-9d8e-9f0a1b2c3d4e"]},"conditions":{"type":"object","description":"Which tickets it covers: { channels, ticketTypes, tags, teamIds }; empty for all (a JSON text also works)","examples":[{"channels":["whatsapp","email"],"teamIds":["2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"]}]},"description":{"type":"string","description":"What it is for","examples":["Reply within an hour, fix within a day"]},"escalationRules":{"type":"array","items":{"type":"object"},"description":"Up to 20 steps for late clocks, each { id, clock: first_response or resolution, afterMinutes, setPriority?, assignTo?, notifyAssignee?, notifyTeamLeads?, notifyUsers? } (a JSON text also works)","examples":[[{"id":"fr-1","clock":"first_response","afterMinutes":30,"setPriority":"urgent","notifyAssignee":true,"notifyTeamLeads":true}]]},"isActive":{"type":"boolean","default":true,"description":"Whether it applies to new tickets","examples":[true]},"name":{"type":"string","description":"The policy's name","examples":["High priority"]},"priority":{"type":"string","enum":["any","urgent","high","medium","low"],"description":"The ticket priority it covers; any for all","examples":["high"]},"settings":{"type":"object","description":"{ pauseStatuses: statuses that pause the resolution clock, warnPercent: 1 to 99 } (a JSON text also works)","examples":[{"pauseStatuses":["waiting_on_customer"],"warnPercent":80}]},"targets":{"type":"object","description":"Deadlines: firstResponse, resolution and nextResponse, each { hours, minutes } (a JSON text also works)","examples":[{"firstResponse":{"hours":1},"resolution":{"hours":24}}]}},"required":["name","priority"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"slaPolicy":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"isActive":{"type":"boolean"},"priority":{"type":"string","description":"The ticket priority it covers: any, urgent, high, medium or low"},"targets":{"type":"object","properties":{"firstResponse":{"type":"object","properties":{"hours":{"type":"integer"}}},"resolution":{"type":"object","properties":{"hours":{"type":"integer"}}}},"description":"Deadlines: firstResponse, resolution and nextResponse, each { hours, minutes }"},"businessHoursId":{"type":"string"},"escalationRules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"clock":{"type":"string"},"afterMinutes":{"type":"integer"},"setPriority":{"type":"string"},"notifyAssignee":{"type":"boolean"},"notifyTeamLeads":{"type":"boolean"}}},"description":"What happens when a clock runs late: { id, clock (first_response or resolution), afterMinutes, setPriority?, assignTo?, notifyAssignee?, notifyTeamLeads?, notifyUsers? }"},"conditions":{"type":"object","properties":{"channels":{"type":"array","items":{"type":"string"}},"teamIds":{"type":"array","items":{"type":"string"}}},"description":"Which tickets it covers: channels, ticketTypes, tags and teamIds (departments); empty means all"},"settings":{"type":"object","properties":{"pauseStatuses":{"type":"array","items":{"type":"string"}},"warnPercent":{"type":"integer"}},"description":"pauseStatuses (statuses that pause the resolution clock; default waiting_on_customer) and warnPercent (share of a deadline after which a clock shows amber; default 80)"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"businessHours":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"timezone":{"type":"string"},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"schedule":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string"},"isWorkingDay":{"type":"boolean"},"shifts":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}}}}}}},"holidays":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"date":{"type":"string"},"isRecurring":{"type":"boolean"}}}},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"description":"The business hours its clocks count in, or null for round the clock"}}}}},"example":{"success":true,"message":"SLA policy created successfully","slaPolicy":{"id":"e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","name":"High priority","description":"Reply within an hour, fix within a day","isActive":true,"priority":"high","targets":{"firstResponse":{"hours":1},"resolution":{"hours":24}},"businessHoursId":"c0b1c2d3-e4f5-4a6b-9d8e-9f0a1b2c3d4e","escalationRules":[{"id":"fr-1","clock":"first_response","afterMinutes":30,"setPriority":"urgent","notifyAssignee":true,"notifyTeamLeads":true}],"conditions":{"channels":["whatsapp","email"],"teamIds":["2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"]},"settings":{"pauseStatuses":["waiting_on_customer"],"warnPercent":80},"deletedAt":null,"createdAt":"2026-06-01T08:00:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z","businessHours":{"id":"c0b1c2d3-e4f5-4a6b-9d8e-9f0a1b2c3d4e","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","name":"Nairobi office hours","description":"Weekdays 8 to 5, Saturday mornings","timezone":"Africa/Nairobi","isDefault":true,"isActive":true,"schedule":[{"day":"monday","isWorkingDay":true,"shifts":[{"start":"08:00","end":"17:00"}]},{"day":"saturday","isWorkingDay":true,"shifts":[{"start":"09:00","end":"13:00"}]},{"day":"sunday","isWorkingDay":false,"shifts":[]}],"holidays":[{"name":"Jamhuri Day","date":"2026-12-12","isRecurring":true}],"deletedAt":null,"createdAt":"2026-06-01T08:00:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}}}}}},"400":{"description":"targets, escalationRules, conditions or settings is not valid JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"A field has the wrong shape: an unknown priority, an escalation step without id, clock or afterMinutes, over 20 steps, or warnPercent outside 1 to 99","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.sla.create"]}],"x-required-scopes":["crm.sla.create"]}},"/sla-policies/{slaPolicyId}":{"get":{"tags":["Help desk setup"],"summary":"Get an SLA policy","description":"Reads one service-level (SLA) policy with its business hours.\n\n**Permission:** `crm.sla.read` (View SLA policies).","operationId":"getSLAPolicy","parameters":[{"name":"slaPolicyId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The policy to read","example":"e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"slaPolicy":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"isActive":{"type":"boolean"},"priority":{"type":"string","description":"The ticket priority it covers: any, urgent, high, medium or low"},"targets":{"type":"object","properties":{"firstResponse":{"type":"object","properties":{"hours":{"type":"integer"}}},"resolution":{"type":"object","properties":{"hours":{"type":"integer"}}}},"description":"Deadlines: firstResponse, resolution and nextResponse, each { hours, minutes }"},"businessHoursId":{"type":"string"},"escalationRules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"clock":{"type":"string"},"afterMinutes":{"type":"integer"},"setPriority":{"type":"string"},"notifyAssignee":{"type":"boolean"},"notifyTeamLeads":{"type":"boolean"}}},"description":"What happens when a clock runs late: { id, clock (first_response or resolution), afterMinutes, setPriority?, assignTo?, notifyAssignee?, notifyTeamLeads?, notifyUsers? }"},"conditions":{"type":"object","properties":{"channels":{"type":"array","items":{"type":"string"}},"teamIds":{"type":"array","items":{"type":"string"}}},"description":"Which tickets it covers: channels, ticketTypes, tags and teamIds (departments); empty means all"},"settings":{"type":"object","properties":{"pauseStatuses":{"type":"array","items":{"type":"string"}},"warnPercent":{"type":"integer"}},"description":"pauseStatuses (statuses that pause the resolution clock; default waiting_on_customer) and warnPercent (share of a deadline after which a clock shows amber; default 80)"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"businessHours":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"timezone":{"type":"string"},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"schedule":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string"},"isWorkingDay":{"type":"boolean"},"shifts":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}}}}}}},"holidays":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"date":{"type":"string"},"isRecurring":{"type":"boolean"}}}},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"description":"The business hours its clocks count in, or null for round the clock"}}}}},"example":{"success":true,"slaPolicy":{"id":"e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","name":"High priority","description":"Reply within an hour, fix within a day","isActive":true,"priority":"high","targets":{"firstResponse":{"hours":1},"resolution":{"hours":24}},"businessHoursId":"c0b1c2d3-e4f5-4a6b-9d8e-9f0a1b2c3d4e","escalationRules":[{"id":"fr-1","clock":"first_response","afterMinutes":30,"setPriority":"urgent","notifyAssignee":true,"notifyTeamLeads":true}],"conditions":{"channels":["whatsapp","email"],"teamIds":["2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"]},"settings":{"pauseStatuses":["waiting_on_customer"],"warnPercent":80},"deletedAt":null,"createdAt":"2026-06-01T08:00:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z","businessHours":{"id":"c0b1c2d3-e4f5-4a6b-9d8e-9f0a1b2c3d4e","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","name":"Nairobi office hours","description":"Weekdays 8 to 5, Saturday mornings","timezone":"Africa/Nairobi","isDefault":true,"isActive":true,"schedule":[{"day":"monday","isWorkingDay":true,"shifts":[{"start":"08:00","end":"17:00"}]},{"day":"saturday","isWorkingDay":true,"shifts":[{"start":"09:00","end":"13:00"}]},{"day":"sunday","isWorkingDay":false,"shifts":[]}],"holidays":[{"name":"Jamhuri Day","date":"2026-12-12","isRecurring":true}],"deletedAt":null,"createdAt":"2026-06-01T08:00:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No SLA policy with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.sla.read"]}],"x-required-scopes":["crm.sla.read"]},"put":{"tags":["Help desk setup"],"summary":"Update an SLA policy","description":"Changes a service-level (SLA) policy. Only the fields you send change; targets, escalationRules, conditions and settings are each replaced whole.\n\n**Permission:** `crm.sla.update` (Update SLA policies).","operationId":"updateSLAPolicy","parameters":[{"name":"slaPolicyId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The policy to change","example":"e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"businessHoursId":{"type":"string","format":"uuid","description":"Business hours its clocks count in; empty for round the clock","examples":["c0b1c2d3-e4f5-4a6b-9d8e-9f0a1b2c3d4e"]},"conditions":{"type":"object","description":"Which tickets it covers: { channels, ticketTypes, tags, teamIds } (a JSON text also works)"},"description":{"type":"string","description":"New description; null clears it"},"escalationRules":{"type":"array","items":{"type":"object"},"description":"The full new list of steps for late clocks (up to 20), each { id, clock, afterMinutes, setPriority?, assignTo?, notifyAssignee?, notifyTeamLeads?, notifyUsers? } (a JSON text also works)","examples":[[{"id":"fr-1","clock":"first_response","afterMinutes":30,"setPriority":"urgent","notifyAssignee":true,"notifyTeamLeads":true}]]},"isActive":{"type":"boolean","description":"Whether it applies to new tickets"},"name":{"type":"string","description":"New name","examples":["High priority"]},"priority":{"type":"string","enum":["any","urgent","high","medium","low"],"description":"The ticket priority it covers; empty means any","examples":["high"]},"settings":{"type":"object","description":"{ pauseStatuses, warnPercent: 1 to 99 } (a JSON text also works)","examples":[{"pauseStatuses":["waiting_on_customer"],"warnPercent":80}]},"targets":{"type":"object","description":"New deadlines: firstResponse, resolution and nextResponse, each { hours, minutes } (a JSON text also works)","examples":[{"firstResponse":{"hours":1},"resolution":{"hours":24}}]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"slaPolicy":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"isActive":{"type":"boolean"},"priority":{"type":"string","description":"The ticket priority it covers: any, urgent, high, medium or low"},"targets":{"type":"object","properties":{"firstResponse":{"type":"object","properties":{"hours":{"type":"integer"}}},"resolution":{"type":"object","properties":{"hours":{"type":"integer"}}}},"description":"Deadlines: firstResponse, resolution and nextResponse, each { hours, minutes }"},"businessHoursId":{"type":"string"},"escalationRules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"clock":{"type":"string"},"afterMinutes":{"type":"integer"},"setPriority":{"type":"string"},"notifyAssignee":{"type":"boolean"},"notifyTeamLeads":{"type":"boolean"}}},"description":"What happens when a clock runs late: { id, clock (first_response or resolution), afterMinutes, setPriority?, assignTo?, notifyAssignee?, notifyTeamLeads?, notifyUsers? }"},"conditions":{"type":"object","properties":{"channels":{"type":"array","items":{"type":"string"}},"teamIds":{"type":"array","items":{"type":"string"}}},"description":"Which tickets it covers: channels, ticketTypes, tags and teamIds (departments); empty means all"},"settings":{"type":"object","properties":{"pauseStatuses":{"type":"array","items":{"type":"string"}},"warnPercent":{"type":"integer"}},"description":"pauseStatuses (statuses that pause the resolution clock; default waiting_on_customer) and warnPercent (share of a deadline after which a clock shows amber; default 80)"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"businessHours":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"timezone":{"type":"string"},"isDefault":{"type":"boolean"},"isActive":{"type":"boolean"},"schedule":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string"},"isWorkingDay":{"type":"boolean"},"shifts":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}}}}}}},"holidays":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"date":{"type":"string"},"isRecurring":{"type":"boolean"}}}},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"description":"The business hours its clocks count in, or null for round the clock"}}}}},"example":{"success":true,"message":"SLA policy updated successfully","slaPolicy":{"id":"e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","name":"High priority","description":"Reply within an hour, fix within a day","isActive":true,"priority":"high","targets":{"firstResponse":{"hours":1},"resolution":{"hours":24}},"businessHoursId":"c0b1c2d3-e4f5-4a6b-9d8e-9f0a1b2c3d4e","escalationRules":[{"id":"fr-1","clock":"first_response","afterMinutes":30,"setPriority":"urgent","notifyAssignee":true,"notifyTeamLeads":true}],"conditions":{"channels":["whatsapp","email"],"teamIds":["2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"]},"settings":{"pauseStatuses":["waiting_on_customer"],"warnPercent":80},"deletedAt":null,"createdAt":"2026-06-01T08:00:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z","businessHours":{"id":"c0b1c2d3-e4f5-4a6b-9d8e-9f0a1b2c3d4e","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","name":"Nairobi office hours","description":"Weekdays 8 to 5, Saturday mornings","timezone":"Africa/Nairobi","isDefault":true,"isActive":true,"schedule":[{"day":"monday","isWorkingDay":true,"shifts":[{"start":"08:00","end":"17:00"}]},{"day":"saturday","isWorkingDay":true,"shifts":[{"start":"09:00","end":"13:00"}]},{"day":"sunday","isWorkingDay":false,"shifts":[]}],"holidays":[{"name":"Jamhuri Day","date":"2026-12-12","isRecurring":true}],"deletedAt":null,"createdAt":"2026-06-01T08:00:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}}}}}},"400":{"description":"targets, escalationRules, conditions or settings is not valid JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No SLA policy with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"A field has the wrong shape: an unknown priority, a bad escalation step, over 20 steps, or warnPercent outside 1 to 99","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.sla.update"]}],"x-required-scopes":["crm.sla.update"]},"delete":{"tags":["Help desk setup"],"summary":"Delete an SLA policy","description":"Deletes a service-level (SLA) policy (it is kept hidden, not erased), so it no longer applies to new tickets.\n\n**Permission:** `crm.sla.delete` (Delete SLA policies).","operationId":"deleteSLAPolicy","parameters":[{"name":"slaPolicyId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The policy to delete","example":"e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"slaPolicy":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"}}}}},"example":{"success":true,"message":"SLA policy deleted successfully","slaPolicy":{"id":"e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a","name":"High priority","isActive":false}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No SLA policy with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.sla.delete"]}],"x-required-scopes":["crm.sla.delete"]}},"/teams":{"get":{"tags":["Teams & people"],"summary":"List teams","description":"Lists your organisation's teams (departments in Kazee Desk) by name, a page at a time, with their members. Deleted teams are left out.\n\n**Permission:** `crm.teams.read` (View teams).","operationId":"getTeams","parameters":[{"name":"autoAssign","in":"query","required":false,"schema":{"type":"boolean"},"description":"true for teams that assign tickets automatically only, false for the others","example":true},{"name":"isActive","in":"query","required":false,"schema":{"type":"boolean"},"description":"true for active teams only, false for inactive ones only","example":true},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":500,"default":25},"description":"Teams per page, at most 500","example":25},{"name":"memberId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only teams this person is a member of (CRM user ID; a 24-character Kazee sign-in ID also works)","example":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1},"description":"Page number, from 1","example":1},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Only teams whose name contains this text (any case)","example":"support"},{"name":"sort","in":"query","required":false,"schema":{"type":"string"},"description":"Not used: teams always come sorted by name"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"_id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"members":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string","description":"The member's CRM user ID"},"role":{"type":"string","description":"leader or member"},"joinedAt":{"type":"string","format":"date-time"}}}},"isActive":{"type":"boolean"},"autoAssign":{"type":"boolean","description":"Whether new tickets are assigned automatically"},"assignmentMethod":{"type":"string","description":"How new tickets are handed out: manual or round_robin"},"currentAssigneeIndex":{"type":"integer"},"specializations":{"type":"array","items":{"type":"string"}},"ticketVisibility":{"type":"string","description":"Who sees the department's tickets in Kazee Desk: members (its members, the assignee and Desk admins) or everyone"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer","description":"How many teams match, over all pages"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}}}}},"example":{"success":true,"data":[{"id":"3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f","_id":"3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","name":"Fibre support","description":"First-line help for fibre customers","members":[{"userId":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","role":"leader","joinedAt":"2026-10-01T08:30:00.000Z"},{"userId":"8e2f3a4b-5c6d-4e7f-9a0b-1c2d3e4f5a6b","role":"member","joinedAt":"2026-10-01T08:30:00.000Z"}],"isActive":true,"autoAssign":true,"assignmentMethod":"round_robin","currentAssigneeIndex":0,"specializations":["fibre","billing"],"ticketVisibility":"members","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}],"pagination":{"page":1,"limit":25,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.teams.read"]}],"x-required-scopes":["crm.teams.read"]},"post":{"tags":["Teams & people"],"summary":"Create a team","description":"Creates a team (a department in Kazee Desk) and answers with it. Teams can route and hand out help desk tickets to their members.\n\n**Permission:** `crm.teams.create` (Create teams).","operationId":"createTeam","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"assignmentMethod":{"type":"string","enum":["manual","round_robin"],"description":"How new tickets are handed out: by hand, or to each member in turn. Default: manual","examples":["round_robin"]},"autoAssign":{"type":"boolean","description":"Whether new tickets are assigned to its members automatically. Default: false","examples":[true]},"description":{"type":"string","description":"What the team does; an empty string clears it","examples":["First-line help for fibre customers"]},"isActive":{"type":"boolean","default":true,"description":"Whether the team is in use; an inactive team gets no new tickets routed to it","examples":[true]},"members":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string","format":"uuid"},"role":{"type":"string","enum":["leader","member"]}},"required":["userId"]},"description":"The team's people: each a CRM user ID (a 24-character Kazee sign-in ID also works) and role leader or member (default member). Each must be a person of your organisation, listed once. A JSON array in a string also works. Default: none","examples":[[{"userId":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","role":"leader"}]]},"name":{"type":"string","description":"The team's name","examples":["Fibre support"]},"specializations":{"type":"array","items":{"type":"string"},"description":"Topics the team handles. A JSON array in a string also works","examples":[["fibre","billing"]]},"ticketVisibility":{"type":"string","enum":["members","everyone"],"description":"Who sees the team's tickets in Kazee Desk: its members (plus the assignee and Desk admins), or everyone. Default: members","examples":["members"]}},"required":["name"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"_id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"members":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string","description":"The member's CRM user ID"},"role":{"type":"string","description":"leader or member"},"joinedAt":{"type":"string","format":"date-time"}}}},"isActive":{"type":"boolean"},"autoAssign":{"type":"boolean","description":"Whether new tickets are assigned automatically"},"assignmentMethod":{"type":"string","description":"How new tickets are handed out: manual or round_robin"},"currentAssigneeIndex":{"type":"integer"},"specializations":{"type":"array","items":{"type":"string"}},"ticketVisibility":{"type":"string","description":"Who sees the department's tickets in Kazee Desk: members (its members, the assignee and Desk admins) or everyone"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"message":{"type":"string"}}},"example":{"success":true,"data":{"id":"3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f","_id":"3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","name":"Fibre support","description":"First-line help for fibre customers","members":[{"userId":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","role":"leader","joinedAt":"2026-10-01T08:30:00.000Z"},{"userId":"8e2f3a4b-5c6d-4e7f-9a0b-1c2d3e4f5a6b","role":"member","joinedAt":"2026-10-01T08:30:00.000Z"}],"isActive":true,"autoAssign":true,"assignmentMethod":"round_robin","currentAssigneeIndex":0,"specializations":["fibre","billing"],"ticketVisibility":"members","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"},"message":"Team \"Fibre support\" created"}}}},"400":{"description":"No name, an unknown assignmentMethod or ticketVisibility, members or specializations is not valid JSON, a member is listed twice, or a member is not a person of your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.teams.create"]}],"x-required-scopes":["crm.teams.create"]}},"/teams/{teamId}":{"get":{"tags":["Teams & people"],"summary":"Get a team","description":"Answers one team (department) of your organisation with its members. Deleted teams are not found.\n\n**Permission:** `crm.teams.read` (View teams).","operationId":"getTeam","parameters":[{"name":"teamId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The team's ID","example":"3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"_id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"members":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string","description":"The member's CRM user ID"},"role":{"type":"string","description":"leader or member"},"joinedAt":{"type":"string","format":"date-time"}}}},"isActive":{"type":"boolean"},"autoAssign":{"type":"boolean","description":"Whether new tickets are assigned automatically"},"assignmentMethod":{"type":"string","description":"How new tickets are handed out: manual or round_robin"},"currentAssigneeIndex":{"type":"integer"},"specializations":{"type":"array","items":{"type":"string"}},"ticketVisibility":{"type":"string","description":"Who sees the department's tickets in Kazee Desk: members (its members, the assignee and Desk admins) or everyone"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f","_id":"3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","name":"Fibre support","description":"First-line help for fibre customers","members":[{"userId":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","role":"leader","joinedAt":"2026-10-01T08:30:00.000Z"},{"userId":"8e2f3a4b-5c6d-4e7f-9a0b-1c2d3e4f5a6b","role":"member","joinedAt":"2026-10-01T08:30:00.000Z"}],"isActive":true,"autoAssign":true,"assignmentMethod":"round_robin","currentAssigneeIndex":0,"specializations":["fibre","billing"],"ticketVisibility":"members","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No team with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.teams.read"]}],"x-required-scopes":["crm.teams.read"]},"put":{"tags":["Teams & people"],"summary":"Update a team","description":"Changes a team (a department in Kazee Desk) and answers with it. Send only what changes; members, when sent, replaces the whole member list.\n\n**Permission:** `crm.teams.update` (Update teams).","operationId":"updateTeam","parameters":[{"name":"teamId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The team's ID","example":"3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"assignmentMethod":{"type":"string","enum":["manual","round_robin"],"description":"How new tickets are handed out: by hand, or to each member in turn","examples":["round_robin"]},"autoAssign":{"type":"boolean","description":"Whether new tickets are assigned to its members automatically","examples":[true]},"description":{"type":"string","description":"What the team does; an empty string clears it","examples":["First-line help for fibre customers"]},"isActive":{"type":"boolean","description":"Whether the team is in use; an inactive team gets no new tickets routed to it","examples":[true]},"members":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string","format":"uuid"},"role":{"type":"string","enum":["leader","member"]}},"required":["userId"]},"description":"The team's people: each a CRM user ID (a 24-character Kazee sign-in ID also works) and role leader or member (default member). Each must be a person of your organisation, listed once. A JSON array in a string also works. Replaces the whole list","examples":[[{"userId":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","role":"leader"}]]},"name":{"type":"string","description":"The team's name","examples":["Fibre support"]},"specializations":{"type":"array","items":{"type":"string"},"description":"Topics the team handles. A JSON array in a string also works. Replaces the whole list","examples":[["fibre","billing"]]},"ticketVisibility":{"type":"string","enum":["members","everyone"],"description":"Who sees the team's tickets in Kazee Desk: its members (plus the assignee and Desk admins), or everyone","examples":["members"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"_id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"members":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string","description":"The member's CRM user ID"},"role":{"type":"string","description":"leader or member"},"joinedAt":{"type":"string","format":"date-time"}}}},"isActive":{"type":"boolean"},"autoAssign":{"type":"boolean","description":"Whether new tickets are assigned automatically"},"assignmentMethod":{"type":"string","description":"How new tickets are handed out: manual or round_robin"},"currentAssigneeIndex":{"type":"integer"},"specializations":{"type":"array","items":{"type":"string"}},"ticketVisibility":{"type":"string","description":"Who sees the department's tickets in Kazee Desk: members (its members, the assignee and Desk admins) or everyone"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f","_id":"3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","name":"Fibre support","description":"First-line help for fibre customers","members":[{"userId":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","role":"leader","joinedAt":"2026-10-01T08:30:00.000Z"},{"userId":"8e2f3a4b-5c6d-4e7f-9a0b-1c2d3e4f5a6b","role":"member","joinedAt":"2026-10-01T08:30:00.000Z"}],"isActive":true,"autoAssign":true,"assignmentMethod":"round_robin","currentAssigneeIndex":0,"specializations":["fibre","billing"],"ticketVisibility":"members","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"An empty name, an unknown assignmentMethod or ticketVisibility, members or specializations is not valid JSON, a member is listed twice, or a member is not a person of your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No team with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.teams.update"]}],"x-required-scopes":["crm.teams.update"]},"delete":{"tags":["Teams & people"],"summary":"Delete a team","description":"Deletes a team (a department in Kazee Desk). It is a soft delete: its tickets keep it, but Desk then treats them as having no department, channels stop routing new tickets to it, and it stops handing out tickets in turn.\n\n**Permission:** `crm.teams.delete` (Delete teams).","operationId":"deleteTeam","parameters":[{"name":"teamId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The team's ID","example":"3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}},"example":{"success":true,"message":"Department deleted"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No team with that ID in your organisation, or it is already deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.teams.delete"]}],"x-required-scopes":["crm.teams.delete"]}},"/kb/categories":{"get":{"tags":["Knowledge base"],"summary":"List knowledge base categories","description":"Lists knowledge base categories of the departments you may see, in their sort order and then by name, a page at a time.\n\n**Permission:** `crm.kb.read` (View knowledge base).","operationId":"getKBCategories","parameters":[{"name":"isActive","in":"query","required":false,"schema":{"type":"boolean"},"description":"true for only categories in use, false for only unused ones"},{"name":"isPublic","in":"query","required":false,"schema":{"type":"boolean"},"description":"true for only customer-visible categories, false for only internal ones"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25},"description":"Items per page, at most 500","example":25},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"parentId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only the categories directly under this one","example":"d5c6d7e8-f9a0-4b1c-8e3f-4a5b6c7d8e9f"},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text to find in the name","example":"router"},{"name":"sort","in":"query","required":false,"schema":{"type":"string"},"description":"Not used yet: sort order, then name"},{"name":"teamId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"A department: only its items plus the shared ones (those without a department)","example":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string","description":"The category's name in links, unique in your organisation"},"description":{"type":"string"},"parentId":{"type":"string","description":"The category it sits under, or null at the top"},"icon":{"type":"string"},"sortOrder":{"type":"integer","description":"Where it sits among its siblings, lowest first"},"isPublic":{"type":"boolean","description":"Whether customers may see it"},"isActive":{"type":"boolean"},"articleCount":{"type":"integer"},"teamId":{"type":"string","description":"Its department, or null when every department shares it"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer","description":"This page's number, from 1"},"limit":{"type":"integer","description":"Most items in a page (at most 500)"},"total":{"type":"integer","description":"How many items match in all"},"totalPages":{"type":"integer","description":"How many pages there are"},"hasNext":{"type":"boolean","description":"Whether another page follows"},"hasPrev":{"type":"boolean","description":"Whether a page comes before"}}}}},"example":{"success":true,"data":[{"id":"c4b5c6d7-e8f9-4a0b-9d2e-3f4a5b6c7d8e","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","name":"Router setup","slug":"router-setup","description":"Getting your router online and keeping it there.","parentId":"d5c6d7e8-f9a0-4b1c-8e3f-4a5b6c7d8e9f","icon":"router","sortOrder":1,"isPublic":true,"isActive":true,"articleCount":4,"teamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}],"pagination":{"page":1,"limit":25,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false}}}}},"400":{"description":"teamId is not a department ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.kb.read"]}],"x-required-scopes":["crm.kb.read"]},"post":{"tags":["Knowledge base"],"summary":"Create a knowledge base category","description":"Adds a knowledge base category and answers with status 201. Without a slug one is made from the name. Its articles belong to its department.\n\n**Permission:** `crm.kb.create` (Create knowledge base).","operationId":"createKBCategory","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"description":{"type":"string","description":"What it covers","examples":["Getting your router online and keeping it there."]},"icon":{"type":"string","description":"An icon name to show with it","examples":["router"]},"isActive":{"type":"boolean","default":true,"description":"Whether it is in use","examples":[true]},"isPublic":{"type":"boolean","default":true,"description":"Whether customers may see it","examples":[true]},"name":{"type":"string","description":"The category's name","examples":["Router setup"]},"parentId":{"type":"string","format":"uuid","description":"The category to put it under; left out, it sits at the top","examples":["d5c6d7e8-f9a0-4b1c-8e3f-4a5b6c7d8e9f"]},"slug":{"type":"string","description":"Its name in links, up to 255 characters; default: the name in lower case with dashes","examples":["router-setup"]},"sortOrder":{"type":"integer","default":0,"description":"Where it sits among its siblings, lowest first","examples":[1]},"teamId":{"type":"string","format":"uuid","description":"Home department; empty or \"null\" shares it with every department. Must be a live department of your organisation","examples":["2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"]}},"required":["name"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"category":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string","description":"The category's name in links, unique in your organisation"},"description":{"type":"string"},"parentId":{"type":"string","description":"The category it sits under, or null at the top"},"icon":{"type":"string"},"sortOrder":{"type":"integer","description":"Where it sits among its siblings, lowest first"},"isPublic":{"type":"boolean","description":"Whether customers may see it"},"isActive":{"type":"boolean"},"articleCount":{"type":"integer"},"teamId":{"type":"string","description":"Its department, or null when every department shares it"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Knowledge base category created successfully","category":{"id":"c4b5c6d7-e8f9-4a0b-9d2e-3f4a5b6c7d8e","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","name":"Router setup","slug":"router-setup","description":"Getting your router online and keeping it there.","parentId":"d5c6d7e8-f9a0-4b1c-8e3f-4a5b6c7d8e9f","icon":"router","sortOrder":1,"isPublic":true,"isActive":true,"articleCount":0,"teamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}}}}},"400":{"description":"teamId is not a live department of your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"A category with that slug already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"A field has the wrong shape (e.g. an empty name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.kb.create"]}],"x-required-scopes":["crm.kb.create"]}},"/kb/articles":{"get":{"tags":["Knowledge base"],"summary":"List knowledge base articles","description":"Lists knowledge base articles of the departments you may see, newest published first, a page at a time. The tags filter only narrows the page already fetched, so a page can hold fewer items than limit while pagination still counts every match.\n\n**Permission:** `crm.kb.read` (View knowledge base).","operationId":"getKBArticles","parameters":[{"name":"categoryId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this category's articles","example":"c4b5c6d7-e8f9-4a0b-9d2e-3f4a5b6c7d8e"},{"name":"isPublic","in":"query","required":false,"schema":{"type":"boolean"},"description":"true for only customer-visible articles, false for only internal ones"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25},"description":"Items per page, at most 500","example":25},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text to find in the title or body","example":"router"},{"name":"sort","in":"query","required":false,"schema":{"type":"string"},"description":"Not used yet: newest published first"},{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"Only articles in this status (draft, published or archived); repeat for several","example":"published"},{"name":"tags","in":"query","required":false,"schema":{"type":"string"},"description":"Comma-separated tags: only articles with at least one of them (on this page)","example":"router,outage"},{"name":"teamId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"A department: only its items plus the shared ones (those without a department)","example":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"title":{"type":"string"},"slug":{"type":"string","description":"The article's name in links, unique in your organisation"},"content":{"type":"string"},"excerpt":{"type":"string"},"categoryId":{"type":"string"},"authorId":{"type":"string"},"status":{"type":"string","description":"draft, published or archived"},"isPublic":{"type":"boolean","description":"Whether customers may see it"},"tags":{"type":"array","items":{"type":"string"}},"attachments":{"type":"array"},"viewCount":{"type":"integer"},"helpfulCount":{"type":"integer"},"notHelpfulCount":{"type":"integer"},"relatedArticles":{"type":"array","items":{"type":"string"},"description":"IDs of articles to read next"},"seoTitle":{},"seoDescription":{},"publishedAt":{"type":"string","format":"date-time","description":"When it was first published"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer","description":"This page's number, from 1"},"limit":{"type":"integer","description":"Most items in a page (at most 500)"},"total":{"type":"integer","description":"How many items match in all"},"totalPages":{"type":"integer","description":"How many pages there are"},"hasNext":{"type":"boolean","description":"Whether another page follows"},"hasPrev":{"type":"boolean","description":"Whether a page comes before"}}}}},"example":{"success":true,"data":[{"id":"e6d7e8f9-a0b1-4c2d-9f4a-5b6c7d8e9f0a","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","title":"How to restart your router","slug":"how-to-restart-your-router","content":"1. Unplug the router.\n2. Wait 30 seconds.\n3. Plug it back in and wait for the green light.","excerpt":"Three steps that fix most connection drops.","categoryId":"c4b5c6d7-e8f9-4a0b-9d2e-3f4a5b6c7d8e","authorId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","status":"published","isPublic":true,"tags":["router","outage"],"attachments":[],"viewCount":128,"helpfulCount":40,"notHelpfulCount":3,"relatedArticles":["f7e8f9a0-b1c2-4d3e-8a5b-6c7d8e9f0a1b"],"seoTitle":null,"seoDescription":null,"publishedAt":"2026-09-02T09:00:00.000Z","deletedAt":null,"createdAt":"2026-09-02T08:30:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}],"pagination":{"page":1,"limit":25,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false}}}}},"400":{"description":"teamId is not a department ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.kb.read"]}],"x-required-scopes":["crm.kb.read"]},"post":{"tags":["Knowledge base"],"summary":"Create a knowledge base article","description":"Writes a knowledge base article in a category and answers with status 201. Without a slug one is made from the title. It starts as a draft unless status is \"published\", which also stamps publishedAt. The article belongs to its category's department.\n\n**Permission:** `crm.kb.create` (Create knowledge base).","operationId":"createKBArticle","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"attachments":{"type":"array","items":{"type":"object"},"description":"Files to show with it, each { name, url, type?, size? } (a JSON text also works)","examples":[[]]},"authorId":{"type":"string","format":"uuid","description":"Who wrote it (a user ID)","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]},"categoryId":{"type":"string","format":"uuid","description":"The category it goes in","examples":["c4b5c6d7-e8f9-4a0b-9d2e-3f4a5b6c7d8e"]},"content":{"type":"string","description":"The article's body","examples":["1. Unplug the router.\n2. Wait 30 seconds.\n3. Plug it back in and wait for the green light."]},"excerpt":{"type":"string","description":"A one-line summary for lists","examples":["Three steps that fix most connection drops."]},"isPublic":{"type":"boolean","default":true,"description":"Whether customers may see it","examples":[true]},"relatedArticles":{"type":"array","items":{"type":"string","format":"uuid"},"description":"IDs of articles to read next (a JSON text also works)","examples":[["f7e8f9a0-b1c2-4d3e-8a5b-6c7d8e9f0a1b"]]},"seoDescription":{"type":"string","description":"Description for search engines"},"seoTitle":{"type":"string","description":"Title for search engines"},"slug":{"type":"string","description":"Its name in links, up to 255 characters; default: the title in lower case with dashes","examples":["how-to-restart-your-router"]},"status":{"type":"string","default":"draft","description":"draft, published or archived","examples":["published"]},"tags":{"type":"array","items":{"type":"string"},"description":"Labels (a comma-separated text also works)","examples":[["router","outage"]]},"title":{"type":"string","description":"The article's title","examples":["How to restart your router"]}},"required":["authorId","categoryId","content","title"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"article":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"title":{"type":"string"},"slug":{"type":"string","description":"The article's name in links, unique in your organisation"},"content":{"type":"string"},"excerpt":{"type":"string"},"categoryId":{"type":"string"},"authorId":{"type":"string"},"status":{"type":"string","description":"draft, published or archived"},"isPublic":{"type":"boolean","description":"Whether customers may see it"},"tags":{"type":"array","items":{"type":"string"}},"attachments":{"type":"array"},"viewCount":{"type":"integer"},"helpfulCount":{"type":"integer"},"notHelpfulCount":{"type":"integer"},"relatedArticles":{"type":"array","items":{"type":"string"},"description":"IDs of articles to read next"},"seoTitle":{},"seoDescription":{},"publishedAt":{"type":"string","format":"date-time","description":"When it was first published"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Article created successfully","article":{"id":"e6d7e8f9-a0b1-4c2d-9f4a-5b6c7d8e9f0a","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","title":"How to restart your router","slug":"how-to-restart-your-router","content":"1. Unplug the router.\n2. Wait 30 seconds.\n3. Plug it back in and wait for the green light.","excerpt":"Three steps that fix most connection drops.","categoryId":"c4b5c6d7-e8f9-4a0b-9d2e-3f4a5b6c7d8e","authorId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","status":"published","isPublic":true,"tags":["router","outage"],"attachments":[],"viewCount":0,"helpfulCount":0,"notHelpfulCount":0,"relatedArticles":["f7e8f9a0-b1c2-4d3e-8a5b-6c7d8e9f0a1b"],"seoTitle":null,"seoDescription":null,"publishedAt":"2026-09-02T09:00:00.000Z","deletedAt":null,"createdAt":"2026-09-02T08:30:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}}}}},"400":{"description":"attachments or relatedArticles is not valid JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"An article with that slug already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"A field has the wrong shape (e.g. an empty title or an ID that is not one)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.kb.create"]}],"x-required-scopes":["crm.kb.create"]}},"/kb/articles/{articleId}":{"get":{"tags":["Knowledge base"],"summary":"Get a knowledge base article","description":"Reads one knowledge base article and counts a view (the reply shows the count from before this view).\n\n**Permission:** `crm.kb.read` (View knowledge base).","operationId":"getKBArticle","parameters":[{"name":"articleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The article to read","example":"e6d7e8f9-a0b1-4c2d-9f4a-5b6c7d8e9f0a"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"article":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"title":{"type":"string"},"slug":{"type":"string","description":"The article's name in links, unique in your organisation"},"content":{"type":"string"},"excerpt":{"type":"string"},"categoryId":{"type":"string"},"authorId":{"type":"string"},"status":{"type":"string","description":"draft, published or archived"},"isPublic":{"type":"boolean","description":"Whether customers may see it"},"tags":{"type":"array","items":{"type":"string"}},"attachments":{"type":"array"},"viewCount":{"type":"integer"},"helpfulCount":{"type":"integer"},"notHelpfulCount":{"type":"integer"},"relatedArticles":{"type":"array","items":{"type":"string"},"description":"IDs of articles to read next"},"seoTitle":{},"seoDescription":{},"publishedAt":{"type":"string","format":"date-time","description":"When it was first published"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"article":{"id":"e6d7e8f9-a0b1-4c2d-9f4a-5b6c7d8e9f0a","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","title":"How to restart your router","slug":"how-to-restart-your-router","content":"1. Unplug the router.\n2. Wait 30 seconds.\n3. Plug it back in and wait for the green light.","excerpt":"Three steps that fix most connection drops.","categoryId":"c4b5c6d7-e8f9-4a0b-9d2e-3f4a5b6c7d8e","authorId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","status":"published","isPublic":true,"tags":["router","outage"],"attachments":[],"viewCount":128,"helpfulCount":40,"notHelpfulCount":3,"relatedArticles":["f7e8f9a0-b1c2-4d3e-8a5b-6c7d8e9f0a1b"],"seoTitle":null,"seoDescription":null,"publishedAt":"2026-09-02T09:00:00.000Z","deletedAt":null,"createdAt":"2026-09-02T08:30:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No article with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.kb.read"]}],"x-required-scopes":["crm.kb.read"]},"put":{"tags":["Knowledge base"],"summary":"Update a knowledge base article","description":"Changes a knowledge base article. Only the fields you send change. Moving it to \"published\" for the first time stamps publishedAt.\n\n**Permission:** `crm.kb.update` (Update knowledge base).","operationId":"updateKBArticle","parameters":[{"name":"articleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The article to change","example":"e6d7e8f9-a0b1-4c2d-9f4a-5b6c7d8e9f0a"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"attachments":{"type":"array","items":{"type":"object"},"description":"The full new list of files, each { name, url, type?, size? } (a JSON text also works)"},"authorId":{"type":"string","format":"uuid","description":"New author (a user ID)","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]},"categoryId":{"type":"string","format":"uuid","description":"Category to move it to","examples":["c4b5c6d7-e8f9-4a0b-9d2e-3f4a5b6c7d8e"]},"content":{"type":"string","description":"New body"},"excerpt":{"type":"string","description":"New summary; null clears it"},"isPublic":{"type":"boolean","description":"Whether customers may see it"},"relatedArticles":{"type":"array","items":{"type":"string","format":"uuid"},"description":"The full new list of related article IDs (a JSON text also works)"},"seoDescription":{"type":"string","description":"Description for search engines; null clears it"},"seoTitle":{"type":"string","description":"Title for search engines; null clears it"},"slug":{"type":"string","description":"New name in links (not made from a new title)"},"status":{"type":"string","description":"draft, published or archived","examples":["published"]},"tags":{"type":"array","items":{"type":"string"},"description":"The full new list of labels (a comma-separated text also works)","examples":[["router","outage"]]},"title":{"type":"string","description":"New title","examples":["How to restart your router"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"article":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"title":{"type":"string"},"slug":{"type":"string","description":"The article's name in links, unique in your organisation"},"content":{"type":"string"},"excerpt":{"type":"string"},"categoryId":{"type":"string"},"authorId":{"type":"string"},"status":{"type":"string","description":"draft, published or archived"},"isPublic":{"type":"boolean","description":"Whether customers may see it"},"tags":{"type":"array","items":{"type":"string"}},"attachments":{"type":"array"},"viewCount":{"type":"integer"},"helpfulCount":{"type":"integer"},"notHelpfulCount":{"type":"integer"},"relatedArticles":{"type":"array","items":{"type":"string"},"description":"IDs of articles to read next"},"seoTitle":{},"seoDescription":{},"publishedAt":{"type":"string","format":"date-time","description":"When it was first published"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Article updated successfully","article":{"id":"e6d7e8f9-a0b1-4c2d-9f4a-5b6c7d8e9f0a","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","title":"How to restart your router","slug":"how-to-restart-your-router","content":"1. Unplug the router.\n2. Wait 30 seconds.\n3. Plug it back in and wait for the green light.","excerpt":"Three steps that fix most connection drops.","categoryId":"c4b5c6d7-e8f9-4a0b-9d2e-3f4a5b6c7d8e","authorId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","status":"published","isPublic":true,"tags":["router","outage"],"attachments":[],"viewCount":128,"helpfulCount":40,"notHelpfulCount":3,"relatedArticles":["f7e8f9a0-b1c2-4d3e-8a5b-6c7d8e9f0a1b"],"seoTitle":null,"seoDescription":null,"publishedAt":"2026-09-02T09:00:00.000Z","deletedAt":null,"createdAt":"2026-09-02T08:30:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}}}}},"400":{"description":"attachments or relatedArticles is not valid JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No article with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"The change could not be saved (e.g. the slug is already used by another article)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.kb.update"]}],"x-required-scopes":["crm.kb.update"]},"delete":{"tags":["Knowledge base"],"summary":"Archive a knowledge base article","description":"Archives a knowledge base article: its status becomes \"archived\". The article is kept and can be published again with an update.\n\n**Permission:** `crm.kb.delete` (Delete knowledge base).","operationId":"deleteKBArticle","parameters":[{"name":"articleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The article to archive","example":"e6d7e8f9-a0b1-4c2d-9f4a-5b6c7d8e9f0a"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"article":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"status":{"type":"string"}}}}},"example":{"success":true,"message":"Article archived successfully","article":{"id":"e6d7e8f9-a0b1-4c2d-9f4a-5b6c7d8e9f0a","title":"How to restart your router","status":"archived"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No article with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.kb.delete"]}],"x-required-scopes":["crm.kb.delete"]}},"/automation-rules":{"get":{"tags":["Automations"],"summary":"List automation rules","description":"Lists your organisation's automation rules (buttons included), in run order (executionOrder), a page at a time. Filter by trigger, pipeline, record type, on/off or a word in the name.\n\n**Permission:** `crm.automations.read` (View automations).","operationId":"getAutomationRules","parameters":[{"name":"customModuleSlug","in":"query","required":false,"schema":{"type":"string"},"description":"Only rules for this custom module (its slug)","example":"site-surveys"},{"name":"includeStats","in":"query","required":false,"schema":{"type":"boolean"},"description":"true adds stats: rule counts by trigger and on/off","example":true},{"name":"isActive","in":"query","required":false,"schema":{"type":"boolean"},"description":"true: only rules that run; false: only switched-off ones","example":true},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25},"description":"Rules per page, up to 500","example":25},{"name":"moduleType","in":"query","required":false,"schema":{"type":"string"},"description":"Only rules for this record type: leads, deals, subscriptions, tickets, requests, accounts, contacts or custom","example":"leads"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"pipelineId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only rules of this pipeline","example":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7"},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Only rules whose name contains this text (any case)","example":"welcome"},{"name":"sort","in":"query","required":false,"schema":{"type":"string"},"description":"Not used: rules always come in run order (executionOrder, lowest first)"},{"name":"triggerEvent","in":"query","required":false,"schema":{"type":"string"},"description":"Only rules started by this event: record_created, record_updated, stage_changed, field_changed, scheduled, manual, books_estimate_paid, books_extra_estimate_paid, record_mentioned, line_disconnected, line_reconnected","example":"record_created"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"isActive":{"type":"boolean"},"pipelineId":{"type":"string"},"moduleType":{"type":"string"},"customModuleSlug":{},"triggerEvent":{"type":"string","description":"What starts the rule: record_created, record_updated, stage_changed, field_changed, scheduled, manual, books_estimate_paid, books_extra_estimate_paid, record_mentioned, line_disconnected, line_reconnected (the two Books ones on deals only), or another event name the workflow engine sends"},"triggerConfig":{"type":"object","properties":{},"description":"Settings for the trigger, e.g. the field for field_changed, or for a button (manual) `{ \"button\": { \"label\", \"description\", \"allowedUserIds\", \"zohoCrmOrg\" } }`"},"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"operator":{"type":"string"},"value":{"type":"string"},"logicOperator":{"type":"string"}}},"description":"Checks the record must pass, each { field, operator, value, logicOperator }"},"conditionLogic":{"type":"string","description":"all: every condition must hold; any: one is enough"},"actions":{"type":"array","items":{"type":"object","properties":{"actionType":{"type":"string"},"name":{"type":"string"},"config":{"type":"object","properties":{"title":{"type":"string"},"message":{"type":"string"}}},"orderNum":{"type":"integer"},"isActive":{"type":"boolean"}}},"description":"The steps, run in orderNum order, each { actionType, name, config, orderNum, isActive }"},"executionOrder":{"type":"integer","description":"Rules on the same event run lowest first; lists come in this order"},"stopProcessing":{"type":"boolean","description":"When true, rules after this one do not run for the same event"},"executionCount":{"type":"integer","description":"How many times the rule has run"},"lastExecutedAt":{"type":"string","format":"date-time"},"createdBy":{"type":"string"},"deletedAt":{"description":"Always null here: deleted rules are left out"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"},"hasNext":{"type":"boolean"},"hasPrev":{"type":"boolean"}}},"stats":{"type":"object","properties":{"byTriggerEvent":{"type":"object","properties":{"record_created":{"type":"integer"},"manual":{"type":"integer"}}},"active":{"type":"integer"},"inactive":{"type":"integer"},"total":{"type":"integer"}},"description":"Only with includeStats: counts over all rules (up to 1,000), ignoring the filters"}}},"example":{"success":true,"data":[{"id":"17a8b9c0-4e5f-4a6b-8c7d-8e9f0a1b2c3d","organizationId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","name":"Welcome new fibre leads","description":"Emails the lead and tells the owner when a lead comes in","isActive":true,"pipelineId":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","moduleType":"leads","customModuleSlug":null,"triggerEvent":"record_created","triggerConfig":{},"conditions":[{"field":"source","operator":"equals","value":"website","logicOperator":"AND"}],"conditionLogic":"all","actions":[{"actionType":"notification","name":"Tell the owner","config":{"title":"New fibre lead","message":"{{record.firstName}} {{record.lastName}} asked for a call"},"orderNum":1,"isActive":true}],"executionOrder":0,"stopProcessing":false,"executionCount":42,"lastExecutedAt":"2026-10-20T08:15:00.000Z","createdBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","deletedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z"}],"pagination":{"page":1,"limit":25,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false},"stats":{"byTriggerEvent":{"record_created":3,"manual":1},"active":3,"inactive":1,"total":4}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.automations.read"]}],"x-required-scopes":["crm.automations.read"]},"post":{"tags":["Automations"],"summary":"Create an automation rule","description":"Creates an automation rule: when the trigger event happens on a record that passes the conditions, the steps run. With triggerEvent \"manual\" it is a button people press on a deal instead. Answers 201.\n\n**Permission:** `crm.automations.create` (Create automations).","operationId":"createAutomationRule","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"actions":{"type":"array","items":{"type":"object"},"description":"The steps to run, each { actionType, name, config, orderNum, isActive } (a JSON list, or JSON text). actionType is one of email, notification, webhook, create_record, update_field, update_related, assign_user, add_tag, remove_tag, schedule_task or custom, plus the record type's own (see the workflow capabilities call). A button takes only zoho_crm_record and books_zoho_push steps","examples":[[{"actionType":"notification","name":"Tell the owner","config":{"title":"New fibre lead","message":"{{record.firstName}} {{record.lastName}} asked for a call"},"orderNum":1,"isActive":true}]]},"conditionLogic":{"type":"string","enum":["AND","OR","all","any"],"default":"AND","description":"How the conditions combine: OR or any means one is enough; anything else means all must hold","examples":["AND"]},"conditions":{"type":"array","items":{"type":"object"},"description":"Checks the record must pass, each { field, operator, value, logicOperator } (a JSON list, or JSON text). Operators: equals, not_equals, greater_than, greater_than_or_equals, less_than, less_than_or_equals, contains, not_contains, starts_with, ends_with, is_empty, is_not_empty, in, not_in, matches_regex","examples":[[{"field":"source","operator":"equals","value":"website","logicOperator":"AND"}]]},"customModuleSlug":{"type":"string","description":"For a custom module, the module's slug (up to 64 characters)","examples":["site-surveys"]},"description":{"type":"string","description":"What the rule is for","examples":["Emails the lead and tells the owner when a lead comes in"]},"executionOrder":{"type":"integer","default":0,"description":"Rules on the same event run lowest first","examples":[0]},"isActive":{"type":"boolean","default":true,"description":"Whether the rule runs (the text \"true\" or \"false\" also works)","examples":[true]},"moduleType":{"type":"string","description":"Record type the rule acts on: leads, deals, subscriptions, tickets, requests, accounts, contacts or custom (up to 16 characters). A button must be deals","examples":["leads"]},"name":{"type":"string","description":"Name of the rule","examples":["Welcome new fibre leads"]},"pipelineId":{"type":"string","format":"uuid","description":"Pipeline the rule belongs to, if any","examples":["6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7"]},"stopProcessing":{"type":"boolean","default":false,"description":"When true, rules after this one do not run for the same event","examples":[false]},"triggerConfig":{"type":"object","description":"Settings for the trigger (a JSON object, or JSON text). A button needs `{ \"button\": { \"label\": \"1 to 60 characters\", \"allowedUserIds\": [\"…\"] } }`, with an optional description and zohoCrmOrg","examples":[{}]},"triggerEvent":{"type":"string","description":"What starts the rule (up to 40 characters): record_created, record_updated, stage_changed, field_changed, scheduled, manual, books_estimate_paid, books_extra_estimate_paid, record_mentioned, line_disconnected, line_reconnected. \"manual\" makes it a button people press on a deal","examples":["record_created"]}},"required":["actions","name","triggerEvent"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"isActive":{"type":"boolean"},"pipelineId":{"type":"string"},"moduleType":{"type":"string"},"customModuleSlug":{},"triggerEvent":{"type":"string","description":"What starts the rule: record_created, record_updated, stage_changed, field_changed, scheduled, manual, books_estimate_paid, books_extra_estimate_paid, record_mentioned, line_disconnected, line_reconnected (the two Books ones on deals only), or another event name the workflow engine sends"},"triggerConfig":{"type":"object","properties":{},"description":"Settings for the trigger, e.g. the field for field_changed, or for a button (manual) `{ \"button\": { \"label\", \"description\", \"allowedUserIds\", \"zohoCrmOrg\" } }`"},"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"operator":{"type":"string"},"value":{"type":"string"},"logicOperator":{"type":"string"}}},"description":"Checks the record must pass, each { field, operator, value, logicOperator }"},"conditionLogic":{"type":"string","description":"all: every condition must hold; any: one is enough"},"actions":{"type":"array","items":{"type":"object","properties":{"actionType":{"type":"string"},"name":{"type":"string"},"config":{"type":"object","properties":{"title":{"type":"string"},"message":{"type":"string"}}},"orderNum":{"type":"integer"},"isActive":{"type":"boolean"}}},"description":"The steps, run in orderNum order, each { actionType, name, config, orderNum, isActive }"},"executionOrder":{"type":"integer","description":"Rules on the same event run lowest first; lists come in this order"},"stopProcessing":{"type":"boolean","description":"When true, rules after this one do not run for the same event"},"executionCount":{"type":"integer","description":"How many times the rule has run"},"lastExecutedAt":{},"createdBy":{"type":"string"},"deletedAt":{"description":"Always null here: deleted rules are left out"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Automation rule \"Welcome new fibre leads\" created successfully","data":{"id":"17a8b9c0-4e5f-4a6b-8c7d-8e9f0a1b2c3d","organizationId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","name":"Welcome new fibre leads","description":"Emails the lead and tells the owner when a lead comes in","isActive":true,"pipelineId":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","moduleType":"leads","customModuleSlug":null,"triggerEvent":"record_created","triggerConfig":{},"conditions":[{"field":"source","operator":"equals","value":"website","logicOperator":"AND"}],"conditionLogic":"all","actions":[{"actionType":"notification","name":"Tell the owner","config":{"title":"New fibre lead","message":"{{record.firstName}} {{record.lastName}} asked for a call"},"orderNum":1,"isActive":true}],"executionOrder":0,"stopProcessing":false,"executionCount":0,"lastExecutedAt":null,"createdBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","deletedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"A JSON value is not valid, or a button (manual trigger) is not set up right: it must be on deals, with a label of 1 to 60 characters, allowedUserIds and at least one step, each of a type a button can check (zoho_crm_record, books_zoho_push)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.automations.create"]}],"x-required-scopes":["crm.automations.create"]}},"/automation-execution-logs":{"get":{"tags":["Automations"],"summary":"List automation run logs","description":"Lists what automation rules, buttons and pipeline transitions did when they ran, newest first, a page at a time: the record, each step's result and how long it took. Runs on tickets you may not see are left out.\n\n**Permission:** `crm.automations.read` (View automations).","operationId":"getAutomationExecutionLogs","parameters":[{"name":"customModuleSlug","in":"query","required":false,"schema":{"type":"string"},"description":"Only runs on this custom module (its slug)","example":"site-surveys"},{"name":"executedBy","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only runs started by this person","example":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b"},{"name":"fromDate","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only runs at or after this time","example":"2026-10-01T00:00:00.000Z"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50},"description":"Runs per page, up to 500","example":50},{"name":"moduleType","in":"query","required":false,"schema":{"type":"string"},"description":"Only runs on this record type: leads, deals, subscriptions, tickets, requests, accounts, contacts or custom","example":"leads"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"pipelineId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only runs on this pipeline","example":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7"},{"name":"recordId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only runs on this record","example":"d4e5f6a7-1b2c-4d3e-9f4a-5b6c7d8e9f0a"},{"name":"ruleId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only runs of this automation rule or transition (matched on sourceId)","example":"17a8b9c0-4e5f-4a6b-8c7d-8e9f0a1b2c3d"},{"name":"source","in":"query","required":false,"schema":{"type":"string","enum":["automation","transition"]},"description":"Only runs of rules and buttons (automation) or of pipeline transitions (transition); other values are ignored","example":"automation"},{"name":"sourceId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only runs of this rule or transition","example":"17a8b9c0-4e5f-4a6b-8c7d-8e9f0a1b2c3d"},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["success","partial_failure","failure"]},"description":"Only runs that ended this way; other values are ignored","example":"failure"},{"name":"success","in":"query","required":false,"schema":{"type":"boolean"},"description":"true: only runs that fully worked; false: only runs where a step failed (failure or partial_failure)","example":false},{"name":"toDate","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only runs at or before this time","example":"2026-10-21T00:00:00.000Z"},{"name":"triggerEvent","in":"query","required":false,"schema":{"type":"string"},"description":"Only runs started by this event, e.g. record_created, record_updated, stage_changed or transition_executed","example":"record_created"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"logs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"pipelineId":{"type":"string"},"moduleType":{"type":"string"},"customModuleSlug":{},"recordId":{"type":"string"},"recordType":{"type":"string"},"recordTitle":{"type":"string"},"source":{"type":"string","description":"automation (a rule or button; sourceId is the rule) or transition (a pipeline transition; sourceId is the transition)"},"sourceId":{"type":"string"},"sourceName":{"type":"string"},"triggerEvent":{"type":"string"},"status":{"type":"string","description":"success, partial_failure or failure"},"actionResults":{"type":"array","items":{"type":"object","properties":{"actionType":{"type":"string"},"actionName":{"type":"string"},"success":{"type":"boolean"},"logs":{"type":"array"},"sideEffects":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"recipientId":{"type":"string"},"title":{"type":"string"}}}},"durationMs":{"type":"integer"}}},"description":"One entry per step: actionType, actionName, success, error, logs, sideEffects, durationMs"},"totalDurationMs":{"type":"integer"},"executedBy":{"type":"string","description":"The person whose change or press started it, when there was one"},"metadata":{"type":"object","properties":{}},"createdAt":{"type":"string","format":"date-time"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"}}}}},"example":{"success":true,"logs":[{"id":"4ad1e2f3-7b8c-4d9e-8f0a-1b2c3d4e5f6a","organizationId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","pipelineId":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","moduleType":"leads","customModuleSlug":null,"recordId":"d4e5f6a7-1b2c-4d3e-9f4a-5b6c7d8e9f0a","recordType":"lead","recordTitle":"Amina Wanjiru","source":"automation","sourceId":"17a8b9c0-4e5f-4a6b-8c7d-8e9f0a1b2c3d","sourceName":"Welcome new fibre leads","triggerEvent":"record_created","status":"success","actionResults":[{"actionType":"notification","actionName":"Tell the owner","success":true,"logs":[],"sideEffects":[{"type":"notification","recipientId":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","title":"New fibre lead"}],"durationMs":38}],"totalDurationMs":41,"executedBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","metadata":{},"createdAt":"2026-10-20T08:15:00.000Z"}],"pagination":{"page":1,"limit":50,"total":1,"totalPages":1}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.automations.read"]}],"x-required-scopes":["crm.automations.read"]}},"/automation-rules/{ruleId}":{"get":{"tags":["Automations"],"summary":"Get an automation rule","description":"Answers one automation rule of your organisation: its trigger, conditions and steps. Deleted rules are not found.\n\n**Permission:** `crm.automations.read` (View automations).","operationId":"getAutomationRule","parameters":[{"name":"ruleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The automation rule","example":"17a8b9c0-4e5f-4a6b-8c7d-8e9f0a1b2c3d"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"isActive":{"type":"boolean"},"pipelineId":{"type":"string"},"moduleType":{"type":"string"},"customModuleSlug":{},"triggerEvent":{"type":"string","description":"What starts the rule: record_created, record_updated, stage_changed, field_changed, scheduled, manual, books_estimate_paid, books_extra_estimate_paid, record_mentioned, line_disconnected, line_reconnected (the two Books ones on deals only), or another event name the workflow engine sends"},"triggerConfig":{"type":"object","properties":{},"description":"Settings for the trigger, e.g. the field for field_changed, or for a button (manual) `{ \"button\": { \"label\", \"description\", \"allowedUserIds\", \"zohoCrmOrg\" } }`"},"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"operator":{"type":"string"},"value":{"type":"string"},"logicOperator":{"type":"string"}}},"description":"Checks the record must pass, each { field, operator, value, logicOperator }"},"conditionLogic":{"type":"string","description":"all: every condition must hold; any: one is enough"},"actions":{"type":"array","items":{"type":"object","properties":{"actionType":{"type":"string"},"name":{"type":"string"},"config":{"type":"object","properties":{"title":{"type":"string"},"message":{"type":"string"}}},"orderNum":{"type":"integer"},"isActive":{"type":"boolean"}}},"description":"The steps, run in orderNum order, each { actionType, name, config, orderNum, isActive }"},"executionOrder":{"type":"integer","description":"Rules on the same event run lowest first; lists come in this order"},"stopProcessing":{"type":"boolean","description":"When true, rules after this one do not run for the same event"},"executionCount":{"type":"integer","description":"How many times the rule has run"},"lastExecutedAt":{"type":"string","format":"date-time"},"createdBy":{"type":"string"},"deletedAt":{"description":"Always null here: deleted rules are left out"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"17a8b9c0-4e5f-4a6b-8c7d-8e9f0a1b2c3d","organizationId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","name":"Welcome new fibre leads","description":"Emails the lead and tells the owner when a lead comes in","isActive":true,"pipelineId":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","moduleType":"leads","customModuleSlug":null,"triggerEvent":"record_created","triggerConfig":{},"conditions":[{"field":"source","operator":"equals","value":"website","logicOperator":"AND"}],"conditionLogic":"all","actions":[{"actionType":"notification","name":"Tell the owner","config":{"title":"New fibre lead","message":"{{record.firstName}} {{record.lastName}} asked for a call"},"orderNum":1,"isActive":true}],"executionOrder":0,"stopProcessing":false,"executionCount":42,"lastExecutedAt":"2026-10-20T08:15:00.000Z","createdBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","deletedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No automation rule with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.automations.read"]}],"x-required-scopes":["crm.automations.read"]},"put":{"tags":["Automations"],"summary":"Update an automation rule","description":"Changes an automation rule: only the fields you send change. Sending conditions, actions or triggerConfig replaces the whole list or object.\n\n**Permission:** `crm.automations.update` (Update automations).","operationId":"updateAutomationRule","parameters":[{"name":"ruleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The automation rule to change","example":"17a8b9c0-4e5f-4a6b-8c7d-8e9f0a1b2c3d"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"actions":{"type":"array","items":{"type":"object"},"description":"The steps to run, each { actionType, name, config, orderNum, isActive } (a JSON list, or JSON text). actionType is one of email, notification, webhook, create_record, update_field, update_related, assign_user, add_tag, remove_tag, schedule_task or custom, plus the record type's own (see the workflow capabilities call). A button takes only zoho_crm_record and books_zoho_push steps. Leave out to keep it","examples":[[{"actionType":"notification","name":"Tell the owner","config":{"title":"New fibre lead","message":"{{record.firstName}} {{record.lastName}} asked for a call"},"orderNum":1,"isActive":true}]]},"conditionLogic":{"type":"string","enum":["AND","OR","all","any"],"description":"How the conditions combine: OR or any means one is enough; anything else means all must hold. Leave out to keep it","examples":["AND"]},"conditions":{"type":"array","items":{"type":"object"},"description":"Checks the record must pass, each { field, operator, value, logicOperator } (a JSON list, or JSON text). Operators: equals, not_equals, greater_than, greater_than_or_equals, less_than, less_than_or_equals, contains, not_contains, starts_with, ends_with, is_empty, is_not_empty, in, not_in, matches_regex","examples":[[{"field":"source","operator":"equals","value":"website","logicOperator":"AND"}]]},"customModuleSlug":{"type":"string","description":"For a custom module, the module's slug (up to 64 characters); empty clears it","examples":["site-surveys"]},"description":{"type":"string","description":"What the rule is for. Leave out to keep it; null clears it","examples":["Emails the lead and tells the owner when a lead comes in"]},"executionOrder":{"type":"integer","description":"Rules on the same event run lowest first. Leave out to keep it","examples":[0]},"isActive":{"type":"boolean","description":"Whether the rule runs (the text \"true\" or \"false\" also works). Leave out to keep it","examples":[true]},"moduleType":{"type":"string","description":"Record type the rule acts on: leads, deals, subscriptions, tickets, requests, accounts, contacts or custom (up to 16 characters). A button must be deals. Empty clears it","examples":["leads"]},"name":{"type":"string","description":"Name of the rule. Leave out to keep it","examples":["Welcome new fibre leads"]},"pipelineId":{"type":"string","format":"uuid","description":"Pipeline the rule belongs to, if any; empty clears it","examples":["6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7"]},"stopProcessing":{"type":"boolean","description":"When true, rules after this one do not run for the same event. Leave out to keep it","examples":[false]},"triggerConfig":{"type":"object","description":"Settings for the trigger (a JSON object, or JSON text). A button needs `{ \"button\": { \"label\": \"1 to 60 characters\", \"allowedUserIds\": [\"…\"] } }`, with an optional description and zohoCrmOrg. Sent empty, it becomes {}","examples":[{}]},"triggerEvent":{"type":"string","description":"What starts the rule (up to 40 characters): record_created, record_updated, stage_changed, field_changed, scheduled, manual, books_estimate_paid, books_extra_estimate_paid, record_mentioned, line_disconnected, line_reconnected. \"manual\" makes it a button people press on a deal. Leave out to keep it","examples":["record_created"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"isActive":{"type":"boolean"},"pipelineId":{"type":"string"},"moduleType":{"type":"string"},"customModuleSlug":{},"triggerEvent":{"type":"string","description":"What starts the rule: record_created, record_updated, stage_changed, field_changed, scheduled, manual, books_estimate_paid, books_extra_estimate_paid, record_mentioned, line_disconnected, line_reconnected (the two Books ones on deals only), or another event name the workflow engine sends"},"triggerConfig":{"type":"object","properties":{},"description":"Settings for the trigger, e.g. the field for field_changed, or for a button (manual) `{ \"button\": { \"label\", \"description\", \"allowedUserIds\", \"zohoCrmOrg\" } }`"},"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"operator":{"type":"string"},"value":{"type":"string"},"logicOperator":{"type":"string"}}},"description":"Checks the record must pass, each { field, operator, value, logicOperator }"},"conditionLogic":{"type":"string","description":"all: every condition must hold; any: one is enough"},"actions":{"type":"array","items":{"type":"object","properties":{"actionType":{"type":"string"},"name":{"type":"string"},"config":{"type":"object","properties":{"title":{"type":"string"},"message":{"type":"string"}}},"orderNum":{"type":"integer"},"isActive":{"type":"boolean"}}},"description":"The steps, run in orderNum order, each { actionType, name, config, orderNum, isActive }"},"executionOrder":{"type":"integer","description":"Rules on the same event run lowest first; lists come in this order"},"stopProcessing":{"type":"boolean","description":"When true, rules after this one do not run for the same event"},"executionCount":{"type":"integer","description":"How many times the rule has run"},"lastExecutedAt":{"type":"string","format":"date-time"},"createdBy":{"type":"string"},"deletedAt":{"description":"Always null here: deleted rules are left out"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Automation rule \"Welcome new fibre leads\" updated successfully","data":{"id":"17a8b9c0-4e5f-4a6b-8c7d-8e9f0a1b2c3d","organizationId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","name":"Welcome new fibre leads","description":"Emails the lead and tells the owner when a lead comes in","isActive":true,"pipelineId":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","moduleType":"leads","customModuleSlug":null,"triggerEvent":"record_created","triggerConfig":{},"conditions":[{"field":"source","operator":"equals","value":"website","logicOperator":"AND"}],"conditionLogic":"all","actions":[{"actionType":"notification","name":"Tell the owner","config":{"title":"New fibre lead","message":"{{record.firstName}} {{record.lastName}} asked for a call"},"orderNum":1,"isActive":true}],"executionOrder":0,"stopProcessing":false,"executionCount":42,"lastExecutedAt":"2026-10-20T08:15:00.000Z","createdBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","deletedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z"}}}}},"400":{"description":"A JSON value is not valid, or the rule is a button (manual trigger) and would no longer be one that can be checked: it must be on deals, with a label of 1 to 60 characters, allowedUserIds and at least one step, each of a type a button can check (zoho_crm_record, books_zoho_push)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No automation rule with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.automations.update"]}],"x-required-scopes":["crm.automations.update"]},"delete":{"tags":["Automations"],"summary":"Delete an automation rule","description":"Deletes an automation rule, so it stops running and leaves the lists. The rule is kept in the database (marked deleted), and its past run logs stay.\n\n**Permission:** `crm.automations.delete` (Delete automations).","operationId":"deleteAutomationRule","parameters":[{"name":"ruleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The automation rule to delete","example":"17a8b9c0-4e5f-4a6b-8c7d-8e9f0a1b2c3d"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}}}},"example":{"success":true,"message":"Automation rule \"Welcome new fibre leads\" has been deleted","data":{"id":"17a8b9c0-4e5f-4a6b-8c7d-8e9f0a1b2c3d","name":"Welcome new fibre leads"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No automation rule with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.automations.delete"]}],"x-required-scopes":["crm.automations.delete"]}},"/macros":{"get":{"tags":["Help desk setup"],"summary":"List macros","description":"Lists macros of the departments you may see, most used first, a page at a time. Known problem: the sign-in fills the createdBy filter with you, so only the macros you made are listed unless you send createdBy empty (createdBy=).\n\n**Permission:** `crm.macros.read` (View macros).","operationId":"getMacros","parameters":[{"name":"category","in":"query","required":false,"schema":{"type":"string"},"description":"Only this category","example":"Connectivity"},{"name":"isActive","in":"query","required":false,"schema":{"type":"boolean"},"description":"true for only macros that can run, false for only switched-off ones"},{"name":"isShared","in":"query","required":false,"schema":{"type":"boolean"},"description":"true for only shared macros, false for only private ones"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25},"description":"Items per page, at most 500","example":25},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text to find in the name","example":"restart"},{"name":"sort","in":"query","required":false,"schema":{"type":"string"},"description":"Not used yet: most used first, then by name"},{"name":"teamId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"A department: only its items plus the shared ones (those without a department)","example":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"actions":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"}}},"description":"The steps, in order, each { type, value }: set_status (a status), set_priority (a priority), assign_to (a user ID), add_tag / remove_tag (a tag), add_note (an internal note's text), send_reply (a reply's text, sent on the ticket's channel) and set_type (a ticket type)"},"isShared":{"type":"boolean","description":"Whether colleagues may use it too"},"createdBy":{"type":"string"},"usageCount":{"type":"integer","description":"How often it was run"},"isActive":{"type":"boolean"},"category":{"type":"string"},"shortcutKey":{"type":"string"},"teamId":{"type":"string","description":"Its department, or null when every department shares it"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer","description":"This page's number, from 1"},"limit":{"type":"integer","description":"Most items in a page (at most 500)"},"total":{"type":"integer","description":"How many items match in all"},"totalPages":{"type":"integer","description":"How many pages there are"},"hasNext":{"type":"boolean","description":"Whether another page follows"},"hasPrev":{"type":"boolean","description":"Whether a page comes before"}}}}},"example":{"success":true,"data":[{"id":"b9a0b1c2-d3e4-4f5a-8c7d-8e9f0a1b2c3d","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","name":"Line restarted, waiting on customer","description":"Tell the customer we restarted the line and wait for them.","actions":[{"type":"set_status","value":"waiting_on_customer"},{"type":"add_tag","value":"line-restart"},{"type":"send_reply","value":"I have restarted your line; please check now."}],"isShared":true,"createdBy":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","usageCount":12,"isActive":true,"category":"Connectivity","shortcutKey":"ctrl+shift+r","teamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","deletedAt":null,"createdAt":"2026-08-20T08:00:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}],"pagination":{"page":1,"limit":25,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false}}}}},"400":{"description":"teamId is not a department ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.macros.read"]}],"x-required-scopes":["crm.macros.read"]},"post":{"tags":["Help desk setup"],"summary":"Create a macro","description":"Saves a macro: a named list of steps an agent can run on a ticket in one click (reply, set status or priority, assign, tag, add a note). Answers with status 201. You are recorded as its author; it is private to you unless isShared is true.\n\n**Permission:** `crm.macros.create` (Create macros).","operationId":"createMacro","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"actions":{"type":"array","items":{"type":"object"},"description":"The steps, in order, each { type, value } (a JSON text also works): set_status (a status), set_priority (a priority), assign_to (a user ID), add_tag / remove_tag (a tag), add_note (an internal note's text), send_reply (a reply's text, sent on the ticket's channel) and set_type (a ticket type)","examples":[[{"type":"set_status","value":"waiting_on_customer"},{"type":"add_tag","value":"line-restart"},{"type":"send_reply","value":"I have restarted your line; please check now."}]]},"category":{"type":"string","description":"A group to file it under","examples":["Connectivity"]},"description":{"type":"string","description":"What it is for","examples":["Tell the customer we restarted the line and wait for them."]},"isActive":{"type":"boolean","default":true,"description":"Whether it can be run","examples":[true]},"isShared":{"type":"boolean","default":false,"description":"Whether colleagues may use it too","examples":[true]},"name":{"type":"string","description":"Its name in the picker","examples":["Line restarted, waiting on customer"]},"shortcutKey":{"type":"string","description":"Keyboard shortcut, up to 32 characters","examples":["ctrl+shift+r"]},"teamId":{"type":"string","format":"uuid","description":"Home department; empty or \"null\" shares it with every department. Must be a live department of your organisation","examples":["2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"]}},"required":["actions","name"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"actions":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"}}},"description":"The steps, in order, each { type, value }: set_status (a status), set_priority (a priority), assign_to (a user ID), add_tag / remove_tag (a tag), add_note (an internal note's text), send_reply (a reply's text, sent on the ticket's channel) and set_type (a ticket type)"},"isShared":{"type":"boolean","description":"Whether colleagues may use it too"},"createdBy":{"type":"string"},"usageCount":{"type":"integer","description":"How often it was run"},"isActive":{"type":"boolean"},"category":{"type":"string"},"shortcutKey":{"type":"string"},"teamId":{"type":"string","description":"Its department, or null when every department shares it"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Macro \"Line restarted, waiting on customer\" created successfully","data":{"id":"b9a0b1c2-d3e4-4f5a-8c7d-8e9f0a1b2c3d","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","name":"Line restarted, waiting on customer","description":"Tell the customer we restarted the line and wait for them.","actions":[{"type":"set_status","value":"waiting_on_customer"},{"type":"add_tag","value":"line-restart"},{"type":"send_reply","value":"I have restarted your line; please check now."}],"isShared":true,"createdBy":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","usageCount":0,"isActive":true,"category":"Connectivity","shortcutKey":"ctrl+shift+r","teamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","deletedAt":null,"createdAt":"2026-08-20T08:00:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}}}}},"400":{"description":"actions is not valid JSON, or teamId is not a live department of your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"A field has the wrong shape (e.g. an empty name or a shortcut key over 32 characters)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.macros.create"]}],"x-required-scopes":["crm.macros.create"]}},"/canned-responses":{"get":{"tags":["Help desk setup"],"summary":"List canned responses","description":"Lists canned responses of the departments you may see, most used first, a page at a time. Known problem: the sign-in fills the createdBy filter with you, so only the canned responses you wrote are listed unless you send createdBy empty (createdBy=), and that filter only narrows the page already fetched (pagination still counts every match).\n\n**Permission:** `crm.cannedResponses.read` (View canned responses).","operationId":"getCannedResponses","parameters":[{"name":"category","in":"query","required":false,"schema":{"type":"string"},"description":"Only this category","example":"Connectivity"},{"name":"isActive","in":"query","required":false,"schema":{"type":"boolean"},"description":"true for only those offered to agents, false for only switched-off ones"},{"name":"isShared","in":"query","required":false,"schema":{"type":"boolean"},"description":"true for only shared ones, false for only private ones"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25},"description":"Items per page, at most 500","example":25},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text to find in the title or text","example":"router"},{"name":"sort","in":"query","required":false,"schema":{"type":"string"},"description":"Not used yet: most used first, then by title"},{"name":"teamId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"A department: only its items plus the shared ones (those without a department)","example":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"title":{"type":"string"},"content":{"type":"string"},"category":{"type":"string"},"shortCode":{"type":"string","description":"What an agent types to insert it, e.g. /restart"},"isShared":{"type":"boolean","description":"Whether colleagues may use it too"},"createdBy":{"type":"string"},"usageCount":{"type":"integer","description":"How often it was used"},"tags":{"type":"array","items":{"type":"string"}},"attachments":{"type":"array"},"isActive":{"type":"boolean"},"teamId":{"type":"string","description":"Its department, or null when every department shares it"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer","description":"This page's number, from 1"},"limit":{"type":"integer","description":"Most items in a page (at most 500)"},"total":{"type":"integer","description":"How many items match in all"},"totalPages":{"type":"integer","description":"How many pages there are"},"hasNext":{"type":"boolean","description":"Whether another page follows"},"hasPrev":{"type":"boolean","description":"Whether a page comes before"}}}}},"example":{"success":true,"data":[{"id":"a8f9a0b1-c2d3-4e4f-9b6c-7d8e9f0a1b2c","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","title":"Restart the router","content":"Please unplug your router for 30 seconds, plug it back in and tell us if the light turns green.","category":"Connectivity","shortCode":"/restart","isShared":true,"createdBy":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","usageCount":57,"tags":["router"],"attachments":[],"isActive":true,"teamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","deletedAt":null,"createdAt":"2026-08-15T08:00:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}],"pagination":{"page":1,"limit":25,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false}}}}},"400":{"description":"teamId is not a department ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.cannedResponses.read"]}],"x-required-scopes":["crm.cannedResponses.read"]},"post":{"tags":["Help desk setup"],"summary":"Create a canned response","description":"Saves a ready-made reply agents can insert into tickets, and answers with status 201. You are recorded as its author. It is private to you unless isShared is true.\n\n**Permission:** `crm.cannedResponses.create` (Create canned responses).","operationId":"createCannedResponse","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"attachments":{"type":"array","items":{"type":"object"},"description":"Files that go with it, each { name, url, type?, size? } (a JSON text also works)","examples":[[]]},"category":{"type":"string","description":"A group to file it under","examples":["Connectivity"]},"content":{"type":"string","description":"The reply's text","examples":["Please unplug your router for 30 seconds, plug it back in and tell us if the light turns green."]},"isActive":{"type":"boolean","default":true,"description":"Whether it is offered to agents","examples":[true]},"isShared":{"type":"boolean","default":false,"description":"Whether colleagues may use it too","examples":[true]},"shortCode":{"type":"string","description":"What an agent types to insert it","examples":["/restart"]},"tags":{"type":"array","items":{"type":"string"},"description":"Labels (a JSON text also works)","examples":[["router"]]},"teamId":{"type":"string","format":"uuid","description":"Home department; empty or \"null\" shares it with every department. Must be a live department of your organisation","examples":["2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"]},"title":{"type":"string","description":"Its name in the picker","examples":["Restart the router"]}},"required":["content","title"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"title":{"type":"string"},"content":{"type":"string"},"category":{"type":"string"},"shortCode":{"type":"string","description":"What an agent types to insert it, e.g. /restart"},"isShared":{"type":"boolean","description":"Whether colleagues may use it too"},"createdBy":{"type":"string"},"usageCount":{"type":"integer","description":"How often it was used"},"tags":{"type":"array","items":{"type":"string"}},"attachments":{"type":"array"},"isActive":{"type":"boolean"},"teamId":{"type":"string","description":"Its department, or null when every department shares it"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Canned response \"Restart the router\" created successfully","data":{"id":"a8f9a0b1-c2d3-4e4f-9b6c-7d8e9f0a1b2c","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","title":"Restart the router","content":"Please unplug your router for 30 seconds, plug it back in and tell us if the light turns green.","category":"Connectivity","shortCode":"/restart","isShared":true,"createdBy":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","usageCount":0,"tags":["router"],"attachments":[],"isActive":true,"teamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","deletedAt":null,"createdAt":"2026-08-15T08:00:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}}}}},"400":{"description":"tags or attachments is not valid JSON, or teamId is not a live department of your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"A field has the wrong shape (e.g. an empty title)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.cannedResponses.create"]}],"x-required-scopes":["crm.cannedResponses.create"]}},"/helpdesk-customers":{"get":{"tags":["Desk"],"summary":"List help desk customers","description":"Lists customers (CRM accounts) for the help desk, each with how many tickets it has, how many are open and when the newest was opened. Ticket figures count only the tickets you may see.\n\n**Permission:** `crm.helpdesk.read` (View helpdesk).","operationId":"getHelpdeskCustomers","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"description":"Customers per page, at most 100","example":25},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1},"description":"Page number, from 1","example":1},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Words in the account's name or number; a phone number or email also finds the account through its people","example":"Wanjiru"},{"name":"sort","in":"query","required":false,"schema":{"type":"string","enum":["-lastTicketAt","lastTicketAt","-createdAt","createdAt","-updatedAt","updatedAt","name","-name","currentBalance","-currentBalance"],"default":"-lastTicketAt"},"description":"Field to sort by, with a leading - for newest or largest first. lastTicketAt actually sorts by when the account last changed","example":"-updatedAt"},{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"Only accounts with this status, e.g. active","example":"active"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"customers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"accountNumber":{"type":"string"},"status":{"type":"string","description":"The account's status, e.g. active"},"tags":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"totalTickets":{"type":"integer","description":"Tickets the account has, in all"},"openTickets":{"type":"integer","description":"Its tickets in any status but resolved or closed"},"lastTicketAt":{"type":"string","format":"date-time","description":"When its newest ticket was opened; null when it has none"}}}},"pagination":{"type":"object","properties":{"total":{"type":"integer","description":"How many accounts match, in all"},"page":{"type":"integer"},"totalPages":{"type":"integer"},"limit":{"type":"integer"}}}}},"example":{"success":true,"customers":[{"id":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","name":"Wanjiru Household","accountNumber":"ACC-000318","status":"active","tags":["home-fibre"],"createdAt":"2026-03-02T10:00:00.000Z","updatedAt":"2026-10-20T09:05:00.000Z","totalTickets":3,"openTickets":1,"lastTicketAt":"2026-10-20T08:15:00.000Z"}],"pagination":{"total":1,"page":1,"totalPages":1,"limit":25}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.helpdesk.read"]}],"x-required-scopes":["crm.helpdesk.read"]}},"/helpdesk-customers/{customerId}":{"get":{"tags":["Desk"],"summary":"Get a help desk customer","description":"Reads one customer for the help desk: the CRM account with its primary contact, its linked people, its subscriptions (up to 100), ticket figures and its 10 newest tickets. Ticket figures and tickets count only the tickets you may see.\n\n**Permission:** `crm.helpdesk.read` (View helpdesk).","operationId":"getHelpdeskCustomer","parameters":[{"name":"customerId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The customer's CRM account ID","example":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"customer":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"accountNumber":{"type":"string"},"status":{"type":"string"},"description":{},"addressId":{},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string"},"currentBalance":{"type":"string"},"originLeadId":{},"walletId":{},"billingContactId":{"type":"string"},"serviceContactId":{"type":"string"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{}},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{},"cacheDisplayName":{},"cachePrimaryPhone":{},"cachePrimaryEmail":{},"cachePartyRevision":{},"cacheUpdatedAt":{},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"primaryContact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{}},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{},"cacheDisplayName":{},"cachePrimaryPhone":{},"cachePrimaryEmail":{},"cachePartyRevision":{},"cacheUpdatedAt":{},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"description":"The account's primary person, in full"},"contacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The ID of the link between the account and a person (not the person's own ID)"},"status":{"type":"string","description":"Whether that link is active or inactive"}}}},"subscriptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"},"startDate":{"type":"string","format":"date-time"},"billingCycle":{"type":"string","description":"How often it is billed, e.g. monthly"},"nextBillingAt":{"type":"string","format":"date-time"}}}},"ticketStats":{"type":"object","properties":{"totalTickets":{"type":"integer"},"openTickets":{"type":"integer","description":"Tickets in any status but resolved or closed"},"resolvedTickets":{"type":"integer","description":"Tickets resolved or closed"},"lastTicketAt":{"type":"string","format":"date-time"},"avgSatisfaction":{"type":"string","description":"Average satisfaction rating (1 to 5) as a decimal in text; null when none was rated"}}},"recentTickets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"ticketNumber":{"type":"string"},"subject":{"type":"string"},"description":{"type":"string"},"channel":{"type":"string"},"status":{"type":"string","description":"open, pending, in_progress, waiting_on_customer, waiting_on_third_party, resolved or closed"},"priority":{"type":"string","description":"low, medium, high or urgent"},"type":{"type":"string"},"category":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"subscriptionId":{"type":"string"},"leadId":{},"dealId":{},"requesterEmail":{"type":"string"},"requesterName":{"type":"string"},"ownerId":{"type":"string"},"assignedTo":{"type":"string"},"teamId":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"slaPolicyId":{"type":"string"},"slaStatus":{"type":"object","properties":{"firstResponseDue":{"type":"string","format":"date-time"},"resolutionDue":{"type":"string","format":"date-time"},"firstResponseAt":{"type":"string","format":"date-time"}}},"customFields":{"type":"object","properties":{}},"mergedInto":{},"relatedTickets":{"type":"array"},"satisfaction":{"type":"object","properties":{}},"firstResponseTime":{"type":"integer"},"resolutionTime":{},"reopenCount":{"type":"integer"},"holdUntil":{},"holdMode":{},"holdStartedAt":{},"holdBy":{},"holdPreviousStatus":{},"backFromHoldAt":{},"backFromHoldReason":{},"version":{"type":"integer"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"description":"The 10 newest tickets, as stored"}},"description":"The account's own fields, plus the parts below"}}},"example":{"success":true,"customer":{"id":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","name":"Wanjiru Household","accountNumber":"ACC-000318","status":"active","description":null,"addressId":null,"onboardingDate":"2026-03-02T00:00:00.000Z","terminationDate":null,"creditLimit":"0.00","currentBalance":"2500.00","originLeadId":null,"walletId":null,"billingContactId":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","serviceContactId":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","ownerId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","notificationPreferences":{},"tags":["home-fibre"],"customFields":{},"externalIds":[],"partyId":null,"cacheDisplayName":null,"cachePrimaryPhone":null,"cachePrimaryEmail":null,"cachePartyRevision":null,"cacheUpdatedAt":null,"cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-03-02T10:00:00.000Z","updatedAt":"2026-10-20T09:05:00.000Z","primaryContact":{"id":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","firstName":"Amina","lastName":"Wanjiru","email":"amina.wanjiru@example.com","phone":"+254700000001","addressId":null,"status":"active","preferences":{},"ownerId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","notes":null,"customFields":{},"externalIds":[],"partyId":null,"cacheDisplayName":null,"cachePrimaryPhone":null,"cachePrimaryEmail":null,"cachePartyRevision":null,"cacheUpdatedAt":null,"cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-03-02T10:00:00.000Z","updatedAt":"2026-03-02T10:00:00.000Z"},"contacts":[{"id":"a2f3a4b5-c6d7-4e8f-9b0c-1d2e3f4a5b6c","status":"active"}],"subscriptions":[{"id":"b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d","name":"Home Fibre 50 Mbps","status":"active","startDate":"2026-03-05T00:00:00.000Z","billingCycle":"monthly","nextBillingAt":"2026-11-05T00:00:00.000Z"}],"ticketStats":{"totalTickets":3,"openTickets":1,"resolvedTickets":2,"lastTicketAt":"2026-10-20T08:15:00.000Z","avgSatisfaction":"4.5000000000000000"},"recentTickets":[{"id":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","ticketNumber":"TKT-001042","subject":"Internet down since this morning","description":"The router shows a red light and nothing loads since about 7am.","channel":"whatsapp","status":"open","priority":"high","type":"incident","category":"Connectivity","accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","contactId":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","subscriptionId":"b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d","leadId":null,"dealId":null,"requesterEmail":"amina.wanjiru@example.com","requesterName":"Amina Wanjiru","ownerId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","assignedTo":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","teamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","tags":["outage"],"slaPolicyId":"e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a","slaStatus":{"firstResponseDue":"2026-10-20T10:15:00.000Z","resolutionDue":"2026-10-21T08:15:00.000Z","firstResponseAt":"2026-10-20T08:42:00.000Z"},"customFields":{},"mergedInto":null,"relatedTickets":[],"satisfaction":{},"firstResponseTime":27,"resolutionTime":null,"reopenCount":0,"holdUntil":null,"holdMode":null,"holdStartedAt":null,"holdBy":null,"holdPreviousStatus":null,"backFromHoldAt":null,"backFromHoldReason":null,"version":2,"deletedAt":null,"createdAt":"2026-10-20T08:15:00.000Z","updatedAt":"2026-10-20T09:05:00.000Z"}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No account with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.helpdesk.read"]}],"x-required-scopes":["crm.helpdesk.read"]}},"/business-hours":{"get":{"tags":["Help desk setup"],"summary":"List business hours","description":"Lists your organisation's business hours by name, a page at a time.\n\n**Permission:** `crm.businessHours.read` (View business hours).","operationId":"getBusinessHours","parameters":[{"name":"isActive","in":"query","required":false,"schema":{"type":"boolean"},"description":"true for only those in use, false for only unused ones"},{"name":"isDefault","in":"query","required":false,"schema":{"type":"boolean"},"description":"true for only the default hours"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25},"description":"Items per page, at most 500","example":25},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Text to find in the name","example":"Nairobi"},{"name":"sort","in":"query","required":false,"schema":{"type":"string"},"description":"Not used yet: by name"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"timezone":{"type":"string","description":"IANA time zone the hours are in, e.g. Africa/Nairobi"},"isDefault":{"type":"boolean","description":"The hours SLA clocks use when a policy names none; only one set is the default"},"isActive":{"type":"boolean"},"schedule":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string"},"isWorkingDay":{"type":"boolean"},"shifts":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}}}}}},"description":"One entry per weekday: { day (monday to sunday), isWorkingDay, shifts: [{ start, end }] } with times as HH:MM"},"holidays":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"date":{"type":"string"},"isRecurring":{"type":"boolean"}}},"description":"Days off: { name, date (YYYY-MM-DD), isRecurring (every year) }"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer","description":"This page's number, from 1"},"limit":{"type":"integer","description":"Most items in a page (at most 500)"},"total":{"type":"integer","description":"How many items match in all"},"totalPages":{"type":"integer","description":"How many pages there are"},"hasNext":{"type":"boolean","description":"Whether another page follows"},"hasPrev":{"type":"boolean","description":"Whether a page comes before"}}}}},"example":{"success":true,"data":[{"id":"c0b1c2d3-e4f5-4a6b-9d8e-9f0a1b2c3d4e","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","name":"Nairobi office hours","description":"Weekdays 8 to 5, Saturday mornings","timezone":"Africa/Nairobi","isDefault":true,"isActive":true,"schedule":[{"day":"monday","isWorkingDay":true,"shifts":[{"start":"08:00","end":"17:00"}]},{"day":"saturday","isWorkingDay":true,"shifts":[{"start":"09:00","end":"13:00"}]},{"day":"sunday","isWorkingDay":false,"shifts":[]}],"holidays":[{"name":"Jamhuri Day","date":"2026-12-12","isRecurring":true}],"deletedAt":null,"createdAt":"2026-06-01T08:00:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}],"pagination":{"page":1,"limit":25,"total":1,"totalPages":1,"hasNext":false,"hasPrev":false}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.businessHours.read"]}],"x-required-scopes":["crm.businessHours.read"]},"post":{"tags":["Help desk setup"],"summary":"Create business hours","description":"Adds a set of business hours (working days, shifts, holidays) that SLA clocks count in, and answers with status 201. Making it the default takes the default mark off the others.\n\n**Permission:** `crm.businessHours.create` (Create business hours).","operationId":"createBusinessHours","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"description":{"type":"string","description":"What the hours are for","examples":["Weekdays 8 to 5, Saturday mornings"]},"holidays":{"type":"array","items":{"type":"object"},"description":"Days off, each { name, date: YYYY-MM-DD, isRecurring } (a JSON text also works)","examples":[[{"name":"Jamhuri Day","date":"2026-12-12","isRecurring":true}]]},"isActive":{"type":"boolean","default":true,"description":"Whether they are in use","examples":[true]},"isDefault":{"type":"boolean","default":false,"description":"Make these the default hours","examples":[true]},"name":{"type":"string","description":"The name, e.g. the office it is for","examples":["Nairobi office hours"]},"schedule":{"type":"array","items":{"type":"object"},"description":"One entry per weekday, each { day: monday … sunday, isWorkingDay, shifts: [{ start: \"08:00\", end: \"17:00\" }] } (a JSON text also works)","examples":[[{"day":"monday","isWorkingDay":true,"shifts":[{"start":"08:00","end":"17:00"}]},{"day":"saturday","isWorkingDay":true,"shifts":[{"start":"09:00","end":"13:00"}]},{"day":"sunday","isWorkingDay":false,"shifts":[]}]]},"timezone":{"type":"string","description":"IANA time zone the times are in","examples":["Africa/Nairobi"]}},"required":["name","schedule","timezone"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"timezone":{"type":"string","description":"IANA time zone the hours are in, e.g. Africa/Nairobi"},"isDefault":{"type":"boolean","description":"The hours SLA clocks use when a policy names none; only one set is the default"},"isActive":{"type":"boolean"},"schedule":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string"},"isWorkingDay":{"type":"boolean"},"shifts":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}}}}}},"description":"One entry per weekday: { day (monday to sunday), isWorkingDay, shifts: [{ start, end }] } with times as HH:MM"},"holidays":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"date":{"type":"string"},"isRecurring":{"type":"boolean"}}},"description":"Days off: { name, date (YYYY-MM-DD), isRecurring (every year) }"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Business hours \"Nairobi office hours\" created successfully","data":{"id":"c0b1c2d3-e4f5-4a6b-9d8e-9f0a1b2c3d4e","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","name":"Nairobi office hours","description":"Weekdays 8 to 5, Saturday mornings","timezone":"Africa/Nairobi","isDefault":true,"isActive":true,"schedule":[{"day":"monday","isWorkingDay":true,"shifts":[{"start":"08:00","end":"17:00"}]},{"day":"saturday","isWorkingDay":true,"shifts":[{"start":"09:00","end":"13:00"}]},{"day":"sunday","isWorkingDay":false,"shifts":[]}],"holidays":[{"name":"Jamhuri Day","date":"2026-12-12","isRecurring":true}],"deletedAt":null,"createdAt":"2026-06-01T08:00:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}}}}},"400":{"description":"schedule or holidays is not valid JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"A field has the wrong shape (e.g. an empty name or a time zone over 64 characters)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.businessHours.create"]}],"x-required-scopes":["crm.businessHours.create"]}},"/helpdesk/dashboard":{"get":{"tags":["Dashboards"],"summary":"Get the help desk dashboard","description":"The help desk dashboard's figures for the tickets you may see, opened in a date range: counts by status, service-level (SLA) compliance, average first-reply and resolution times, tickets by channel and priority, the ten newest tickets, the unassigned count and the five people who resolved the most.\n\n**Permission:** `crm.helpdesk.read` (View helpdesk).","operationId":"getHelpdeskDashboard","parameters":[{"name":"dateRange","in":"query","required":false,"schema":{"type":"object"},"description":"The tickets' opening dates to cover, as { \"from\", \"to\" } (an object or a JSON string). Plain days (YYYY-MM-DD) are whole business days in East Africa Time, the last one included; exact moments are kept as sent. Leave out for all time","example":{"from":"2026-10-01","to":"2026-10-20"}},{"name":"teamId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this department's tickets; left out, every ticket you may see","example":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"ticketStats":{"type":"object","properties":{"total":{"type":"integer"},"open":{"type":"integer"},"pending":{"type":"integer"},"in_progress":{"type":"integer"},"resolved":{"type":"integer"},"closed":{"type":"integer"}}},"slaCompliance":{"type":"object","properties":{"measured":{"type":"boolean"},"firstResponse":{"type":"integer","description":"Percent of first-reply clocks met, of those met or missed; null when none has stopped or lapsed yet"},"resolution":{"type":"integer","description":"The same for resolution clocks"},"counts":{"type":"object","properties":{"firstResponse":{"type":"object","properties":{"total":{"type":"integer"},"met":{"type":"integer"},"breached":{"type":"integer"},"pending":{"type":"integer","description":"Clocks still running with time left (not counted against anyone)"}}},"resolution":{"type":"object","properties":{"total":{"type":"integer"},"met":{"type":"integer"},"breached":{"type":"integer"},"pending":{"type":"integer"}}}}}}},"averageTimes":{"type":"object","properties":{"measured":{"type":"boolean"},"firstResponseTime":{"type":"integer","description":"Average minutes to the first reply; null when nothing was measured"},"resolutionTime":{"type":"integer","description":"Average minutes to resolution, over resolved and closed tickets"},"samples":{"type":"object","properties":{"firstResponse":{"type":"integer"},"resolution":{"type":"integer"}},"description":"How many tickets each average is over"}}},"ticketsByChannel":{"type":"object","properties":{"whatsapp":{"type":"integer"},"email":{"type":"integer"},"phone":{"type":"integer"}},"description":"Ticket count per channel"},"ticketsByPriority":{"type":"object","properties":{"medium":{"type":"integer"},"high":{"type":"integer"},"low":{"type":"integer"},"urgent":{"type":"integer"}},"description":"Ticket count per priority"},"recentTickets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ticket_number":{"type":"string"},"subject":{"type":"string"},"status":{"type":"string"},"priority":{"type":"string"},"channel":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"requester_name":{"type":"string"},"requester_email":{"type":"string"},"account_id":{"type":"string"},"account_name":{"type":"string"},"contact_id":{"type":"string"},"contact_first_name":{"type":"string"},"contact_last_name":{"type":"string"},"agent_id":{"type":"string"},"agent_first_name":{"type":"string"},"agent_last_name":{"type":"string"},"agent_email":{"type":"string"}},"description":"The ten newest tickets, with account, contact and assignee (agent_*)"}},"unassignedCount":{"type":"integer"},"agentPerformance":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"agent_name":{"type":"string"},"agent_email":{"type":"string"},"resolved_count":{"type":"integer"}},"description":"The five people with the most resolved or closed tickets"}}}}}},"example":{"success":true,"data":{"ticketStats":{"total":42,"open":9,"pending":4,"in_progress":6,"resolved":15,"closed":8},"slaCompliance":{"measured":true,"firstResponse":92,"resolution":85,"counts":{"firstResponse":{"total":40,"met":35,"breached":3,"pending":2},"resolution":{"total":40,"met":22,"breached":4,"pending":14}}},"averageTimes":{"measured":true,"firstResponseTime":24,"resolutionTime":610,"samples":{"firstResponse":38,"resolution":23}},"ticketsByChannel":{"whatsapp":25,"email":12,"phone":5},"ticketsByPriority":{"medium":20,"high":15,"low":5,"urgent":2},"recentTickets":[{"id":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","ticket_number":"TKT-001042","subject":"Internet down since this morning","status":"open","priority":"high","channel":"whatsapp","created_at":"2026-10-20T08:15:00.000Z","requester_name":"Amina Wanjiru","requester_email":"amina.wanjiru@example.com","account_id":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","account_name":"Wanjiru Household","contact_id":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","contact_first_name":"Amina","contact_last_name":"Wanjiru","agent_id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","agent_first_name":"Brian","agent_last_name":"Otieno","agent_email":"brian.otieno@example.com"}],"unassignedCount":3,"agentPerformance":[{"id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","agent_name":"Brian Otieno","agent_email":"brian.otieno@example.com","resolved_count":12}]}}}}},"400":{"description":"teamId is not a department ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.helpdesk.read"]}],"x-required-scopes":["crm.helpdesk.read"]}},"/helpdesk/reports":{"get":{"tags":["Dashboards"],"summary":"Get a help desk report","description":"One help desk report over the tickets you may see, opened in a date range. data's rows depend on reportType: ticket_volume (tickets per day, week or month), agent_performance (per person: assigned, resolved, average resolution minutes and rating), channel_breakdown (per channel), sla_compliance (missed first-reply and resolution clocks per priority), customer_satisfaction (one summary: rating spread, average, response rate and trend), csat_trend (average rating per period) or resolution_times (average, fastest and slowest resolution minutes per period).\n\n**Permission:** `crm.helpdesk.read` (View helpdesk).","operationId":"getHelpdeskReports","parameters":[{"name":"dateRange","in":"query","required":false,"schema":{"type":"object"},"description":"The tickets' opening dates to cover, as { \"from\", \"to\" } (an object or a JSON string). Plain days (YYYY-MM-DD) are whole business days in East Africa Time, the last one included; exact moments are kept as sent. Leave out for all time","example":{"from":"2026-10-01","to":"2026-10-20"}},{"name":"groupBy","in":"query","required":false,"schema":{"type":"string","enum":["day","week","month"],"default":"day"},"description":"Period for ticket_volume, csat_trend, resolution_times and the satisfaction trend","example":"week"},{"name":"reportType","in":"query","required":true,"schema":{"type":"string","enum":["ticket_volume","agent_performance","channel_breakdown","sla_compliance","customer_satisfaction","resolution_times","csat_trend"]},"description":"Which report to build","example":"ticket_volume"},{"name":"teamId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this department's tickets; left out, every ticket you may see","example":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"reportType":{"type":"string"},"groupBy":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"bucket":{"type":"string","description":"Start of the day, week or month (YYYY-MM-DD)"},"count":{"type":"integer"}},"description":"The report's rows; the example is a ticket_volume report"}}}},"example":{"success":true,"reportType":"ticket_volume","groupBy":"week","data":[{"bucket":"2026-10-05","count":18},{"bucket":"2026-10-12","count":24}]}}}},"400":{"description":"reportType or groupBy is not one of the listed values, or teamId is not a department ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.helpdesk.read"]}],"x-required-scopes":["crm.helpdesk.read"]}},"/helpdesk/cs-dashboard":{"get":{"tags":["Dashboards"],"summary":"Get the customer service dashboard","description":"Everything the Customer service dashboard shows, for the tickets you may see: live tiles (open, on hold, overdue, due today, unassigned) and the tickets opened and resolved in the range; tickets received and resolved per day, week or month with the backlog at the end of each; per agent the tickets handled and resolved, median first reply, handling and resolution minutes against the SLA target, and average rating; and the customer ratings given in the range. Days are whole days in the organisation's timezone (its default business-hours calendar's). Times are minutes as the SLA clocks measure them (working minutes in the policy's calendar).\n\n**Permission:** `crm.helpdesk.read` (View helpdesk).","operationId":"getCustomerServiceDashboard","parameters":[{"name":"agentId","in":"query","required":false,"schema":{"type":"string"},"description":"Only this person's tickets (their user id), or unassigned for tickets nobody has","example":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"},{"name":"channel","in":"query","required":false,"schema":{"type":"string"},"description":"Only tickets from this channel (email, whatsapp, live_chat…)","example":"whatsapp"},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date"},"description":"First day of a custom range (YYYY-MM-DD), included","example":"2026-10-01"},{"name":"group","in":"query","required":false,"schema":{"type":"string","enum":["day","week","month"],"default":"day"},"description":"The series' and rating trend's period","example":"week"},{"name":"range","in":"query","required":false,"schema":{"type":"string","enum":["today","yesterday","last_7_days","last_30_days","this_month","last_month","custom"]},"description":"A quick range in the organisation's time; custom (or left out with from and to) uses from and to. Neither: the last 30 days","example":"last_7_days"},{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"Only tickets now in these statuses, comma separated (open, pending, in_progress, waiting_on_customer, waiting_on_third_party, resolved, closed)","example":"open,in_progress"},{"name":"teamId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this department's tickets; left out, every ticket you may see","example":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date"},"description":"Last day of a custom range (YYYY-MM-DD), included in full","example":"2026-10-30"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"timezone":{"type":"string","description":"The organisation's timezone the days are counted in (its default business-hours calendar's)"},"range":{"type":"object","properties":{"preset":{"type":"string","description":"The quick range asked for; null for a custom range"},"from":{"type":"string"},"to":{"type":"string"},"startsAt":{"type":"string","format":"date-time","description":"00:00 on the first day, as an exact moment"},"endsBefore":{"type":"string","format":"date-time","description":"00:00 the day after the last day (not included)"}}},"group":{"type":"string"},"slaTargets":{"type":"object","properties":{"firstResponseMinutes":{"type":"integer"},"resolutionMinutes":{"type":"integer"},"policyId":{"type":"string"},"policyName":{"type":"string"}},"description":"The SLA policy targets the agent figures are marked against (the department's policy when one is picked, else the general one); nulls when no policy applies"},"tiles":{"type":"object","properties":{"open":{"type":"integer","description":"Tickets open right now (every status but resolved and closed)"},"onHold":{"type":"integer","description":"Open tickets waiting on the customer"},"overdue":{"type":"integer","description":"Open tickets with an SLA clock running past its due time"},"dueToday":{"type":"integer","description":"Open tickets, not overdue, with a clock running out before midnight today"},"unassigned":{"type":"integer","description":"Open tickets nobody has"},"createdInRange":{"type":"integer","description":"Tickets opened in the range"},"closedInRange":{"type":"integer","description":"Tickets resolved or closed in the range"}}},"series":{"type":"array","items":{"type":"object","properties":{"bucket":{"type":"string","description":"First day of the day, week (Monday) or month"},"received":{"type":"integer"},"resolved":{"type":"integer"},"backlog":{"type":"integer","description":"Tickets open at the end of the period"}}}},"agents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"handled":{"type":"integer","description":"The person's tickets opened or resolved in the range"},"resolved":{"type":"integer"},"resolvedRate":{"type":"number"},"medianFirstResponseMinutes":{"type":"integer","description":"Median minutes to the first public reply; null when none was measured"},"medianHandlingMinutes":{"type":"integer","description":"Median minutes from the first reply to resolution, over tickets resolved in the range"},"medianResolutionMinutes":{"type":"integer","description":"Median minutes from opening to resolution, over tickets resolved in the range"},"averageRating":{"type":"number"},"ratings":{"type":"integer"},"firstResponseBreaches":{"type":"integer"},"resolutionBreaches":{"type":"integer"},"firstResponseOverSla":{"type":"boolean","description":"The median first reply is slower than the SLA target"},"resolutionOverSla":{"type":"boolean","description":"The median resolution is slower than the SLA target"}}}},"totals":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"handled":{"type":"integer"},"resolved":{"type":"integer"},"resolvedRate":{"type":"number"},"medianFirstResponseMinutes":{"type":"integer"},"medianHandlingMinutes":{"type":"integer"},"medianResolutionMinutes":{"type":"integer"},"averageRating":{"type":"number"},"ratings":{"type":"integer"},"firstResponseBreaches":{"type":"integer"},"resolutionBreaches":{"type":"integer"},"firstResponseOverSla":{"type":"boolean"},"resolutionOverSla":{"type":"boolean"},"agents":{"type":"integer"}},"description":"The same figures over every agent's tickets together"},"feedback":{"type":"object","properties":{"ratings":{"type":"integer"},"averageRating":{"type":"number"},"distribution":{"type":"array","items":{"type":"object","properties":{"rating":{"type":"integer"},"count":{"type":"integer"}}}},"positive":{"type":"integer"},"neutral":{"type":"integer"},"negative":{"type":"integer"},"responseRate":{"type":"number","description":"Ratings given in the range per 100 tickets resolved in it"},"trend":{"type":"array","items":{"type":"object","properties":{"bucket":{"type":"string"},"averageRating":{"type":"number"},"ratings":{"type":"integer"}}}},"latestComments":{"type":"array","items":{"type":"object","properties":{"ticketId":{"type":"string"},"ticketNumber":{"type":"string"},"rating":{"type":"integer"},"comment":{"type":"string"},"ratedAt":{"type":"string","format":"date-time"},"agentName":{"type":"string"}},"description":"The newest ratings with a comment"}}}},"generatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"timezone":"Africa/Nairobi","range":{"preset":"last_7_days","from":"2026-10-24","to":"2026-10-30","startsAt":"2026-10-23T21:00:00.000Z","endsBefore":"2026-10-30T21:00:00.000Z"},"group":"day","slaTargets":{"firstResponseMinutes":15,"resolutionMinutes":480,"policyId":"e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a","policyName":"Standard"},"tiles":{"open":364,"onHold":58,"overdue":17,"dueToday":42,"unassigned":23,"createdInRange":596,"closedInRange":540},"series":[{"bucket":"2026-10-29","received":88,"resolved":84,"backlog":349},{"bucket":"2026-10-30","received":96,"resolved":81,"backlog":364}],"agents":[{"id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","name":"Brian Otieno","email":"brian.otieno@example.com","handled":312,"resolved":288,"resolvedRate":92.3,"medianFirstResponseMinutes":6,"medianHandlingMinutes":14,"medianResolutionMinutes":220,"averageRating":4.6,"ratings":104,"firstResponseBreaches":4,"resolutionBreaches":9,"firstResponseOverSla":false,"resolutionOverSla":false}],"totals":{"id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","name":"Brian Otieno","email":"brian.otieno@example.com","handled":2402,"resolved":2165,"resolvedRate":90.1,"medianFirstResponseMinutes":9,"medianHandlingMinutes":14,"medianResolutionMinutes":220,"averageRating":4.4,"ratings":412,"firstResponseBreaches":4,"resolutionBreaches":9,"firstResponseOverSla":false,"resolutionOverSla":false,"agents":9},"feedback":{"ratings":412,"averageRating":4.4,"distribution":[{"rating":1,"count":20},{"rating":2,"count":25},{"rating":3,"count":62},{"rating":4,"count":105},{"rating":5,"count":200}],"positive":305,"neutral":62,"negative":45,"responseRate":38.2,"trend":[{"bucket":"2026-10-29","averageRating":4.5,"ratings":61},{"bucket":"2026-10-30","averageRating":4.4,"ratings":58}],"latestComments":[{"ticketId":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","ticketNumber":"TKT-001042","rating":5,"comment":"Sorted within the hour, thank you","ratedAt":"2026-10-30T09:12:00.000Z","agentName":"Brian Otieno"}]},"generatedAt":"2026-10-30T07:42:00.000Z"}}}}},"400":{"description":"range, group, from/to, agentId, teamId, status or channel is malformed, to is before from, or the range is longer than 366 days","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.helpdesk.read"]}],"x-required-scopes":["crm.helpdesk.read"]}},"/helpdesk/csat/settings":{"get":{"tags":["Help desk setup"],"summary":"Get satisfaction survey settings","description":"Reads your organisation's customer satisfaction (CSAT) survey settings. An organisation without any gets the defaults saved and returned (surveys off).\n\n**Permission:** `crm.csat.read` (View CSAT).","operationId":"getCsatSettings","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"settings":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"enabled":{"type":"boolean","description":"Whether closing a ticket emails the customer a survey"},"badRatingThreshold":{"type":"integer","description":"Ratings at or below this (1 to 5) count as bad"},"autoReopen":{"type":"boolean","description":"Reopen the ticket on a bad rating"},"notifyAssignee":{"type":"boolean","description":"Tell the ticket's assignee about a bad rating"},"notifyManager":{"type":"boolean","description":"Tell the department's leaders about a bad rating"},"escalationTeamId":{"type":"string","description":"Department a badly rated ticket moves to, or null"},"csatTag":{"type":"string","description":"Tag put on a badly rated ticket"},"surveySubject":{"type":"string","description":"The survey email's subject; {{ticketNumber}} is filled in"},"surveyBodyTemplate":{"type":"string","description":"The survey email's text; {{requesterName}}, {{ticketNumber}} and {{rateUrl}} are filled in"},"thankYouMessage":{"type":"string"},"surveyExpiryDays":{"type":"integer","description":"Days a survey link stays open"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"settings":{"id":"d1c2d3e4-f5a6-4b7c-8e9f-0a1b2c3d4e5f","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","enabled":true,"badRatingThreshold":2,"autoReopen":true,"notifyAssignee":true,"notifyManager":true,"escalationTeamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","csatTag":"csat-negative","surveySubject":"How did we do? — Ticket {{ticketNumber}}","surveyBodyTemplate":"Hi {{requesterName}},\n\nYour support ticket {{ticketNumber}} has just been closed. Tap an emoji below to tell us how we did.\n\n{{rateUrl}}\n\nThanks!","thankYouMessage":"Thanks for the feedback — we really appreciate it!","surveyExpiryDays":14,"createdAt":"2026-06-01T08:00:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.csat.read"]}],"x-required-scopes":["crm.csat.read"]},"put":{"tags":["Help desk setup"],"summary":"Update satisfaction survey settings","description":"Changes your organisation's customer satisfaction (CSAT) survey settings: whether surveys go out, the email's wording, and what a bad rating does. Only the fields you send change.\n\n**Permission:** `crm.csat.update` (Update CSAT).","operationId":"updateCsatSettings","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"autoReopen":{"type":"boolean","description":"Reopen the ticket on a bad rating"},"badRatingThreshold":{"type":"integer","minimum":1,"maximum":5,"description":"Ratings at or below this count as bad","examples":[2]},"csatTag":{"type":"string","description":"Tag put on a badly rated ticket, up to 64 characters","examples":["csat-negative"]},"enabled":{"type":"boolean","description":"Whether closing a ticket emails the customer a survey","examples":[true]},"escalationTeamId":{"type":"string","format":"uuid","description":"Department a badly rated ticket moves to; empty or \"none\" for none","examples":["2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"]},"notifyAssignee":{"type":"boolean","description":"Tell the ticket's assignee about a bad rating"},"notifyManager":{"type":"boolean","description":"Tell the department's leaders about a bad rating"},"surveyBodyTemplate":{"type":"string","description":"The survey email's text; {{requesterName}}, {{ticketNumber}} and {{rateUrl}} are filled in"},"surveyExpiryDays":{"type":"integer","minimum":1,"maximum":365,"description":"Days a survey link stays open","examples":[14]},"surveySubject":{"type":"string","description":"The survey email's subject, up to 255 characters; {{ticketNumber}} is filled in","examples":["How did we do? — Ticket {{ticketNumber}}"]},"thankYouMessage":{"type":"string","description":"Shown to the customer after they rate","examples":["Thanks for the feedback — we really appreciate it!"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"settings":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"enabled":{"type":"boolean","description":"Whether closing a ticket emails the customer a survey"},"badRatingThreshold":{"type":"integer","description":"Ratings at or below this (1 to 5) count as bad"},"autoReopen":{"type":"boolean","description":"Reopen the ticket on a bad rating"},"notifyAssignee":{"type":"boolean","description":"Tell the ticket's assignee about a bad rating"},"notifyManager":{"type":"boolean","description":"Tell the department's leaders about a bad rating"},"escalationTeamId":{"type":"string","description":"Department a badly rated ticket moves to, or null"},"csatTag":{"type":"string","description":"Tag put on a badly rated ticket"},"surveySubject":{"type":"string","description":"The survey email's subject; {{ticketNumber}} is filled in"},"surveyBodyTemplate":{"type":"string","description":"The survey email's text; {{requesterName}}, {{ticketNumber}} and {{rateUrl}} are filled in"},"thankYouMessage":{"type":"string"},"surveyExpiryDays":{"type":"integer","description":"Days a survey link stays open"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"settings":{"id":"d1c2d3e4-f5a6-4b7c-8e9f-0a1b2c3d4e5f","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","enabled":true,"badRatingThreshold":2,"autoReopen":true,"notifyAssignee":true,"notifyManager":true,"escalationTeamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","csatTag":"csat-negative","surveySubject":"How did we do? — Ticket {{ticketNumber}}","surveyBodyTemplate":"Hi {{requesterName}},\n\nYour support ticket {{ticketNumber}} has just been closed. Tap an emoji below to tell us how we did.\n\n{{rateUrl}}\n\nThanks!","thankYouMessage":"Thanks for the feedback — we really appreciate it!","surveyExpiryDays":14,"createdAt":"2026-06-01T08:00:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"A field has the wrong shape (e.g. badRatingThreshold outside 1 to 5, surveyExpiryDays outside 1 to 365, an empty text)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.csat.update"]}],"x-required-scopes":["crm.csat.update"]}},"/public/csat/{token}":{"get":{"tags":["Help desk setup"],"summary":"Look up a satisfaction survey","description":"Looks up a satisfaction survey by the token in its link, for the survey page: the ticket's number and subject, whether it was already rated or has expired, and the rating scale. Changes nothing. A malformed or unknown token answers 200 with success false, error \"invalid_token\" or \"not_found\", and the scale. Meant for the customer's survey page, but today it still needs a sign-in with this permission.\n\n**Permission:** `crm.csat.read` (View CSAT).","operationId":"getPublicCsat","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"},"description":"The survey's token, from the survey link","example":"q3Zp8Kd2LmX9vR4tYw7NbC1sHf6GjE5aUo0iPk2Lr8M"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"emojis":{"type":"array","items":{"type":"object","properties":{"rating":{"type":"integer"},"emoji":{"type":"string"},"label":{"type":"string"}}},"description":"The 1 to 5 rating scale with its emoji and label"},"thankYouMessage":{"type":"string"},"survey":{"type":"object","properties":{"ticketNumber":{"type":"string"},"ticketSubject":{"type":"string"},"recipientName":{"type":"string"},"rating":{},"comment":{},"ratedAt":{},"expiresAt":{"type":"string","format":"date-time"},"alreadyRated":{"type":"boolean","description":"The customer has rated already (rating and comment are theirs)"},"expired":{"type":"boolean","description":"The link has expired without a rating"},"badRatingThreshold":{"type":"integer","description":"Ratings at or below this count as bad"}}}}},"example":{"success":true,"emojis":[{"rating":1,"emoji":"😡","label":"Very bad"},{"rating":2,"emoji":"😕","label":"Bad"},{"rating":3,"emoji":"😐","label":"Neutral"},{"rating":4,"emoji":"🙂","label":"Good"},{"rating":5,"emoji":"😍","label":"Amazing"}],"thankYouMessage":"Thanks for the feedback — we really appreciate it!","survey":{"ticketNumber":"TKT-001042","ticketSubject":"Internet down since this morning","recipientName":"Amina Wanjiru","rating":null,"comment":null,"ratedAt":null,"expiresAt":"2026-11-04T09:00:00.000Z","alreadyRated":false,"expired":false,"badRatingThreshold":2}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.csat.read"]}],"x-required-scopes":["crm.csat.read"]},"post":{"tags":["Help desk setup"],"summary":"Rate a ticket","description":"Records a customer's 1 to 5 rating (and comment) for the ticket their survey link is for, and updates their average. A rating at or below the organisation's bad threshold can reopen the ticket, tag it, move it to the escalation department and notify the assignee and department leaders. A survey can be rated once; sending again answers with the first rating and alreadyRated true. Meant for the customer's survey page, but today it still needs a sign-in with this permission.\n\n**Permission:** `crm.csat.create` (Create CSAT).","operationId":"submitTicketSatisfaction","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"},"description":"The survey's token, from the survey link","example":"q3Zp8Kd2LmX9vR4tYw7NbC1sHf6GjE5aUo0iPk2Lr8M"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"comment":{"type":"string","description":"What the customer adds, up to 2,000 characters","examples":["Quick fix, thank you"]},"rating":{"type":"integer","minimum":1,"maximum":5,"description":"1 (very bad) to 5 (amazing)","examples":[4]}},"required":["rating"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"alreadyRated":{"type":"boolean","description":"The survey had been rated before; the reply shows that rating"},"rating":{"type":"integer"},"comment":{"type":"string"},"badRatingHandled":{"type":"boolean","description":"The bad-rating steps ran"},"thankYouMessage":{"type":"string"},"survey":{"type":"object","properties":{"ratedAt":{"type":"string","format":"date-time","description":"When the rating was recorded"}}}}},"example":{"success":true,"alreadyRated":false,"rating":4,"comment":"Quick fix, thank you","badRatingHandled":false,"thankYouMessage":"Thanks for the feedback — we really appreciate it!","survey":{"ratedAt":"2026-10-21T10:00:00.000Z"}}}}},"400":{"description":"The token is malformed, unknown or expired, the rating is not 1 to 5, or the ticket no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.csat.create"]}],"x-required-scopes":["crm.csat.create"]}},"/connections":{"get":{"tags":["Integrations"],"summary":"List connections","description":"Lists your organisation's named outbound connections: the outside addresses (with their sign-in headers) that workflow custom code and steps call by name. Header values are masked: only their last four characters show.\n\n**Permission:** `crm.connections.read` (View connections).","operationId":"listConnections","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"baseUrl":{"type":"string"},"displayName":{"type":"string"},"isActive":{"type":"boolean","description":"Switched-off connections are refused when called"},"headers":{"type":"object","properties":{"Authorization":{"type":"string"}},"description":"The headers sent with every call, values masked as ********abcd"}}}}}},"example":{"success":true,"data":[{"name":"billing-api","baseUrl":"https://api.billing.example.com/v2","displayName":"Billing system","isActive":true,"headers":{"Authorization":"********9f3c"}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.connections.read"]}],"x-required-scopes":["crm.connections.read"]},"post":{"tags":["Integrations"],"summary":"Save a connection","description":"Creates a named outbound connection, or replaces the one with that name: an outside https address plus the headers (e.g. an API key) sent with every call, which workflow custom code and steps then use by name without seeing the secret. Answers 201 when it is new and 200 when it replaced one. Header values are never sent back; leaving headers out on a replace keeps the stored ones.\n\n**Permission:** `crm.connections.update` (Manage connections).","operationId":"saveConnection","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"baseUrl":{"type":"string","description":"The address calls start from: https only, never localhost or a private network address","examples":["https://api.billing.example.com/v2"]},"displayName":{"type":"string","description":"A friendlier name to show; leave out for none","examples":["Billing system"]},"headers":{"type":"object","description":"Headers sent with every call, as a JSON object (or JSON text) of text values, e.g. an Authorization header. Host may not be set. Leave out on a replace to keep the stored ones","examples":[{"Authorization":"Bearer sk_live_example_9f3c"}]},"isActive":{"type":"boolean","description":"Whether calls through it are allowed (default true)","examples":[true]},"name":{"type":"string","description":"The connection's name: 1 to 63 characters of a-z, 0-9, \"_\" or \"-\", starting with a letter or digit (capitals are made small)","examples":["billing-api"]}},"required":["baseUrl","name"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"baseUrl":{"type":"string"},"displayName":{"type":"string"},"isActive":{"type":"boolean"},"headerNames":{"type":"array","items":{"type":"string"},"description":"Names of the stored headers; their values are never shown"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"6cf3a4b5-9d0e-4f1a-8b2c-3d4e5f6a7b8c","name":"billing-api","baseUrl":"https://api.billing.example.com/v2","displayName":"Billing system","isActive":true,"headerNames":["Authorization"],"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"The name is not valid, baseUrl is not an https address or points at a local or private address, or headers is not a JSON object of text values (or sets Host)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.connections.update"]}],"x-required-scopes":["crm.connections.update"]}},"/workflow/webhook/calls":{"get":{"tags":["Automations"],"summary":"List a rule's webhook calls","description":"Lists the calls Kazee's webhook service has sent (or is still retrying) for one rule's Call Webhook steps, newest first, with each call's last answer. Header values and bodies are not shown. Needs automations.read or pipelines.admin. Only for organisations whose webhook steps go through the webhook service.\n\n**Permission:** `crm.automations.read` (View automations) or `crm.pipelines.admin` (Administer pipelines).","operationId":"listWorkflowWebhookCalls","parameters":[{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"nextCursor from the page before, for the next older page","example":"wfd_019a2b3c4d5e6f708192a3b4c5d6e7f0"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100},"description":"Calls per page, 1 to 100 (50 when left out)","example":50},{"name":"ruleId","in":"query","required":true,"schema":{"type":"string"},"description":"The automation rule or pipeline transition whose calls to list","example":"17a8b9c0-4e5f-4a6b-8c7d-8e9f0a1b2c3d"},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["pending","failed","succeeded","abandoned"]},"description":"Only calls in this state","example":"failed"},{"name":"stepIndex","in":"query","required":false,"schema":{"type":"integer"},"description":"Only the calls of this step (its place in the rule's steps)","example":2}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"deliveries":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","description":"pending (waiting), failed (will be tried again at nextAttemptAt), succeeded or abandoned (gave up: the receiver answered 410 or three days passed)"},"inFlight":{"type":"boolean","description":"An attempt is being sent right now"},"attempts":{"type":"integer"},"sourceApp":{"type":"string"},"idempotencyKey":{"type":"string"},"ruleKind":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"stepIndex":{"type":"integer"},"workflowEventId":{"type":"string"},"recordType":{"type":"string"},"recordId":{"type":"string"},"url":{"type":"string"},"method":{"type":"string"},"headerNames":{"type":"array","items":{"type":"string"},"description":"Names of the headers sent; their values are never shown"},"lastStatusCode":{"type":"integer"},"lastError":{},"lastResponse":{"type":"string","description":"The start of the receiver's last answer"},"lastDurationMs":{"type":"integer"},"createdAt":{"type":"string","format":"date-time"},"firstAttemptAt":{"type":"string","format":"date-time"},"nextAttemptAt":{},"deliveredAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"nextCursor":{"description":"Pass as cursor to get the next, older page; null on the last page"}}},"example":{"deliveries":[{"id":"wfd_019a2b3c4d5e6f708192a3b4c5d6e7f8","status":"succeeded","inFlight":false,"attempts":1,"sourceApp":"kazee-crm","idempotencyKey":"automation:17a8b9c0-4e5f-4a6b-8c7d-8e9f0a1b2c3d:evt_1:2","ruleKind":"automation","ruleId":"17a8b9c0-4e5f-4a6b-8c7d-8e9f0a1b2c3d","ruleName":"Welcome new fibre leads","stepIndex":2,"workflowEventId":"evt_1","recordType":"lead","recordId":"d4e5f6a7-1b2c-4d3e-9f4a-5b6c7d8e9f0a","url":"https://hooks.example.com/kazee","method":"POST","headerNames":["Content-Type","Authorization"],"lastStatusCode":200,"lastError":null,"lastResponse":"{\"received\":true}","lastDurationMs":312,"createdAt":"2026-10-20T08:15:00.000Z","firstAttemptAt":"2026-10-20T08:15:01.000Z","nextAttemptAt":null,"deliveredAt":"2026-10-20T08:15:01.000Z","updatedAt":"2026-10-20T08:15:01.000Z"}],"nextCursor":null}}}},"400":{"description":"ruleId is missing or over 100 characters, stepIndex is not a whole number, a status or cursor is not valid, or this organisation has no Kazee Central ID, or the webhook service refused the request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Kazee's webhook service is not connected to CRM for this organisation yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee's webhook service refused CRM's credential or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee's webhook service cannot be reached right now","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.automations.read","crm.pipelines.admin"]}],"x-required-scopes":["crm.automations.read","crm.pipelines.admin"],"x-scope-mode":"any"}},"/workflow/webhook/signing-secret":{"get":{"tags":["Automations"],"summary":"Get the webhook signing secret's status","description":"Says whether your organisation has a workflow webhook signing secret (receivers use it to check that a call came from Kazee), and its last four characters, never the secret itself. Needs automations.read, pipelines.admin or connections.read.\n\n**Permission:** `crm.automations.read` (View automations) or `crm.pipelines.admin` (Administer pipelines) or `crm.connections.read` (View connections).","operationId":"getWorkflowWebhookSigningSecret","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"exists":{"type":"boolean","description":"The organisation has a signing secret"},"createdAt":{"type":"string","format":"date-time"},"rotatedAt":{"description":"When it was last replaced; null if never"},"hint":{"type":"string","description":"The secret's last four characters; null when there is none"}}},"example":{"exists":true,"createdAt":"2026-10-02T09:00:00.000Z","rotatedAt":null,"hint":"x9Qa"}}}},"400":{"description":"This organisation has no Kazee Central ID, or the webhook service refused the request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Kazee's webhook service is not connected to CRM for this organisation yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee's webhook service refused CRM's credential or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee's webhook service cannot be reached right now","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.automations.read","crm.pipelines.admin","crm.connections.read"]}],"x-required-scopes":["crm.automations.read","crm.pipelines.admin","crm.connections.read"],"x-scope-mode":"any"}},"/integrations":{"get":{"tags":["Integrations"],"summary":"List import connections","description":"Lists every system CRM can import from (Zoho CRM today) with your organisation's connection to it: whether it is set up, switched on, and how often it syncs.\n\n**Permission:** `crm.importer.read` (View importer).","operationId":"listImporters","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string","description":"The system's key, used as :source in the other integration calls"},"displayName":{"type":"string"},"capabilities":{"type":"object","properties":{"incrementalSync":{"type":"boolean"},"oauth":{"type":"boolean"},"push":{"type":"boolean"},"userDirectory":{"type":"boolean"},"relatedLists":{"type":"boolean"}},"description":"What the system supports: incremental sync, OAuth sign-in, writing back (push), a user list, and related-list linking"},"configured":{"type":"boolean","description":"Your organisation has saved a connection"},"isActive":{"type":"boolean","description":"The connection passed its test and syncs; false until it does"},"syncSchedule":{"type":"string","description":"1h, 6h, 12h, daily, weekly or manual; null when not set up"},"configId":{"type":"string","description":"The saved connection's ID; null when not set up"},"configDisplayName":{"type":"string"}}}}}},"example":{"success":true,"data":[{"source":"zoho","displayName":"Zoho CRM","capabilities":{"incrementalSync":true,"oauth":true,"push":false,"userDirectory":true,"relatedLists":true},"configured":true,"isActive":true,"syncSchedule":"daily","configId":"5b7e2c1a-8d4f-4e3a-9b6c-2f1e0d9c8b7a","configDisplayName":"Example Telecom Ltd"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.read"]}],"x-required-scopes":["crm.importer.read"]}},"/integrations/{source}/config":{"get":{"tags":["Integrations"],"summary":"Get an import connection","description":"Shows your organisation's saved connection to one system: its settings, sync schedule and whether it is switched on. Secrets (client secret, tokens, keys, passwords) come back masked as ******** plus their last 4 characters. Answers data: null when nothing is saved.\n\n**Permission:** `crm.importer.read` (View importer).","operationId":"getImporterConfig","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"source":{"type":"string"},"displayName":{"type":"string"},"credentials":{"type":"object","properties":{"clientId":{"type":"string"},"clientSecret":{"type":"string"},"refreshToken":{"type":"string"},"accessToken":{"type":"string"},"accessTokenExpiresAt":{"type":"string","format":"date-time"}},"description":"The stored sign-in details; secret values masked (********abcd)"},"settings":{"type":"object","properties":{"dataCenter":{"type":"string"}},"description":"Settings that are not secret, e.g. Zoho's dataCenter"},"syncSchedule":{"type":"string","description":"1h, 6h, 12h, daily, weekly or manual"},"isActive":{"type":"boolean","description":"The connection passed its test and syncs"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"description":"The saved connection, or null when there is none"}}},"example":{"success":true,"data":{"id":"5b7e2c1a-8d4f-4e3a-9b6c-2f1e0d9c8b7a","source":"zoho","displayName":"Example Telecom Ltd","credentials":{"clientId":"1000.ABCDEFGHIJKLMNOPQRSTUVWXYZ","clientSecret":"********9f3a","refreshToken":"********71c2","accessToken":"********0d4e","accessTokenExpiresAt":"2026-10-20T10:00:00.000Z"},"settings":{"dataCenter":"com"},"syncSchedule":"daily","isActive":true,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"source is missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.read"]}],"x-required-scopes":["crm.importer.read"]},"post":{"tags":["Integrations"],"summary":"Save an import connection","description":"Saves your organisation's connection to a system, replacing any saved one. It is saved switched off: test it (POST /integrations/{source}/test) to switch it on. Answers 201 when it is new and 200 when it replaced one; secrets are never sent back.\n\n**Permission:** `crm.importer.update` (Update importer).","operationId":"saveImporterConfig","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"credentials":{"type":"object","description":"The system's sign-in details, stored as sent. For Zoho: clientId, clientSecret and refreshToken. A JSON object in a string also works","examples":[{"clientId":"1000.ABCDEFGHIJKLMNOPQRSTUVWXYZ","clientSecret":"0123456789abcdef0123456789abcdef9f3a","refreshToken":"1000.0123456789abcdef.0123456789abcdef71c2"}]},"displayName":{"type":"string","description":"A name for the connection; a successful test replaces it with the name the system gives","examples":["Example Telecom Ltd"]},"settings":{"type":"object","description":"Settings that are not secret. For Zoho: dataCenter (\"com\", \"eu\", \"in\", \"com.au\", \"com.cn\" or \"jp\"; \"com\" when left out)","examples":[{"dataCenter":"com"}]},"syncSchedule":{"type":"string","description":"How often it syncs: 1h, 6h, 12h, daily, weekly or manual. Keeps the saved one, or daily, when left out","examples":["daily"]}},"required":["credentials"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"source":{"type":"string"},"displayName":{},"settings":{"type":"object","properties":{"dataCenter":{"type":"string"}}},"syncSchedule":{"type":"string"},"isActive":{"type":"boolean","description":"Always false after a save, until a test succeeds"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"5b7e2c1a-8d4f-4e3a-9b6c-2f1e0d9c8b7a","source":"zoho","displayName":null,"settings":{"dataCenter":"com"},"syncSchedule":"daily","isActive":false,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"source is missing or not a known system, or credentials or settings is not a JSON object","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.update"]}],"x-required-scopes":["crm.importer.update"]},"delete":{"tags":["Integrations"],"summary":"Remove an import connection","description":"Removes your organisation's connection to a system and its saved field mappings, so nothing more is synced from it. Records already imported stay.\n\n**Permission:** `crm.importer.delete` (Delete importer).","operationId":"deleteImporterConfig","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}},"example":{"success":true,"message":"zoho integration removed"}}}},"400":{"description":"source is missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No connection to that system is saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.delete"]}],"x-required-scopes":["crm.importer.delete"]}},"/integrations/{source}/modules":{"get":{"tags":["Integrations"],"summary":"List a connected system's modules","description":"Asks the connected system which modules (record types, e.g. Zoho's Leads and Deals) your connection can read, to pick what to import.\n\n**Permission:** `crm.importer.read` (View importer).","operationId":"getImporterModules","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"apiName":{"type":"string","description":"The module's name in the system; use it as :moduleName"},"label":{"type":"string"},"pluralLabel":{"type":"string"},"creatable":{"type":"boolean"},"editable":{"type":"boolean"},"deletable":{"type":"boolean"}}}}}},"example":{"success":true,"data":[{"apiName":"Leads","label":"Lead","pluralLabel":"Leads","creatable":true,"editable":true,"deletable":true},{"apiName":"Deals","label":"Deal","pluralLabel":"Deals","creatable":true,"editable":true,"deletable":true}]}}}},"400":{"description":"source is missing or not a known system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No switched-on connection to that system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.read"]}],"x-required-scopes":["crm.importer.read"]}},"/integrations/{source}/modules/{moduleName}/fields":{"get":{"tags":["Integrations"],"summary":"List a module's fields in a connected system","description":"Asks the connected system for the fields of one of its modules: names, types, choices and lookups, to map them to CRM fields.\n\n**Permission:** `crm.importer.read` (View importer).","operationId":"getImporterModuleFields","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"},{"name":"moduleName","in":"path","required":true,"schema":{"type":"string"},"description":"The module's name in the system (apiName from the modules list)","example":"Deals"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"apiName":{"type":"string","description":"The field's name in the system (remoteField in a mapping)"},"label":{"type":"string"},"dataType":{"type":"string","description":"The system's own type name, e.g. text, picklist, lookup, date"},"required":{"type":"boolean"},"readOnly":{"type":"boolean"},"custom":{"type":"boolean"},"pickListValues":{"type":"array","description":"The choices of a picklist field; empty otherwise"},"lookupModule":{"type":"string","description":"For a lookup field, the module it points to"}}}}}},"example":{"success":true,"data":[{"apiName":"Account_Name","label":"Account Name","dataType":"lookup","required":false,"readOnly":false,"custom":false,"pickListValues":[],"lookupModule":"Accounts"},{"apiName":"Stage","label":"Stage","dataType":"picklist","required":true,"readOnly":false,"custom":false,"pickListValues":[{"value":"Interested","label":"Interested"},{"value":"Closed Won","label":"Closed Won"}]}]}}}},"400":{"description":"source or moduleName is missing, or source is not a known system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No switched-on connection to that system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.read"]}],"x-required-scopes":["crm.importer.read"]}},"/integrations/{source}/modules/{moduleName}/count":{"get":{"tags":["Integrations"],"summary":"Count a module's records in a connected system","description":"Asks the connected system how many records one of its modules holds, e.g. to show progress. Answers 0 when the system cannot count.\n\n**Permission:** `crm.importer.read` (View importer).","operationId":"getImporterModuleCount","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"},{"name":"moduleName","in":"path","required":true,"schema":{"type":"string"},"description":"The module's name in the system (apiName from the modules list)","example":"Deals"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"count":{"type":"integer"}}}}},"example":{"success":true,"data":{"count":1240}}}}},"400":{"description":"source or moduleName is missing, or source is not a known system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No switched-on connection to that system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.read"]}],"x-required-scopes":["crm.importer.read"]}},"/integrations/{source}/mappings":{"get":{"tags":["Integrations"],"summary":"List field mappings","description":"Lists the saved field-mapping sets for a system (one per module, used by one-off imports): which of the system's fields go to which CRM field. Up to 500, by module name.\n\n**Permission:** `crm.importer.read` (View importer).","operationId":"getImporterMappings","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"source":{"type":"string"},"remoteModule":{"type":"string"},"remoteModuleLabel":{"type":"string"},"crmModule":{"type":"string","description":"The CRM record type it fills: leads, contacts, accounts, deals, products or quotes"},"fieldMappings":{"type":"array","items":{"type":"object","properties":{"remoteField":{"type":"string"},"remoteFieldLabel":{"type":"string"},"remoteFieldType":{"type":"string"},"crmField":{"type":"string"},"crmFieldLabel":{"type":"string"}}},"description":"Each system field (remoteField) and the CRM field it fills (crmField)"},"isActive":{"type":"boolean"},"lastImportAt":{"type":"string","format":"date-time"},"createdBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}},"example":{"success":true,"data":[{"id":"c3a9d8e7-1f2b-4c5d-8e9f-0a1b2c3d4e5f","organizationId":"11111111-2222-4333-8444-555555555555","source":"zoho","remoteModule":"Leads","remoteModuleLabel":"Leads","crmModule":"leads","fieldMappings":[{"remoteField":"Last_Name","remoteFieldLabel":"Last Name","remoteFieldType":"text","crmField":"lastName","crmFieldLabel":"Last name"}],"isActive":true,"lastImportAt":"2026-10-20T09:04:12.000Z","createdBy":"8b1d3f5a-7c9e-4b0d-8f2a-4c6e8a0b2d4f","deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}]}}}},"400":{"description":"source is missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.read"]}],"x-required-scopes":["crm.importer.read"]},"post":{"tags":["Integrations"],"summary":"Replace field mappings","description":"Replaces all saved field-mapping sets for a system with the ones sent (the old ones are removed first). Used by one-off imports; recurring syncs use module links instead.\n\n**Permission:** `crm.importer.update` (Update importer).","operationId":"saveImporterMappings","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mappings":{"type":"array","items":{"type":"object","properties":{"remoteModule":{"type":"string"},"remoteModuleLabel":{"type":"string"},"crmModule":{"type":"string"},"fieldMappings":{"type":"array","items":{"type":"object"}},"isActive":{"type":"boolean"}},"required":["remoteModule","crmModule"]},"description":"One set per module: remoteModule (the system's module), remoteModuleLabel, crmModule (leads, contacts, accounts, deals, products or quotes), fieldMappings (each { remoteField, crmField, transform? }, transform being lowercase, number or date) and isActive (true when left out). A JSON array in a string also works","examples":[[{"remoteModule":"Leads","remoteModuleLabel":"Leads","crmModule":"leads","fieldMappings":[{"remoteField":"Last_Name","remoteFieldLabel":"Last Name","remoteFieldType":"text","crmField":"lastName","crmFieldLabel":"Last name"}]}]]}},"required":["mappings"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"source":{"type":"string"},"remoteModule":{"type":"string"},"remoteModuleLabel":{"type":"string"},"crmModule":{"type":"string"},"fieldMappings":{"type":"array","items":{"type":"object","properties":{"remoteField":{"type":"string"},"remoteFieldLabel":{"type":"string"},"remoteFieldType":{"type":"string"},"crmField":{"type":"string"},"crmFieldLabel":{"type":"string"}}}},"isActive":{"type":"boolean"},"lastImportAt":{"type":"string","format":"date-time"},"createdBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"message":{"type":"string"}}},"example":{"success":true,"data":[{"id":"c3a9d8e7-1f2b-4c5d-8e9f-0a1b2c3d4e5f","organizationId":"11111111-2222-4333-8444-555555555555","source":"zoho","remoteModule":"Leads","remoteModuleLabel":"Leads","crmModule":"leads","fieldMappings":[{"remoteField":"Last_Name","remoteFieldLabel":"Last Name","remoteFieldType":"text","crmField":"lastName","crmFieldLabel":"Last name"}],"isActive":true,"lastImportAt":"2026-10-20T09:04:12.000Z","createdBy":"8b1d3f5a-7c9e-4b0d-8f2a-4c6e8a0b2d4f","deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}],"message":"1 mapping(s) saved"}}}},"400":{"description":"source is missing or not a known system, mappings is not a non-empty JSON array, or a mapping has no remoteModule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.update"]}],"x-required-scopes":["crm.importer.update"]}},"/integrations/{source}/module-links":{"get":{"tags":["Integrations"],"summary":"List module links","description":"Lists the module links for a system (which of its modules sync into which CRM record type, with their field mappings), each with its last sync run and how many records it has synced in total. Up to 200.\n\n**Permission:** `crm.importer.read` (View importer).","operationId":"getImporterModuleLinks","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"source":{"type":"string"},"remoteModule":{"type":"string"},"remoteModuleLabel":{"type":"string"},"crmTarget":{"type":"string","description":"The CRM record type it writes, e.g. leads or deals, or a custom module's key"},"crmTargetType":{"type":"string","description":"builtin, or custom for a custom module"},"fieldMappings":{"type":"array","items":{"type":"object","properties":{"remoteField":{"type":"string"},"remoteFieldLabel":{"type":"string"},"remoteFieldType":{"type":"string"},"crmField":{"type":"string"},"crmFieldLabel":{"type":"string"}}}},"skippedFields":{"type":"array","items":{"type":"string"}},"syncEnabled":{"type":"boolean"},"syncMode":{"type":"string","description":"incremental (only what changed since the last sync) or full"},"lastSyncAt":{"type":"string","format":"date-time"},"unmappedFieldAction":{"type":"string","description":"ignore, or create_custom_field to keep unmapped fields as new custom fields"},"createdBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"lastSync":{"type":"object","properties":{"status":{"type":"string"},"startedAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"},"recordsFetched":{"type":"integer"},"recordsCreated":{"type":"integer"},"recordsUpdated":{"type":"integer"},"recordsFailed":{"type":"integer"}},"description":"The latest sync run of this link; null when it never ran"},"totalRecordsSynced":{"type":"integer","description":"Records created plus updated over all its runs"}}}}}},"example":{"success":true,"data":[{"id":"7f6e5d4c-3b2a-4190-8f7e-6d5c4b3a2918","organizationId":"11111111-2222-4333-8444-555555555555","source":"zoho","remoteModule":"Deals","remoteModuleLabel":"Deals","crmTarget":"deals","crmTargetType":"builtin","fieldMappings":[{"remoteField":"Deal_Name","remoteFieldLabel":"Deal Name","remoteFieldType":"text","crmField":"name","crmFieldLabel":"Name"},{"remoteField":"Stage","remoteFieldLabel":"Stage","remoteFieldType":"picklist","crmField":"stage","crmFieldLabel":"Stage","valueMap":{"Interested":"qualification","Closed Won":"won"}}],"skippedFields":["Description"],"syncEnabled":true,"syncMode":"incremental","lastSyncAt":"2026-10-20T09:04:12.000Z","unmappedFieldAction":"ignore","createdBy":"8b1d3f5a-7c9e-4b0d-8f2a-4c6e8a0b2d4f","deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z","lastSync":{"status":"completed","startedAt":"2026-10-20T09:00:00.000Z","completedAt":"2026-10-20T09:04:12.000Z","recordsFetched":120,"recordsCreated":14,"recordsUpdated":104,"recordsFailed":2},"totalRecordsSynced":5310}]}}}},"400":{"description":"source is missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.read"]}],"x-required-scopes":["crm.importer.read"]},"post":{"tags":["Integrations"],"summary":"Save a module link","description":"Creates a module link (one of the system's modules syncing into a CRM record type, with its field mappings), or updates one when linkId is given. A new link answers 201, an update 200. Value maps already saved on a stage or status mapping are kept when the update leaves them out.\n\n**Permission:** `crm.importer.update` (Update importer).","operationId":"saveImporterModuleLink","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"crmTarget":{"type":"string","description":"The CRM record type to write: leads, contacts, accounts, deals, products, quotes, or a custom module's key","examples":["deals"]},"crmTargetType":{"type":"string","enum":["builtin","custom"],"default":"builtin","description":"custom when crmTarget is a custom module; anything else counts as builtin","examples":["builtin"]},"fieldMappings":{"type":"array","items":{"type":"object"},"description":"Each { remoteField, crmField, remoteFieldLabel?, crmFieldLabel?, transform? }. A stage (deals) or status (leads) mapping may carry a valueMap: the system's word to the CRM value, at most 50. A JSON array in a string also works","examples":[[{"remoteField":"Stage","remoteFieldLabel":"Stage","remoteFieldType":"picklist","crmField":"stage","crmFieldLabel":"Stage","valueMap":{"Interested":"qualification","Closed Won":"won"}}]]},"linkId":{"type":"string","format":"uuid","description":"The link to update; leave out to create one","examples":["7f6e5d4c-3b2a-4190-8f7e-6d5c4b3a2918"]},"remoteModule":{"type":"string","description":"The system's module (apiName from the modules list)","examples":["Deals"]},"remoteModuleLabel":{"type":"string","description":"The module's name as people see it","examples":["Deals"]},"syncEnabled":{"type":"boolean","default":true,"description":"false switches the link's sync off; anything else leaves it on","examples":[true]},"syncMode":{"type":"string","enum":["incremental","full"],"default":"incremental","description":"incremental reads only what changed since the last sync; full reads everything each time","examples":["incremental"]},"unmappedFieldAction":{"type":"string","enum":["ignore","create_custom_field"],"default":"ignore","description":"What happens to the system's fields that are not mapped: ignored, or kept as new custom fields","examples":["ignore"]}},"required":["crmTarget","fieldMappings","remoteModule","remoteModuleLabel"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"source":{"type":"string"},"remoteModule":{"type":"string"},"remoteModuleLabel":{"type":"string"},"crmTarget":{"type":"string"},"crmTargetType":{"type":"string"},"fieldMappings":{"type":"array","items":{"type":"object","properties":{"remoteField":{"type":"string"},"remoteFieldLabel":{"type":"string"},"remoteFieldType":{"type":"string"},"crmField":{"type":"string"},"crmFieldLabel":{"type":"string"}}}},"skippedFields":{"type":"array","items":{"type":"string"}},"syncEnabled":{"type":"boolean"},"syncMode":{"type":"string"},"lastSyncAt":{"type":"string","format":"date-time"},"unmappedFieldAction":{"type":"string"},"createdBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"7f6e5d4c-3b2a-4190-8f7e-6d5c4b3a2918","organizationId":"11111111-2222-4333-8444-555555555555","source":"zoho","remoteModule":"Deals","remoteModuleLabel":"Deals","crmTarget":"deals","crmTargetType":"builtin","fieldMappings":[{"remoteField":"Deal_Name","remoteFieldLabel":"Deal Name","remoteFieldType":"text","crmField":"name","crmFieldLabel":"Name"},{"remoteField":"Stage","remoteFieldLabel":"Stage","remoteFieldType":"picklist","crmField":"stage","crmFieldLabel":"Stage","valueMap":{"Interested":"qualification","Closed Won":"won"}}],"skippedFields":["Description"],"syncEnabled":true,"syncMode":"incremental","lastSyncAt":"2026-10-20T09:04:12.000Z","unmappedFieldAction":"ignore","createdBy":"8b1d3f5a-7c9e-4b0d-8f2a-4c6e8a0b2d4f","deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"source is missing or not a known system, fieldMappings is not JSON, or a value map is malformed (not on stage or status, over 50 words, a word twice, a value over 64 characters)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No module link with that linkId","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A link from this module to this CRM record type already exists (send its linkId to update it)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.update"]}],"x-required-scopes":["crm.importer.update"]}},"/integrations/{source}/sync-logs":{"get":{"tags":["Integrations"],"summary":"List sync runs","description":"Lists sync runs, newest first, with what each fetched, created, updated, skipped and failed. Filter by system or module link; 20 a page, at most 100.\n\n**Permission:** `crm.importer.read` (View importer).","operationId":"getImporterSyncLogs","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"Only runs of this system, e.g. \"zoho\"","example":"zoho"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20},"description":"Runs per page, 1 to 100","example":20},{"name":"moduleLinkId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only runs of this module link","example":"7f6e5d4c-3b2a-4190-8f7e-6d5c4b3a2918"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1},"description":"Page number, from 1","example":1}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"source":{"type":"string"},"moduleLinkId":{"type":"string"},"status":{"type":"string","description":"running, completed, failed or cancelled"},"syncType":{"type":"string","description":"scheduled, or manual when someone started it"},"recordsFetched":{"type":"integer"},"recordsCreated":{"type":"integer"},"recordsUpdated":{"type":"integer"},"recordsSkipped":{"type":"integer"},"recordsFailed":{"type":"integer"},"errors":{"type":"array","items":{"type":"object","properties":{"remoteRecordId":{"type":"string"},"error":{"type":"string"}}},"description":"Records that failed: the system's record ID and why"},"startedAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"},"queryFilter":{},"createdAt":{"type":"string","format":"date-time"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"}}}}},"example":{"success":true,"data":[{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","organizationId":"11111111-2222-4333-8444-555555555555","source":"zoho","moduleLinkId":"7f6e5d4c-3b2a-4190-8f7e-6d5c4b3a2918","status":"completed","syncType":"scheduled","recordsFetched":120,"recordsCreated":14,"recordsUpdated":104,"recordsSkipped":0,"recordsFailed":2,"errors":[{"remoteRecordId":"5123000004567001","error":"Account not found for this deal"}],"startedAt":"2026-10-20T09:00:00.000Z","completedAt":"2026-10-20T09:04:12.000Z","queryFilter":null,"createdAt":"2026-10-20T09:00:00.000Z"}],"pagination":{"page":1,"limit":20,"total":1,"totalPages":1}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.read"]}],"x-required-scopes":["crm.importer.read"]}},"/integrations/{source}/users":{"get":{"tags":["Integrations"],"summary":"List a connected system's users","description":"Asks the connected system for its users, to map each to a CRM user (who owns what is imported).\n\n**Permission:** `crm.importer.read` (View importer).","operationId":"getImporterUsers","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The user's ID in the system"},"email":{"type":"string"},"fullName":{"type":"string"},"status":{"type":"string","description":"The system's own word, e.g. active; empty when it gives none"}}}}}},"example":{"success":true,"data":[{"id":"5123000000123006","email":"amina@example.com","fullName":"Amina Otieno","status":"active"}]}}}},"400":{"description":"source is missing or not a known system, or the system has no user list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No switched-on connection to that system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.read"]}],"x-required-scopes":["crm.importer.read"]}},"/integrations/{source}/user-mappings":{"get":{"tags":["Integrations"],"summary":"Get user mappings","description":"Shows which CRM user stands for each of the system's users, the user who gets records with no match, and whether users are matched by email. Answers data: null when nothing is saved.\n\n**Permission:** `crm.importer.read` (View importer).","operationId":"getImporterUserMappings","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"source":{"type":"string"},"mappings":{"type":"array","items":{"type":"object","properties":{"remoteUserId":{"type":"string"},"remoteUserName":{"type":"string"},"remoteUserEmail":{"type":"string"},"crmUserId":{"type":"string"},"crmUserName":{"type":"string"}}}},"defaultCrmUserId":{"type":"string","description":"Gets records whose system user has no match"},"autoMatchByEmail":{"type":"boolean","description":"Users with the same email are matched without a mapping"},"updatedAt":{"type":"string","format":"date-time"}},"description":"The saved user mappings, or null when there are none"}}},"example":{"success":true,"data":{"id":"2c4e6a8b-0d1f-4a3c-9e5b-7d9f1b3d5f7a","source":"zoho","mappings":[{"remoteUserId":"5123000000123006","remoteUserName":"Amina Otieno","remoteUserEmail":"amina@example.com","crmUserId":"8b1d3f5a-7c9e-4b0d-8f2a-4c6e8a0b2d4f","crmUserName":"Amina Otieno"}],"defaultCrmUserId":"4f2d0b8e-6c4a-4e9f-9d7b-5a3c1e9f7d5b","autoMatchByEmail":true,"updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"source is missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.read"]}],"x-required-scopes":["crm.importer.read"]},"post":{"tags":["Integrations"],"summary":"Save user mappings","description":"Saves which CRM user stands for each of the system's users, replacing the saved list. Records owned by a system user are given to the mapped CRM user.\n\n**Permission:** `crm.importer.update` (Update importer).","operationId":"saveImporterUserMappings","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"autoMatchByEmail":{"type":"boolean","description":"Match users with the same email without a mapping (true when left out)","examples":[true]},"defaultCrmUserId":{"type":"string","format":"uuid","description":"CRM user who gets records whose system user has no match; none when left out","examples":["4f2d0b8e-6c4a-4e9f-9d7b-5a3c1e9f7d5b"]},"mappings":{"type":"array","items":{"type":"object","properties":{"remoteUserId":{"type":"string"},"remoteUserName":{"type":"string"},"remoteUserEmail":{"type":"string"},"crmUserId":{"type":"string","format":"uuid"},"crmUserName":{"type":"string"}},"required":["remoteUserId"]},"description":"Each system user (remoteUserId, with its name and email) and the CRM user that stands for it (crmUserId). A JSON array in a string also works","examples":[[{"remoteUserId":"5123000000123006","remoteUserName":"Amina Otieno","remoteUserEmail":"amina@example.com","crmUserId":"8b1d3f5a-7c9e-4b0d-8f2a-4c6e8a0b2d4f","crmUserName":"Amina Otieno"}]]},"userId":{"type":"string","format":"uuid","description":"Filled in from your sign-in (recorded as who saved it); you do not need to send it","examples":["8b1d3f5a-7c9e-4b0d-8f2a-4c6e8a0b2d4f"]}},"required":["mappings"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"source":{"type":"string"},"mappings":{"type":"array","items":{"type":"object","properties":{"remoteUserId":{"type":"string"},"remoteUserName":{"type":"string"},"remoteUserEmail":{"type":"string"},"crmUserId":{"type":"string"},"crmUserName":{"type":"string"}}}},"defaultCrmUserId":{"type":"string"},"autoMatchByEmail":{"type":"boolean"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"2c4e6a8b-0d1f-4a3c-9e5b-7d9f1b3d5f7a","source":"zoho","mappings":[{"remoteUserId":"5123000000123006","remoteUserName":"Amina Otieno","remoteUserEmail":"amina@example.com","crmUserId":"8b1d3f5a-7c9e-4b0d-8f2a-4c6e8a0b2d4f","crmUserName":"Amina Otieno"}],"defaultCrmUserId":"4f2d0b8e-6c4a-4e9f-9d7b-5a3c1e9f7d5b","autoMatchByEmail":true,"updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"source is missing, or mappings is not a JSON array","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.update"]}],"x-required-scopes":["crm.importer.update"]}},"/integrations/import-jobs/{jobId}":{"get":{"tags":["Integrations"],"summary":"Get an import's progress","description":"Shows how a one-off import is going: its status, counts and up to 50 of its errors.\n\n**Permission:** `crm.importer.read` (View importer).","operationId":"getImporterImportStatus","parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The import, as the start call answered (jobId)","example":"e9d8c7b6-a5f4-4e3d-9c2b-1a0f9e8d7c6b"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"source":{"type":"string"},"mappingId":{"type":"string"},"remoteModule":{"type":"string"},"crmModule":{"type":"string"},"status":{"type":"string","description":"pending, running, completed or failed"},"totalRecords":{"type":"integer"},"importedRecords":{"type":"integer"},"skippedRecords":{"type":"integer"},"failedRecords":{"type":"integer"},"errors":{"type":"array","items":{"type":"object","properties":{"remoteRecordId":{"type":"string"},"error":{"type":"string"}}},"description":"The first 50 errors: the system's record ID and why"},"errorCount":{"type":"integer","description":"All errors, also those not listed"},"startedAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"e9d8c7b6-a5f4-4e3d-9c2b-1a0f9e8d7c6b","source":"zoho","mappingId":"c3a9d8e7-1f2b-4c5d-8e9f-0a1b2c3d4e5f","remoteModule":"Leads","crmModule":"leads","status":"completed","totalRecords":412,"importedRecords":405,"skippedRecords":4,"failedRecords":3,"errors":[{"remoteRecordId":"5123000003456002","error":"email is not valid"}],"errorCount":3,"startedAt":"2026-10-20T09:00:00.000Z","completedAt":"2026-10-20T09:04:12.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No import with that ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.read"]}],"x-required-scopes":["crm.importer.read"]}},"/custom-fields":{"get":{"tags":["Custom fields & modules"],"summary":"List custom fields","description":"Lists the custom fields your organisation added, in display order, retired ones included unless activeOnly is true. With a moduleType, also lists keys found in that module's records that no field defines, marked `inferred: true` with a guessed type (such a guessed field has `_id` instead of `id` and cannot be edited).\n\n**Permission:** `crm.customFields.read` (View custom fields).","operationId":"getCustomFieldDefinitions","parameters":[{"name":"activeOnly","in":"query","required":false,"schema":{"type":"boolean"},"description":"true to leave out retired fields","example":true},{"name":"moduleType","in":"query","required":false,"schema":{"type":"string"},"description":"Only this record type's fields. Record type: leads, deals, accounts, contacts, tickets, requests, subscriptions, quotes or products","example":"leads"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"moduleType":{"type":"string","description":"Record type the field sits on: leads, deals, accounts, contacts, tickets, requests, subscriptions, quotes or products"},"fieldKey":{"type":"string","description":"The key the value is stored under in each record's customFields"},"label":{"type":"string"},"type":{"type":"string","description":"text, textarea, email, phone, url, number, decimal, currency, date, datetime, select, multiselect, checkbox, lookup, multilookup, user, file or files"},"description":{"type":"string"},"required":{"type":"boolean"},"defaultValue":{},"options":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"},"color":{"type":"string"}}},"description":"The choices of a select or multiselect field"},"lookupModule":{"description":"For a lookup or multilookup field: the record type it points at, e.g. users"},"importSource":{},"validation":{"type":"object","properties":{"dependsOn":{"type":"object","properties":{"fieldId":{"type":"string"},"values":{"type":"array","items":{"type":"string"}},"requiredWhenVisible":{"type":"boolean"}}}},"description":"Rules: min, max, minLength, maxLength, pattern, mimeTypes, maxBytes, minCount, maxCount, appliesTo, and the show-when conditions dependsOn, alsoDependsOn and conditionsMatch"},"displayOrder":{"type":"integer"},"showInTable":{"type":"boolean"},"showInFilter":{"type":"boolean"},"isActive":{"type":"boolean","description":"false when the field is retired: hidden from forms, its values kept"},"createdBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}},"example":{"success":true,"data":[{"id":"e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8a9b","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","moduleType":"leads","fieldKey":"connection_type","label":"Connection type","type":"select","description":"How the customer wants to be connected","required":false,"defaultValue":null,"options":[{"value":"fibre","label":"Fibre","color":"#22c55e"},{"value":"wireless","label":"Wireless"}],"lookupModule":null,"importSource":null,"validation":{"dependsOn":{"fieldId":"_stage","values":["qualified"],"requiredWhenVisible":true}},"displayOrder":3,"showInTable":true,"showInFilter":true,"isActive":true,"createdBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"},{"_id":"inferred-leads-site_contact","moduleType":"leads","fieldKey":"site_contact","label":"Site Contact","type":"text","required":false,"displayOrder":100000,"showInTable":false,"showInFilter":true,"isActive":true,"createdBy":"","createdAt":"","updatedAt":"","inferred":true}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customFields.read"]}],"x-required-scopes":["crm.customFields.read"]},"post":{"tags":["Custom fields & modules"],"summary":"Create a custom field","description":"Adds a custom field to a record type (leads, deals and so on) and answers with status 201. Without a fieldKey, one is made from the label (\"Connection type\" becomes connection_type). A field named like a credential (password, secret, token…) is hidden from then on, with only admins able to press Show.\n\n**Permission:** `crm.customFields.create` (Create custom fields).","operationId":"createCustomFieldDefinition","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"alsoDependsOn":{"type":"array","items":{"type":"object","properties":{"fieldId":{"type":"string"},"values":{"type":"array"}}},"description":"Further conditions like dependsOn ({ fieldId, values } each), combined as conditionsMatch says. An empty list removes them","examples":[[{"fieldId":"connection_method","values":["wireless"]}]]},"conditionsMatch":{"type":"string","enum":["all","any"],"description":"all (the default): the field shows when every condition holds; any: when at least one does","examples":["all"]},"defaultValue":{"type":"string","description":"Value a new record starts with: usually text, though a number, true/false or a list is stored as given","examples":["fibre"]},"dependsOn":{"type":"object","properties":{"fieldId":{"type":"string"},"values":{"type":"array"},"requiredWhenVisible":{"type":"boolean"}},"description":"Show the field only while another field holds one of some values: { fieldId, values, requiredWhenVisible? }. fieldId is a select, multiselect or checkbox field's key of the same module, or _stage / _status for the record's stage, or _transition for the step being run. Send null to remove the condition","examples":[{"fieldId":"_stage","values":["qualified"],"requiredWhenVisible":true}]},"description":{"type":"string","description":"Help text shown with the field","examples":["How the customer wants to be connected"]},"displayOrder":{"type":"integer","description":"Position among the module's fields, smallest first","examples":[3]},"fieldKey":{"type":"string","description":"Key the value is stored under (up to 64 characters); made from the label when left out. Cannot be changed later","examples":["connection_type"]},"label":{"type":"string","description":"Name people see","examples":["Connection type"]},"lookupModule":{"type":"string","description":"For a lookup or multilookup field: the record type it points at, e.g. users","examples":["users"]},"moduleType":{"type":"string","description":"Record type the field goes on: leads, deals, accounts, contacts, tickets, requests, subscriptions, quotes or products","examples":["leads"]},"options":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"},"color":{"type":"string"}}},"description":"The choices of a select or multiselect field: { value, label, color? } each (a JSON array in a string also works)","examples":[[{"value":"fibre","label":"Fibre","color":"#22c55e"},{"value":"wireless","label":"Wireless"}]]},"required":{"type":"boolean","description":"Whether a form must fill the field in","examples":[false]},"showInFilter":{"type":"boolean","description":"Offer it in list filters","examples":[true]},"showInTable":{"type":"boolean","description":"Show it as a column in list views","examples":[true]},"type":{"type":"string","enum":["text","textarea","email","phone","url","number","decimal","currency","date","datetime","select","multiselect","checkbox","lookup","multilookup","user","file","files"],"description":"Kind of value it holds. Cannot be changed later with an update","examples":["select"]},"validation":{"type":"object","description":"Rules as an object (or its JSON text): min, max, minLength, maxLength, pattern, mimeTypes, maxBytes, minCount, maxCount, appliesTo; may also hold the show-when conditions","examples":[{"maxLength":50}]}},"required":["label","moduleType","type"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"moduleType":{"type":"string","description":"Record type the field sits on: leads, deals, accounts, contacts, tickets, requests, subscriptions, quotes or products"},"fieldKey":{"type":"string","description":"The key the value is stored under in each record's customFields"},"label":{"type":"string"},"type":{"type":"string","description":"text, textarea, email, phone, url, number, decimal, currency, date, datetime, select, multiselect, checkbox, lookup, multilookup, user, file or files"},"description":{"type":"string"},"required":{"type":"boolean"},"defaultValue":{},"options":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"},"color":{"type":"string"}}},"description":"The choices of a select or multiselect field"},"lookupModule":{"description":"For a lookup or multilookup field: the record type it points at, e.g. users"},"importSource":{},"validation":{"type":"object","properties":{"dependsOn":{"type":"object","properties":{"fieldId":{"type":"string"},"values":{"type":"array","items":{"type":"string"}},"requiredWhenVisible":{"type":"boolean"}}}},"description":"Rules: min, max, minLength, maxLength, pattern, mimeTypes, maxBytes, minCount, maxCount, appliesTo, and the show-when conditions dependsOn, alsoDependsOn and conditionsMatch"},"displayOrder":{"type":"integer"},"showInTable":{"type":"boolean"},"showInFilter":{"type":"boolean"},"isActive":{"type":"boolean","description":"false when the field is retired: hidden from forms, its values kept"},"createdBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8a9b","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","moduleType":"leads","fieldKey":"connection_type","label":"Connection type","type":"select","description":"How the customer wants to be connected","required":false,"defaultValue":null,"options":[{"value":"fibre","label":"Fibre","color":"#22c55e"},{"value":"wireless","label":"Wireless"}],"lookupModule":null,"importSource":null,"validation":{"dependsOn":{"fieldId":"_stage","values":["qualified"],"requiredWhenVisible":true}},"displayOrder":3,"showInTable":true,"showInFilter":true,"isActive":true,"createdBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"A show-when condition names a field that does not exist or cannot be a parent, or a JSON value is not valid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"The module already has a field with that key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customFields.create"]}],"x-required-scopes":["crm.customFields.create"]}},"/field-restrictions/mine":{"get":{"tags":["Custom fields & modules"],"summary":"List the hidden fields I may show","description":"Lists the hidden fields of one record type that you may press Show on: those whose who-can-see list names you, your role or one of your teams, or all of them if you are a custom fields admin. Everyone else sees only dots for them.\n\n**Permission:** `crm.customFields.read` (View custom fields).","operationId":"getMyFieldRestrictions","parameters":[{"name":"moduleType","in":"query","required":true,"schema":{"type":"string","enum":["leads","deals","accounts","contacts","products","quotes","requests","subscriptions","tickets"]},"description":"Record type","example":"leads"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"moduleType":{"type":"string"},"fields":{"type":"array","items":{"type":"string"},"description":"Keys of hidden custom fields you may press Show on"},"nativeFields":{"type":"array","items":{"type":"string"},"description":"Names of hidden system fields you may press Show on (empty for record types without hideable system fields). On subscriptions, password is listed for holders of the reveal permission and admins"}}}}},"example":{"success":true,"data":{"moduleType":"leads","fields":["router_password"],"nativeFields":["phone"]}}}}},"400":{"description":"moduleType is not a record type whose fields can be hidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customFields.read"]}],"x-required-scopes":["crm.customFields.read"]}},"/field-restrictions/native-fields":{"get":{"tags":["Custom fields & modules"],"summary":"List the system fields that can be hidden","description":"Lists the built-in fields that can be hidden (a fixed list, such as a lead's phone or a deal's value), each with its who-can-see list, or null when it is not hidden. Hide one with Hide a field and origin native.\n\n**Permission:** `crm.customFields.read` (View custom fields).","operationId":"getNativeFieldRestrictions","parameters":[{"name":"moduleType","in":"query","required":false,"schema":{"type":"string","enum":["leads","deals","contacts","accounts"]},"description":"Only this record type's system fields; leave out for all","example":"deals"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"moduleType":{"type":"string"},"key":{"type":"string","description":"The system field's name, the fieldKey to hide it with"},"label":{"type":"string"},"covers":{"type":"array","items":{"type":"string"},"description":"Every reply field carrying the same value, all hidden together"},"rule":{"type":"object","properties":{"id":{"type":"string","description":"The rule's ID; null for a credential-named field hidden by its name alone"},"moduleType":{"type":"string"},"fieldKey":{"type":"string"},"origin":{"type":"string","description":"custom (fieldKey is a custom field's key) or native (a system field's name)"},"roles":{"type":"array","items":{"type":"string"},"description":"Role names whose holders may press Show"},"teams":{"type":"array","items":{"type":"string"},"description":"Team IDs whose members may press Show"},"users":{"type":"array","items":{"type":"string"},"description":"IDs of people who may press Show"},"byName":{"type":"boolean","description":"true when the field is hidden only because it is named like a credential (password, secret, token…); then only admins may press Show"},"updatedBy":{"type":"string"},"updatedAt":{"type":"string","format":"date-time"}},"description":"Who may press Show; null when the field is not hidden"}}}}}},"example":{"success":true,"data":[{"moduleType":"deals","key":"value","label":"Value","covers":["value","recurringValue"],"rule":{"id":"b8c9d0e1-f2a3-4b4c-9d5e-7f8a9b0c1d2e","moduleType":"deals","fieldKey":"value","origin":"native","roles":["Support Lead"],"teams":["3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f"],"users":["7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"],"byName":false,"updatedBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","updatedAt":"2026-10-20T09:00:00.000Z"}},{"moduleType":"deals","key":"notes","label":"Notes","covers":["notes"],"rule":null}]}}}},"400":{"description":"moduleType is not leads, deals, contacts or accounts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customFields.read"]}],"x-required-scopes":["crm.customFields.read"]}},"/field-restrictions":{"get":{"tags":["Custom fields & modules"],"summary":"List hidden fields","description":"Lists the hidden fields and who may press Show on each, sorted by record type and key. A hidden field's value answers as a { secret: true, set: true } marker everywhere; Show (Reveal a hidden field) is the one way to read it. A custom field named like a credential (password, secret, token…) counts as hidden even with no list: it comes back with byName true, id null and empty lists (only admins may press Show).\n\n**Permission:** `crm.customFields.read` (View custom fields).","operationId":"getFieldRestrictions","parameters":[{"name":"moduleType","in":"query","required":false,"schema":{"type":"string","enum":["leads","deals","accounts","contacts","products","quotes","requests","subscriptions","tickets"]},"description":"Only this record type's hidden fields; leave out for all","example":"leads"},{"name":"origin","in":"query","required":false,"schema":{"type":"string","enum":["custom","native","all"]},"description":"Whose fields: custom (the default, custom fields), native (system fields such as a lead's phone) or all","example":"custom"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The rule's ID; null for a credential-named field hidden by its name alone"},"moduleType":{"type":"string"},"fieldKey":{"type":"string"},"origin":{"type":"string","description":"custom (fieldKey is a custom field's key) or native (a system field's name)"},"roles":{"type":"array","items":{"type":"string"},"description":"Role names whose holders may press Show"},"teams":{"type":"array","items":{"type":"string"},"description":"Team IDs whose members may press Show"},"users":{"type":"array","items":{"type":"string"},"description":"IDs of people who may press Show"},"byName":{"type":"boolean","description":"true when the field is hidden only because it is named like a credential (password, secret, token…); then only admins may press Show"},"updatedBy":{"type":"string"},"updatedAt":{"type":"string","format":"date-time"}}}}}},"example":{"success":true,"data":[{"id":"a7b8c9d0-e1f2-4a3b-8c4d-6e7f8a9b0c1d","moduleType":"leads","fieldKey":"router_password","origin":"custom","roles":["Support Lead"],"teams":["3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f"],"users":["7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"],"byName":false,"updatedBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","updatedAt":"2026-10-20T09:00:00.000Z"},{"id":null,"moduleType":"subscriptions","fieldKey":"wifi_password","origin":"custom","roles":[],"teams":[],"users":[],"byName":true,"updatedBy":null,"updatedAt":null}]}}}},"400":{"description":"moduleType is not a record type whose fields can be hidden, or origin is not custom, native or all","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customFields.read"]}],"x-required-scopes":["crm.customFields.read"]},"put":{"tags":["Custom fields & modules"],"summary":"Hide a field","description":"Marks a field hidden, or replaces who may press Show on it. From then on its value answers as a { secret: true, set: true } marker to everyone, and only the people on the list (by role, team or person) and custom fields admins can reveal it. Empty lists keep the field hidden with admins alone able to press Show. Needs the custom fields admin permission; each change is recorded with who made it.\n\n**Permission:** `crm.customFields.admin` (Convert custom field types).","operationId":"putFieldRestriction","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fieldKey":{"type":"string","description":"The custom field's key, or for origin native the system field's name from List the system fields that can be hidden","examples":["router_password"]},"moduleType":{"type":"string","enum":["leads","deals","accounts","contacts","products","quotes","requests","subscriptions","tickets"],"description":"Record type of the field. System fields (origin native) can be hidden on leads, deals, contacts and accounts only","examples":["leads"]},"origin":{"type":"string","enum":["custom","native"],"description":"custom (the default) for a custom field, native for a system field","examples":["custom"]},"roles":{"type":"array","items":{"type":"string"},"description":"Role names (as in List roles) whose holders may press Show; at most 200 (a JSON array in a string also works)","examples":[["Support Lead"]]},"teams":{"type":"array","items":{"type":"string","format":"uuid"},"description":"IDs of teams whose members may press Show; at most 200","examples":[["3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f"]]},"users":{"type":"array","items":{"type":"string","format":"uuid"},"description":"IDs of people who may press Show; at most 200","examples":[["7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"]]}},"required":["fieldKey","moduleType"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string","description":"The rule's ID; null for a credential-named field hidden by its name alone"},"moduleType":{"type":"string"},"fieldKey":{"type":"string"},"origin":{"type":"string","description":"custom (fieldKey is a custom field's key) or native (a system field's name)"},"roles":{"type":"array","items":{"type":"string"},"description":"Role names whose holders may press Show"},"teams":{"type":"array","items":{"type":"string"},"description":"Team IDs whose members may press Show"},"users":{"type":"array","items":{"type":"string"},"description":"IDs of people who may press Show"},"byName":{"type":"boolean","description":"true when the field is hidden only because it is named like a credential (password, secret, token…); then only admins may press Show"},"updatedBy":{"type":"string"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"a7b8c9d0-e1f2-4a3b-8c4d-6e7f8a9b0c1d","moduleType":"leads","fieldKey":"router_password","origin":"custom","roles":["Support Lead"],"teams":["3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f"],"users":["7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"],"byName":false,"updatedBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"origin is not custom or native; the record type's fields cannot be hidden; no such custom field, or the system field is not one that can be hidden; or a list is not a list, holds over 200 entries, a role name over 64 characters or an ID that is not valid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customFields.admin"]}],"x-required-scopes":["crm.customFields.admin"]}},"/filter-fields/values":{"get":{"tags":["Saved filters"],"summary":"List filter field values","description":"Lists the values actually stored in one choice field across all the module's records, with how many records hold each, most common first (at most 200). Values that are no longer options are included, so records holding them stay findable.\n\n**Permission:** `crm.leads.read` (View leads) or `crm.deals.read` (View deals) or `crm.contacts.read` (View contacts) or `crm.accounts.read` (View accounts) or `crm.requests.read` (View requests) or `crm.plans.read` (View plans) or `crm.quotes.read` (View quotes) or `crm.customModules.read` (View custom modules) or `crm.calls.read` (View calls).","operationId":"getFilterFieldValues","parameters":[{"name":"field","in":"query","required":true,"schema":{"type":"string"},"description":"The field's id from List filter fields (one with listsValues true), e.g. status or cf:<key>","example":"status"},{"name":"moduleType","in":"query","required":true,"schema":{"type":"string"},"description":"The module: leads, deals, contacts, accounts, requests, plans, quotes, calls, or custom:<slug> for your own module","example":"leads"},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Only values containing this text (any case)","example":"qual"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"},"count":{"type":"integer","description":"How many records hold the value"},"retired":{"type":"boolean","description":"true when records hold the value but it is not (or no longer) one of the field's options"}}}}}},"example":{"success":true,"data":[{"value":"qualified","label":"Qualified","count":42,"retired":false},{"value":"Hot","label":"Hot","count":3,"retired":true}]}}}},"400":{"description":"Filters are not available for that module, the field is unknown, or its stored values are not listed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You may not read that module (e.g. leads.read for leads, customModules.read for your own modules)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.read","crm.deals.read","crm.contacts.read","crm.accounts.read","crm.requests.read","crm.plans.read","crm.quotes.read","crm.customModules.read","crm.calls.read"]}],"x-required-scopes":["crm.leads.read","crm.deals.read","crm.contacts.read","crm.accounts.read","crm.requests.read","crm.plans.read","crm.quotes.read","crm.customModules.read","crm.calls.read"],"x-scope-mode":"any"}},"/filter-fields":{"get":{"tags":["Saved filters"],"summary":"List filter fields","description":"Lists every field a module's list can be filtered by (built-in and active custom fields), in record page layout order, each with its type and the operators it accepts. Any module read permission opens the call; you also need read permission on the module you ask about.\n\n**Permission:** `crm.leads.read` (View leads) or `crm.deals.read` (View deals) or `crm.contacts.read` (View contacts) or `crm.accounts.read` (View accounts) or `crm.requests.read` (View requests) or `crm.plans.read` (View plans) or `crm.quotes.read` (View quotes) or `crm.customModules.read` (View custom modules) or `crm.calls.read` (View calls).","operationId":"getFilterFields","parameters":[{"name":"moduleType","in":"query","required":true,"schema":{"type":"string"},"description":"The module: leads, deals, contacts, accounts, requests, plans, quotes, calls, or custom:<slug> for your own module","example":"leads"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"moduleType":{"type":"string"},"timezone":{"type":"string","description":"Day filters on date-time fields cut at midnight in this time zone"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"What to send back as the key in a list's filters: a native field name, or cf:<key> for a custom field"},"key":{"type":"string"},"label":{"type":"string"},"type":{"type":"string","description":"text, textarea, email, phone, url, number, decimal, currency, date, datetime, select, multiselect, checkbox, lookup, multilookup, user, file or files"},"operators":{"type":"array","items":{"type":"string"},"description":"The operators this field accepts"},"origin":{"type":"string","description":"native (built in) or custom"},"options":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}}}},"section":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"}},"description":"The record page section the field sits in, or null"},"layoutOrder":{"type":"integer"},"suggested":{"type":"boolean","description":"Suggested to pin in the filter bar"},"listsValues":{"type":"boolean","description":"true when List filter field values can list the values stored in it"}}}},"diagnostics":{"type":"object","properties":{"unresolvedLayoutKeys":{"type":"array"},"retiredLayoutKeys":{"type":"array","items":{"type":"string"}}},"description":"Only for people who may change fields or layouts: layout spots that point at no field (unresolvedLayoutKeys) or at a retired one (retiredLayoutKeys)"}}}}},"example":{"success":true,"data":{"moduleType":"leads","timezone":"Africa/Nairobi","fields":[{"id":"status","key":"status","label":"Status","type":"select","operators":["is","is_not","any_of","none_of","is_empty","is_not_empty"],"origin":"native","options":[{"value":"new","label":"New"},{"value":"qualified","label":"Qualified"}],"section":{"id":"overview","title":"Overview"},"layoutOrder":0,"suggested":true,"listsValues":true},{"id":"cf:installation_date","key":"installation_date","label":"Installation date","type":"date","operators":["is","is_not","greater_than","less_than","at_least","at_most","between","is_empty","is_not_empty"],"origin":"custom","section":null,"layoutOrder":null,"suggested":false,"listsValues":false}],"diagnostics":{"unresolvedLayoutKeys":[],"retiredLayoutKeys":["cf:old_router_model"]}}}}}},"400":{"description":"Filters are not available for that module","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You may not read that module (e.g. leads.read for leads, customModules.read for your own modules)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.read","crm.deals.read","crm.contacts.read","crm.accounts.read","crm.requests.read","crm.plans.read","crm.quotes.read","crm.customModules.read","crm.calls.read"]}],"x-required-scopes":["crm.leads.read","crm.deals.read","crm.contacts.read","crm.accounts.read","crm.requests.read","crm.plans.read","crm.quotes.read","crm.customModules.read","crm.calls.read"],"x-scope-mode":"any"}},"/custom-modules":{"get":{"tags":["Custom fields & modules"],"summary":"List custom modules","description":"Lists your organisation's own record types (custom modules) with their fields, by name. Answers at most 200 modules.\n\n**Permission:** `crm.customModules.read` (View custom modules).","operationId":"getCustomModules","parameters":[{"name":"activeOnly","in":"query","required":false,"schema":{"type":"boolean"},"description":"true to leave out switched-off modules","example":true}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string","description":"The module's address: its records are at /custom-modules/<slug>/records"},"singularLabel":{"type":"string"},"pluralLabel":{"type":"string"},"description":{"type":"string"},"icon":{"type":"string"},"color":{"type":"string"},"fields":{"type":"array","items":{"type":"object","properties":{"fieldKey":{"type":"string","description":"The key the field's value is stored under in each record's data"},"label":{"type":"string"},"type":{"type":"string","description":"Field type, e.g. text, number, date, select, multiselect, checkbox, lookup"},"required":{"type":"boolean"},"displayOrder":{"type":"integer"},"showInTable":{"type":"boolean"},"showInFilter":{"type":"boolean"},"isPrimary":{"type":"boolean","description":"The one field whose value names a record in lists, lookups and search"}}}},"enablePipeline":{"type":"boolean","description":"Whether records move through pipeline stages on a board"},"defaultPipelineId":{},"isActive":{"type":"boolean"},"createdBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}},"example":{"success":true,"data":[{"id":"e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8a9b","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","name":"Site surveys","slug":"site-surveys","singularLabel":"Site survey","pluralLabel":"Site surveys","description":"Surveys done before a fibre installation","icon":"clipboard-list","color":"#6366f1","fields":[{"fieldKey":"site_name","label":"Site name","type":"text","required":true,"displayOrder":0,"showInTable":true,"showInFilter":true,"isPrimary":true},{"fieldKey":"survey_result","label":"Result","type":"select","required":false,"options":[{"value":"ready","label":"Ready to install"},{"value":"needs_work","label":"Needs civil works"}],"displayOrder":1,"showInTable":true,"showInFilter":true,"isPrimary":false}],"enablePipeline":false,"defaultPipelineId":null,"isActive":true,"createdBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customModules.read"]}],"x-required-scopes":["crm.customModules.read"]},"post":{"tags":["Custom fields & modules"],"summary":"Create a custom module","description":"Creates a new record type of your own (a custom module) with its fields, and answers with status 201. The module also gets its own menu entry. The slug is made from the name (lowercase, dashes) unless you send one. If no field is marked primary, the first readable field becomes the one that names records.\n\n**Permission:** `crm.customModules.create` (Create custom modules).","operationId":"createCustomModule","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"color":{"type":"string","description":"Colour as a hex code (default #6366f1)","examples":["#6366f1"]},"description":{"type":"string","description":"What the module is for","examples":["Surveys done before a fibre installation"]},"enablePipeline":{"type":"boolean","description":"Whether records move through pipeline stages (default false)","examples":[false]},"fields":{"type":"array","items":{"type":"object"},"description":"The module's fields, each with fieldKey, label and type, and optionally required, options, lookupModule, displayOrder, showInTable, showInFilter and isPrimary (a JSON array in a string also works)","examples":[[{"fieldKey":"site_name","label":"Site name","type":"text","required":true,"displayOrder":0,"showInTable":true,"showInFilter":true,"isPrimary":true}]]},"icon":{"type":"string","description":"Icon name for the menu (default box)","examples":["clipboard-list"]},"name":{"type":"string","description":"The module's name","examples":["Site surveys"]},"pluralLabel":{"type":"string","description":"What several records are called; also the menu label","examples":["Site surveys"]},"singularLabel":{"type":"string","description":"What one record is called","examples":["Site survey"]},"slug":{"type":"string","description":"The module's address; made lowercase with dashes. Leave out to build it from the name","examples":["site-surveys"]}},"required":["name","pluralLabel","singularLabel"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string","description":"The module's address: its records are at /custom-modules/<slug>/records"},"singularLabel":{"type":"string"},"pluralLabel":{"type":"string"},"description":{"type":"string"},"icon":{"type":"string"},"color":{"type":"string"},"fields":{"type":"array","items":{"type":"object","properties":{"fieldKey":{"type":"string","description":"The key the field's value is stored under in each record's data"},"label":{"type":"string"},"type":{"type":"string","description":"Field type, e.g. text, number, date, select, multiselect, checkbox, lookup"},"required":{"type":"boolean"},"displayOrder":{"type":"integer"},"showInTable":{"type":"boolean"},"showInFilter":{"type":"boolean"},"isPrimary":{"type":"boolean","description":"The one field whose value names a record in lists, lookups and search"}}}},"enablePipeline":{"type":"boolean","description":"Whether records move through pipeline stages on a board"},"defaultPipelineId":{},"isActive":{"type":"boolean"},"createdBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8a9b","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","name":"Site surveys","slug":"site-surveys","singularLabel":"Site survey","pluralLabel":"Site surveys","description":"Surveys done before a fibre installation","icon":"clipboard-list","color":"#6366f1","fields":[{"fieldKey":"site_name","label":"Site name","type":"text","required":true,"displayOrder":0,"showInTable":true,"showInFilter":true,"isPrimary":true},{"fieldKey":"survey_result","label":"Result","type":"select","required":false,"options":[{"value":"ready","label":"Ready to install"},{"value":"needs_work","label":"Needs civil works"}],"displayOrder":1,"showInTable":true,"showInFilter":true,"isPrimary":false}],"enablePipeline":false,"defaultPipelineId":null,"isActive":true,"createdBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"The slug has no letter or number, is taken by an existing CRM page (e.g. sales, settings, billing), or fields is not valid JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"A module with that slug already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customModules.create"]}],"x-required-scopes":["crm.customModules.create"]}},"/custom-modules/{moduleSlug}":{"get":{"tags":["Custom fields & modules"],"summary":"Get a custom module","description":"Answers one of your organisation's own record types (a custom module) with its fields, found by its slug. Switched-off modules are answered too.\n\n**Permission:** `crm.customModules.read` (View custom modules).","operationId":"getCustomModule","parameters":[{"name":"moduleSlug","in":"path","required":true,"schema":{"type":"string"},"description":"The module's slug","example":"site-surveys"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string","description":"The module's address: its records are at /custom-modules/<slug>/records"},"singularLabel":{"type":"string"},"pluralLabel":{"type":"string"},"description":{"type":"string"},"icon":{"type":"string"},"color":{"type":"string"},"fields":{"type":"array","items":{"type":"object","properties":{"fieldKey":{"type":"string","description":"The key the field's value is stored under in each record's data"},"label":{"type":"string"},"type":{"type":"string","description":"Field type, e.g. text, number, date, select, multiselect, checkbox, lookup"},"required":{"type":"boolean"},"displayOrder":{"type":"integer"},"showInTable":{"type":"boolean"},"showInFilter":{"type":"boolean"},"isPrimary":{"type":"boolean","description":"The one field whose value names a record in lists, lookups and search"}}}},"enablePipeline":{"type":"boolean","description":"Whether records move through pipeline stages on a board"},"defaultPipelineId":{},"isActive":{"type":"boolean"},"createdBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8a9b","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","name":"Site surveys","slug":"site-surveys","singularLabel":"Site survey","pluralLabel":"Site surveys","description":"Surveys done before a fibre installation","icon":"clipboard-list","color":"#6366f1","fields":[{"fieldKey":"site_name","label":"Site name","type":"text","required":true,"displayOrder":0,"showInTable":true,"showInFilter":true,"isPrimary":true},{"fieldKey":"survey_result","label":"Result","type":"select","required":false,"options":[{"value":"ready","label":"Ready to install"},{"value":"needs_work","label":"Needs civil works"}],"displayOrder":1,"showInTable":true,"showInFilter":true,"isPrimary":false}],"enablePipeline":false,"defaultPipelineId":null,"isActive":true,"createdBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No custom module with that slug in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customModules.read"]}],"x-required-scopes":["crm.customModules.read"]}},"/custom-modules/{moduleSlug}/records":{"get":{"tags":["Custom fields & modules"],"summary":"List a custom module's records","description":"Lists the records of one of your organisation's own modules, newest first, a page at a time, with the owner's name beside the owner's ID. Narrow it with a free-text search (q) or field conditions (filters).\n\n**Permission:** `crm.customModules.read` (View custom modules).","operationId":"getCustomRecords","parameters":[{"name":"moduleSlug","in":"path","required":true,"schema":{"type":"string"},"description":"The module's slug","example":"site-surveys"},{"name":"filters","in":"query","required":false,"schema":{"type":"object"},"description":"Field conditions keyed by filter field ID (a native field name, or cf:<key> for your own fields; GET /filter-fields lists them), each { operator, value } or { operator, values }; a bare value means \"is\". A JSON object in a string also works. An invalid condition answers 400 naming the field","example":{"status":{"operator":"is","values":["active"]}}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"},"description":"Records per page (default 25, at most 500)","example":25},{"name":"page","in":"query","required":false,"schema":{"type":"integer"},"description":"Page number, from 1 (default 1)","example":1},{"name":"q","in":"query","required":false,"schema":{"type":"string"},"description":"Free text matched against every value in the record","example":"Westlands"},{"name":"sort","in":"query","required":false,"schema":{"type":"string"},"description":"Accepted but not used yet: records always come newest first","example":"createdAt"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"moduleSlug":{"type":"string"},"data":{"type":"object","properties":{"site_name":{"type":"string"},"survey_result":{"type":"string"}},"description":"The record's values, keyed by the module's field keys"},"ownerId":{"type":"string"},"createdBy":{"type":"string"},"updatedBy":{},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"_expanded":{"type":"object","properties":{"ownerId":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"module":{"type":"string"}}}},"description":"Names for the IDs the record holds (here the owner), keyed by field: id, label and module"}}}},"total":{"type":"integer","description":"How many records match, over all pages"},"page":{"type":"integer"},"limit":{"type":"integer"}}},"example":{"success":true,"data":[{"id":"f6a7b8c9-d0e1-4f2a-9b3c-5d6e7f8a9b0c","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","moduleSlug":"site-surveys","data":{"site_name":"Westlands Plaza, 4th floor","survey_result":"ready"},"ownerId":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","createdBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","updatedBy":null,"externalIds":[],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z","_expanded":{"ownerId":{"id":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","label":"Amina Otieno","module":"users"}}}],"total":1,"page":1,"limit":25}}}},"400":{"description":"filters is not valid JSON, names an unknown field, or uses an operator or value the field does not take","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customModules.read"]}],"x-required-scopes":["crm.customModules.read"]},"post":{"tags":["Custom fields & modules"],"summary":"Create a custom module record","description":"Adds a record to one of your organisation's own modules and answers with status 201. The values are checked against the module's fields (required ones, and choices); keys the module does not know are kept as sent. Automations set to run on new records of the module are started.\n\n**Permission:** `crm.customModules.create` (Create custom modules).","operationId":"createCustomRecord","parameters":[{"name":"moduleSlug","in":"path","required":true,"schema":{"type":"string"},"description":"The module's slug","example":"site-surveys"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","description":"The record's values, keyed by the module's field keys (a JSON object in a string also works)","examples":[{"site_name":"Westlands Plaza, 4th floor","survey_result":"ready"}]},"ownerId":{"type":"string","format":"uuid","description":"CRM user ID of the record's owner; defaults to you","examples":["7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"]}},"required":["data"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"moduleSlug":{"type":"string"},"data":{"type":"object","properties":{"site_name":{"type":"string"},"survey_result":{"type":"string"}},"description":"The record's values, keyed by the module's field keys"},"ownerId":{"type":"string"},"createdBy":{"type":"string"},"updatedBy":{},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"f6a7b8c9-d0e1-4f2a-9b3c-5d6e7f8a9b0c","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","moduleSlug":"site-surveys","data":{"site_name":"Westlands Plaza, 4th floor","survey_result":"ready"},"ownerId":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","createdBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","updatedBy":null,"externalIds":[],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"A required field is empty, or a choice field holds a value that is not one of its options","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No custom module with that slug in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customModules.create"]}],"x-required-scopes":["crm.customModules.create"]}},"/custom-modules/{moduleSlug}/records/{recordId}":{"get":{"tags":["Custom fields & modules"],"summary":"Get a custom module record","description":"Answers one record of one of your organisation's own modules, with the owner's name beside the owner's ID. The record must belong to the module in the address.\n\n**Permission:** `crm.customModules.read` (View custom modules).","operationId":"getCustomRecord","parameters":[{"name":"moduleSlug","in":"path","required":true,"schema":{"type":"string"},"description":"The module's slug","example":"site-surveys"},{"name":"recordId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The record's ID","example":"f6a7b8c9-d0e1-4f2a-9b3c-5d6e7f8a9b0c"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"moduleSlug":{"type":"string"},"data":{"type":"object","properties":{"site_name":{"type":"string"},"survey_result":{"type":"string"}},"description":"The record's values, keyed by the module's field keys"},"ownerId":{"type":"string"},"createdBy":{"type":"string"},"updatedBy":{},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"_expanded":{"type":"object","properties":{"ownerId":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"module":{"type":"string"}}}},"description":"Names for the IDs the record holds (here the owner), keyed by field: id, label and module"}}}}},"example":{"success":true,"data":{"id":"f6a7b8c9-d0e1-4f2a-9b3c-5d6e7f8a9b0c","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","moduleSlug":"site-surveys","data":{"site_name":"Westlands Plaza, 4th floor","survey_result":"ready"},"ownerId":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","createdBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","updatedBy":null,"externalIds":[],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z","_expanded":{"ownerId":{"id":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","label":"Amina Otieno","module":"users"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No record with that ID in that module of your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customModules.read"]}],"x-required-scopes":["crm.customModules.read"]}},"/channel-configs":{"get":{"tags":["Help desk setup"],"summary":"List channels","description":"Lists your organisation's channels (email, WhatsApp, live chat and the like) by name, up to 200, with their secrets masked.\n\n**Permission:** `crm.channelConfigs.read` (View channel configs).","operationId":"getChannelConfigs","parameters":[{"name":"channelType","in":"query","required":false,"schema":{"type":"string","enum":["whatsapp","whatsapp_web","zoho_desk","facebook","instagram","tiktok","email","sms","live_chat","web_widget","api"]},"description":"Only channels of this type","example":"email"},{"name":"isActive","in":"query","required":false,"schema":{"type":"boolean"},"description":"true for only switched-on channels, anything else for only switched-off ones"},{"name":"isDefaultNotifier","in":"query","required":false,"schema":{"type":"boolean"},"description":"true for only the default notifier, anything else for the rest"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"configs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"channelType":{"type":"string","description":"whatsapp, whatsapp_web, zoho_desk, facebook, instagram, tiktok, email, sms, live_chat, web_widget or api"},"name":{"type":"string"},"isActive":{"type":"boolean"},"isDefaultNotifier":{"type":"boolean","description":"The email channel survey and ticket-update emails go out from (one per organisation)"},"credentials":{"type":"object","properties":{"imapHost":{"type":"string"},"imapPort":{"type":"integer"},"smtpHost":{"type":"string"},"smtpPort":{"type":"integer"},"username":{"type":"string"},"password":{"type":"string"},"fromAddress":{"type":"string"},"fromName":{"type":"string"}},"description":"The provider settings. Secrets come back masked to their last four characters (****9f2c); email server names, ports, username and From details come back in clear"},"webhookSecret":{"type":"string","description":"The secret the provider's webhook calls are checked with (sent in clear)"},"webhookUrl":{"type":"string","description":"The address to give the provider for incoming messages"},"routingRules":{"type":"object","properties":{"autoCreateTicket":{"type":"boolean"},"defaultTeamId":{"type":"string"},"defaultPriority":{"type":"string"}},"description":"Defaults for new tickets: autoCreateTicket, defaultTeamId (department), defaultPriority, defaultAssignee, defaultTags; Zoho Desk also departmentTeams"},"metadata":{"type":"object","properties":{}},"lastSyncAt":{"type":"string","format":"date-time"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}},"example":{"success":true,"configs":[{"id":"b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","channelType":"email","name":"Support mailbox","isActive":true,"isDefaultNotifier":true,"credentials":{"imapHost":"mail.example.com","imapPort":993,"smtpHost":"mail.example.com","smtpPort":465,"username":"support@example.com","password":"****9f2c","fromAddress":"support@example.com","fromName":"Example Support"},"webhookSecret":"6f1d2c3b4a5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f8","webhookUrl":"https://api.crm.kazee.africa/api/webhooks/channels/b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d","routingRules":{"autoCreateTicket":true,"defaultTeamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","defaultPriority":"medium"},"metadata":{},"lastSyncAt":"2026-10-21T08:55:00.000Z","deletedAt":null,"createdAt":"2026-06-01T08:00:00.000Z","updatedAt":"2026-10-21T08:55:00.000Z"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.channelConfigs.read"]}],"x-required-scopes":["crm.channelConfigs.read"]},"post":{"tags":["Help desk setup"],"summary":"Create a channel","description":"Connects a channel tickets come in through (an email mailbox, WhatsApp, Zoho Desk, live chat and the like). It gets a webhook secret and address for the provider, and opens tickets by default. A WhatsApp linked device (whatsapp_web) needs the disclaimer accepted and is registered with the WhatsApp bridge (bridge: { registered, error? }); Zoho Desk exchanges its one-time grant code and starts in test mode (zohoWebhook: { registered, error? }); an email channel with metadata.mode \"forwarding\" gets a Kazee forwarding address instead of a mailbox. Secrets in the reply are masked.\n\n**Permission:** `crm.channelConfigs.create` (Create channel configs).","operationId":"createChannelConfig","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"channelType":{"type":"string","enum":["whatsapp","whatsapp_web","zoho_desk","facebook","instagram","tiktok","email","sms","live_chat","web_widget","api"],"description":"What kind of channel","examples":["email"]},"credentials":{"type":"object","default":{},"description":"The provider's settings: for email imapHost, imapPort, smtpHost, smtpPort, username, password, fromAddress, fromName; for WhatsApp Cloud API its access token and phone number ID; for Zoho Desk the one-time grant code (a JSON text also works)","examples":[{"imapHost":"mail.example.com","imapPort":993,"smtpHost":"mail.example.com","smtpPort":465,"username":"support@example.com","password":"example-mailbox-password","fromAddress":"support@example.com"}]},"disclaimerAccepted":{"type":"boolean","description":"whatsapp_web only: you accept the unofficial-connection disclaimer; required for that type"},"disclaimerVersion":{"type":"string","description":"whatsapp_web only: the disclaimer version accepted; default the current one"},"isActive":{"type":"boolean","default":true,"description":"Whether it is switched on","examples":[true]},"metadata":{"type":"object","default":{},"description":"Channel-specific settings; for a forwarding email address { \"mode\": \"forwarding\", \"publicAddress\": \"support@yourcompany.com\" } (a JSON text also works)","examples":[{}]},"name":{"type":"string","description":"Its name in Desk","examples":["Support mailbox"]},"routingRules":{"type":"object","default":{},"description":"Defaults for tickets the channel opens: { autoCreateTicket, defaultTeamId, defaultPriority, defaultAssignee, defaultTags, departmentTeams (Zoho Desk: { \"<zoho department ID>\": \"<department ID>\" }) }. Keys you send are merged into what is stored; null clears one. Departments must be live ones of your organisation (a JSON text also works)","examples":[{"autoCreateTicket":true,"defaultTeamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","defaultPriority":"medium"}]}},"required":["channelType","name"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"config":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"channelType":{"type":"string","description":"whatsapp, whatsapp_web, zoho_desk, facebook, instagram, tiktok, email, sms, live_chat, web_widget or api"},"name":{"type":"string"},"isActive":{"type":"boolean"},"isDefaultNotifier":{"type":"boolean","description":"The email channel survey and ticket-update emails go out from (one per organisation)"},"credentials":{"type":"object","properties":{"imapHost":{"type":"string"},"imapPort":{"type":"integer"},"smtpHost":{"type":"string"},"smtpPort":{"type":"integer"},"username":{"type":"string"},"password":{"type":"string"},"fromAddress":{"type":"string"},"fromName":{"type":"string"}},"description":"The provider settings. Secrets come back masked to their last four characters (****9f2c); email server names, ports, username and From details come back in clear"},"webhookSecret":{"type":"string","description":"The secret the provider's webhook calls are checked with (sent in clear)"},"webhookUrl":{"type":"string","description":"The address to give the provider for incoming messages"},"routingRules":{"type":"object","properties":{"autoCreateTicket":{"type":"boolean"},"defaultTeamId":{"type":"string"},"defaultPriority":{"type":"string"}},"description":"Defaults for new tickets: autoCreateTicket, defaultTeamId (department), defaultPriority, defaultAssignee, defaultTags; Zoho Desk also departmentTeams"},"metadata":{"type":"object","properties":{}},"lastSyncAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Channel configuration created","config":{"id":"b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","channelType":"email","name":"Support mailbox","isActive":true,"isDefaultNotifier":false,"credentials":{"imapHost":"mail.example.com","imapPort":993,"smtpHost":"mail.example.com","smtpPort":465,"username":"support@example.com","password":"****9f2c","fromAddress":"support@example.com","fromName":"Example Support"},"webhookSecret":"6f1d2c3b4a5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f8","webhookUrl":"https://api.crm.kazee.africa/api/webhooks/channels/b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d","routingRules":{"autoCreateTicket":true,"defaultTeamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","defaultPriority":"medium"},"metadata":{},"lastSyncAt":null,"deletedAt":null,"createdAt":"2026-06-01T08:00:00.000Z","updatedAt":"2026-10-21T08:55:00.000Z"}}}}},"400":{"description":"channelType is not one of the listed values, a JSON field is not valid, the WhatsApp disclaimer was not accepted, a department in routingRules is not a live one, or a forwarding address is not valid or already used","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.channelConfigs.create"]}],"x-required-scopes":["crm.channelConfigs.create"]}},"/channel-configs/{configId}":{"get":{"tags":["Help desk setup"],"summary":"Get a channel","description":"Reads one channel, with its secrets masked.\n\n**Permission:** `crm.channelConfigs.read` (View channel configs).","operationId":"getChannelConfig","parameters":[{"name":"configId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The channel to read","example":"b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"config":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"channelType":{"type":"string","description":"whatsapp, whatsapp_web, zoho_desk, facebook, instagram, tiktok, email, sms, live_chat, web_widget or api"},"name":{"type":"string"},"isActive":{"type":"boolean"},"isDefaultNotifier":{"type":"boolean","description":"The email channel survey and ticket-update emails go out from (one per organisation)"},"credentials":{"type":"object","properties":{"imapHost":{"type":"string"},"imapPort":{"type":"integer"},"smtpHost":{"type":"string"},"smtpPort":{"type":"integer"},"username":{"type":"string"},"password":{"type":"string"},"fromAddress":{"type":"string"},"fromName":{"type":"string"}},"description":"The provider settings. Secrets come back masked to their last four characters (****9f2c); email server names, ports, username and From details come back in clear"},"webhookSecret":{"type":"string","description":"The secret the provider's webhook calls are checked with (sent in clear)"},"webhookUrl":{"type":"string","description":"The address to give the provider for incoming messages"},"routingRules":{"type":"object","properties":{"autoCreateTicket":{"type":"boolean"},"defaultTeamId":{"type":"string"},"defaultPriority":{"type":"string"}},"description":"Defaults for new tickets: autoCreateTicket, defaultTeamId (department), defaultPriority, defaultAssignee, defaultTags; Zoho Desk also departmentTeams"},"metadata":{"type":"object","properties":{}},"lastSyncAt":{"type":"string","format":"date-time"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"config":{"id":"b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","channelType":"email","name":"Support mailbox","isActive":true,"isDefaultNotifier":true,"credentials":{"imapHost":"mail.example.com","imapPort":993,"smtpHost":"mail.example.com","smtpPort":465,"username":"support@example.com","password":"****9f2c","fromAddress":"support@example.com","fromName":"Example Support"},"webhookSecret":"6f1d2c3b4a5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f8","webhookUrl":"https://api.crm.kazee.africa/api/webhooks/channels/b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d","routingRules":{"autoCreateTicket":true,"defaultTeamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","defaultPriority":"medium"},"metadata":{},"lastSyncAt":"2026-10-21T08:55:00.000Z","deletedAt":null,"createdAt":"2026-06-01T08:00:00.000Z","updatedAt":"2026-10-21T08:55:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No channel with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.channelConfigs.read"]}],"x-required-scopes":["crm.channelConfigs.read"]},"put":{"tags":["Help desk setup"],"summary":"Update a channel","description":"Changes a channel. Only what you send changes: a masked or empty secret (as the reads return it) keeps the stored one, routing rules are merged, and a forwarding email address keeps its address and test state. Switching a WhatsApp linked device on or off starts or stops it on the bridge (bridge); Zoho Desk settings merge in, a new grant code reconnects, and its webhook follows (zohoWebhook). Secrets in the reply are masked.\n\n**Permission:** `crm.channelConfigs.update` (Update channel configs).","operationId":"updateChannelConfig","parameters":[{"name":"configId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The channel to change","example":"b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"credentials":{"type":"object","description":"Provider settings to change; masked or empty values keep what is stored. Ignored for a forwarding address (a JSON text also works)","examples":[{"password":"new-mailbox-password"}]},"isActive":{"type":"boolean","description":"Whether it is switched on","examples":[true]},"metadata":{"type":"object","description":"Channel-specific settings (a JSON text also works)"},"name":{"type":"string","description":"New name","examples":["Support mailbox"]},"routingRules":{"type":"object","description":"Defaults for tickets the channel opens: { autoCreateTicket, defaultTeamId, defaultPriority, defaultAssignee, defaultTags, departmentTeams (Zoho Desk: { \"<zoho department ID>\": \"<department ID>\" }) }. Keys you send are merged into what is stored; null clears one. Departments must be live ones of your organisation (a JSON text also works)","examples":[{"defaultPriority":"high"}]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"config":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"channelType":{"type":"string","description":"whatsapp, whatsapp_web, zoho_desk, facebook, instagram, tiktok, email, sms, live_chat, web_widget or api"},"name":{"type":"string"},"isActive":{"type":"boolean"},"isDefaultNotifier":{"type":"boolean","description":"The email channel survey and ticket-update emails go out from (one per organisation)"},"credentials":{"type":"object","properties":{"imapHost":{"type":"string"},"imapPort":{"type":"integer"},"smtpHost":{"type":"string"},"smtpPort":{"type":"integer"},"username":{"type":"string"},"password":{"type":"string"},"fromAddress":{"type":"string"},"fromName":{"type":"string"}},"description":"The provider settings. Secrets come back masked to their last four characters (****9f2c); email server names, ports, username and From details come back in clear"},"webhookSecret":{"type":"string","description":"The secret the provider's webhook calls are checked with (sent in clear)"},"webhookUrl":{"type":"string","description":"The address to give the provider for incoming messages"},"routingRules":{"type":"object","properties":{"autoCreateTicket":{"type":"boolean"},"defaultTeamId":{"type":"string"},"defaultPriority":{"type":"string"}},"description":"Defaults for new tickets: autoCreateTicket, defaultTeamId (department), defaultPriority, defaultAssignee, defaultTags; Zoho Desk also departmentTeams"},"metadata":{"type":"object","properties":{}},"lastSyncAt":{"type":"string","format":"date-time"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Channel configuration updated","config":{"id":"b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","channelType":"email","name":"Support mailbox","isActive":true,"isDefaultNotifier":true,"credentials":{"imapHost":"mail.example.com","imapPort":993,"smtpHost":"mail.example.com","smtpPort":465,"username":"support@example.com","password":"****9f2c","fromAddress":"support@example.com","fromName":"Example Support"},"webhookSecret":"6f1d2c3b4a5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f8","webhookUrl":"https://api.crm.kazee.africa/api/webhooks/channels/b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d","routingRules":{"autoCreateTicket":true,"defaultTeamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","defaultPriority":"medium"},"metadata":{},"lastSyncAt":"2026-10-21T08:55:00.000Z","deletedAt":null,"createdAt":"2026-06-01T08:00:00.000Z","updatedAt":"2026-10-21T08:55:00.000Z"}}}}},"400":{"description":"A JSON field is not valid, a department in routingRules is not a live one, or a forwarding address cannot be switched on","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No channel with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.channelConfigs.update"]}],"x-required-scopes":["crm.channelConfigs.update"]},"delete":{"tags":["Help desk setup"],"summary":"Delete a channel","description":"Removes a channel. By default it is switched off and hidden, its tickets and history kept. With hard true it is erased, unless it has message history, in which case it is hidden instead. A WhatsApp linked device is also unlinked and wiped on the bridge (bridge: { removed, error? }). A Zoho Desk channel is only disconnected (webhook removed, switched off) and answers { disconnected, zohoWebhookRemoved }.\n\n**Permission:** `crm.channelConfigs.delete` (Delete channel configs).","operationId":"deleteChannelConfig","parameters":[{"name":"configId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The channel to remove","example":"b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d"},{"name":"hard","in":"query","required":false,"schema":{"type":"boolean","default":false},"description":"true to erase it when it has no message history","example":false}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"softDeleted":{"type":"boolean","description":"The channel was hidden rather than erased"},"message":{"type":"string"}}},"example":{"success":true,"softDeleted":true,"message":"Channel configuration deactivated"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No channel with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.channelConfigs.delete"]}],"x-required-scopes":["crm.channelConfigs.delete"]}},"/channel-configs/{configId}/forwarding-status":{"get":{"tags":["Help desk setup"],"summary":"Get a channel's forwarding status","description":"Says whether a forwarding email channel's test email has come back through the forwarding path, with a plain-words message for the person setting it up.\n\n**Permission:** `crm.channelConfigs.read` (View channel configs).","operationId":"getChannelForwardingStatus","parameters":[{"name":"configId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The forwarding email channel","example":"b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"state":{"type":"string","description":"never_tested, waiting (sent, not back yet), verified, or not_arrived (nothing after 5 minutes)"},"lastTestSentAt":{"type":"string","format":"date-time"},"verifiedAt":{"type":"string","format":"date-time"},"sendAs":{"type":"string","description":"public or desk: the address replies go out as"},"message":{"type":"string","description":"What to do next, in words"}}},"example":{"success":true,"state":"verified","lastTestSentAt":"2026-10-21T09:00:00.000Z","verifiedAt":"2026-10-21T09:00:40.000Z","sendAs":"public","message":"Forwarding works. Replies go out as support@example.com."}}}},"400":{"description":"The channel is not a forwarding address","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No channel with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.channelConfigs.read"]}],"x-required-scopes":["crm.channelConfigs.read"]}},"/channel-configs/{configId}/templates":{"get":{"tags":["Help desk setup"],"summary":"List a channel's message templates","description":"Lists the provider's approved message templates for a channel (WhatsApp message templates), which are what can still be sent once a customer's 24-hour reply window has closed. Kept for 10 minutes; refresh fetches them again. A channel without templates answers supported false. When the provider fails, the reply has success false, the error and the last templates kept.\n\n**Permission:** `crm.channelConfigs.read` (View channel configs).","operationId":"getChannelTemplates","parameters":[{"name":"configId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The channel","example":"b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d"},{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean"},"description":"true to fetch from the provider now instead of the stored list","example":false}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"supported":{"type":"boolean","description":"Whether the channel has templates at all"},"cached":{"type":"boolean","description":"The list came from the 10-minute store"},"templates":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"language":{"type":"string"},"category":{"type":"string"},"body":{"type":"string","description":"The text with {{1}}-style placeholders"},"bodyParamCount":{"type":"integer","description":"How many {{n}} values the template needs"}}}}}},"example":{"success":true,"supported":true,"cached":false,"templates":[{"name":"ticket_update","language":"en","category":"UTILITY","body":"Hi {{1}}, there is an update on your ticket {{2}}.","bodyParamCount":2}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No channel with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.channelConfigs.read"]}],"x-required-scopes":["crm.channelConfigs.read"]}},"/desk/channels/linked-device/disclaimer":{"get":{"tags":["Help desk setup"],"summary":"Get the WhatsApp linked-device disclaimer","description":"Answers the warning an admin must read and accept before adding a WhatsApp linked-device channel (an unofficial WhatsApp Web style connection), and whether this server can run one at all.\n\n**Permission:** `crm.channelConfigs.read` (View channel configs).","operationId":"getLinkedDeviceDisclaimer","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"version":{"type":"string","description":"The disclaimer's version; the accepted version is stored with the channel"},"title":{"type":"string"},"points":{"type":"array","items":{"type":"string"},"description":"The warnings, one per line"},"acknowledgement":{"type":"string","description":"The sentence the admin ticks to accept"},"available":{"type":"boolean","description":"Whether this server has the linked-device bridge set up, so such a channel can work"}}},"example":{"success":true,"version":"2026-09-19","title":"WhatsApp — linked device: unofficial connection","points":["This option connects Kazee Desk to your WhatsApp number the way WhatsApp Web does, using open-source software (wacli / whatsmeow) that is not provided, endorsed or supported by WhatsApp or Meta.","It may breach WhatsApp's Terms of Service. WhatsApp can restrict or permanently ban the number at any time, without notice. Kazee cannot prevent or reverse a ban."],"acknowledgement":"I understand this is an unofficial connection and accept the risk of losing this WhatsApp number.","available":true}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.channelConfigs.read"]}],"x-required-scopes":["crm.channelConfigs.read"]}},"/desk/channels/{configId}/pairing":{"get":{"tags":["Help desk setup"],"summary":"Get a WhatsApp linked-device channel's pairing status","description":"Says whether a WhatsApp linked-device channel is paired and connected, with the open pairing code or QR and the last error. The Desk pairing screen polls it every few seconds. For any other kind of channel it answers supported: false; while the bridge server is not set up or has not met this channel yet, account is null and error says why.\n\n**Permission:** `crm.channelConfigs.read` (View channel configs).","operationId":"getDeskChannelPairing","parameters":[{"name":"configId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The WhatsApp linked-device channel","example":"b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"supported":{"type":"boolean","description":"False when the channel is not a WhatsApp linked-device (whatsapp_web) channel"},"bridgeConfigured":{"type":"boolean","description":"Whether this server has the linked-device bridge set up"},"account":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"enabled":{"type":"boolean"},"status":{"type":"string","description":"unpaired, pairing, starting, connected, disconnected, logged_out or stopped"},"paired":{"type":"boolean","description":"Whether a phone is linked"},"lastEventAt":{"type":"string","format":"date-time"},"lastEvent":{"type":"string"},"lastError":{"description":"The bridge's last error, if any"},"restarts":{"type":"integer","description":"How many times the bridge restarted this connection"},"pairing":{"type":"object","properties":{"mode":{"type":"string"},"code":{"type":"string"},"startedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"}},"description":"The open pairing attempt: mode code (type the code on the phone under Linked devices) or qr (scan qr); null when none is open"}},"description":"The bridge's view of the number; null when it has none"}}},"example":{"success":true,"supported":true,"bridgeConfigured":true,"account":{"id":"b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d","label":"Support WhatsApp","enabled":true,"status":"pairing","paired":false,"lastEventAt":"2026-10-20T08:30:00.000Z","lastEvent":"pairing_code","lastError":null,"restarts":0,"pairing":{"mode":"code","code":"K7QX-2M9P","startedAt":"2026-10-20T08:30:00.000Z","expiresAt":"2026-10-20T08:33:00.000Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No channel with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.channelConfigs.read"]}],"x-required-scopes":["crm.channelConfigs.read"]},"post":{"tags":["Help desk setup"],"summary":"Start pairing a WhatsApp linked-device channel","description":"Starts linking a WhatsApp number to a linked-device channel. With phone, WhatsApp shows an 8-character code to type on the phone under Linked devices, Link with phone number; without it, a QR code to scan. Then poll the pairing status for the code or QR and the outcome. Needs channel admin permission.\n\n**Permission:** `crm.channelConfigs.admin` (Administer channel configs).","operationId":"startDeskChannelPairing","parameters":[{"name":"configId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The WhatsApp linked-device channel","example":"b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"phone":{"type":"string","description":"The WhatsApp number in full international form, to pair by code; leave out to pair by QR","examples":["+254712345678"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"account":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"enabled":{"type":"boolean"},"status":{"type":"string","description":"unpaired, pairing, starting, connected, disconnected, logged_out or stopped"},"paired":{"type":"boolean","description":"Whether a phone is linked"},"lastEventAt":{"type":"string","format":"date-time"},"lastEvent":{"type":"string"},"lastError":{"description":"The bridge's last error, if any"},"restarts":{"type":"integer","description":"How many times the bridge restarted this connection"},"pairing":{"type":"object","properties":{"mode":{"type":"string"},"code":{"type":"string"},"startedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"}},"description":"The open pairing attempt: mode code (type the code on the phone under Linked devices) or qr (scan qr); null when none is open"}}}}},"example":{"success":true,"account":{"id":"b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d","label":"Support WhatsApp","enabled":true,"status":"pairing","paired":false,"lastEventAt":"2026-10-20T08:30:00.000Z","lastEvent":"pairing_code","lastError":null,"restarts":0,"pairing":{"mode":"code","code":"K7QX-2M9P","startedAt":"2026-10-20T08:30:00.000Z","expiresAt":"2026-10-20T08:33:00.000Z"}}}}}},"400":{"description":"The channel is not a WhatsApp linked-device channel, has no webhook address or secret (recreate it), or phone is not a full international number","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No channel with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"The linked-device bridge is not set up, unreachable or refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.channelConfigs.admin"]}],"x-required-scopes":["crm.channelConfigs.admin"]}},"/live-chat/{configId}/messages":{"get":{"tags":["Live chat"],"summary":"Get a live chat's messages","description":"For the live chat widget on a website: answers the conversation of a visitor's chat session (never internal notes), oldest first, so the widget can poll for new replies. Needs no sign-in. A session with no ticket yet answers an empty list.\n\n**Sign-in:** not needed.","operationId":"liveChatGetMessages","parameters":[{"name":"configId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The organisation's live chat channel, from the widget's embed code","example":"b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d"},{"name":"after","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only messages sent after this moment: send the newest sentAt you have","example":"2026-10-20T08:15:00.000Z"},{"name":"sessionId","in":"query","required":true,"schema":{"type":"string"},"description":"The visitor's chat session, made up by the widget and kept in the browser; it ties the visitor to their ticket","example":"visitor-7f3a9c2e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"content":{"type":"string"},"senderType":{"type":"string","description":"customer (the visitor), agent or system"},"sentAt":{"type":"string","format":"date-time"},"senderName":{"type":"string","description":"Support Agent for agent and system messages; the visitor's name (or You) for theirs"}}}},"ticketId":{"type":"string","description":"The ticket the chat lives in; absent while there is none"},"status":{"type":"string","description":"The ticket's status; absent while there is none"}}},"example":{"success":true,"messages":[{"id":"6b7c8d9e-0f1a-4b2c-9d4e-5f6a7b8c9d0e","content":"Hi, my internet has been down since 7am.","senderType":"customer","sentAt":"2026-10-20T08:15:00.000Z","senderName":"Amina Wanjiru"},{"id":"5a6b7c8d-9e0f-4a1b-8c3d-4e5f6a7b8c9d","content":"Sorry about that. I have restarted your line; please check now.","senderType":"agent","sentAt":"2026-10-20T08:42:00.000Z","senderName":"Support Agent"}],"ticketId":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","status":"open"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"description":"The channel is switched off","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No channel with that ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"Something went wrong reading the chat","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]},"post":{"tags":["Live chat"],"summary":"Send a live chat message","description":"For the live chat widget on a website: sends a visitor's message. The first message of a session (or the first after its ticket was resolved or closed) opens a new live chat ticket in the channel's department; later ones are added to that ticket as customer replies (a ticket on hold or waiting on the customer goes back to in progress, tagged back from hold), and the agents get notified. Needs no sign-in.\n\n**Sign-in:** not needed.","operationId":"liveChatSendMessage","parameters":[{"name":"configId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The organisation's live chat channel, from the widget's embed code","example":"b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string","description":"The visitor's message","examples":["Hi, my internet has been down since 7am."]},"senderEmail":{"type":"string","format":"email","description":"The visitor's email; fills in the ticket's requester when it has none","examples":["amina.wanjiru@example.com"]},"senderName":{"type":"string","description":"The visitor's name; fills in the ticket's requester when it has none (Visitor while unknown)","examples":["Amina Wanjiru"]},"sessionId":{"type":"string","description":"The visitor's chat session, made up by the widget and kept in the browser; it ties the visitor to their ticket","examples":["visitor-7f3a9c2e"]}},"required":["content","sessionId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"ticketId":{"type":"string","description":"The ticket the message went into"},"sessionId":{"type":"string","description":"The session, as sent"}}},"example":{"success":true,"ticketId":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","sessionId":"visitor-7f3a9c2e"}}}},"400":{"description":"content is empty, or the channel is not a live chat or web widget channel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The channel is switched off","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No channel with that ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"Something went wrong saving the message","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/detail-layouts":{"get":{"tags":["Layouts"],"summary":"Get a record page layout","description":"Answers how a module's record page is laid out: its sections and the fields in each. A module with no saved layout answers just { moduleType, sections: [] }.\n\n**Permission:** `crm.detailLayouts.read` (View detail layouts).","operationId":"getDetailLayout","parameters":[{"name":"moduleType","in":"query","required":true,"schema":{"type":"string"},"description":"Which record page: a module such as leads, deals, accounts, contacts, quotes or requests, or custom:<slug> for your own module","example":"leads"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"moduleType":{"type":"string"},"sections":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"width":{"type":"string","description":"full, half or third of the page"},"columns":{"type":"integer","description":"1, 2 or 3 columns inside the section"},"fields":{"type":"array","items":{"type":"object","properties":{"fieldKey":{"type":"string","description":"The field placed there: a native field name or cf:<key> for a custom field"},"colSpan":{"type":"integer","description":"How many columns the field spans"}}}}}}},"updatedBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"b8c9d0e1-f2a3-4b4c-9d5e-7f8a9b0c1d2e","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","moduleType":"leads","sections":[{"id":"contact","title":"Contact details","width":"full","columns":2,"fields":[{"fieldKey":"email","colSpan":1},{"fieldKey":"phone","colSpan":1},{"fieldKey":"cf:installation_notes","colSpan":2}]}],"updatedBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.detailLayouts.read"]}],"x-required-scopes":["crm.detailLayouts.read"]},"put":{"tags":["Layouts"],"summary":"Save a record page layout","description":"Saves how a module's record page is laid out for the whole organisation, replacing the layout it had (or making the first one).\n\n**Permission:** `crm.detailLayouts.update` (Update detail layouts).","operationId":"saveDetailLayout","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"moduleType":{"type":"string","description":"Which record page: a module such as leads, deals, accounts, contacts, quotes or requests, or custom:<slug> for your own module","examples":["leads"]},"sections":{"type":"array","items":{"type":"object"},"description":"The page's sections in order, each { id, title, width: full|half|third, columns: 1-3, fields: [{ fieldKey, colSpan }] }; colSpan defaults to 1 (a JSON array in a string also works)","examples":[[{"id":"contact","title":"Contact details","width":"full","columns":2,"fields":[{"fieldKey":"email","colSpan":1},{"fieldKey":"phone","colSpan":1},{"fieldKey":"cf:installation_notes","colSpan":2}]}]]},"userId":{"type":"string","format":"uuid","description":"CRM user ID recorded as the one who saved it","examples":["7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"]}},"required":["moduleType","sections"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"moduleType":{"type":"string"},"sections":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"width":{"type":"string"},"columns":{"type":"integer"},"fields":{"type":"array","items":{"type":"object","properties":{"fieldKey":{"type":"string"},"colSpan":{"type":"integer","description":"How many columns the field spans"}}}}}}},"updatedBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"b8c9d0e1-f2a3-4b4c-9d5e-7f8a9b0c1d2e","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","moduleType":"leads","sections":[{"id":"contact","title":"Contact details","width":"full","columns":2,"fields":[{"fieldKey":"email","colSpan":1},{"fieldKey":"phone","colSpan":1},{"fieldKey":"cf:installation_notes","colSpan":2}]}],"updatedBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"sections is not a list or not valid JSON, a section has no id or title, a width that is not full, half or third, columns other than 1 to 3, a field without fieldKey, or a colSpan above its section's columns","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.detailLayouts.update"]}],"x-required-scopes":["crm.detailLayouts.update"]}},"/nav-config":{"get":{"tags":["Layouts"],"summary":"Get the menu","description":"Answers the organisation's side menu: its groups and the entries in each, with visibility and order. Modules added to CRM since the menu was saved are added in. The first call for an organisation saves the default menu and answers with status 201; later calls answer 200.\n\n**Permission:** `crm.navConfig.read` (View navigation config).","operationId":"getNavConfig","responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"version":{"type":"integer","description":"Goes up by one on every save"},"groups":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"icon":{"type":"string"},"isVisible":{"type":"boolean","description":"Whether the group shows in the menu"},"displayOrder":{"type":"integer","description":"Position in the menu, from 0"},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"href":{"type":"string","description":"The page the menu entry opens"},"icon":{"type":"string"},"isVisible":{"type":"boolean"},"displayOrder":{"type":"integer"}}}}}}},"updatedBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"c9d0e1f2-a3b4-4c5d-8e6f-8a9b0c1d2e3f","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","version":3,"groups":[{"id":"sales","label":"Sales","icon":"briefcase","isVisible":true,"displayOrder":0,"items":[{"id":"leads","label":"Leads","href":"/sales/leads","icon":"user-plus","isVisible":true,"displayOrder":0},{"id":"d0e1f2a3-b4c5-4d6e-9f7a-9b0c1d2e3f4a","label":"Site surveys","href":"/site-surveys","icon":"clipboard-list","isVisible":true,"displayOrder":1}]}],"updatedBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.navConfig.read"]}],"x-required-scopes":["crm.navConfig.read"]},"put":{"tags":["Layouts"],"summary":"Update the menu","description":"Saves the organisation's side menu, replacing all its groups and entries (labels, icons, visibility and order), and raises its version by one. The menu must exist first (Get the menu makes it).\n\n**Permission:** `crm.navConfig.update` (Update navigation config).","operationId":"updateNavConfig","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"groups":{"type":"array","items":{"type":"object"},"description":"The whole menu: groups, each { id, label, icon, isVisible, displayOrder, items: [{ id, label, href, icon, isVisible, displayOrder }] } (a JSON array in a string also works)","examples":[[{"id":"sales","label":"Sales","icon":"briefcase","isVisible":true,"displayOrder":0,"items":[{"id":"leads","label":"Leads","href":"/sales/leads","icon":"user-plus","isVisible":true,"displayOrder":0},{"id":"d0e1f2a3-b4c5-4d6e-9f7a-9b0c1d2e3f4a","label":"Site surveys","href":"/site-surveys","icon":"clipboard-list","isVisible":true,"displayOrder":1}]}]]}},"required":["groups"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"version":{"type":"integer","description":"Goes up by one on every save"},"groups":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"icon":{"type":"string"},"isVisible":{"type":"boolean","description":"Whether the group shows in the menu"},"displayOrder":{"type":"integer","description":"Position in the menu, from 0"},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"href":{"type":"string","description":"The page the menu entry opens"},"icon":{"type":"string"},"isVisible":{"type":"boolean"},"displayOrder":{"type":"integer"}}}}}}},"updatedBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"c9d0e1f2-a3b4-4c5d-8e6f-8a9b0c1d2e3f","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","version":3,"groups":[{"id":"sales","label":"Sales","icon":"briefcase","isVisible":true,"displayOrder":0,"items":[{"id":"leads","label":"Leads","href":"/sales/leads","icon":"user-plus","isVisible":true,"displayOrder":0},{"id":"d0e1f2a3-b4c5-4d6e-9f7a-9b0c1d2e3f4a","label":"Site surveys","href":"/site-surveys","icon":"clipboard-list","isVisible":true,"displayOrder":1}]}],"updatedBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"groups is not a list or not valid JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"The organisation has no saved menu yet: call Get the menu first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.navConfig.update"]}],"x-required-scopes":["crm.navConfig.update"]}},"/desk/settings":{"get":{"tags":["Help desk setup"],"summary":"Get Desk settings","description":"Reads the Desk app's saved settings: the organisation's (default) or your own personal ones, never another person's. A setting never saved is simply absent, except ticketCategories and ticketProcess, which answer their defaults (version 0, isDefault true) until an admin saves them. Also says whether the organisation can add email forwarding addresses.\n\n**Permission:** `crm.desk.read` (Use Desk: own notifications, follows and preferences).","operationId":"getDeskSettings","parameters":[{"name":"keys","in":"query","required":false,"schema":{"type":"string"},"description":"Only these settings, comma-separated (a list also works). Organisation keys: navigation, general, tags, email_templates, notification_defaults, queues, ticketCategories, emailSignature, ticketProcess; personal keys: preferences, notification_preferences, columns. Leave out for all","example":"general,ticketCategories"},{"name":"scope","in":"query","required":false,"schema":{"type":"string","enum":["organization","user"],"default":"organization"},"description":"organization: the organisation's settings; user: your own personal settings","example":"organization"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"scope":{"type":"string","description":"organization or user, as asked"},"settings":{"type":"object","properties":{"general":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"object","properties":{"applicationName":{"type":"string"},"defaultPriority":{"type":"string"},"autoCloseDays":{"type":"integer"},"timezone":{"type":"string"}}},"version":{"type":"integer","description":"Goes up by one on every save; send it back as expectedVersion to save only over that version"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string"}}},"ticketCategories":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"array","items":{"type":"string"}},"version":{"type":"integer"},"updatedAt":{},"updatedBy":{},"isDefault":{"type":"boolean","description":"Present and true only while the organisation never saved its own categories"}}},"ticketProcess":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"object","properties":{"defaultHoldMinutes":{"type":"integer"},"holdChoices":{"type":"array","items":{"type":"integer"}},"noReplyHoldMinutes":{"type":"integer"},"holdReturnAlert":{"type":"boolean"}},"description":"Holds: defaultHoldMinutes, holdChoices (minutes), noReplyHoldMinutes (how long an until-they-reply hold waits, minutes), holdReturnAlert. What a ticket needs before a hold or a resolve is its ticket blueprint's moves (GET /tickets/:id/moves)"},"version":{"type":"integer"},"updatedAt":{},"updatedBy":{},"isDefault":{"type":"boolean"}}}},"description":"The saved settings by key; each has key, value, version, updatedAt and updatedBy"},"emailForwarding":{"type":"object","properties":{"available":{"type":"boolean","description":"Whether the organisation can add forwarding email addresses"},"address":{"type":"object","properties":{"localPart":{"type":"string"},"domain":{"type":"string"}},"description":"The Desk inbox address forwarding addresses are built on"}}}}}}},"example":{"success":true,"data":{"scope":"organization","settings":{"general":{"key":"general","value":{"applicationName":"Help Desk","defaultPriority":"medium","autoCloseDays":7,"timezone":"Africa/Nairobi"},"version":3,"updatedAt":"2026-10-20T09:05:00.000Z","updatedBy":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"},"ticketCategories":{"key":"ticketCategories","value":["Billing","Technical Support","Sales","Inquiries"],"version":0,"updatedAt":null,"updatedBy":null,"isDefault":true},"ticketProcess":{"key":"ticketProcess","value":{"defaultHoldMinutes":240,"holdChoices":[60,240,1440,4320],"noReplyHoldMinutes":240,"holdReturnAlert":true},"version":0,"updatedAt":null,"updatedBy":null,"isDefault":true}},"emailForwarding":{"available":true,"address":{"localPart":"support","domain":"desk.example.com"}}}}}}},"400":{"description":"scope is not organization or user, or keys names an unknown setting","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"scope=user but the call was not made by a signed-in person","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.desk.read"]}],"x-required-scopes":["crm.desk.read"]}},"/desk/departments/mine":{"get":{"tags":["Desk"],"summary":"List my Desk departments","description":"Lists the departments the Desk ticket switcher offers you: the active ones you are a member of plus those open to everyone, or every active department when you are a Desk or system admin. Also says whether departments decide who sees which ticket in your organisation.\n\n**Permission:** `crm.desk.read` (Use Desk: own notifications, follows and preferences).","operationId":"getMyDeskDepartments","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"enabled":{"type":"boolean","description":"Whether departments decide who sees which ticket in your organisation"},"canSeeAll":{"type":"boolean","description":"Whether you see every ticket (Desk or system admin)"},"departments":{"type":"array","items":{"type":"object","properties":{"_id":{"type":"string","description":"The department's (team's) ID"},"name":{"type":"string","description":"The department's name"}}}}}},"example":{"success":true,"enabled":true,"canSeeAll":false,"departments":[{"_id":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","name":"Technical Support"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.desk.read"]}],"x-required-scopes":["crm.desk.read"]}},"/desk/tickets/{ticketId}/calls":{"get":{"tags":["Desk"],"summary":"List the calls filed under a ticket","description":"Lists the phone calls agents filed under a ticket, newest first, each with the call itself. This is not the customer's whole call history (the calls list answers that by contact or account). Needs both ticket and call reading permissions.\n\n**Permission:** `crm.tickets.read` (View tickets) and `crm.calls.read` (View calls).","operationId":"getDeskTicketCalls","parameters":[{"name":"ticketId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The ticket","example":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ticketId":{"type":"string"},"callLogId":{"type":"string","description":"The call's ID"},"linkedBy":{"type":"string","description":"Who filed the call under the ticket"},"note":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"call":{"type":"object","properties":{"id":{"type":"string"},"pbxCallId":{"type":"string"},"direction":{"type":"string","description":"inbound, outbound or internal"},"externalNumber":{"type":"string"},"externalName":{"type":"string"},"internalNumber":{"type":"string"},"extension":{"type":"string"},"agentName":{"type":"string"},"agentUserId":{"type":"string"},"centralParticipants":{"type":"array","items":{"type":"object","properties":{"centralUserId":{"type":"string"},"direction":{"type":"string"},"extensions":{"type":"array","items":{"type":"string"}},"answeredAt":{"type":"string","format":"date-time"}}}},"queue":{"type":"string"},"did":{"type":"string"},"startedAt":{"type":"string","format":"date-time"},"answeredAt":{"type":"string","format":"date-time"},"endedAt":{"type":"string","format":"date-time"},"durationSeconds":{"type":"integer"},"talkSeconds":{"type":"integer"},"outcome":{"type":"string","description":"pending, answered, missed, busy, failed, voicemail or cancelled"},"hangupCause":{"type":"string"},"hasRecording":{"type":"boolean"},"recordingStatus":{"type":"string"},"partyId":{},"contactId":{"type":"string"},"contactName":{"type":"string"},"accountId":{"type":"string"},"accountName":{"type":"string"},"leadId":{},"leadName":{},"linkedTo":{"type":"string"},"matchMethod":{"type":"string"},"notes":{},"disposition":{},"metadata":{"type":"object","properties":{}},"transcriptStatus":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"description":"The call as the calls module shows it; null if it has since been removed"}}}}}},"example":{"success":true,"data":[{"id":"e0d1e2f3-a4b5-4c6d-9f8a-9b0c1d2e3f4a","ticketId":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","callLogId":"f1e2f3a4-b5c6-4d7e-8a9b-0c1d2e3f4a5b","linkedBy":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","note":"Customer called to chase the outage","createdAt":"2026-10-20T08:25:00.000Z","call":{"id":"f1e2f3a4-b5c6-4d7e-8a9b-0c1d2e3f4a5b","pbxCallId":"1729412400.1042","direction":"inbound","externalNumber":"+254700000001","externalName":"Amina Wanjiru","internalNumber":"+254709000000","extension":"204","agentName":"Brian Otieno","agentUserId":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","centralParticipants":[{"centralUserId":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","direction":"incoming","extensions":["204"],"answeredAt":"2026-10-20T08:20:07.000Z"}],"queue":"support","did":"+254709000000","startedAt":"2026-10-20T08:20:00.000Z","answeredAt":"2026-10-20T08:20:07.000Z","endedAt":"2026-10-20T08:24:12.000Z","durationSeconds":252,"talkSeconds":245,"outcome":"answered","hangupCause":"NORMAL_CLEARING","hasRecording":true,"recordingStatus":"ready","partyId":null,"contactId":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","contactName":"Amina Wanjiru","accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","accountName":"Wanjiru Household","leadId":null,"leadName":null,"linkedTo":"contact","matchMethod":"phone","notes":null,"disposition":null,"metadata":{},"transcriptStatus":"ready","createdAt":"2026-10-20T08:20:00.000Z","updatedAt":"2026-10-20T08:24:15.000Z"}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No ticket with that ID in your organisation that you may see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.read","crm.calls.read"]}],"x-required-scopes":["crm.tickets.read","crm.calls.read"],"x-scope-mode":"all"},"post":{"tags":["Desk"],"summary":"File a call under a ticket","description":"Files a phone call under a ticket. Only the link is written: the call keeps the contact, account or lead it was matched to, and the ticket itself is unchanged apart from a call.linked entry in its history. Filing the same call again changes nothing and answers created: false.\n\n**Permission:** `crm.tickets.update` (Update tickets).","operationId":"linkDeskTicketCall","parameters":[{"name":"ticketId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The ticket","example":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"callId":{"type":"string","format":"uuid","description":"The call to file under it","examples":["f1e2f3a4-b5c6-4d7e-8a9b-0c1d2e3f4a5b"]},"note":{"type":"string","description":"Why it belongs there; kept to 2,000 characters. Only stored the first time the call is filed","examples":["Customer called to chase the outage"]}},"required":["callId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"ticketId":{"type":"string"},"callLogId":{"type":"string","description":"The call's ID"},"linkedBy":{"type":"string","description":"Who filed the call under the ticket"},"note":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"call":{"type":"object","properties":{"id":{"type":"string"},"pbxCallId":{"type":"string"},"direction":{"type":"string","description":"inbound, outbound or internal"},"externalNumber":{"type":"string"},"externalName":{"type":"string"},"internalNumber":{"type":"string"},"extension":{"type":"string"},"agentName":{"type":"string"},"agentUserId":{"type":"string"},"centralParticipants":{"type":"array","items":{"type":"object","properties":{"centralUserId":{"type":"string"},"direction":{"type":"string"},"extensions":{"type":"array","items":{"type":"string"}},"answeredAt":{"type":"string","format":"date-time"}}}},"queue":{"type":"string"},"did":{"type":"string"},"startedAt":{"type":"string","format":"date-time"},"answeredAt":{"type":"string","format":"date-time"},"endedAt":{"type":"string","format":"date-time"},"durationSeconds":{"type":"integer"},"talkSeconds":{"type":"integer"},"outcome":{"type":"string","description":"pending, answered, missed, busy, failed, voicemail or cancelled"},"hangupCause":{"type":"string"},"hasRecording":{"type":"boolean"},"recordingStatus":{"type":"string"},"partyId":{},"contactId":{"type":"string"},"contactName":{"type":"string"},"accountId":{"type":"string"},"accountName":{"type":"string"},"leadId":{},"leadName":{},"linkedTo":{"type":"string"},"matchMethod":{"type":"string"},"notes":{},"disposition":{},"metadata":{"type":"object","properties":{}},"transcriptStatus":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"description":"The call as the calls module shows it; null if it has since been removed"}}},"created":{"type":"boolean","description":"False when the call was already filed under the ticket"}}},"example":{"success":true,"data":{"id":"e0d1e2f3-a4b5-4c6d-9f8a-9b0c1d2e3f4a","ticketId":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","callLogId":"f1e2f3a4-b5c6-4d7e-8a9b-0c1d2e3f4a5b","linkedBy":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","note":"Customer called to chase the outage","createdAt":"2026-10-20T08:25:00.000Z","call":{"id":"f1e2f3a4-b5c6-4d7e-8a9b-0c1d2e3f4a5b","pbxCallId":"1729412400.1042","direction":"inbound","externalNumber":"+254700000001","externalName":"Amina Wanjiru","internalNumber":"+254709000000","extension":"204","agentName":"Brian Otieno","agentUserId":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","centralParticipants":[{"centralUserId":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","direction":"incoming","extensions":["204"],"answeredAt":"2026-10-20T08:20:07.000Z"}],"queue":"support","did":"+254709000000","startedAt":"2026-10-20T08:20:00.000Z","answeredAt":"2026-10-20T08:20:07.000Z","endedAt":"2026-10-20T08:24:12.000Z","durationSeconds":252,"talkSeconds":245,"outcome":"answered","hangupCause":"NORMAL_CLEARING","hasRecording":true,"recordingStatus":"ready","partyId":null,"contactId":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","contactName":"Amina Wanjiru","accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","accountName":"Wanjiru Household","leadId":null,"leadName":null,"linkedTo":"contact","matchMethod":"phone","notes":null,"disposition":null,"metadata":{},"transcriptStatus":"ready","createdAt":"2026-10-20T08:20:00.000Z","updatedAt":"2026-10-20T08:24:15.000Z"}},"created":true}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No ticket with that ID that you may see, or no call with that ID, in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.update"]}],"x-required-scopes":["crm.tickets.update"]}},"/desk/notifications":{"get":{"tags":["Desk"],"summary":"List my Desk notifications","description":"Lists your own Desk notifications (the bell), newest first, with how many are unread. It is always your feed; nobody can read someone else's. Notifications about tickets you can no longer see are left out and not counted.\n\n**Permission:** `crm.desk.read` (Use Desk: own notifications, follows and preferences).","operationId":"getDeskNotifications","parameters":[{"name":"before","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only notifications created before this moment: send the oldest createdAt you have to load the next page","example":"2026-10-20T08:15:00.000Z"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100},"description":"Most notifications to answer (default 20, at most 100)","example":20},{"name":"unread","in":"query","required":false,"schema":{"type":"boolean"},"description":"true: only unread notifications","example":true}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"ticket.assigned, ticket.customer_reply, ticket.status_changed, ticket.mention, ticket.automation, ticket.sla or channel.linked_device"},"ticketId":{"type":"string","description":"The ticket it is about, if any"},"title":{"type":"string"},"body":{"type":"string"},"data":{"type":"object","properties":{"ticketNumber":{"type":"string"},"subject":{"type":"string"}},"description":"Extra details for the screen, such as the ticket number and subject"},"actorUserId":{"type":"string","description":"Who caused it; null for the system or a customer"},"readAt":{"description":"When it was read; null while unread"},"createdAt":{"type":"string","format":"date-time"}}}},"unreadCount":{"type":"integer","description":"How many of your notifications are unread, in all"}}},"example":{"success":true,"data":[{"id":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e","type":"ticket.assigned","ticketId":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","title":"Ticket TKT-001042 was assigned to you","body":"Internet down since this morning","data":{"ticketNumber":"TKT-001042","subject":"Internet down since this morning"},"actorUserId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","readAt":null,"createdAt":"2026-10-20T08:15:00.000Z"}],"unreadCount":1}}}},"400":{"description":"before is not a date, or limit is not a number","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"The call was not made by a signed-in person","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.desk.read"]}],"x-required-scopes":["crm.desk.read"]}},"/desk/ticket-alerts":{"get":{"tags":["Desk"],"summary":"List my new-ticket popups","description":"Lists your new-ticket popups: tickets that came into your department, still open and with nobody on them, that you have not ignored. Oldest first, at most 20. Ignore one by marking its notification read; it goes for everyone once someone has the ticket.\n\n**Permission:** `crm.desk.read` (Use Desk: own notifications, follows and preferences).","operationId":"getDeskTicketAlerts","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The popup's notification ID: mark it read to ignore it"},"ticketId":{"type":"string","description":"The ticket"},"ticketNumber":{"type":"string"},"subject":{"type":"string"},"requesterName":{"type":"string"},"channel":{"type":"string"},"priority":{"type":"string"},"teamName":{"type":"string","description":"The department it came into"},"createdAt":{"type":"string","format":"date-time"}}}}}},"example":{"success":true,"data":[{"id":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","ticketId":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","ticketNumber":"TKT-001201","subject":"No internet since morning","requesterName":"Wanjiku Kamau","channel":"whatsapp","priority":"medium","teamName":"Customer Service","createdAt":"2026-10-29T10:15:00.000Z"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"description":"The call was not made by a signed-in person","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.desk.read"]}],"x-required-scopes":["crm.desk.read"]}},"/desk/chat-bot":{"get":{"tags":["Help desk setup"],"summary":"Show the Kazee Chat bot","description":"Shows which Kazee Chat bot Desk automations send from, or null when none is connected. Never shows the bot's key.\n\n**Permission:** `crm.desk.read` (Use Desk: own notifications, follows and preferences).","operationId":"getDeskChatBot","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"botId":{"type":"string","description":"The bot's Kazee id"},"botName":{"type":"string","description":"The bot's name in Kazee Chat"},"connectedBy":{"type":"string","description":"Who connected it"},"connectedAt":{"type":"string","format":"date-time","description":"When"}},"description":"The connected bot, or null"}}},"example":{"success":true,"data":{"botId":"6a42ef47d2e731c9d1b6a30e","botName":"Kazee Desk","connectedBy":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","connectedAt":"2026-10-29T19:30:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"description":"The call was not made by a signed-in person","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.desk.read"]}],"x-required-scopes":["crm.desk.read"]},"put":{"tags":["Help desk setup"],"summary":"Connect the Kazee Chat bot","description":"Connects the Kazee Chat bot Desk automations send from, replacing any connected before. Kazee checks the key first: it must be a bot of your company that may message anyone there. The key is stored encrypted and never shown again.\n\n**Permission:** `crm.desk.admin` (Configure Desk).","operationId":"putDeskChatBot","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string","description":"The bot's key from Kazee (it starts with kzb_)","examples":["kzb_0123456789abcdef0123"]}},"required":["key"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"botId":{"type":"string"},"botName":{"type":"string"},"connectedBy":{"type":"string"},"connectedAt":{"type":"string","format":"date-time"}},"description":"The bot now connected (no key)"}}},"example":{"success":true,"data":{"botId":"6a42ef47d2e731c9d1b6a30e","botName":"Kazee Desk","connectedBy":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","connectedAt":"2026-10-29T19:30:00.000Z"}}}}},"400":{"description":"Not a bot key, Kazee did not accept it, the bot belongs to another company or may only message its owner, or this server cannot store keys encrypted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"The call was not made by a signed-in person","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.desk.admin"]}],"x-required-scopes":["crm.desk.admin"]},"delete":{"tags":["Help desk setup"],"summary":"Disconnect the Kazee Chat bot","description":"Disconnects the Kazee Chat bot. Its stored key is deleted; Message in Kazee Chat steps fail until a bot is connected again.\n\n**Permission:** `crm.desk.admin` (Configure Desk).","operationId":"deleteDeskChatBot","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"removed":{"type":"boolean","description":"false when none was connected"}}}}},"example":{"success":true,"data":{"removed":true}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"description":"The call was not made by a signed-in person","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.desk.admin"]}],"x-required-scopes":["crm.desk.admin"]}},"/desk/tickets/{ticketId}/watchers":{"get":{"tags":["Desk"],"summary":"List a ticket's followers","description":"Lists who follows a ticket in Desk (followers hear about its replies and status changes), oldest follower first, and whether you are one of them.\n\n**Permission:** `crm.tickets.read` (View tickets).","operationId":"getDeskTicketWatchers","parameters":[{"name":"ticketId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The ticket","example":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"userIds":{"type":"array","items":{"type":"string"},"description":"The IDs of the people following the ticket"},"watching":{"type":"boolean","description":"Whether you follow it"}}}}},"example":{"success":true,"data":{"userIds":["1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"],"watching":true}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No ticket with that ID in your organisation that you may see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.read"]}],"x-required-scopes":["crm.tickets.read"]}},"/tickets/{ticketId}/attachments/{attachmentId}/url":{"get":{"tags":["Tickets"],"summary":"Get a ticket file's download link","description":"Gives a download link for one file on a ticket. The link is signed fresh on each call and works for 15 minutes; a file kept elsewhere answers its stored address with no expiry.\n\n**Permission:** `crm.tickets.read` (View tickets).","operationId":"getTicketAttachmentUrl","parameters":[{"name":"ticketId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The ticket the file is on","example":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b"},{"name":"attachmentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The file","example":"7c8d9e0f-1a2b-4c3d-8e5f-6a7b8c9d0e1f"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"fileName":{"type":"string","description":"The name the file was uploaded with"},"mimeType":{"type":"string"},"fileSize":{"type":"integer"},"url":{"type":"string"},"expiresInSeconds":{"type":"integer","description":"How long the link works (900), or null for a link that does not expire"}}}}},"example":{"success":true,"data":{"id":"7c8d9e0f-1a2b-4c3d-8e5f-6a7b8c9d0e1f","fileName":"router-photo.jpg","mimeType":"image/jpeg","fileSize":245760,"url":"https://files.example.com/tickets/router-photo.jpg?X-Amz-Expires=900&X-Amz-Signature=3f1c","expiresInSeconds":900}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No ticket with that ID that you may see, or no such file on it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.read"]}],"x-required-scopes":["crm.tickets.read"]}},"/users":{"get":{"tags":["Teams & people"],"summary":"List people to mention","description":"Answers up to 100 active people of your organisation, A to Z by first name, for an @ mention list. Switched-off and removed people are left out. With teamId, only that team's members are listed (everyone when the team is inactive, unknown or has no members).\n\n**Permission:** `crm.users.read` (View users).","operationId":"usersList","parameters":[{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Part of a first name, last name or email to match","example":"amina"},{"name":"teamId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only list the members of this team (department)","example":"3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"users":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"profilePicture":{"type":"string","description":"Link to the person's profile photo (valid about 7 days), or null when they have none"}}}}}},"example":{"success":true,"users":[{"id":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","firstName":"Amina","lastName":"Otieno","email":"amina.otieno@example.com","profilePicture":"https://files.example.com/profile-pictures/amina.jpg?X-Amz-Expires=604800&X-Amz-Signature=abc123"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.users.read"]}],"x-required-scopes":["crm.users.read"]}},"/users/roles":{"get":{"tags":["Teams & people"],"summary":"List the roles people have","description":"Answers every role name the people of your organisation have, each once and in A to Z order, e.g. to pick which roles may move records through a pipeline stage.\n\n**Permission:** `crm.users.read` (View users).","operationId":"userRolesList","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"roles":{"type":"array","items":{"type":"string"}}}},"example":{"success":true,"roles":["Admin","Sales","Support"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.users.read"]}],"x-required-scopes":["crm.users.read"]}},"/users/{userId}":{"get":{"tags":["Teams & people"],"summary":"Get a person's name","description":"Answers one person of your organisation by ID with their name and email, e.g. to show who a stored person ID is. Switched-off and removed people are found too.\n\n**Permission:** `crm.users.read` (View users).","operationId":"getUser","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The person's CRM user ID","example":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"}}}}},"example":{"success":true,"data":{"id":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","firstName":"Amina","lastName":"Otieno","email":"amina.otieno@example.com"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No person with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.users.read"]}],"x-required-scopes":["crm.users.read"]}},"/organization/service-identifier-counter":{"put":{"tags":["Settings"],"summary":"Set the service number counter","description":"Sets the last generated service number used (e.g. the 5537 in acme0005537), so the next generated identifier carries on from it. The next number is one above the highest of this counter, the numbers already taken in CRM and Zoho's counter when set up, so a lower value changes nothing. Recorded with who changed it.\n\n**Permission:** `crm.onboarding.update` (Manage onboarding settings).","operationId":"updateServiceIdentifierCounter","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"lastUsed":{"type":"integer","minimum":0,"description":"The last number used: a whole number from 0 up to all 9s for the organisation's identifier digit count","examples":[5537]}},"required":["lastUsed"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"serviceIdentifierCounter":{"type":"object","properties":{"lastUsed":{"type":"integer"},"updatedAt":{"type":"string","format":"date-time"},"source":{"type":"string","description":"manual: set by a person"}}}}},"example":{"success":true,"serviceIdentifierCounter":{"lastUsed":5537,"updatedAt":"2026-10-20T09:00:00.000Z","source":"manual"}}}}},"400":{"description":"lastUsed is not a whole number from 0 to the largest number the organisation's identifier digits allow","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Your organisation has no CRM record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.onboarding.update"]}],"x-required-scopes":["crm.onboarding.update"]}},"/comments/{commentId}":{"put":{"tags":["Comments"],"summary":"Edit a comment","description":"Replaces the text of a comment. Only its author may edit it. A bot editing it for its owner is named as actedBy. Answers the comment as it is now.\n\n**Permission:** `crm.comments.update` (Update comments).","operationId":"updateComment","parameters":[{"name":"commentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The comment's ID","example":"2c8d5a3f-0b4e-4fbd-9acc-3e5f7b9dbace"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string","description":"The new text, up to 50,000 characters","examples":["Called her this morning; she wants the 50 Mbps plan."]},"userId":{"type":"string","description":"Your own user ID (the web app sends it); the call checks it against the comment's author","examples":["0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac"]}},"required":["content","userId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"targetType":{"type":"string","description":"The kind of record the comment is on, e.g. lead, deal, account, contact, ticket, call, custom, or books:<resource> for a Kazee Books record"},"targetId":{"type":"string","description":"The record's ID (a Books record answers with its Books ID, e.g. inv_…)"},"authorId":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"profilePicture":{}},"description":"Who wrote it"},"content":{"type":"string","description":"The text; the web app saves it as a rich-text (TipTap) document in JSON"},"attachments":{"type":"array","description":"Files on the comment (empty: these calls never add any)"},"parentId":{"description":"The comment this one replies to, or null"},"replyCount":{"type":"integer"},"mentions":{"type":"array","items":{"type":"string"},"description":"IDs of the people tagged in it"},"reactions":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"emoji":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}}},"seenBy":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"seenAt":{"type":"string","format":"date-time"}}},"description":"Who has seen it, and when"},"actedBy":{"description":"The bot that wrote or last edited it, { id, name, ai: true }: a personal bot acting for the author, or a bot as itself (shown with the AI diamond); null when a person did"},"isDeleted":{"type":"boolean"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"2c8d5a3f-0b4e-4fbd-9acc-3e5f7b9dbace","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","targetType":"lead","targetId":"d37e0b8a-5c9f-4ace-8b7d-8f0a2c4e6b79","authorId":{"_id":"0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac","id":"0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac","firstName":"Peter","lastName":"Otieno","email":"peter.otieno@example.com","profilePicture":null},"content":"Called her this morning; she wants the 50 Mbps plan.","attachments":[],"parentId":null,"replyCount":0,"mentions":["1b7c4f2e-9a3d-4eac-8fbb-2d4e6a8cafbd"],"reactions":[{"userId":"1b7c4f2e-9a3d-4eac-8fbb-2d4e6a8cafbd","emoji":"👍","createdAt":"2026-10-20T10:15:00.000Z"}],"seenBy":[{"userId":"1b7c4f2e-9a3d-4eac-8fbb-2d4e6a8cafbd","seenAt":"2026-10-20T10:14:00.000Z"}],"actedBy":null,"isDeleted":false,"deletedAt":null,"createdAt":"2026-10-20T10:00:00.000Z","updatedAt":"2026-10-20T10:30:00.000Z"}}}}},"400":{"description":"userId is not a valid user ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You are not the comment's author, or userId is not your own user ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No comment with that ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.comments.update"]}],"x-required-scopes":["crm.comments.update"]},"delete":{"tags":["Comments"],"summary":"Delete a comment","description":"Deletes a comment; only its author may. The comment is hidden from lists, not erased.\n\n**Permission:** `crm.comments.delete` (Delete comments).","operationId":"deleteComment","parameters":[{"name":"commentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The comment's ID","example":"2c8d5a3f-0b4e-4fbd-9acc-3e5f7b9dbace"},{"name":"userId","in":"query","required":true,"schema":{"type":"string"},"description":"Your own user ID (the web app sends it); the call checks it against the comment's author","example":"0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}},"example":{"success":true,"message":"Comment deleted"}}}},"400":{"description":"userId is not a valid user ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You are not the comment's author, or userId is not your own user ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No comment with that ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.comments.delete"]}],"x-required-scopes":["crm.comments.delete"]}},"/calls/{callId}/link":{"put":{"tags":["Calls"],"summary":"File a call under a record","description":"Files a call by hand under a contact and/or account, or under a lead, for a call that was not matched or was matched wrongly. Send at least one of contactId, accountId, leadId; when you name only a contact or only an account, the other is filled in from the same customer. A call filed by hand is never refiled by automatic matching, and it appears on the record's timeline. Answers the call as it is now.\n\n**Permission:** `crm.calls.update` (Link calls).","operationId":"linkCall","parameters":[{"name":"callId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The call's ID","example":"7d1e4b2a-9c3f-4a6e-8b1d-2f4a6c8e0b13"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid","description":"The account to file it under","examples":["c26d9a7f-4b8e-4fbd-9a6c-7e9f1b3d5a68"]},"contactId":{"type":"string","format":"uuid","description":"The contact to file it under","examples":["b15c8f6e-3a7d-4eac-8f5b-6d8e0a2c4f57"]},"leadId":{"type":"string","format":"uuid","description":"The lead to file it under. Sent alone, the call's contact and account are cleared","examples":["d37e0b8a-5c9f-4ace-8b7d-8f0a2c4e6b79"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"pbxCallId":{"type":"string","description":"The phone system's own ID for the call"},"direction":{"type":"string","description":"inbound, outbound or internal"},"externalNumber":{"type":"string","description":"The customer's number (E.164), or null for an internal call"},"externalName":{"type":"string"},"internalNumber":{"type":"string"},"extension":{"type":"string"},"agentName":{"type":"string"},"agentUserId":{"type":"string"},"centralParticipants":{"type":"array","items":{"type":"object","properties":{"centralUserId":{"type":"string"},"direction":{"type":"string"},"extensions":{"type":"array","items":{"type":"string"}},"answeredAt":{"type":"string","format":"date-time"}}},"description":"The Kazee people who took part, as Kazee Phone recorded them"},"queue":{"type":"string"},"did":{"type":"string"},"startedAt":{"type":"string","format":"date-time"},"answeredAt":{"type":"string","format":"date-time"},"endedAt":{"type":"string","format":"date-time"},"durationSeconds":{"type":"integer"},"talkSeconds":{"type":"integer"},"outcome":{"type":"string","description":"pending, answered, missed, busy, failed, voicemail or cancelled"},"hangupCause":{"type":"string"},"hasRecording":{"type":"boolean"},"recordingStatus":{"type":"string","description":"none, pending (still being archived), ready or failed"},"partyId":{"type":"string"},"contactId":{"type":"string"},"contactName":{"type":"string"},"accountId":{"type":"string"},"accountName":{"type":"string"},"leadId":{},"leadName":{},"linkedTo":{"type":"string","description":"Which record the call is filed under: contact, account, lead or none"},"matchMethod":{"type":"string","description":"How it was filed: none, cache, contacts, created, manual or lead. manual (a person filed it) is never changed by automatic matching"},"notes":{"type":"string"},"disposition":{"type":"string","description":"The agent's outcome code for the call, from the organisation's list, or null"},"metadata":{"type":"object","properties":{}},"transcriptStatus":{"type":"string","description":"none, queued, processing, ready, failed or skipped"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"7d1e4b2a-9c3f-4a6e-8b1d-2f4a6c8e0b13","pbxCallId":"1729500000.4521","direction":"inbound","externalNumber":"+254700000123","externalName":"JANE WANJIKU","internalNumber":"+254200000100","extension":"204","agentName":"Peter Otieno","agentUserId":"0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac","centralParticipants":[{"centralUserId":"65f1a2b3c4d5e6f7a8b9c0d1","direction":"incoming","extensions":["204"],"answeredAt":"2026-10-20T09:00:12.000Z"}],"queue":"support","did":"+254200000100","startedAt":"2026-10-20T09:00:00.000Z","answeredAt":"2026-10-20T09:00:12.000Z","endedAt":"2026-10-20T09:04:30.000Z","durationSeconds":270,"talkSeconds":258,"outcome":"answered","hangupCause":"NORMAL_CLEARING","hasRecording":true,"recordingStatus":"ready","partyId":"f59a2d0c-7e1b-4cea-8d9f-0b2c4e6a8d9b","contactId":"b15c8f6e-3a7d-4eac-8f5b-6d8e0a2c4f57","contactName":"Jane Wanjiku","accountId":"c26d9a7f-4b8e-4fbd-9a6c-7e9f1b3d5a68","accountName":"Wanjiku Household","leadId":null,"leadName":null,"linkedTo":"contact","matchMethod":"manual","notes":"Router reset; customer will call back if the line drops again","disposition":"resolved","metadata":{},"transcriptStatus":"ready","createdAt":"2026-10-20T09:00:01.000Z","updatedAt":"2026-10-20T09:05:00.000Z"}}}}},"400":{"description":"None of contactId, accountId or leadId was sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No call with that ID, or no such contact, account or lead in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.calls.update"]}],"x-required-scopes":["crm.calls.update"]},"delete":{"tags":["Calls"],"summary":"Take a call off its record","description":"Takes a call off the record it was filed under (a wrong match). The call becomes unfiled and automatic matching will not file it again, so a person files it under the right record. The entry already on the old record's timeline stays as history. Answers the call as it is now.\n\n**Permission:** `crm.calls.update` (Link calls).","operationId":"unlinkCall","parameters":[{"name":"callId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The call's ID","example":"7d1e4b2a-9c3f-4a6e-8b1d-2f4a6c8e0b13"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"pbxCallId":{"type":"string","description":"The phone system's own ID for the call"},"direction":{"type":"string","description":"inbound, outbound or internal"},"externalNumber":{"type":"string","description":"The customer's number (E.164), or null for an internal call"},"externalName":{"type":"string"},"internalNumber":{"type":"string"},"extension":{"type":"string"},"agentName":{"type":"string"},"agentUserId":{"type":"string"},"centralParticipants":{"type":"array","items":{"type":"object","properties":{"centralUserId":{"type":"string"},"direction":{"type":"string"},"extensions":{"type":"array","items":{"type":"string"}},"answeredAt":{"type":"string","format":"date-time"}}},"description":"The Kazee people who took part, as Kazee Phone recorded them"},"queue":{"type":"string"},"did":{"type":"string"},"startedAt":{"type":"string","format":"date-time"},"answeredAt":{"type":"string","format":"date-time"},"endedAt":{"type":"string","format":"date-time"},"durationSeconds":{"type":"integer"},"talkSeconds":{"type":"integer"},"outcome":{"type":"string","description":"pending, answered, missed, busy, failed, voicemail or cancelled"},"hangupCause":{"type":"string"},"hasRecording":{"type":"boolean"},"recordingStatus":{"type":"string","description":"none, pending (still being archived), ready or failed"},"partyId":{},"contactId":{},"contactName":{},"accountId":{},"accountName":{},"leadId":{},"leadName":{},"linkedTo":{"type":"string","description":"Which record the call is filed under: contact, account, lead or none"},"matchMethod":{"type":"string","description":"How it was filed: none, cache, contacts, created, manual or lead. manual (a person filed it) is never changed by automatic matching"},"notes":{"type":"string"},"disposition":{"type":"string","description":"The agent's outcome code for the call, from the organisation's list, or null"},"metadata":{"type":"object","properties":{}},"transcriptStatus":{"type":"string","description":"none, queued, processing, ready, failed or skipped"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"7d1e4b2a-9c3f-4a6e-8b1d-2f4a6c8e0b13","pbxCallId":"1729500000.4521","direction":"inbound","externalNumber":"+254700000123","externalName":"JANE WANJIKU","internalNumber":"+254200000100","extension":"204","agentName":"Peter Otieno","agentUserId":"0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac","centralParticipants":[{"centralUserId":"65f1a2b3c4d5e6f7a8b9c0d1","direction":"incoming","extensions":["204"],"answeredAt":"2026-10-20T09:00:12.000Z"}],"queue":"support","did":"+254200000100","startedAt":"2026-10-20T09:00:00.000Z","answeredAt":"2026-10-20T09:00:12.000Z","endedAt":"2026-10-20T09:04:30.000Z","durationSeconds":270,"talkSeconds":258,"outcome":"answered","hangupCause":"NORMAL_CLEARING","hasRecording":true,"recordingStatus":"ready","partyId":null,"contactId":null,"contactName":null,"accountId":null,"accountName":null,"leadId":null,"leadName":null,"linkedTo":"none","matchMethod":"manual","notes":"Router reset; customer will call back if the line drops again","disposition":"resolved","metadata":{},"transcriptStatus":"ready","createdAt":"2026-10-20T09:00:01.000Z","updatedAt":"2026-10-20T09:05:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No call with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.calls.update"]}],"x-required-scopes":["crm.calls.update"]}},"/calls/{callId}/notes":{"put":{"tags":["Calls"],"summary":"Set a call's notes","description":"Replaces a call's notes with the text sent (leave notes out to clear them). The separate entries added from Kazee Phone are kept. Answers the call as it is now.\n\n**Permission:** `crm.calls.update` (Link calls).","operationId":"updateCallNotes","parameters":[{"name":"callId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The call's ID","example":"7d1e4b2a-9c3f-4a6e-8b1d-2f4a6c8e0b13"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"notes":{"type":"string","description":"The notes, up to 5,000 characters. Leave out (or null) to clear them","examples":["Customer asked for a technician visit on Friday"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"pbxCallId":{"type":"string","description":"The phone system's own ID for the call"},"direction":{"type":"string","description":"inbound, outbound or internal"},"externalNumber":{"type":"string","description":"The customer's number (E.164), or null for an internal call"},"externalName":{"type":"string"},"internalNumber":{"type":"string"},"extension":{"type":"string"},"agentName":{"type":"string"},"agentUserId":{"type":"string"},"centralParticipants":{"type":"array","items":{"type":"object","properties":{"centralUserId":{"type":"string"},"direction":{"type":"string"},"extensions":{"type":"array","items":{"type":"string"}},"answeredAt":{"type":"string","format":"date-time"}}},"description":"The Kazee people who took part, as Kazee Phone recorded them"},"queue":{"type":"string"},"did":{"type":"string"},"startedAt":{"type":"string","format":"date-time"},"answeredAt":{"type":"string","format":"date-time"},"endedAt":{"type":"string","format":"date-time"},"durationSeconds":{"type":"integer"},"talkSeconds":{"type":"integer"},"outcome":{"type":"string","description":"pending, answered, missed, busy, failed, voicemail or cancelled"},"hangupCause":{"type":"string"},"hasRecording":{"type":"boolean"},"recordingStatus":{"type":"string","description":"none, pending (still being archived), ready or failed"},"partyId":{"type":"string"},"contactId":{"type":"string"},"contactName":{"type":"string"},"accountId":{"type":"string"},"accountName":{"type":"string"},"leadId":{},"leadName":{},"linkedTo":{"type":"string","description":"Which record the call is filed under: contact, account, lead or none"},"matchMethod":{"type":"string","description":"How it was filed: none, cache, contacts, created, manual or lead. manual (a person filed it) is never changed by automatic matching"},"notes":{"type":"string"},"disposition":{"type":"string","description":"The agent's outcome code for the call, from the organisation's list, or null"},"metadata":{"type":"object","properties":{}},"transcriptStatus":{"type":"string","description":"none, queued, processing, ready, failed or skipped"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"7d1e4b2a-9c3f-4a6e-8b1d-2f4a6c8e0b13","pbxCallId":"1729500000.4521","direction":"inbound","externalNumber":"+254700000123","externalName":"JANE WANJIKU","internalNumber":"+254200000100","extension":"204","agentName":"Peter Otieno","agentUserId":"0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac","centralParticipants":[{"centralUserId":"65f1a2b3c4d5e6f7a8b9c0d1","direction":"incoming","extensions":["204"],"answeredAt":"2026-10-20T09:00:12.000Z"}],"queue":"support","did":"+254200000100","startedAt":"2026-10-20T09:00:00.000Z","answeredAt":"2026-10-20T09:00:12.000Z","endedAt":"2026-10-20T09:04:30.000Z","durationSeconds":270,"talkSeconds":258,"outcome":"answered","hangupCause":"NORMAL_CLEARING","hasRecording":true,"recordingStatus":"ready","partyId":"f59a2d0c-7e1b-4cea-8d9f-0b2c4e6a8d9b","contactId":"b15c8f6e-3a7d-4eac-8f5b-6d8e0a2c4f57","contactName":"Jane Wanjiku","accountId":"c26d9a7f-4b8e-4fbd-9a6c-7e9f1b3d5a68","accountName":"Wanjiku Household","leadId":null,"leadName":null,"linkedTo":"contact","matchMethod":"contacts","notes":"Customer asked for a technician visit on Friday","disposition":"resolved","metadata":{},"transcriptStatus":"ready","createdAt":"2026-10-20T09:00:01.000Z","updatedAt":"2026-10-20T09:05:00.000Z"}}}}},"400":{"description":"notes is over 5,000 characters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No call with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.calls.update"]}],"x-required-scopes":["crm.calls.update"]}},"/leads/{leadId}/status":{"put":{"tags":["Leads"],"summary":"Change a lead's status directly","description":"Retired: always answers 400. A lead's stage changes only through its pipeline transitions (the board or the transition calls). Kept so old callers get a clear message.\n\n**Permission:** `crm.leads.update` (Update leads).","operationId":"updateLeadStatus","parameters":[{"name":"leadId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The lead's ID","example":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"notes":{"type":"string","description":"Not used","examples":["Customer confirmed interest"]},"status":{"type":"string","description":"The stage asked for (refused)","examples":["hot_lead"]}},"required":["status"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}},"example":{"error":"Direct status changes are not allowed. Move the lead through its pipeline transitions (kanban board or the transition API) instead."}}}},"400":{"description":"Always: direct status changes are not allowed; move the lead through its pipeline transitions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.update"]}],"x-required-scopes":["crm.leads.update"]}},"/quotes/{quoteId}/status":{"put":{"tags":["Quotes"],"summary":"Change a quote's status","description":"Moves a quote to another status along the quote pipeline: draft to sent or declined; sent to accepted, declined or expired; accepted to paid (only when fully paid); paid back to accepted; declined or expired back to draft. Moving to sent emails the quote to the lead's or account's email address. For a quote mirrored to a Kazee Books estimate, sent, accepted and declined are made on the estimate in Books, as you, and the quote follows (no email from CRM); Books' own refusals are passed on.\n\n**Permission:** `crm.quotes.update` (Update quotes).","operationId":"updateQuoteStatus","parameters":[{"name":"quoteId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The quote to move","example":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","description":"Why, e.g. why the customer declined (not stored at present)","examples":["Customer chose a cheaper plan"]},"status":{"type":"string","enum":["draft","sent","accepted","declined","expired","paid"],"description":"The status to move the quote to","examples":["sent"]}},"required":["status"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"quoteNumber":{"type":"string","description":"The quote's number, unique in your organisation (a revision adds -r2, -r3 ...)"},"leadId":{"type":"string"},"accountId":{"type":"string"},"subscriptionId":{},"requestId":{},"dealId":{"type":"string","description":"The deal the quote was made from, if any"},"ownerId":{"type":"string"},"type":{"type":"string","description":"quote (for a lead or account), request (for a service request) or subscription (for a running service)"},"subtotal":{"type":"string","description":"Sum of the lines before tax, a decimal in a string"},"total":{"type":"string","description":"Sum of the lines with tax, a decimal in a string"},"status":{"type":"string","description":"draft, sent, accepted, declined, expired or paid"},"paidAmount":{"type":"string","description":"What the customer has paid so far"},"balance":{"type":"string","description":"What is still to pay: total less paidAmount"},"paidAt":{},"validityStart":{"type":"string","format":"date-time"},"validityEnd":{"type":"string","format":"date-time","description":"The last moment the customer can accept the quote"},"sentAt":{"type":"string","format":"date-time"},"acceptedAt":{},"declinedAt":{},"expiredAt":{},"notes":{"type":"string"},"revisionOf":{"description":"The quote this one revises, if it is a revision"},"revisionNumber":{"type":"integer"},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array","description":"IDs of this quote in other systems (e.g. its Kazee Books estimate or Zoho quotation)"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"previousStatus":{"type":"string","description":"The status before this call"},"workflow":{"type":"object","properties":{"statusChanged":{"type":"boolean","description":"True when the status really changed"},"leadUpdated":{"type":"boolean","description":"Always false: lead stages are not moved by quotes"}}},"books":{"type":"object","properties":{"estimateId":{"type":"string"},"status":{"type":"string"}},"description":"Only for a quote mirrored to a Books estimate: the estimate's ID and its status in Books now"}}},"example":{"success":true,"data":{"id":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","quoteNumber":"QUOTE-00042","leadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","subscriptionId":null,"requestId":null,"dealId":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","type":"quote","subtotal":"6500.00","total":"7540.00","status":"sent","paidAmount":"0.00","balance":"7540.00","paidAt":null,"validityStart":"2026-10-01T08:30:00.000Z","validityEnd":"2026-11-01T00:00:00.000Z","sentAt":"2026-10-15T14:05:00.000Z","acceptedAt":null,"declinedAt":null,"expiredAt":null,"notes":"Includes free router for 12 months","revisionOf":null,"revisionNumber":1,"customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"},"previousStatus":"draft","workflow":{"statusChanged":true,"leadUpdated":false},"books":{"estimateId":"est_5d6e7f8091a2b3c4d5e6f708192a3b4c","status":"sent"}}}}},"400":{"description":"status is not valid, the move is not allowed from the current status, the quote was raised in Zoho, it is not fully paid (for paid), or no recipient email was found or the email could not be sent (for sent)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No quote with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.quotes.update"]}],"x-required-scopes":["crm.quotes.update"]}},"/quote-templates/{templateId}":{"put":{"tags":["Quotes"],"summary":"Update a quote template","description":"Changes a quote template: only the fields you send change. Lines sent are checked again in Kazee Books. Quotes already made from the template do not change. Needs the Administer quotes permission and Books item read access.\n\n**Permission:** `crm.quotes.admin` (Administer quotes) and `crm.books.items.read`.","operationId":"updateQuoteTemplate","parameters":[{"name":"templateId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The template to change","example":"7c6f5d8f-0a2b-4d9e-8b4c-8f9a0b1c2d3e"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"description":{"type":"string","description":"What the template is for, up to 2,000 characters","examples":["Installation and first month for home fibre"]},"lines":{"type":"array","items":{"type":"object","properties":{"booksItemId":{"type":"string","description":"Kazee Books item ID (itm_ and 32 hex characters); leave out for a line of one of your products"},"productId":{"type":"string","format":"uuid","description":"One of your products, for a line that is not a Books item"},"description":{"type":"string","description":"Line wording, up to 500 characters"},"quantity":{"type":"number","description":"More than zero, at most 2 decimals; default 1"},"unitPrice":{"type":"number","description":"A fixed price (zero or more); leave out to use the item's price when the quote is made"},"discountType":{"type":"string","enum":["amount","percent"]},"discountValue":{"type":"number","description":"Money off the line, or a percent 0 to 100; default 0"},"treatment":{"type":"string","enum":["invoice","retainer"],"description":"What the line becomes when paid; default invoice"}}},"description":"Replaces all the template's lines, at least one (a JSON array in a string also works). Each line is checked again in Kazee Books (or your catalogue)","examples":[[{"booksItemId":"itm_3f9a2b7c1d4e5f60718293a4b5c6d7e8","quantity":1,"unitPrice":2000,"discountType":"amount","discountValue":0,"treatment":"invoice"}]]},"name":{"type":"string","description":"New name, up to 160 characters, unique in your organisation (case does not matter)","examples":["Home fibre standard"]},"planIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Replaces the plans this template is the default for (a JSON array in a string also works). A plan can be the default of one active template only","examples":[["9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a"]]},"productIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Replaces the products this template is the default for (a JSON array in a string also works). A product can be the default of one active template only","examples":[[]]},"status":{"type":"string","enum":["active","archived"],"description":"archived takes the template out of the quote dialog and frees its plans and products; active brings it back","examples":["active"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","description":"active (offered in the quote dialog) or archived"},"planIds":{"type":"array","items":{"type":"string"},"description":"Plans this template is the default for; a plan is the default of one active template at most"},"productIds":{"type":"array","description":"Products this template is the default for; a product is the default of one active template at most"},"lines":{"type":"array","items":{"type":"object","properties":{"booksItemId":{"type":"string","description":"The Kazee Books item the line is (empty for a line of one of your products, when Books is not connected)"},"booksItemName":{"type":"string","description":"The item's name, as Books has it"},"productId":{},"productName":{},"description":{"type":"string"},"quantity":{"type":"integer"},"unitPrice":{"type":"integer","description":"A fixed price, or null to use the item's price at the time the quote is made"},"discountType":{"type":"string","description":"amount (discountValue is money off the line) or percent (0 to 100)"},"discountValue":{"type":"integer"},"treatment":{"type":"string","description":"What the line becomes when paid: invoice or retainer"}}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"7c6f5d8f-0a2b-4d9e-8b4c-8f9a0b1c2d3e","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Home fibre standard","description":"Installation and first month for home fibre","status":"active","planIds":["9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a"],"productIds":[],"lines":[{"booksItemId":"itm_3f9a2b7c1d4e5f60718293a4b5c6d7e8","booksItemName":"Router installation","productId":null,"productName":null,"description":"Router and fibre drop","quantity":1,"unitPrice":2000,"discountType":"amount","discountValue":0,"treatment":"invoice"}],"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}}}}},"400":{"description":"No template with that ID in your organisation, a text too long, a line's number, discount or treatment not valid, a Books item missing, inactive or not sold, or a plan or product not of your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Another template already has that name, or a plan or product you named is already the default of another active template (the message names it)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"description":"Quote templates are not enabled yet on this server","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.quotes.admin","crm.books.items.read"]}],"x-required-scopes":["crm.quotes.admin","crm.books.items.read"],"x-scope-mode":"all"},"delete":{"tags":["Quotes"],"summary":"Delete a quote template","description":"Deletes a quote template: it leaves the quote dialog's picker and the plans and products it was the default for are freed. Quotes already made from it do not change. Needs the Administer quotes permission.\n\n**Permission:** `crm.quotes.admin` (Administer quotes).","operationId":"deleteQuoteTemplate","parameters":[{"name":"templateId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The template to delete","example":"7c6f5d8f-0a2b-4d9e-8b4c-8f9a0b1c2d3e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"deleted":{"type":"boolean","description":"Always true"}}}}},"example":{"success":true,"data":{"id":"7c6f5d8f-0a2b-4d9e-8b4c-8f9a0b1c2d3e","deleted":true}}}}},"400":{"description":"No quote template with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"The template was deleted at the same moment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"description":"Quote templates are not enabled yet on this server","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.quotes.admin"]}],"x-required-scopes":["crm.quotes.admin"]}},"/accounts/{accountId}/status":{"put":{"tags":["Accounts"],"summary":"Change an account's status","description":"Move an account to active, suspended or terminated. Allowed moves: active to suspended or terminated, suspended to active or terminated; a terminated account stays terminated (and gets its termination date). Setting the status it already has changes nothing.\n\n**Permission:** `crm.accounts.update` (Update accounts).","operationId":"updateAccountStatus","parameters":[{"name":"accountId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The account's ID","example":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","description":"Why, kept in the account's history","examples":["Unpaid since August"]},"status":{"type":"string","enum":["active","suspended","terminated"],"description":"The new status","examples":["suspended"]}},"required":["status"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the account is filed under one, else the account's own"},"accountNumber":{"type":"string","description":"Your organisation's account number, unique"},"status":{"type":"string","description":"active, inactive, suspended or terminated"},"description":{"type":"string"},"addressId":{"type":"string"},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string","description":"Credit limit, a decimal number in a string"},"currentBalance":{"type":"string","description":"Current balance, a decimal number in a string"},"originLeadId":{"type":"string","description":"The lead the account was converted from, if any"},"walletId":{},"billingContactId":{"type":"string","description":"The contact who gets the bills"},"serviceContactId":{"type":"string","description":"The contact for service matters"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"}}},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array","description":"IDs of this account in other systems, e.g. { source: zoho, externalId }"},"partyId":{"type":"string","description":"The person or company record in Kazee Contacts the account is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"previousStatus":{"type":"string","description":"The status before; not there when nothing changed"}}},"example":{"success":true,"message":"Account status updated from active to suspended","data":{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Baraka Traders Ltd","accountNumber":"ACC-000123","status":"suspended","description":"Hardware shop on Moi Avenue","addressId":"f4e13f6b-8c0d-4f7a-9d2e-6b7c8d9e0f1a","onboardingDate":"2026-10-01T08:30:00.000Z","terminationDate":null,"creditLimit":"50000.00","currentBalance":"0.00","originLeadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","walletId":null,"billingContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","serviceContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notificationPreferences":{"email":true,"sms":true},"tags":["retail"],"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Baraka Traders Ltd","cachePrimaryPhone":"+254700000101","cachePrimaryEmail":"info@baraka-traders.example","cachePartyRevision":3,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"},"previousStatus":"active"}}}},"400":{"description":"status is not active, suspended or terminated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No account with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"That move is not allowed, e.g. out of terminated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.accounts.update"]}],"x-required-scopes":["crm.accounts.update"]}},"/contacts/{contactId}/status":{"put":{"tags":["Contacts"],"summary":"Change a contact's status","description":"Move a contact to active, invited, inactive or suspended. Allowed moves: active to inactive, suspended or invited; invited to active or inactive; inactive to active or invited; suspended to active or inactive. Setting the status it already has changes nothing.\n\n**Permission:** `crm.contacts.update` (Update contacts).","operationId":"updateContactStatus","parameters":[{"name":"contactId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The contact's ID","example":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","description":"Why, kept in the contact's history","examples":["Left the company"]},"status":{"type":"string","enum":["active","invited","inactive","suspended"],"description":"The new status","examples":["inactive"]}},"required":["status"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","description":"The email to show (the shared record's when filed under one)"},"phone":{"type":"string","description":"The phone to show (the shared record's when filed under one)"},"addressId":{},"status":{"type":"string","description":"active, invited, inactive or suspended"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}},"description":"Communication preferences: notifications, preferredContactMethod (email, phone or sms), timezone, language"},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array"},"partyId":{"type":"string","description":"The person record in Kazee Contacts the contact is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the contact is filed under one, else first and last name"},"fullName":{"type":"string","description":"Same as displayName"}}},"previousStatus":{"type":"string","description":"The status before; not there when nothing changed"}}},"example":{"success":true,"message":"Contact status updated from active to inactive","data":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"inactive","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau"},"previousStatus":"active"}}}},"400":{"description":"status is not active, invited, inactive or suspended","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No contact with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"That move is not allowed, e.g. suspended to invited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.contacts.update"]}],"x-required-scopes":["crm.contacts.update"]}},"/contacts/{contactId}/preferences":{"put":{"tags":["Contacts"],"summary":"Update a contact's preferences","description":"Replace a contact's communication preferences: notifications, billing, technical, preferredContactMethod (email, phone or sms), timezone and language. Keys left out are removed.\n\n**Permission:** `crm.contacts.update` (Update contacts).","operationId":"updateContactPreferences","parameters":[{"name":"contactId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The contact's ID","example":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"preferences":{"type":"object","properties":{"notifications":{"type":"object"},"billing":{"type":"object"},"technical":{"type":"object"},"preferredContactMethod":{"type":"string","enum":["email","phone","sms"]},"timezone":{"type":"string"},"language":{"type":"string"}},"description":"The new preferences, replacing the stored ones (a JSON string also works)","examples":[{"preferredContactMethod":"sms","timezone":"Africa/Nairobi","language":"en","notifications":{"outages":true,"marketing":false}}]},"userId":{"type":"string","format":"uuid","description":"ID of the person making the change (for the history)","examples":["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]}},"required":["preferences","userId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","description":"The email to show (the shared record's when filed under one)"},"phone":{"type":"string","description":"The phone to show (the shared record's when filed under one)"},"addressId":{},"status":{"type":"string","description":"active, invited, inactive or suspended"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"},"timezone":{"type":"string"},"language":{"type":"string"},"notifications":{"type":"object","properties":{"outages":{"type":"boolean"},"marketing":{"type":"boolean"}}}},"description":"Communication preferences: notifications, preferredContactMethod (email, phone or sms), timezone, language"},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array"},"partyId":{"type":"string","description":"The person record in Kazee Contacts the contact is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the contact is filed under one, else first and last name"},"fullName":{"type":"string","description":"Same as displayName"}}}}},"example":{"success":true,"message":"Contact preferences updated successfully","data":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"sms","timezone":"Africa/Nairobi","language":"en","notifications":{"outages":true,"marketing":false}},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau"}}}}},"400":{"description":"preferences is not valid JSON, has an unknown key, or preferredContactMethod is not email, phone or sms","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No contact with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.contacts.update"]}],"x-required-scopes":["crm.contacts.update"]}},"/requests/{requestId}/status":{"put":{"tags":["Requests"],"summary":"Change a request's status","description":"Moves a service request to another status. The step must be allowed: new → assigned, closed or expired; assigned → in_progress, closed or expired; in_progress → completed, assigned or closed; completed → closed; closed or expired → new. The change is written to the audit log.\n\n**Permission:** `crm.requests.update` (Update requests).","operationId":"updateRequestStatus","parameters":[{"name":"requestId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The request to move","example":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"notes":{"type":"string","description":"Why it moved; kept in the audit log","examples":["Technician on site"]},"status":{"type":"string","enum":["new","assigned","in_progress","completed","closed","expired"],"description":"The new status","examples":["in_progress"]}},"required":["status"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"request":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string"},"requestType":{"type":"string","description":"What is asked for, e.g. relocation, upgrade or maintenance"},"title":{"type":"string"},"description":{"type":"string"},"accountId":{"type":"string"},"subscriptionId":{"type":"string"},"quoteId":{},"status":{"type":"string","description":"new, assigned, in_progress, completed, closed or expired (rejected once rejected)"},"assignedTo":{"type":"string","description":"The person handling it"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"expiryAt":{"type":"string","format":"date-time","description":"When the request lapses if nobody finishes it"}}}}},"example":{"success":true,"message":"Request status updated successfully","request":{"_id":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e","id":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e","requestType":"relocation","title":"Relocation - Move the fibre line to the new house in Kileleshwa","description":"Move the fibre line to the new house in Kileleshwa from 1 November.","accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","subscriptionId":"b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d","quoteId":null,"status":"in_progress","assignedTo":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","createdAt":"2026-10-20T08:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z","expiryAt":"2026-11-19T08:00:00.000Z"}}}}},"400":{"description":"The status is unknown or may not follow the current one","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No request with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"The status could not be saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.requests.update"]}],"x-required-scopes":["crm.requests.update"]}},"/plans/{planId}/books-item":{"put":{"tags":["Products & plans"],"summary":"Link a plan to a Books item","description":"Links a service plan to the Kazee Books item it is sold as, or clears the link (booksItemId empty). The link decides what every new quote for the plan charges for the first month: this item, at its Books price and tax. The item is checked in Books first (active, sold, with an income account and a usable tax). Written to the audit log; nobody is notified. Needs Books item read access beside plan update.\n\n**Permission:** `crm.plans.update` (Update plans) and `crm.books.items.read`.","operationId":"setPlanBooksItem","parameters":[{"name":"planId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The plan to link","example":"9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"booksItemId":{"type":"string","description":"The Kazee Books item ID (itm_ and 32 hex characters); leave out or empty to clear the link","examples":["itm_8c1d4e5f60718293a4b5c6d7e8f9a0b1"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"plan":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"limitStr":{"type":"string"},"burstMaxDown":{"description":"Burst settings (burstMax*, burstThresh*, burstTime*): the router's burst speed, threshold and time, or null"},"burstMaxUp":{},"burstThreshDown":{},"burstThreshUp":{},"burstTimeDown":{},"burstTimeUp":{},"connectionType":{"type":"array","items":{"type":"string"},"description":"Fiber, Wireless or both"},"installationFee":{"type":"string","description":"Installation fee, a decimal in a string"},"maxLimitDown":{"type":"integer","description":"Download speed limit, in kbit/s"},"maxLimitUp":{"type":"integer","description":"Upload speed limit, in kbit/s"},"rateLimit":{"type":"string","description":"The router's rate-limit text, e.g. 50M/25M"},"serviceTypeId":{"type":"string"},"statusLevelId":{},"status":{"type":"string","description":"active, inactive or suspended"},"productId":{"type":"string","description":"The product this plan is a speed option of"},"booksItemId":{"type":"string","description":"The Kazee Books item the plan is sold as (the first month of its quotes), or null"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"booksItem":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"sku":{"type":"string"},"rate":{"type":"string","description":"The item's price in Books, a decimal in a string"},"tax":{"type":"object","properties":{"kind":{"type":"string"},"id":{"type":"string"}},"description":"The item's tax: { kind: tax or group, id } or { kind: none }"},"taxLabel":{"type":"string","description":"The tax as people read it, e.g. VAT (16%)"}},"description":"The linked item as Books has it (null when the link was cleared)"}}}}},"example":{"success":true,"data":{"plan":{"id":"9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Home Fibre 50 Mbps","limitStr":"","burstMaxDown":null,"burstMaxUp":null,"burstThreshDown":null,"burstThreshUp":null,"burstTimeDown":null,"burstTimeUp":null,"connectionType":["Fiber"],"installationFee":"2000.00","maxLimitDown":51200,"maxLimitUp":25600,"rateLimit":"50M/25M","serviceTypeId":"e3d02e5a-7b9c-4e6f-9c1d-5a6b7c8d9e0f","statusLevelId":null,"status":"active","productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","booksItemId":"itm_8c1d4e5f60718293a4b5c6d7e8f9a0b1","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"},"booksItem":{"id":"itm_8c1d4e5f60718293a4b5c6d7e8f9a0b1","name":"Home Fibre 50 Mbps - monthly","sku":"HF-50-M","rate":"4500.00","tax":{"kind":"tax","id":"tax_4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e"},"taxLabel":"VAT (16%)"}}}}}},"400":{"description":"booksItemId is not a Books item ID (itm_ and 32 hex characters), or the item cannot be quoted (the message says why)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No plan with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.plans.update","crm.books.items.read"]}],"x-required-scopes":["crm.plans.update","crm.books.items.read"],"x-scope-mode":"all"}},"/pipelines/{pipelineId}":{"put":{"tags":["Pipelines"],"summary":"Update a pipeline","description":"Changes a pipeline's name, description, custom module, default mark, active mark or card date field; only the inputs you send change. Making it the default takes the mark off the record type's other pipeline. The card date field is a date or date-time field of the pipeline's records that the board shows on every card and orders each column by, earliest first (overdue, then today, then upcoming), records without it last. Its record type cannot change. Needs pipelines.update, or Desk admin for a ticket pipeline.\n\n**Permission:** `crm.pipelines.update` (Update pipelines) or `crm.desk.admin` (Configure Desk).","operationId":"updatePipeline","parameters":[{"name":"pipelineId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The pipeline","example":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"cardDateField":{"type":"string","description":"Key of a date or date-time field of this pipeline's record type shown on board cards and used to order each column, earliest first (records without a date last); empty or null clears it","examples":["follow_up_date"]},"customModuleSlug":{"type":"string","description":"Custom module slug; empty clears it","examples":["site-surveys"]},"description":{"type":"string","description":"New description; null clears it","examples":["Leads for home fibre, from first contact to qualified"]},"isActive":{"type":"boolean","description":"Turn the pipeline on or off","examples":[true]},"isDefault":{"type":"boolean","description":"Make it (true) or stop it being (false) the default pipeline","examples":[true]},"name":{"type":"string","description":"New name","examples":["Home fibre sales"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"pipeline":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"moduleType":{"type":"string","description":"Record type the pipeline moves: leads, deals, subscriptions, tickets, requests, accounts, contacts or custom"},"customModuleSlug":{"description":"For a custom module's pipeline, the module's slug; otherwise null"},"isDefault":{"type":"boolean","description":"The pipeline new records of that type start in (one per record type, or per custom module)"},"isActive":{"type":"boolean"},"cardDateField":{"type":"string","description":"Key of the date or date-time field the board shows on every card and orders each column by (earliest first, records without it last); null: no card date, columns by last update"},"createdBy":{"type":"string"},"updatedBy":{"type":"string"},"deletedAt":{"description":"Always null here: deleted pipelines are left out"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"pipeline":{"id":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","organizationId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","name":"Home fibre sales","description":"Leads for home fibre, from first contact to qualified","moduleType":"leads","customModuleSlug":null,"isDefault":true,"isActive":true,"cardDateField":"follow_up_date","createdBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","updatedBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","deletedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z"}}}}},"400":{"description":"cardDateField is not an active date or date-time field of the pipeline's record type","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You lack pipelines.update, or you are a Desk admin and the pipeline is not a ticket pipeline","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No pipeline with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.pipelines.update","crm.desk.admin"]}],"x-required-scopes":["crm.pipelines.update","crm.desk.admin"],"x-scope-mode":"any"}},"/pipelines/{pipelineId}/stages/{stageId}":{"put":{"tags":["Pipelines"],"summary":"Update a stage","description":"Changes a stage; only the inputs you send change. Positions are rounded to whole numbers. The stage is found by its ID alone: the pipeline in the address is not checked. A ticket stage keeps its statusKey through a rename, so its tickets stay on it; a ticket stage without one gets its old name's status as its statusKey when renamed. Needs pipelines.update, or Desk admin for a ticket pipeline.\n\n**Permission:** `crm.pipelines.update` (Update pipelines) or `crm.desk.admin` (Configure Desk).","operationId":"updateStage","parameters":[{"name":"pipelineId","in":"path","required":true,"schema":{"type":"string"}},{"name":"stageId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The stage","example":"a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"color":{"type":"string","description":"Colour, a hex code like #8b5cf6","examples":["#8b5cf6"]},"description":{"type":"string","description":"New description; null clears it","examples":["We have spoken to them"]},"isDefault":{"type":"boolean","description":"New records start in this stage","examples":[false]},"isTerminal":{"type":"boolean","description":"Records in this stage are finished","examples":[false]},"name":{"type":"string","description":"New name","examples":["Contacted"]},"orderNum":{"type":"integer","description":"Position on the board, from 0","examples":[1]},"positionX":{"type":"number","description":"Place on the workflow editor's canvas, left to right","examples":[200]},"positionY":{"type":"number","description":"Place on the workflow editor's canvas, top to bottom","examples":[100]},"stageType":{"type":"string","description":"Kind of stage: active, won or lost","examples":["active"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"stage":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"pipelineId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"color":{"type":"string","description":"Colour of the stage, a hex code like #8b5cf6"},"orderNum":{"type":"integer","description":"Position of the stage on the board, from 0"},"stageType":{"type":"string","description":"Kind of stage: active, won or lost (free text; these are the ones CRM sets up)"},"isDefault":{"type":"boolean","description":"New records start in this stage"},"isTerminal":{"type":"boolean","description":"Records in this stage are finished"},"positionX":{"type":"integer","description":"Where the stage sits on the workflow editor's canvas, left to right"},"positionY":{"type":"integer","description":"Where the stage sits on the workflow editor's canvas, top to bottom"},"statusKey":{"description":"Ticket pipelines: the ticket status this stage stands for, set when the stage is made and kept through a rename; null elsewhere (the status is then the name in lowercase with underscores)"},"createdBy":{"type":"string"},"updatedBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"stage":{"id":"a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b","organizationId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","pipelineId":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","name":"Contacted","description":"We have spoken to them","color":"#8b5cf6","orderNum":1,"stageType":"active","isDefault":false,"isTerminal":false,"positionX":200,"positionY":100,"statusKey":null,"createdBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","updatedBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","deletedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You lack pipelines.update, or you are a Desk admin and the pipeline is not a ticket pipeline","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No stage with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.pipelines.update","crm.desk.admin"]}],"x-required-scopes":["crm.pipelines.update","crm.desk.admin"],"x-scope-mode":"any"},"delete":{"tags":["Pipelines"],"summary":"Delete a stage","description":"Deletes a stage. Refused while any transition leads to or from it. The stage is found by its ID alone: the pipeline in the address is not checked. Records still in the stage are not moved. Needs pipelines.delete, or Desk admin for a ticket pipeline.\n\n**Permission:** `crm.pipelines.delete` (Delete pipelines) or `crm.desk.admin` (Configure Desk).","operationId":"deleteStage","parameters":[{"name":"pipelineId","in":"path","required":true,"schema":{"type":"string"}},{"name":"stageId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The stage","example":"a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}},"example":{"success":true,"message":"Stage deleted successfully"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You lack pipelines.delete, or you are a Desk admin and the pipeline is not a ticket pipeline","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No stage with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Transitions still lead to or from the stage: delete or change them first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.pipelines.delete","crm.desk.admin"]}],"x-required-scopes":["crm.pipelines.delete","crm.desk.admin"],"x-scope-mode":"any"}},"/pipelines/{pipelineId}/stages-positions":{"put":{"tags":["Pipelines"],"summary":"Save stage positions","description":"Saves where many stages sit on the workflow editor's canvas in one go: all are saved or none. Positions are rounded to whole numbers. Stages not in this pipeline, and entries without an id, are skipped. Answers the stages it saved. Needs pipelines.update, or Desk admin for a ticket pipeline.\n\n**Permission:** `crm.pipelines.update` (Update pipelines) or `crm.desk.admin` (Configure Desk).","operationId":"saveStagePositions","parameters":[{"name":"pipelineId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The pipeline","example":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"positions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"positionX":{"type":"number"},"positionY":{"type":"number"}}},"description":"One entry per stage: its id and new positionX and positionY. A JSON string also works","examples":[[{"id":"a1b2c3d4-0001-4e5f-8a9b-0c1d2e3f4a5b","positionX":0,"positionY":100},{"id":"a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b","positionX":200.4,"positionY":100}]]}},"required":["positions"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"stages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"pipelineId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"color":{"type":"string","description":"Colour of the stage, a hex code like #8b5cf6"},"orderNum":{"type":"integer","description":"Position of the stage on the board, from 0"},"stageType":{"type":"string","description":"Kind of stage: active, won or lost (free text; these are the ones CRM sets up)"},"isDefault":{"type":"boolean","description":"New records start in this stage"},"isTerminal":{"type":"boolean","description":"Records in this stage are finished"},"positionX":{"type":"integer","description":"Where the stage sits on the workflow editor's canvas, left to right"},"positionY":{"type":"integer","description":"Where the stage sits on the workflow editor's canvas, top to bottom"},"statusKey":{"description":"Ticket pipelines: the ticket status this stage stands for, set when the stage is made and kept through a rename; null elsewhere (the status is then the name in lowercase with underscores)"},"createdBy":{"type":"string"},"updatedBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}},"example":{"success":true,"stages":[{"id":"a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b","organizationId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","pipelineId":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","name":"Contacted","description":"We have spoken to them","color":"#8b5cf6","orderNum":1,"stageType":"active","isDefault":false,"isTerminal":false,"positionX":200,"positionY":100,"statusKey":null,"createdBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","updatedBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","deletedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z"}]}}}},"400":{"description":"positions is not a list (or not valid JSON), or a position is not a number","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You lack pipelines.update, or you are a Desk admin and the pipeline is not a ticket pipeline","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No pipeline with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.pipelines.update","crm.desk.admin"]}],"x-required-scopes":["crm.pipelines.update","crm.desk.admin"],"x-scope-mode":"any"}},"/pipelines/{pipelineId}/transitions/{transitionId}":{"put":{"tags":["Pipelines"],"summary":"Update a transition","description":"Changes a transition; only the inputs you send change. Condition operators are saved in their short form (equals becomes eq). The transition is found by its ID alone: the pipeline in the address is not checked. New stage IDs must be stages of the transition's own pipeline. Needs pipelines.update, or Desk admin for a ticket pipeline.\n\n**Permission:** `crm.pipelines.update` (Update pipelines) or `crm.desk.admin` (Configure Desk).","operationId":"updateTransition","parameters":[{"name":"pipelineId","in":"path","required":true,"schema":{"type":"string"}},{"name":"transitionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The transition","example":"b2c3d4e5-0001-4f60-9a7b-1c2d3e4f5a6b"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"actions":{"type":"array","items":{"type":"object"},"description":"Steps run when it is run, in order: each with actionType (email, webhook, notification, update_field, assign_user, create_ticket, start_billing...), name, isActive and config. See the workflow capabilities for what each takes. A JSON string also works","examples":[[{"id":"qualify-action-0","actionType":"notification","name":"Tell the owner","isActive":true,"config":{"to":"owner","title":"Lead qualified","message":"{{record.name}} moved to {{stage.to}}"},"orderNum":0}]]},"allowedRoles":{"type":"array","items":{"type":"string"},"description":"Roles that may run it; with allowedUsers both empty, anyone may. A JSON array or comma-separated text also works","examples":[["sales"]]},"allowedUsers":{"type":"array","items":{"type":"string","format":"uuid"},"description":"People (user IDs) that may run it, besides allowedRoles. A JSON array or comma-separated text also works","examples":[["f6a7b8c9-3d4e-4f5a-9b6c-7d8e9f0a1b2c"]]},"color":{"type":"string","description":"Button colour, a hex code","examples":["#22c55e"]},"commentPrompt":{"type":"string","description":"The question shown above the comment box","examples":["What did they say?"]},"confirmationMessage":{"type":"string","description":"The question asked when confirmation is needed","examples":["Mark this lead as qualified?"]},"criteria":{"type":"array","items":{"type":"object"},"description":"Conditions the record must meet for the transition to show and run: each with fieldPath, operator (eq, neq, gt, gte, lt, lte, contains, isEmpty, isNotEmpty, in...), value and logicOperator (AND or OR). A JSON string also works","examples":[[{"id":"cond-0","fieldPath":"phone","fieldLabel":"Phone","operator":"isNotEmpty","logicOperator":"AND"}]]},"description":{"type":"string","description":"New description; null clears it","examples":["They want fibre and we can reach them"]},"fields":{"type":"array","items":{"type":"object"},"description":"Retired old-style form fields: ignored. Use requiredFieldIds","examples":[[]]},"fromStageId":{"type":"string","format":"uuid","description":"New stage the record leaves","examples":["a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b"]},"fromStageIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"For a global transition: the stages it can be run from. A JSON array or comma-separated text also works","examples":[["a1b2c3d4-0001-4e5f-8a9b-0c1d2e3f4a5b","a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b"]]},"icon":{"type":"string","description":"Icon name for the button; null clears it","examples":["check-circle"]},"isActive":{"type":"boolean","description":"Turn the transition on or off; an inactive one cannot be run","examples":[true]},"isGlobal":{"type":"boolean","description":"Can be run from any stage in fromStageIds. Turning it off clears fromStageIds unless you send them","examples":[false]},"name":{"type":"string","description":"New button label","examples":["Qualify"]},"requireComment":{"type":"boolean","description":"A comment is needed to run it","examples":[true]},"requiredFieldIds":{"type":"array","items":{"type":"string"},"description":"Record fields (custom field keys) that must be filled to run it; values entered are saved on the record. A JSON array or comma-separated text also works","examples":[["coverage_confirmed"]]},"requiresConfirmation":{"type":"boolean","description":"Ask the person to confirm before it runs","examples":[true]},"toStageId":{"type":"string","format":"uuid","description":"New stage the record moves to","examples":["a1b2c3d4-0003-4e5f-8a9b-0c1d2e3f4a5b"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"transition":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"pipelineId":{"type":"string"},"fromStageId":{"type":"string","description":"Stage the record leaves. For a global transition, the first of fromStageIds"},"toStageId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"color":{"type":"string"},"icon":{"type":"string"},"isActive":{"type":"boolean"},"requiresConfirmation":{"type":"boolean"},"confirmationMessage":{"type":"string"},"isGlobal":{"type":"boolean","description":"Can be run from any stage in fromStageIds, not just fromStageId"},"fromStageIds":{"type":"array","items":{"type":"string"},"description":"Stages a global transition can be run from (the one source stage otherwise)"},"criteria":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"fieldPath":{"type":"string"},"fieldLabel":{"type":"string"},"operator":{"type":"string"},"logicOperator":{"type":"string"}}},"description":"Conditions the record must meet for the transition to show and run"},"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"type":{"type":"string"},"required":{"type":"boolean"}}},"description":"Old-style form fields. Retired: new and edited transitions keep this as it is and never use it"},"requiredFieldIds":{"type":"array","items":{"type":"string"},"description":"Record fields (custom field keys) that must be filled to run it; values entered are saved on the record"},"requireComment":{"type":"boolean","description":"A comment (notes) is needed to run it"},"commentPrompt":{"type":"string"},"actions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"actionType":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"},"config":{"type":"object","properties":{"to":{"type":"string"},"title":{"type":"string"},"message":{"type":"string"}}},"orderNum":{"type":"integer"}}},"description":"Steps run when it is run: actionType (email, webhook, notification, update_field, assign_user, create_ticket, start_billing...), name, config and order"},"allowedRoles":{"type":"array","items":{"type":"string"},"description":"Roles that may run it. With allowedUsers both empty, anyone may"},"allowedUsers":{"type":"array","items":{"type":"string"},"description":"People (user IDs) that may run it, besides allowedRoles"},"createdBy":{"type":"string"},"updatedBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"transition":{"id":"b2c3d4e5-0001-4f60-9a7b-1c2d3e4f5a6b","organizationId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","pipelineId":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","fromStageId":"a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b","toStageId":"a1b2c3d4-0003-4e5f-8a9b-0c1d2e3f4a5b","name":"Qualify","description":"They want fibre and we can reach them","color":"#22c55e","icon":"check-circle","isActive":true,"requiresConfirmation":true,"confirmationMessage":"Mark this lead as qualified?","isGlobal":false,"fromStageIds":["a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b"],"criteria":[{"id":"cond-0","fieldPath":"phone","fieldLabel":"Phone","operator":"isNotEmpty","logicOperator":"AND"}],"fields":[{"key":"survey_date","label":"Survey date","type":"date","required":true}],"requiredFieldIds":["coverage_confirmed"],"requireComment":true,"commentPrompt":"What did they say?","actions":[{"id":"qualify-action-0","actionType":"notification","name":"Tell the owner","isActive":true,"config":{"to":"owner","title":"Lead qualified","message":"{{record.name}} moved to {{stage.to}}"},"orderNum":0}],"allowedRoles":["sales"],"allowedUsers":["f6a7b8c9-3d4e-4f5a-9b6c-7d8e9f0a1b2c"],"createdBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","updatedBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","deletedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z"}}}}},"400":{"description":"criteria or actions is not valid JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You lack pipelines.update, or you are a Desk admin and the pipeline is not a ticket pipeline","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No transition with that ID in your organisation, or a new stage ID is not a stage of its pipeline","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.pipelines.update","crm.desk.admin"]}],"x-required-scopes":["crm.pipelines.update","crm.desk.admin"],"x-scope-mode":"any"},"delete":{"tags":["Pipelines"],"summary":"Delete a transition","description":"Deletes a transition, so records can no longer be moved along it. Past runs stay in the transition history. The transition is found by its ID alone: the pipeline in the address is not checked. Needs pipelines.delete, or Desk admin for a ticket pipeline.\n\n**Permission:** `crm.pipelines.delete` (Delete pipelines) or `crm.desk.admin` (Configure Desk).","operationId":"deleteTransition","parameters":[{"name":"pipelineId","in":"path","required":true,"schema":{"type":"string"}},{"name":"transitionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The transition","example":"b2c3d4e5-0001-4f60-9a7b-1c2d3e4f5a6b"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}},"example":{"success":true,"message":"Transition deleted successfully"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You lack pipelines.delete, or you are a Desk admin and the pipeline is not a ticket pipeline","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No transition with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.pipelines.delete","crm.desk.admin"]}],"x-required-scopes":["crm.pipelines.delete","crm.desk.admin"],"x-scope-mode":"any"}},"/deals/{dealId}/stage":{"put":{"tags":["Deals"],"summary":"Move a deal to another stage","description":"Move a deal to another stage and log the move in the deal's activities. Without a probability, the stage sets it (qualification 10, discovery 25, proposal 50, negotiation 75, closed_won 100, closed_lost 0). closed_lost needs a lost reason and records today as the close date; closed_won records actualCloseDate (default now). This is a plain move: it does not run the pipeline's transition checks or forms.\n\n**Permission:** `crm.deals.update` (Update deals).","operationId":"updateDealStage","parameters":[{"name":"dealId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The deal's ID","example":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"actualCloseDate":{"type":"string","format":"date-time","description":"When it was won (closed_won only); default now","examples":["2026-10-15T14:05:00.000Z"]},"competitorId":{"type":"string","description":"Who won the deal instead (closed_lost only)","examples":["competitor-airfibre"]},"lostReason":{"type":"string","description":"Why the deal was lost; required for closed_lost","examples":["price"]},"lostReasonDetails":{"type":"string","description":"More about why it was lost (closed_lost only)","examples":["Went with a cheaper wireless offer"]},"notes":{"type":"string","description":"A note kept on the stage-change activity","examples":["Customer accepted the price; waiting for sign-off"]},"probability":{"type":"integer","description":"Chance of winning, 0 to 100; defaults from the stage (left empty for a stage outside the six defaults)","examples":[75]},"stage":{"type":"string","description":"The stage key to move to","examples":["negotiation"]}},"required":["stage"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"deal":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"leadId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"value":{"type":"string","description":"Deal value, a decimal string in the deal's currency (two decimals)"},"currency":{"type":"string","description":"ISO 4217 code; the organisation's currency unless set"},"recurringValue":{"type":"string"},"stage":{"type":"string","description":"Stage key on the deals pipeline, e.g. qualification, discovery, proposal, negotiation, closed_won, closed_lost (organisations may use their own)"},"probability":{"type":"integer","description":"Chance of winning, 0 to 100"},"expectedCloseDate":{"type":"string","format":"date-time"},"actualCloseDate":{},"ownerId":{"type":"string"},"teamId":{},"priority":{"type":"string","description":"low, medium or high"},"source":{"type":"string"},"dealType":{"type":"string"},"lostReason":{},"lostReasonDetails":{},"competitorId":{},"lastActivityDate":{"type":"string","format":"date-time"},"nextActivityDate":{},"nextActivityType":{},"notes":{"type":"string"},"customFields":{"type":"object","properties":{"contract_term_length":{"type":"string"}},"description":"The organisation's own fields, by field key. Fields hidden from you come back masked"},"externalIds":{"type":"array"},"createdBy":{"type":"string"},"updatedBy":{"type":"string"},"version":{"type":"integer","description":"Goes up by one on every change"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"message":{"type":"string"},"previousStage":{"type":"string","description":"The stage the deal was at before"}}},"example":{"deal":{"id":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Acacia Bakery: 50 Mbps fibre","description":"Fibre for the bakery's main branch","leadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","value":"4500.00","currency":"KES","recurringValue":"4500.00","stage":"negotiation","probability":75,"expectedCloseDate":"2026-11-01T00:00:00.000Z","actualCloseDate":null,"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","teamId":null,"priority":"high","source":"website","dealType":"new_business","lostReason":null,"lostReasonDetails":null,"competitorId":null,"lastActivityDate":"2026-10-15T14:05:00.000Z","nextActivityDate":null,"nextActivityType":null,"notes":"","customFields":{"contract_term_length":"12 months"},"externalIds":[],"createdBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","updatedBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","version":4,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"},"message":"Deal stage updated to negotiation","previousStage":"proposal"}}}},"400":{"description":"stage is missing, or it is closed_lost without a lostReason","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No deal with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.deals.update"]}],"x-required-scopes":["crm.deals.update"]}},"/tickets/{ticketId}/status":{"put":{"tags":["Tickets"],"summary":"Change a ticket's status","description":"Moves a ticket to another status by a move of the organisation's ticket blueprint (its ticket pipeline): the move must leave from the ticket's current status and be one you may run (GET /tickets/:id/moves lists them). When the move asks for fields or a comment, send them as fieldValues and comment; any still missing refuse it (422, missing, commentRequired and fields, which describes each missing field). The move's form is saved on the ticket, it is noted in the ticket's history, the service-level clocks pause, resume, stop or restart to match, the assignee, owner and followers get a Desk notification, and closing a ticket sends the satisfaction survey when the organisation has one set up. The move's actions and the automations waiting for it run. Leaving waiting_on_customer ends a hold; resolving removes the back-from-hold tag. Ticket-updated automations run afterwards.\n\n**Permission:** `crm.tickets.update` (Update tickets).","operationId":"updateTicketStatus","parameters":[{"name":"ticketId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The ticket to move","example":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"comment":{"type":"string","description":"The move's comment, needed when it asks for one; kept in the move's history","examples":["Refund issued, customer confirmed"]},"fieldValues":{"type":"object","description":"Values for the move's required fields, by field id (a JSON string of the object also works); saved on the ticket","examples":[{"category":"Billing"}]},"reason":{"type":"string","description":"Why, kept in the ticket's history and shown in the notification (the comment when absent)","examples":["Engineer is on the way"]},"status":{"type":"string","description":"The new status: the toStatus of one of the ticket's moves (GET /tickets/:id/moves). The default blueprint's statuses are open, pending, in_progress, waiting_on_customer, waiting_on_third_party, resolved and closed","examples":["in_progress"]},"userId":{"type":"string","format":"uuid","description":"Who makes the change, when the sign-in names nobody","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]}},"required":["status"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"ticketNumber":{"type":"string","description":"The number people quote, e.g. TKT-001042"},"subject":{"type":"string"},"description":{"type":"string"},"channel":{"type":"string","description":"Where it came in: web, email, whatsapp, phone, chat, telegram and the like"},"status":{"type":"string","description":"open, pending, in_progress, waiting_on_customer, waiting_on_third_party, resolved or closed"},"priority":{"type":"string","description":"low, medium, high or urgent"},"type":{"type":"string"},"category":{"type":"string","description":"One of the organisation's Desk ticket categories"},"accountId":{"type":"string"},"contactId":{"type":"string"},"subscriptionId":{"type":"string"},"leadId":{},"dealId":{},"requesterEmail":{"type":"string"},"requesterName":{"type":"string"},"ownerId":{"type":"string","description":"The person who filed it"},"assignedTo":{"type":"string","description":"The person working on it"},"teamId":{"type":"string","description":"The department it sits with"},"tags":{"type":"array","items":{"type":"string"}},"slaPolicyId":{"type":"string"},"slaStatus":{"type":"object","properties":{"firstResponseDue":{"type":"string","format":"date-time"},"resolutionDue":{"type":"string","format":"date-time"},"firstResponseAt":{"type":"string","format":"date-time"}},"description":"The service-level clocks: when the first reply and the resolution are due, and when they happened"},"customFields":{"type":"object","properties":{}},"mergedInto":{},"relatedTickets":{"type":"array"},"satisfaction":{"type":"object","properties":{}},"firstResponseTime":{"type":"integer","description":"Minutes from opening to the first agent reply"},"resolutionTime":{"description":"Minutes from opening to resolution"},"reopenCount":{"type":"integer"},"holdUntil":{"description":"On hold: when it comes back by itself (null when not on hold)"},"holdMode":{"description":"On hold: timer (for a time) or reply (until the customer replies, back after the organisation's no-reply days at most)"},"holdStartedAt":{"description":"On hold: since when"},"holdBy":{"description":"On hold: who put it on hold"},"holdPreviousStatus":{"description":"On hold: the status before, which Undo restores"},"backFromHoldAt":{"description":"When a hold ended by itself; the ticket tops its assignee's My open until an agent replies, holds or resolves it"},"backFromHoldReason":{"description":"timer (no reply in time) or customer_reply"},"version":{"type":"integer","description":"Goes up by one on every change"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Ticket status updated from 'open' to 'in_progress'","data":{"id":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","ticketNumber":"TKT-001042","subject":"Internet down since this morning","description":"The router shows a red light and nothing loads since about 7am.","channel":"whatsapp","status":"in_progress","priority":"high","type":"incident","category":"Connectivity","accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","contactId":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","subscriptionId":"b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d","leadId":null,"dealId":null,"requesterEmail":"amina.wanjiru@example.com","requesterName":"Amina Wanjiru","ownerId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","assignedTo":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","teamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","tags":["outage"],"slaPolicyId":"e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a","slaStatus":{"firstResponseDue":"2026-10-20T10:15:00.000Z","resolutionDue":"2026-10-21T08:15:00.000Z","firstResponseAt":"2026-10-20T08:42:00.000Z"},"customFields":{},"mergedInto":null,"relatedTickets":[],"satisfaction":{},"firstResponseTime":27,"resolutionTime":null,"reopenCount":0,"holdUntil":null,"holdMode":null,"holdStartedAt":null,"holdBy":null,"holdPreviousStatus":null,"backFromHoldAt":null,"backFromHoldReason":null,"version":3,"deletedAt":null,"createdAt":"2026-10-20T08:15:00.000Z","updatedAt":"2026-10-20T09:05:00.000Z"}}}}},"400":{"description":"fieldValues is not a JSON object, or a value breaks its field's rules","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The move is limited to roles or people and you are not one of them","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No ticket with that ID that you may see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The ticket blueprint has no move from the ticket's status to that one (the message lists where it can go)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The move needs fields or a comment that are missing: missing lists the field ids, commentRequired says the comment is wanted, fields describes each missing field as {id, label, type, options?} (error.code MISSING_DETAILS)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.update"]}],"x-required-scopes":["crm.tickets.update"]}},"/kb/categories/{categoryId}":{"put":{"tags":["Knowledge base"],"summary":"Update a knowledge base category","description":"Changes a knowledge base category. Only the fields you send change; moving it to another department moves its articles with it.\n\n**Permission:** `crm.kb.update` (Update knowledge base).","operationId":"updateKBCategory","parameters":[{"name":"categoryId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The category to change","example":"c4b5c6d7-e8f9-4a0b-9d2e-3f4a5b6c7d8e"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"description":{"type":"string","description":"New description; null clears it"},"icon":{"type":"string","description":"New icon name; empty clears it"},"isActive":{"type":"boolean","description":"Whether it is in use"},"isPublic":{"type":"boolean","description":"Whether customers may see it"},"name":{"type":"string","description":"New name","examples":["Router setup"]},"parentId":{"type":"string","format":"uuid","description":"Category to put it under; empty moves it to the top"},"slug":{"type":"string","description":"New name in links"},"sortOrder":{"type":"integer","description":"Where it sits among its siblings, lowest first","examples":[2]},"teamId":{"type":"string","format":"uuid","description":"Home department; empty or \"null\" shares it with every department. Must be a live department of your organisation","examples":["2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"category":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string","description":"The category's name in links, unique in your organisation"},"description":{"type":"string"},"parentId":{"type":"string","description":"The category it sits under, or null at the top"},"icon":{"type":"string"},"sortOrder":{"type":"integer","description":"Where it sits among its siblings, lowest first"},"isPublic":{"type":"boolean","description":"Whether customers may see it"},"isActive":{"type":"boolean"},"articleCount":{"type":"integer"},"teamId":{"type":"string","description":"Its department, or null when every department shares it"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Category updated successfully","category":{"id":"c4b5c6d7-e8f9-4a0b-9d2e-3f4a5b6c7d8e","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","name":"Router setup","slug":"router-setup","description":"Getting your router online and keeping it there.","parentId":"d5c6d7e8-f9a0-4b1c-8e3f-4a5b6c7d8e9f","icon":"router","sortOrder":1,"isPublic":true,"isActive":true,"articleCount":4,"teamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}}}}},"400":{"description":"teamId is not a live department of your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No category with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"The change could not be saved (e.g. the slug is already used by another category)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.kb.update"]}],"x-required-scopes":["crm.kb.update"]},"delete":{"tags":["Knowledge base"],"summary":"Delete a knowledge base category","description":"Deletes a knowledge base category (it is kept hidden, not erased). A category that still has active categories under it cannot be deleted.\n\n**Permission:** `crm.kb.delete` (Delete knowledge base).","operationId":"deleteKBCategory","parameters":[{"name":"categoryId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The category to delete","example":"c4b5c6d7-e8f9-4a0b-9d2e-3f4a5b6c7d8e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"category":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"}}}}},"example":{"success":true,"message":"Category deleted successfully","category":{"id":"c4b5c6d7-e8f9-4a0b-9d2e-3f4a5b6c7d8e","name":"Router setup","isActive":false}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No category with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Active categories still sit under it; move or delete them first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.kb.delete"]}],"x-required-scopes":["crm.kb.delete"]}},"/macros/{macroId}":{"put":{"tags":["Help desk setup"],"summary":"Update a macro","description":"Changes a macro. Only the fields you send change.\n\n**Permission:** `crm.macros.update` (Update macros).","operationId":"updateMacro","parameters":[{"name":"macroId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The macro to change","example":"b9a0b1c2-d3e4-4f5a-8c7d-8e9f0a1b2c3d"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"actions":{"type":"array","items":{"type":"object"},"description":"The full new list of steps, each { type, value } (a JSON text also works): set_status (a status), set_priority (a priority), assign_to (a user ID), add_tag / remove_tag (a tag), add_note (an internal note's text), send_reply (a reply's text, sent on the ticket's channel) and set_type (a ticket type)","examples":[[{"type":"set_status","value":"waiting_on_customer"},{"type":"add_tag","value":"line-restart"},{"type":"send_reply","value":"I have restarted your line; please check now."}]]},"category":{"type":"string","description":"New category; empty clears it"},"description":{"type":"string","description":"New description; null clears it"},"isActive":{"type":"boolean","description":"Whether it can be run"},"isShared":{"type":"boolean","description":"Whether colleagues may use it too"},"name":{"type":"string","description":"New name","examples":["Line restarted, waiting on customer"]},"shortcutKey":{"type":"string","description":"New keyboard shortcut; empty clears it"},"teamId":{"type":"string","format":"uuid","description":"Home department; empty or \"null\" shares it with every department. Must be a live department of your organisation","examples":["2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"actions":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"}}},"description":"The steps, in order, each { type, value }: set_status (a status), set_priority (a priority), assign_to (a user ID), add_tag / remove_tag (a tag), add_note (an internal note's text), send_reply (a reply's text, sent on the ticket's channel) and set_type (a ticket type)"},"isShared":{"type":"boolean","description":"Whether colleagues may use it too"},"createdBy":{"type":"string"},"usageCount":{"type":"integer","description":"How often it was run"},"isActive":{"type":"boolean"},"category":{"type":"string"},"shortcutKey":{"type":"string"},"teamId":{"type":"string","description":"Its department, or null when every department shares it"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Macro \"Line restarted, waiting on customer\" updated successfully","data":{"id":"b9a0b1c2-d3e4-4f5a-8c7d-8e9f0a1b2c3d","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","name":"Line restarted, waiting on customer","description":"Tell the customer we restarted the line and wait for them.","actions":[{"type":"set_status","value":"waiting_on_customer"},{"type":"add_tag","value":"line-restart"},{"type":"send_reply","value":"I have restarted your line; please check now."}],"isShared":true,"createdBy":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","usageCount":12,"isActive":true,"category":"Connectivity","shortcutKey":"ctrl+shift+r","teamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","deletedAt":null,"createdAt":"2026-08-20T08:00:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}}}}},"400":{"description":"actions is not valid JSON, or teamId is not a live department of your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No macro with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.macros.update"]}],"x-required-scopes":["crm.macros.update"]},"delete":{"tags":["Help desk setup"],"summary":"Delete a macro","description":"Deletes a macro (it is kept hidden, not erased), so agents can no longer run it.\n\n**Permission:** `crm.macros.delete` (Delete macros).","operationId":"deleteMacro","parameters":[{"name":"macroId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The macro to delete","example":"b9a0b1c2-d3e4-4f5a-8c7d-8e9f0a1b2c3d"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}}}},"example":{"success":true,"message":"Macro \"Line restarted, waiting on customer\" has been deleted","data":{"id":"b9a0b1c2-d3e4-4f5a-8c7d-8e9f0a1b2c3d","name":"Line restarted, waiting on customer"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No macro with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.macros.delete"]}],"x-required-scopes":["crm.macros.delete"]}},"/canned-responses/{responseId}":{"put":{"tags":["Help desk setup"],"summary":"Update a canned response","description":"Changes a canned response. Only the fields you send change.\n\n**Permission:** `crm.cannedResponses.update` (Update canned responses).","operationId":"updateCannedResponse","parameters":[{"name":"responseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The canned response to change","example":"a8f9a0b1-c2d3-4e4f-9b6c-7d8e9f0a1b2c"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"attachments":{"type":"array","items":{"type":"object"},"description":"The full new list of files (a JSON text also works)"},"category":{"type":"string","description":"New category; empty clears it"},"content":{"type":"string","description":"New text"},"isActive":{"type":"boolean","description":"Whether it is offered to agents"},"isShared":{"type":"boolean","description":"Whether colleagues may use it too"},"shortCode":{"type":"string","description":"New short code; empty clears it"},"tags":{"type":"array","items":{"type":"string"},"description":"The full new list of labels (a JSON text also works)"},"teamId":{"type":"string","format":"uuid","description":"Home department; empty or \"null\" shares it with every department. Must be a live department of your organisation","examples":["2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"]},"title":{"type":"string","description":"New name","examples":["Restart the router"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"title":{"type":"string"},"content":{"type":"string"},"category":{"type":"string"},"shortCode":{"type":"string","description":"What an agent types to insert it, e.g. /restart"},"isShared":{"type":"boolean","description":"Whether colleagues may use it too"},"createdBy":{"type":"string"},"usageCount":{"type":"integer","description":"How often it was used"},"tags":{"type":"array","items":{"type":"string"}},"attachments":{"type":"array"},"isActive":{"type":"boolean"},"teamId":{"type":"string","description":"Its department, or null when every department shares it"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Canned response \"Restart the router\" updated successfully","data":{"id":"a8f9a0b1-c2d3-4e4f-9b6c-7d8e9f0a1b2c","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","title":"Restart the router","content":"Please unplug your router for 30 seconds, plug it back in and tell us if the light turns green.","category":"Connectivity","shortCode":"/restart","isShared":true,"createdBy":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","usageCount":57,"tags":["router"],"attachments":[],"isActive":true,"teamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","deletedAt":null,"createdAt":"2026-08-15T08:00:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}}}}},"400":{"description":"tags or attachments is not valid JSON, or teamId is not a live department of your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No canned response with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.cannedResponses.update"]}],"x-required-scopes":["crm.cannedResponses.update"]},"delete":{"tags":["Help desk setup"],"summary":"Delete a canned response","description":"Deletes a canned response (it is kept hidden, not erased), so agents no longer see it.\n\n**Permission:** `crm.cannedResponses.delete` (Delete canned responses).","operationId":"deleteCannedResponse","parameters":[{"name":"responseId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The canned response to delete","example":"a8f9a0b1-c2d3-4e4f-9b6c-7d8e9f0a1b2c"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"}}}}},"example":{"success":true,"message":"Canned response \"Restart the router\" has been deleted","data":{"id":"a8f9a0b1-c2d3-4e4f-9b6c-7d8e9f0a1b2c","title":"Restart the router"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No canned response with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.cannedResponses.delete"]}],"x-required-scopes":["crm.cannedResponses.delete"]}},"/business-hours/{businessHoursId}":{"put":{"tags":["Help desk setup"],"summary":"Update business hours","description":"Changes a set of business hours. Only the fields you send change; schedule and holidays are each replaced whole. Making it the default takes the default mark off the others.\n\n**Permission:** `crm.businessHours.update` (Update business hours).","operationId":"updateBusinessHours","parameters":[{"name":"businessHoursId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The business hours to change","example":"c0b1c2d3-e4f5-4a6b-9d8e-9f0a1b2c3d4e"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"description":{"type":"string","description":"New description; null clears it"},"holidays":{"type":"array","items":{"type":"object"},"description":"Days off, each { name, date: YYYY-MM-DD, isRecurring } (a JSON text also works)"},"isActive":{"type":"boolean","description":"Whether they are in use"},"isDefault":{"type":"boolean","description":"Make these the default hours"},"name":{"type":"string","description":"New name","examples":["Nairobi office hours"]},"schedule":{"type":"array","items":{"type":"object"},"description":"One entry per weekday, each { day: monday … sunday, isWorkingDay, shifts: [{ start: \"08:00\", end: \"17:00\" }] } (a JSON text also works)","examples":[[{"day":"monday","isWorkingDay":true,"shifts":[{"start":"08:00","end":"17:00"}]},{"day":"saturday","isWorkingDay":true,"shifts":[{"start":"09:00","end":"13:00"}]},{"day":"sunday","isWorkingDay":false,"shifts":[]}]]},"timezone":{"type":"string","description":"New IANA time zone","examples":["Africa/Nairobi"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"timezone":{"type":"string","description":"IANA time zone the hours are in, e.g. Africa/Nairobi"},"isDefault":{"type":"boolean","description":"The hours SLA clocks use when a policy names none; only one set is the default"},"isActive":{"type":"boolean"},"schedule":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string"},"isWorkingDay":{"type":"boolean"},"shifts":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}}}}}},"description":"One entry per weekday: { day (monday to sunday), isWorkingDay, shifts: [{ start, end }] } with times as HH:MM"},"holidays":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"date":{"type":"string"},"isRecurring":{"type":"boolean"}}},"description":"Days off: { name, date (YYYY-MM-DD), isRecurring (every year) }"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Business hours updated successfully","data":{"id":"c0b1c2d3-e4f5-4a6b-9d8e-9f0a1b2c3d4e","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","name":"Nairobi office hours","description":"Weekdays 8 to 5, Saturday mornings","timezone":"Africa/Nairobi","isDefault":true,"isActive":true,"schedule":[{"day":"monday","isWorkingDay":true,"shifts":[{"start":"08:00","end":"17:00"}]},{"day":"saturday","isWorkingDay":true,"shifts":[{"start":"09:00","end":"13:00"}]},{"day":"sunday","isWorkingDay":false,"shifts":[]}],"holidays":[{"name":"Jamhuri Day","date":"2026-12-12","isRecurring":true}],"deletedAt":null,"createdAt":"2026-06-01T08:00:00.000Z","updatedAt":"2026-10-01T08:00:00.000Z"}}}}},"400":{"description":"schedule or holidays is not valid JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No business hours with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.businessHours.update"]}],"x-required-scopes":["crm.businessHours.update"]}},"/custom-fields/{fieldId}":{"put":{"tags":["Custom fields & modules"],"summary":"Update a custom field","description":"Changes a custom field's settings; only what you send changes. Its key, record type and kind of value cannot change here (a text field becomes a people field through Convert a custom field's type). Sending validation without the show-when conditions keeps the stored conditions. isActive false retires the field: hidden from forms, its values kept.\n\n**Permission:** `crm.customFields.update` (Update custom fields).","operationId":"updateCustomFieldDefinition","parameters":[{"name":"fieldId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The custom field's ID","example":"e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8a9b"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"alsoDependsOn":{"type":"array","items":{"type":"object","properties":{"fieldId":{"type":"string"},"values":{"type":"array"}}},"description":"Further conditions like dependsOn ({ fieldId, values } each), combined as conditionsMatch says. An empty list removes them","examples":[[{"fieldId":"connection_method","values":["wireless"]}]]},"conditionsMatch":{"type":"string","enum":["all","any"],"description":"all (the default): the field shows when every condition holds; any: when at least one does","examples":["all"]},"defaultValue":{"type":"string","description":"Value a new record starts with: usually text, though a number, true/false or a list is stored as given","examples":["fibre"]},"dependsOn":{"type":"object","properties":{"fieldId":{"type":"string"},"values":{"type":"array"},"requiredWhenVisible":{"type":"boolean"}},"description":"Show the field only while another field holds one of some values: { fieldId, values, requiredWhenVisible? }. fieldId is a select, multiselect or checkbox field's key of the same module, or _stage / _status for the record's stage, or _transition for the step being run. Send null to remove the condition","examples":[{"fieldId":"_stage","values":["qualified"],"requiredWhenVisible":true}]},"description":{"type":"string","description":"Help text shown with the field","examples":["How the customer wants to be connected"]},"displayOrder":{"type":"integer","description":"Position among the module's fields, smallest first","examples":[3]},"isActive":{"type":"boolean","description":"false retires the field, true brings it back","examples":[true]},"label":{"type":"string","description":"Name people see","examples":["Connection type"]},"lookupModule":{"type":"string","description":"For a lookup or multilookup field: the record type it points at, e.g. users","examples":["users"]},"options":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"},"color":{"type":"string"}}},"description":"The choices of a select or multiselect field: { value, label, color? } each (a JSON array in a string also works)","examples":[[{"value":"fibre","label":"Fibre","color":"#22c55e"},{"value":"wireless","label":"Wireless"}]]},"required":{"type":"boolean","description":"Whether a form must fill the field in","examples":[false]},"showInFilter":{"type":"boolean","description":"Offer it in list filters","examples":[true]},"showInTable":{"type":"boolean","description":"Show it as a column in list views","examples":[true]},"validation":{"type":"object","description":"Rules as an object (or its JSON text): min, max, minLength, maxLength, pattern, mimeTypes, maxBytes, minCount, maxCount, appliesTo; may also hold the show-when conditions","examples":[{"maxLength":50}]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"moduleType":{"type":"string","description":"Record type the field sits on: leads, deals, accounts, contacts, tickets, requests, subscriptions, quotes or products"},"fieldKey":{"type":"string","description":"The key the value is stored under in each record's customFields"},"label":{"type":"string"},"type":{"type":"string","description":"text, textarea, email, phone, url, number, decimal, currency, date, datetime, select, multiselect, checkbox, lookup, multilookup, user, file or files"},"description":{"type":"string"},"required":{"type":"boolean"},"defaultValue":{},"options":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"},"color":{"type":"string"}}},"description":"The choices of a select or multiselect field"},"lookupModule":{"description":"For a lookup or multilookup field: the record type it points at, e.g. users"},"importSource":{},"validation":{"type":"object","properties":{"dependsOn":{"type":"object","properties":{"fieldId":{"type":"string"},"values":{"type":"array","items":{"type":"string"}},"requiredWhenVisible":{"type":"boolean"}}}},"description":"Rules: min, max, minLength, maxLength, pattern, mimeTypes, maxBytes, minCount, maxCount, appliesTo, and the show-when conditions dependsOn, alsoDependsOn and conditionsMatch"},"displayOrder":{"type":"integer"},"showInTable":{"type":"boolean"},"showInFilter":{"type":"boolean"},"isActive":{"type":"boolean","description":"false when the field is retired: hidden from forms, its values kept"},"createdBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8a9b","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","moduleType":"leads","fieldKey":"connection_type","label":"Connection type","type":"select","description":"How the customer wants to be connected","required":false,"defaultValue":null,"options":[{"value":"fibre","label":"Fibre","color":"#22c55e"},{"value":"wireless","label":"Wireless"}],"lookupModule":null,"importSource":null,"validation":{"dependsOn":{"fieldId":"_stage","values":["qualified"],"requiredWhenVisible":true}},"displayOrder":3,"showInTable":true,"showInFilter":true,"isActive":true,"createdBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"A show-when condition names a field that does not exist or cannot be a parent, or a JSON value is not valid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No custom field with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customFields.update"]}],"x-required-scopes":["crm.customFields.update"]},"delete":{"tags":["Custom fields & modules"],"summary":"Delete a custom field","description":"Deletes a custom field (a soft delete). Values already stored on records stay in their data. To hide a field but keep it listed, set isActive to false instead.\n\n**Permission:** `crm.customFields.delete` (Delete custom fields).","operationId":"deleteCustomFieldDefinition","parameters":[{"name":"fieldId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The custom field's ID","example":"e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8a9b"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"message":{"type":"string"},"fieldId":{"type":"string"}}}}},"example":{"success":true,"data":{"message":"Field definition deleted","fieldId":"e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8a9b"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No custom field with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customFields.delete"]}],"x-required-scopes":["crm.customFields.delete"]}},"/custom-modules/{moduleId}":{"put":{"tags":["Custom fields & modules"],"summary":"Update a custom module","description":"Changes one of your organisation's own record types (a custom module); only what you send changes. The slug never changes. Turning enablePipeline on also creates default pipeline stages if the module has none; switching the module off hides its menu entry, and switching it on shows it again. Sending fields replaces the whole field list.\n\n**Permission:** `crm.customModules.update` (Update custom modules).","operationId":"updateCustomModule","parameters":[{"name":"moduleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The module's ID (not its slug)","example":"e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8a9b"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"color":{"type":"string","description":"Colour as a hex code","examples":["#6366f1"]},"description":{"type":"string","description":"What the module is for; null clears it","examples":["Surveys done before a fibre installation"]},"enablePipeline":{"type":"boolean","description":"Whether records move through pipeline stages","examples":[true]},"fields":{"type":"array","items":{"type":"object"},"description":"The full new field list (replaces the old one), each with fieldKey, label and type (a JSON array in a string also works). If none is primary, the first readable field becomes primary","examples":[[{"fieldKey":"site_name","label":"Site name","type":"text","required":true,"displayOrder":0,"showInTable":true,"showInFilter":true,"isPrimary":true}]]},"icon":{"type":"string","description":"Icon name","examples":["clipboard-list"]},"isActive":{"type":"boolean","description":"false switches the module off and hides it from the menu","examples":[true]},"name":{"type":"string","description":"New name","examples":["Site surveys"]},"pluralLabel":{"type":"string","description":"What several records are called","examples":["Site surveys"]},"singularLabel":{"type":"string","description":"What one record is called","examples":["Site survey"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string","description":"The module's address: its records are at /custom-modules/<slug>/records"},"singularLabel":{"type":"string"},"pluralLabel":{"type":"string"},"description":{"type":"string"},"icon":{"type":"string"},"color":{"type":"string"},"fields":{"type":"array","items":{"type":"object","properties":{"fieldKey":{"type":"string","description":"The key the field's value is stored under in each record's data"},"label":{"type":"string"},"type":{"type":"string","description":"Field type, e.g. text, number, date, select, multiselect, checkbox, lookup"},"required":{"type":"boolean"},"displayOrder":{"type":"integer"},"showInTable":{"type":"boolean"},"showInFilter":{"type":"boolean"},"isPrimary":{"type":"boolean","description":"The one field whose value names a record in lists, lookups and search"}}}},"enablePipeline":{"type":"boolean","description":"Whether records move through pipeline stages on a board"},"defaultPipelineId":{},"isActive":{"type":"boolean"},"createdBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8a9b","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","name":"Site surveys","slug":"site-surveys","singularLabel":"Site survey","pluralLabel":"Site surveys","description":"Surveys done before a fibre installation","icon":"clipboard-list","color":"#6366f1","fields":[{"fieldKey":"site_name","label":"Site name","type":"text","required":true,"displayOrder":0,"showInTable":true,"showInFilter":true,"isPrimary":true},{"fieldKey":"survey_result","label":"Result","type":"select","required":false,"options":[{"value":"ready","label":"Ready to install"},{"value":"needs_work","label":"Needs civil works"}],"displayOrder":1,"showInTable":true,"showInFilter":true,"isPrimary":false}],"enablePipeline":false,"defaultPipelineId":null,"isActive":true,"createdBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"fields is not valid JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No custom module with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customModules.update"]}],"x-required-scopes":["crm.customModules.update"]}},"/custom-modules/records/{recordId}":{"put":{"tags":["Custom fields & modules"],"summary":"Update a custom module record","description":"Changes a record of one of your organisation's own modules. data REPLACES all the record's values, so send every value, not only the changed ones; it is checked against the module's fields like a new record. Automations set to run on changed records of the module are started.\n\n**Permission:** `crm.customModules.update` (Update custom modules).","operationId":"updateCustomRecord","parameters":[{"name":"recordId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The record's ID","example":"f6a7b8c9-d0e1-4f2a-9b3c-5d6e7f8a9b0c"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","description":"All the record's values, keyed by the module's field keys (a JSON object in a string also works)","examples":[{"site_name":"Westlands Plaza, 4th floor","survey_result":"ready"}]},"ownerId":{"type":"string","format":"uuid","description":"CRM user ID of the new owner; empty clears the owner","examples":["7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"]}},"required":["data"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"moduleSlug":{"type":"string"},"data":{"type":"object","properties":{"site_name":{"type":"string"},"survey_result":{"type":"string"}},"description":"The record's values, keyed by the module's field keys"},"ownerId":{"type":"string"},"createdBy":{"type":"string"},"updatedBy":{"type":"string"},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"f6a7b8c9-d0e1-4f2a-9b3c-5d6e7f8a9b0c","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","moduleSlug":"site-surveys","data":{"site_name":"Westlands Plaza, 4th floor","survey_result":"ready"},"ownerId":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","createdBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","updatedBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","externalIds":[],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"A required field is empty, or a choice field holds a value that is not one of its options","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No record with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customModules.update"]}],"x-required-scopes":["crm.customModules.update"]},"delete":{"tags":["Custom fields & modules"],"summary":"Delete a custom module record","description":"Deletes a record of one of your organisation's own modules. It is a soft delete: the record disappears from lists and reads but is kept in the database.\n\n**Permission:** `crm.customModules.delete` (Delete custom module records).","operationId":"deleteCustomRecord","parameters":[{"name":"recordId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The record's ID","example":"f6a7b8c9-d0e1-4f2a-9b3c-5d6e7f8a9b0c"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"message":{"type":"string"},"recordId":{"type":"string"}}}}},"example":{"success":true,"data":{"message":"Record deleted","recordId":"f6a7b8c9-d0e1-4f2a-9b3c-5d6e7f8a9b0c"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No record with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customModules.delete"]}],"x-required-scopes":["crm.customModules.delete"]}},"/helpdesk/csat/default-notifier":{"put":{"tags":["Help desk setup"],"summary":"Set the default notifier channel","description":"Picks the email channel that satisfaction surveys and ticket-update emails go out from, taking the mark off any other. Without a channelConfigId the mark is just removed (no system emails go out) and channel is null. Note: unlike the channel reads, the reply shows the channel's credentials unmasked.\n\n**Permission:** `crm.csat.update` (Update CSAT).","operationId":"setDefaultNotifierChannel","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"channelConfigId":{"type":"string","format":"uuid","description":"The email channel to use; empty or left out to have none","examples":["b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"channel":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"channelType":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"},"isDefaultNotifier":{"type":"boolean"},"credentials":{"type":"object","properties":{"imapHost":{"type":"string"},"imapPort":{"type":"integer"},"smtpHost":{"type":"string"},"smtpPort":{"type":"integer"},"username":{"type":"string"},"password":{"type":"string"},"fromAddress":{"type":"string"},"fromName":{"type":"string"}},"description":"Returned in clear by this call"},"webhookSecret":{"type":"string"},"webhookUrl":{"type":"string"},"routingRules":{"type":"object","properties":{"autoCreateTicket":{"type":"boolean"},"defaultTeamId":{"type":"string"},"defaultPriority":{"type":"string"}}},"metadata":{"type":"object","properties":{}},"lastSyncAt":{"type":"string","format":"date-time"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"description":"The chosen channel, or null when the mark was removed"}}},"example":{"success":true,"channel":{"id":"b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","channelType":"email","name":"Support mailbox","isActive":true,"isDefaultNotifier":true,"credentials":{"imapHost":"mail.example.com","imapPort":993,"smtpHost":"mail.example.com","smtpPort":465,"username":"support@example.com","password":"example-mailbox-password","fromAddress":"support@example.com","fromName":"Example Support"},"webhookSecret":"6f1d2c3b4a5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f8","webhookUrl":"https://api.crm.kazee.africa/api/webhooks/channels/b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d","routingRules":{"autoCreateTicket":true,"defaultTeamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","defaultPriority":"medium"},"metadata":{},"lastSyncAt":"2026-10-21T08:55:00.000Z","deletedAt":null,"createdAt":"2026-06-01T08:00:00.000Z","updatedAt":"2026-10-21T08:55:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No channel with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The channel is not an email channel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.csat.update"]}],"x-required-scopes":["crm.csat.update"]}},"/desk/settings/{key}":{"put":{"tags":["Desk"],"summary":"Save one of my Desk settings","description":"Creates or replaces one of your own Desk settings: preferences, notification_preferences or columns. Always your own; an organisation setting key is refused with 403.\n\n**Permission:** `crm.desk.read` (Use Desk: own notifications, follows and preferences).","operationId":"putDeskSetting","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","enum":["preferences","notification_preferences","columns"]},"description":"Which of your settings","example":"preferences"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"expectedVersion":{"type":"integer","minimum":1,"description":"Save only if the stored setting is still at this version (from the last read); otherwise answer 409. Leave out to overwrite","examples":[3]},"value":{"type":"object","description":"The whole new value: a JSON object or array (a JSON string of one also works), up to 64 KB. It replaces what is stored","examples":[{"theme":"dark","defaultQueue":"my-open","density":"compact"}]}},"required":["value"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"scope":{"type":"string","description":"user: one of your own settings"},"setting":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"object","properties":{"theme":{"type":"string"},"defaultQueue":{"type":"string"},"density":{"type":"string"}}},"version":{"type":"integer","description":"Goes up by one on every save; send it back as expectedVersion to save only over that version"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string","description":"Who saved it last"}}}}}}},"example":{"success":true,"data":{"scope":"user","setting":{"key":"preferences","value":{"theme":"dark","defaultQueue":"my-open","density":"compact"},"version":2,"updatedAt":"2026-10-20T09:05:00.000Z","updatedBy":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"}}}}}},"400":{"description":"Unknown key, value is not a JSON object or array or is over 64 KB, or expectedVersion is not a positive whole number","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"The call was not made by a signed-in person","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key is an organisation setting; use PUT /desk/organization-settings/{key}","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"expectedVersion was sent and the setting has been saved by someone else since","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.desk.read"]}],"x-required-scopes":["crm.desk.read"]}},"/desk/organization-settings/{key}":{"put":{"tags":["Help desk setup"],"summary":"Save an organisation Desk setting","description":"Creates or replaces one organisation-wide Desk setting (navigation, general, tags, email templates, notification defaults, queues, ticket categories, the email signature or the ticket process). Needs Desk admin permission. Send expectedVersion so two admins cannot silently overwrite each other.\n\n**Permission:** `crm.desk.admin` (Configure Desk).","operationId":"putDeskOrganizationSetting","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","enum":["navigation","general","tags","email_templates","notification_defaults","queues","ticketCategories","emailSignature","ticketProcess"]},"description":"Which organisation setting","example":"general"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"expectedVersion":{"type":"integer","minimum":1,"description":"Save only if the stored setting is still at this version (from the last read); otherwise answer 409. Leave out to overwrite","examples":[3]},"value":{"type":"object","description":"The whole new value: a JSON object or array (a JSON string of one also works), up to 64 KB. It replaces what is stored. ticketCategories takes a list of up to 30 names. emailSignature takes { default: text, departments: { departmentId: text } }: the plain-text footer added to every public email reply, a department's own text over the default, {{agent_name}} for the replying person's name. ticketProcess takes { defaultHoldMinutes, holdChoices, noReplyHoldMinutes, holdReturnAlert } (a key left out takes its default: 240, [60, 240, 1440, 4320], 240, true); the former requiredBeforeHold and requiredBeforeResolve are accepted and dropped, as what a ticket needs before a move is its ticket blueprint","examples":[{"applicationName":"Help Desk","defaultPriority":"medium","autoCloseDays":7,"timezone":"Africa/Nairobi"}]}},"required":["value"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"scope":{"type":"string","description":"organization: an organisation-wide setting"},"setting":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"object","properties":{"applicationName":{"type":"string"},"defaultPriority":{"type":"string"},"autoCloseDays":{"type":"integer"},"timezone":{"type":"string"}}},"version":{"type":"integer","description":"Goes up by one on every save; send it back as expectedVersion to save only over that version"},"updatedAt":{"type":"string","format":"date-time"},"updatedBy":{"type":"string","description":"Who saved it last"}}}}}}},"example":{"success":true,"data":{"scope":"organization","setting":{"key":"general","value":{"applicationName":"Help Desk","defaultPriority":"medium","autoCloseDays":7,"timezone":"Africa/Nairobi"},"version":4,"updatedAt":"2026-10-20T09:05:00.000Z","updatedBy":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"}}}}}},"400":{"description":"Unknown key, value is not a JSON object or array or is over 64 KB, ticketCategories is not a list of up to 30 distinct names of up to 40 characters, emailSignature is not { default, departments } with texts of up to 2000 characters keyed by department id, ticketProcess has a value out of bounds (defaultHoldMinutes and each of up to 8 holdChoices 1 to 43200 minutes, noReplyHoldMinutes 1 to 43200 (the older noReplyHoldDays is read as days), holdReturnAlert true or false), or expectedVersion is not a positive whole number","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key is a personal setting; use PUT /desk/settings/{key}","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"expectedVersion was sent and the setting has been saved by someone else since","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.desk.admin"]}],"x-required-scopes":["crm.desk.admin"]}},"/organization/billing-rules/preview":{"post":{"tags":["Settings"],"summary":"Preview a billing rule","description":"Says how many of your organisation's deals a billing rule's conditions match, before you save it, with up to 10 of them and any problems a save would refuse. Changes nothing. A rule that cannot be read matches no deal; that still answers 200, with valid false.\n\n**Permission:** `crm.onboarding.read` (View onboarding settings).","operationId":"previewBillingRule","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"when":{"type":"array","items":{"type":"object","properties":{"fieldPath":{"type":"string","description":"A path from GET /organization/billing-rules/fields, e.g. customFields.connection_type"},"operator":{"type":"string","enum":["eq","neq","in","notIn","isEmpty","isNotEmpty","exists","notExists","gt","gte","lt","lte","contains","notContains"]},"value":{"description":"What to compare with; a list for in and notIn; left out for isEmpty, isNotEmpty, exists and notExists"},"logicOperator":{"type":"string","enum":["AND","OR"]},"groupId":{"type":"integer"},"groupOperator":{"type":"string","enum":["AND","OR"]}}},"description":"The rule's conditions (a JSON list in a string also works)","examples":[[{"fieldPath":"customFields.connection_type","fieldLabel":"Connection type","operator":"eq","value":"Layer 2"}]]}},"required":["when"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"valid":{"type":"boolean","description":"false when the rule cannot be read; then problems says why and matches is 0"},"problems":{"type":"array","description":"Why the rule cannot be saved, one line each; empty when it is valid"},"normalized":{"type":"array","items":{"type":"object","properties":{"fieldPath":{"type":"string"},"fieldLabel":{"type":"string"},"operator":{"type":"string"},"value":{"type":"string"}}},"description":"The conditions as a save would store them"},"matches":{"type":"integer"},"sample":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}},"description":"Up to 10 of the matching deals"}}},"example":{"success":true,"valid":true,"problems":[],"normalized":[{"fieldPath":"customFields.connection_type","fieldLabel":"Connection type","operator":"eq","value":"Layer 2"}],"matches":1,"sample":[{"id":"0f1a2b3c-4d5e-4f6a-9b7c-8d9e0f1a2b3c","name":"Jane Wanjiku: home fibre"}]}}}},"400":{"description":"when is text that is not JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.onboarding.read"]}],"x-required-scopes":["crm.onboarding.read"]}},"/billing/books/payments-received":{"post":{"tags":["Billing"],"summary":"Record a customer payment","description":"Records money a customer paid, in Kazee Books and in your name: Books posts it to the ledger and records you as the person who took it. You need your own Books permission to record payments. Send an Idempotency-Key header so that a retried request cannot record the payment twice. Answers 200 with the payment as Books now holds it.\n\n**Permission:** `crm.payments.manage` (Record payments and extensions) and `crm.books.payments.create`.","operationId":"recordBooksPayment","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"amount":{"type":"string","description":"How much was paid, as a decimal","examples":["3500.00"]},"applications":{"type":"array","items":{"type":"object","properties":{"invoice_id":{"type":"string"},"amount":{"type":"string"},"wht_withheld":{"type":"string"},"wht_tax_id":{"type":"string"},"certificate_ref":{"type":"string"}},"required":["invoice_id","amount"]},"description":"The customer's invoices to apply the payment to, with the amount for each (and any withholding tax kept back). Left out, the payment stays unapplied","examples":[[{"invoice_id":"inv_1c2d3e4f5a6b4c7d8e9f0a1b2c3d4e5f","amount":"3500.00"}]]},"contact_id":{"type":"string","description":"The Books customer who paid (a con_… ID)","examples":["con_0b9e8c1e3f5a4c2d9a7b1e2f3a4b5c6d"]},"currency":{"type":"string","description":"Currency code; the customer's currency when left out","examples":["KES"]},"date":{"type":"string","format":"date","description":"The day it was paid (YYYY-MM-DD); today when left out","examples":["2026-10-20"]},"deposit_to_account_id":{"type":"string","description":"The Books account the money went into (an acc_… ID); Undeposited funds when left out","examples":["acc_1a2b3c4d5e6f4a7b8c9d0e1f2a3b4c5d"]},"exchange_rate":{"type":"string","description":"Rate to the organisation's base currency, for a foreign-currency payment","examples":["1"]},"mode":{"type":"string","enum":["cash","bank_transfer","paybill","cheque","mpesa","card","other"],"description":"How it was paid; bank_transfer when left out","examples":["mpesa"]},"notes":{"type":"string","description":"Notes kept with the payment","examples":["October service"]},"reference":{"type":"string","description":"The payment's reference, e.g. the M-Pesa code","examples":["TJK4X2Y7QZ"]},"retainer_invoice_id":{"type":"string","description":"A sent retainer invoice (deposit) this pays in full; then amount must equal its total and applications must be left out","examples":["rti_7a8b9c0d1e2f4a3b4c5d6e7f8a9b0c1d"]}},"required":["amount","contact_id"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"source":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"number":{"type":"string"},"contact_id":{"type":"string"},"contact_name":{"type":"string"},"date":{"type":"string"},"amount":{"type":"string"},"currency":{"type":"string"},"exchange_rate":{"type":"string"},"mode":{"type":"string"},"deposit_to_account_id":{"type":"string"},"reference":{"type":"string"},"notes":{"type":"string"},"custom_fields":{"type":"object","properties":{}},"dimension_values":{"type":"object","properties":{}},"retainer_invoice_id":{},"entry_id":{"type":"string"},"refunds":{"type":"array"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"applications":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"invoice_id":{"type":"string"},"amount":{"type":"string"},"wht_withheld":{"type":"string"},"wht_tax_id":{},"entry_id":{"type":"string"}}},"description":"The invoices the payment was applied to"},"unapplied_amount":{"type":"string","description":"What is left of the payment, not applied to any invoice"},"reserved_amount":{"type":"string"}},"description":"The payment as Kazee Books now holds it"}}},"example":{"success":true,"source":"kazee-books","data":{"id":"pay_3e4f5a6b7c8d4e9f0a1b2c3d4e5f6a7b","number":"PAY-000233","contact_id":"con_0b9e8c1e3f5a4c2d9a7b1e2f3a4b5c6d","contact_name":"Amani Wanjiru","date":"2026-10-20","amount":"3500.00","currency":"KES","exchange_rate":"1","mode":"mpesa","deposit_to_account_id":"acc_1a2b3c4d5e6f4a7b8c9d0e1f2a3b4c5d","reference":"TJK4X2Y7QZ","notes":"October service","custom_fields":{},"dimension_values":{},"retainer_invoice_id":null,"entry_id":"jrn_2b3c4d5e6f7a4b8c9d0e1f2a3b4c5d6e","refunds":[],"created_at":"2026-10-20T09:00:00.000Z","updated_at":"2026-10-20T09:00:00.000Z","applications":[{"id":"pal_4a5b6c7d8e9f4a0b1c2d3e4f5a6b7c8d","invoice_id":"inv_1c2d3e4f5a6b4c7d8e9f0a1b2c3d4e5f","amount":"3500.00","wht_withheld":"0.00","wht_tax_id":null,"entry_id":"jrn_2b3c4d5e6f7a4b8c9d0e1f2a3b4c5d6e"}],"unapplied_amount":"0.00","reserved_amount":"0.00"}}}}},"400":{"description":"contact_id or amount is missing, or Books refused an input (an unknown mode, an invoice that is not the customer's, a wrong retainer amount, ...)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You lack the Books permission to record payments","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The retainer invoice is not sent yet, or the Idempotency-Key was used for a different payment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.payments.manage","crm.books.payments.create"]}],"x-required-scopes":["crm.payments.manage","crm.books.payments.create"],"x-scope-mode":"all"}},"/billing/books/subscriptions/{recordId}/extension":{"post":{"tags":["Billing"],"summary":"Grant a subscription payment extension","description":"Gives a customer more time to pay: a promise to pay by a date, which keeps their Kazee Books subscription running until then. If the line was already cut for not paying, Books reconnects it today on a new cycle. Books refuses a date past the organisation's own limit, and refuses when the subscription cannot be extended now. Books records you as the person who granted it, and answers the subscription as it now stands.\n\n**Permission:** `crm.payments.manage` (Record payments and extensions) and `crm.books.sales.extend`.","operationId":"grantBooksSubscriptionExtension","parameters":[{"name":"recordId","in":"path","required":true,"schema":{"type":"string"},"description":"The Books subscription (a sub_… ID)","example":"sub_4f5a6b7c8d9e4f0a1b2c3d4e5f6a7b8c"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"note":{"type":"string","description":"A note kept with the extension, up to 2,000 characters","examples":["Customer called on 20 Oct"]},"promise_date":{"type":"string","format":"date","description":"The day the customer promised to pay by (YYYY-MM-DD)","examples":["2026-11-15"]},"reason":{"type":"string","enum":["promise_to_pay","awaiting_lpo","disputed_bill","other"],"description":"Why the extension was given; promise_to_pay when left out","examples":["promise_to_pay"]}},"required":["promise_date"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"source":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"contact_id":{"type":"string"},"contact_name":{"type":"string"},"name":{"type":"string"},"pppoe":{"type":"string"},"crm_subscription_id":{"type":"string"},"plan_name":{"type":"string"},"monthly_rate":{"type":"string"},"billing_cycle":{"type":"string"},"recurring_profile_id":{"type":"string"},"status":{"type":"string"},"status_level":{"type":"string"},"start_date":{"type":"string"},"next_billing_at":{"type":"string","format":"date-time"},"renewal_date":{"type":"string"},"disconnection_date":{"type":"string"},"last_payment_date":{"type":"string"},"disconnection_reason":{},"disconnection_kind":{},"scheduled_disconnection_date":{},"disconnection_scheduled_at":{},"disconnected_at":{},"reconnection_state":{"type":"string"},"scheduled_reconnection_date":{},"reconnection_requested_at":{},"reconnected_at":{},"enforcement_status":{},"enforcement_last_error":{},"enforcement_updated_at":{},"paid_plan_item_id":{"type":"string"},"access_plan_item_id":{"type":"string"},"confirmed_access_plan_item_id":{"type":"string"},"access_entitlement_version":{"type":"integer"},"deposit_required":{"type":"string"},"deposit_held":{"type":"string"},"deposit_pending":{"type":"string"},"active_change_request_id":{},"active_trial_id":{},"trial_expires_at":{},"external_refs":{"type":"object","properties":{}},"custom_fields":{"type":"object","properties":{}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"description":"The subscription as Kazee Books now holds it"}}},"example":{"success":true,"source":"kazee-books","data":{"id":"sub_4f5a6b7c8d9e4f0a1b2c3d4e5f6a7b8c","contact_id":"con_0b9e8c1e3f5a4c2d9a7b1e2f3a4b5c6d","contact_name":"Amani Wanjiru","name":"Amani Wanjiru - amani.w","pppoe":"amani.w","crm_subscription_id":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","plan_name":"Fibre 20 Mbps","monthly_rate":"3500.00","billing_cycle":"monthly","recurring_profile_id":"rcp_4d5e6f7a8b9c4d0e1f2a3b4c5d6e7f8a","status":"active","status_level":"active","start_date":"2026-03-01","next_billing_at":"2026-11-01T00:00:00.000Z","renewal_date":"2026-11-01","disconnection_date":"2026-11-08","last_payment_date":"2026-10-02","disconnection_reason":null,"disconnection_kind":null,"scheduled_disconnection_date":null,"disconnection_scheduled_at":null,"disconnected_at":null,"reconnection_state":"none","scheduled_reconnection_date":null,"reconnection_requested_at":null,"reconnected_at":null,"enforcement_status":null,"enforcement_last_error":null,"enforcement_updated_at":null,"paid_plan_item_id":"itm_6b7c8d9e0f1a4b2c3d4e5f6a7b8c9d0e","access_plan_item_id":"itm_6b7c8d9e0f1a4b2c3d4e5f6a7b8c9d0e","confirmed_access_plan_item_id":"itm_6b7c8d9e0f1a4b2c3d4e5f6a7b8c9d0e","access_entitlement_version":3,"deposit_required":"3500.00","deposit_held":"3500.00","deposit_pending":"0.00","active_change_request_id":null,"active_trial_id":null,"trial_expires_at":null,"external_refs":{},"custom_fields":{},"created_at":"2026-03-01T08:00:00.000Z","updated_at":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"recordId is not a Books ID, promise_date is missing or outside the allowed dates, or reason is unknown","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Kazee Books is not connected to this CRM organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such subscription","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The subscription cannot be given an extension now (Books says why)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.payments.manage","crm.books.sales.extend"]}],"x-required-scopes":["crm.payments.manage","crm.books.sales.extend"],"x-scope-mode":"all"}},"/billing/books/approvals/{documentType}/{recordId}/submit":{"post":{"tags":["Billing"],"summary":"Submit a Books document for approval","description":"Sends a draft Kazee Books document (an invoice, estimate, credit note, ...) into its approval chain, in your name: Books records you as the submitter. For an estimate (a quote's), you also need the Update quotes permission. Books refuses when the document is not a draft, already waits for approval, is below the approval threshold, or no approval chain is set up.\n\n**Permission:** `crm.billing.read` (View billing documents) and `crm.books.approvals.submit`.","operationId":"submitBooksApproval","parameters":[{"name":"documentType","in":"path","required":true,"schema":{"type":"string","enum":["invoice","estimate","credit_note","bill","purchase_order"]},"description":"What kind of Books document it is","example":"invoice"},{"name":"recordId","in":"path","required":true,"schema":{"type":"string"},"description":"The Books record's ID: a short type prefix and 32 hex digits, e.g. inv_… for an invoice","example":"inv_1c2d3e4f5a6b4c7d8e9f0a1b2c3d4e5f"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"source":{"type":"string"},"documentType":{"type":"string"},"data":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"document_type":{"type":"string"},"document_id":{"type":"string"},"document_number":{"type":"string"},"document_total":{"type":"string"},"status":{"type":"string","description":"pending, approved, rejected or recalled"},"current_level":{"type":"integer"},"submitted_by":{"type":"string"},"submitted_by_name":{"type":"string"},"decided_at":{},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"steps":{"type":"array","items":{"type":"object","properties":{"level":{"type":"integer"},"mode":{"type":"string"},"status":{"type":"string"},"candidates":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"name":{"type":"string"}}},"description":"The people who may sign that step"},"decided_by":{},"decided_by_name":{},"decided_at":{},"comment":{}}}}},"description":"The approval request as Kazee Books now holds it, with its steps (Books' own reply, kept whole)"}}}}},"example":{"success":true,"source":"kazee-books","documentType":"invoice","data":{"data":{"id":"apq_5e6f7a8b9c0d4e1f2a3b4c5d6e7f8a9b","document_type":"invoice","document_id":"inv_1c2d3e4f5a6b4c7d8e9f0a1b2c3d4e5f","document_number":"INV-000412","document_total":"3500.00","status":"pending","current_level":1,"submitted_by":"usr_65f1a2b3c4d5e6f7a8b9c0d100000000","submitted_by_name":"Jane Doe","decided_at":null,"created_at":"2026-10-20T09:00:00.000Z","updated_at":"2026-10-20T09:00:00.000Z","steps":[{"level":1,"mode":"individuals","status":"pending","candidates":[{"userId":"usr_65f1a2b3c4d5e6f7a8b9c0d200000000","name":"Peter Otieno"}],"decided_by":null,"decided_by_name":null,"decided_at":null,"comment":null}]}}}}}},"400":{"description":"documentType is not one Books can approve, recordId is empty, or Books refused (no approval chain, below the threshold)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You lack a permission this needs (for an estimate also Update quotes), or Books is not connected to this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"The document is not a draft, or it already waits for approval","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.billing.read","crm.books.approvals.submit"]}],"x-required-scopes":["crm.billing.read","crm.books.approvals.submit"],"x-scope-mode":"all"}},"/billing/books/approvals/{documentType}/{recordId}/recall":{"post":{"tags":["Billing"],"summary":"Recall a Books approval request","description":"Takes back a Kazee Books approval request that is still waiting, so the document can be edited again. Books lets only the person who submitted it, or a Books admin, recall it. For an estimate (a quote's), you also need the Update quotes permission.\n\n**Permission:** `crm.billing.read` (View billing documents) and `crm.books.approvals.submit`.","operationId":"recallBooksApproval","parameters":[{"name":"documentType","in":"path","required":true,"schema":{"type":"string","enum":["invoice","estimate","credit_note","bill","purchase_order"]},"description":"What kind of Books document it is","example":"invoice"},{"name":"recordId","in":"path","required":true,"schema":{"type":"string"},"description":"The Books record's ID: a short type prefix and 32 hex digits, e.g. inv_… for an invoice","example":"inv_1c2d3e4f5a6b4c7d8e9f0a1b2c3d4e5f"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"source":{"type":"string"},"documentType":{"type":"string"},"data":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"document_type":{"type":"string"},"document_id":{"type":"string"},"document_number":{"type":"string"},"document_total":{"type":"string"},"status":{"type":"string","description":"pending, approved, rejected or recalled"},"current_level":{"type":"integer"},"submitted_by":{"type":"string"},"submitted_by_name":{"type":"string"},"decided_at":{"type":"string","format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"steps":{"type":"array","items":{"type":"object","properties":{"level":{"type":"integer"},"mode":{"type":"string"},"status":{"type":"string"},"candidates":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"name":{"type":"string"}}},"description":"The people who may sign that step"},"decided_by":{},"decided_by_name":{},"decided_at":{},"comment":{}}}}},"description":"The approval request as Kazee Books now holds it, with its steps (Books' own reply, kept whole)"}}}}},"example":{"success":true,"source":"kazee-books","documentType":"invoice","data":{"data":{"id":"apq_5e6f7a8b9c0d4e1f2a3b4c5d6e7f8a9b","document_type":"invoice","document_id":"inv_1c2d3e4f5a6b4c7d8e9f0a1b2c3d4e5f","document_number":"INV-000412","document_total":"3500.00","status":"recalled","current_level":1,"submitted_by":"usr_65f1a2b3c4d5e6f7a8b9c0d100000000","submitted_by_name":"Jane Doe","decided_at":"2026-10-20T10:00:00.000Z","created_at":"2026-10-20T09:00:00.000Z","updated_at":"2026-10-20T10:00:00.000Z","steps":[{"level":1,"mode":"individuals","status":"cancelled","candidates":[{"userId":"usr_65f1a2b3c4d5e6f7a8b9c0d200000000","name":"Peter Otieno"}],"decided_by":null,"decided_by_name":null,"decided_at":null,"comment":null}]}}}}}},"400":{"description":"documentType is not one Books can approve, or recordId is empty","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You lack a permission this needs, or you neither submitted it nor are a Books admin","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The document has no approval request waiting","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.billing.read","crm.books.approvals.submit"]}],"x-required-scopes":["crm.billing.read","crm.books.approvals.submit"],"x-scope-mode":"all"}},"/billing/books/approvals/{documentType}/{recordId}/decide":{"post":{"tags":["Billing"],"summary":"Approve or reject a Books document","description":"Approves or rejects a Kazee Books document that waits for approval, in your name. Books lets you sign only if you are one of the approvers named for the current step. Approving the last step approves the document; rejecting ends the request.\n\n**Permission:** `crm.billing.read` (View billing documents) and `crm.books.approvals.decide`.","operationId":"decideBooksApproval","parameters":[{"name":"documentType","in":"path","required":true,"schema":{"type":"string","enum":["invoice","estimate","credit_note","bill","purchase_order"]},"description":"What kind of Books document it is","example":"invoice"},{"name":"recordId","in":"path","required":true,"schema":{"type":"string"},"description":"The Books record's ID: a short type prefix and 32 hex digits, e.g. inv_… for an invoice","example":"inv_1c2d3e4f5a6b4c7d8e9f0a1b2c3d4e5f"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"comment":{"type":"string","description":"A note kept with your decision in Books","examples":["Checked against the signed order"]},"decision":{"type":"string","enum":["approve","reject"],"description":"Your decision","examples":["approve"]}},"required":["decision"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"source":{"type":"string"},"documentType":{"type":"string"},"decision":{"type":"string"},"data":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"document_type":{"type":"string"},"document_id":{"type":"string"},"document_number":{"type":"string"},"document_total":{"type":"string"},"status":{"type":"string","description":"pending, approved, rejected or recalled"},"current_level":{"type":"integer"},"submitted_by":{"type":"string"},"submitted_by_name":{"type":"string"},"decided_at":{"type":"string","format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"steps":{"type":"array","items":{"type":"object","properties":{"level":{"type":"integer"},"mode":{"type":"string"},"status":{"type":"string"},"candidates":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"name":{"type":"string"}}},"description":"The people who may sign that step"},"decided_by":{"type":"string"},"decided_by_name":{"type":"string"},"decided_at":{"type":"string","format":"date-time"},"comment":{"type":"string"}}}}},"description":"The approval request as Kazee Books now holds it, with its steps (Books' own reply, kept whole)"}}}}},"example":{"success":true,"source":"kazee-books","documentType":"invoice","decision":"approve","data":{"data":{"id":"apq_5e6f7a8b9c0d4e1f2a3b4c5d6e7f8a9b","document_type":"invoice","document_id":"inv_1c2d3e4f5a6b4c7d8e9f0a1b2c3d4e5f","document_number":"INV-000412","document_total":"3500.00","status":"approved","current_level":1,"submitted_by":"usr_65f1a2b3c4d5e6f7a8b9c0d100000000","submitted_by_name":"Jane Doe","decided_at":"2026-10-20T10:00:00.000Z","created_at":"2026-10-20T09:00:00.000Z","updated_at":"2026-10-20T10:00:00.000Z","steps":[{"level":1,"mode":"individuals","status":"approved","candidates":[{"userId":"usr_65f1a2b3c4d5e6f7a8b9c0d200000000","name":"Peter Otieno"}],"decided_by":"usr_65f1a2b3c4d5e6f7a8b9c0d200000000","decided_by_name":"Peter Otieno","decided_at":"2026-10-20T10:00:00.000Z","comment":"Checked against the signed order"}]}}}}}},"400":{"description":"documentType is not one Books can approve, decision is not approve or reject, or recordId is empty","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You lack a permission this needs, or you are not an approver for the current step","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The document has no approval request waiting","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The approval request has no step waiting","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.billing.read","crm.books.approvals.decide"]}],"x-required-scopes":["crm.billing.read","crm.books.approvals.decide"],"x-scope-mode":"all"}},"/billing/books/change-requests/{recordId}/{verb}":{"post":{"tags":["Subscriptions"],"summary":"Move a Books subscription change request on","description":"Moves a Kazee Books subscription change request on, as Books records you: fund pays its deposit top-up from a recorded payment or the customer's credit; confirm puts a request that needs attention back on its way; cancel drops it (money set aside goes back to credit); end-trial ends a trial early; convert keeps the trial plan as a paid upgrade. Send an Idempotency-Key header so that a retry cannot act twice.\n\n**Permission:** `crm.billing.manage` (Change subscription plans) and `crm.books.sales.update`.","operationId":"writeBooksChangeRequestVerb","parameters":[{"name":"recordId","in":"path","required":true,"schema":{"type":"string"},"description":"The change request (an scr_… ID)","example":"scr_5a6b7c8d9e0f4a1b2c3d4e5f6a7b8c9d"},{"name":"verb","in":"path","required":true,"schema":{"type":"string","enum":["fund","confirm","cancel","end-trial","convert"]},"description":"What to do with the request","example":"fund"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"deposit_amount":{"type":"string","description":"fund: how much of that payment to use, as a decimal","examples":["2000.00"]},"effective_date":{"type":"string","format":"date","description":"convert, with effective_mode date: the day (YYYY-MM-DD)","examples":["2026-11-01"]},"effective_mode":{"type":"string","enum":["now","next_cycle","date"],"description":"convert: when the paid plan takes effect","examples":["next_cycle"]},"fingerprint":{"type":"string","description":"confirm and convert: the fingerprint of the fresh preview; convert without it answers the preview only","examples":["9f8e7d6c5b4a39281706f5e4d3c2b1a0"]},"note":{"type":"string","description":"cancel: why it was cancelled, up to 2,000 characters","examples":["Customer changed their mind"]},"payment_id":{"type":"string","description":"fund: the recorded payment (a pay_… ID) to pay the top-up from","examples":["pay_3e4f5a6b7c8d4e9f0a1b2c3d4e5f6a7b"]},"use_available_credit":{"type":"boolean","description":"fund and convert: pay from the customer's unused payments","examples":[false]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"source":{"type":"string"},"verb":{"type":"string"},"data":{"type":"object","properties":{"request":{"type":"object","properties":{"id":{"type":"string"},"subscription_id":{"type":"string"},"contact_id":{"type":"string"},"kind":{"type":"string"},"status":{"type":"string"},"direction":{"type":"string"},"from_item_id":{"type":"string"},"to_item_id":{"type":"string"},"from_charge":{"type":"string"},"to_charge":{"type":"string"},"effective_mode":{"type":"string"},"effective_date":{"type":"string"},"choices":{"type":"object","properties":{"downgrade_excess":{}}},"preview":{"type":"object","properties":{}},"money":{"type":"object","properties":{"currency_id":{"type":"string"},"topup_required":{"type":"string"},"topup_earmarked":{"type":"string"},"release":{"type":"string"},"service_now":{"type":"string"},"service_later":{"type":"string"},"allocations":{"type":"array"}}},"documents":{"type":"object","properties":{}},"approval":{"type":"object","properties":{"required":{"type":"boolean"},"decided":{"type":"boolean"},"mode":{"type":"string"}}},"access":{"type":"object","properties":{}},"trial":{"type":"object","properties":{}},"history":{"type":"array","items":{"type":"object","properties":{"at":{"type":"string","format":"date-time"},"action":{"type":"string"},"by":{"type":"object","properties":{"user_id":{"type":"string"},"name":{"type":"string"},"source":{"type":"string"}}},"detail":{"type":"object","properties":{"direction":{"type":"string"}}}}}},"requested_by":{"type":"string"},"requested_at":{"type":"string","format":"date-time"},"applied_at":{},"completed_at":{},"cancelled_at":{},"error":{},"version":{"type":"integer"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"applied":{"type":"boolean","description":"True when the change took effect"}},"description":"Kazee Books' answer, as is. fund: request and applied; confirm: request, preview and applied; cancel and end-trial: the request; convert: trial, preview, request (once made) and applied"}}},"example":{"success":true,"source":"kazee-books","verb":"fund","data":{"request":{"id":"scr_5a6b7c8d9e0f4a1b2c3d4e5f6a7b8c9d","subscription_id":"sub_4f5a6b7c8d9e4f0a1b2c3d4e5f6a7b8c","contact_id":"con_0b9e8c1e3f5a4c2d9a7b1e2f3a4b5c6d","kind":"change","status":"scheduled","direction":"upgrade","from_item_id":"itm_6b7c8d9e0f1a4b2c3d4e5f6a7b8c9d0e","to_item_id":"itm_7c8d9e0f1a2b4c3d4e5f6a7b8c9d0e1f","from_charge":"3500.00","to_charge":"5500.00","effective_mode":"next_cycle","effective_date":"2026-11-01","choices":{"downgrade_excess":null},"preview":{},"money":{"currency_id":"c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f","topup_required":"2000.00","topup_earmarked":"2000.00","release":"0.00","service_now":"0.00","service_later":"5500.00","allocations":[]},"documents":{},"approval":{"required":false,"decided":true,"mode":"respect_org_approvals"},"access":{},"trial":{},"history":[{"at":"2026-10-20T09:00:00.000Z","action":"requested","by":{"user_id":"65f1a2b3-c4d5-e6f7-a8b9-c0d100000000","name":"Jane Doe","source":"api"},"detail":{"direction":"upgrade"}}],"requested_by":"65f1a2b3-c4d5-e6f7-a8b9-c0d100000000","requested_at":"2026-10-20T09:00:00.000Z","applied_at":null,"completed_at":null,"cancelled_at":null,"error":null,"version":1,"created_at":"2026-10-20T09:00:00.000Z","updated_at":"2026-10-20T09:00:00.000Z"},"applied":false}}}}},"400":{"description":"verb is unknown, recordId is not a Books ID, or Books refused an input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Kazee Books is not connected to this CRM organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The request cannot move this way now, the preview changed since (fingerprint), or the Idempotency-Key was used for something else","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.billing.manage","crm.books.sales.update"]}],"x-required-scopes":["crm.billing.manage","crm.books.sales.update"],"x-scope-mode":"all"}},"/billing/books/{resource}/{recordId}/send":{"post":{"tags":["Billing"],"summary":"Email a Books document","description":"Emails one Kazee Books document (an invoice, estimate, credit note, ...) to the customer with its PDF attached; Books records you as the sender. Switched off for now: until Books has a verified mail setup the call answers 503, because Books would otherwise report mail as sent that never left.\n\n**Permission:** `crm.billing.read` (View billing documents) and `crm.books.comms.send`.","operationId":"sendBooksDocument","parameters":[{"name":"resource","in":"path","required":true,"schema":{"type":"string","enum":["estimates","invoices","subscription-invoices","credit-notes","retainer-invoices","payments-received"]},"description":"Which Kazee Books collection the document is in; only these have a printable document","example":"invoices"},{"name":"recordId","in":"path","required":true,"schema":{"type":"string"},"description":"The Books record's ID: a short type prefix and 32 hex digits, e.g. inv_… for an invoice","example":"inv_1c2d3e4f5a6b4c7d8e9f0a1b2c3d4e5f"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":"string","description":"The email's text","examples":["Dear Amani, please find your October invoice attached."]},"cc":{"type":"array","items":{"type":"string","format":"email"},"description":"Email addresses to copy","examples":[["accounts@example.com"]]},"extra_document_ids":{"type":"array","items":{"type":"string"},"description":"Other Books documents (doc_… IDs) to attach as well","examples":[["doc_8a9b0c1d2e3f4a4b5c6d7e8f9a0b1c2d"]]},"subject":{"type":"string","description":"The email's subject","examples":["Invoice INV-000412 from Example ISP"]},"to":{"type":"array","items":{"type":"string","format":"email"},"description":"Email addresses to send it to (one address as text also works)","examples":[["amani@example.com"]]}},"required":["to"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"source":{"type":"string"},"resource":{"type":"string"},"recordId":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"record_ref":{"type":"object","properties":{"entity_type":{"type":"string"},"entity_id":{"type":"string"}}},"channel":{"type":"string"},"to":{"type":"array","items":{"type":"string"}},"cc":{"type":"array"},"subject":{"type":"string"},"body":{"type":"string"},"attachments":{"type":"array"},"status":{"type":"string","description":"queued, sending, sent, failed (to be retried), exhausted (gave up) or superseded"},"permanent_failure":{"type":"boolean"},"attempts":{"type":"array","items":{"type":"object","properties":{"at":{"type":"string","format":"date-time"},"outcome":{"type":"string"}}}},"next_retry_at":{},"superseded_by":{},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"description":"The email delivery Kazee Books queued, with its first attempt"}}},"example":{"success":true,"source":"kazee-books","resource":"invoices","recordId":"inv_1c2d3e4f5a6b4c7d8e9f0a1b2c3d4e5f","data":{"id":"snd_6f7a8b9c0d1e4f2a3b4c5d6e7f8a9b0c","record_ref":{"entity_type":"invoice","entity_id":"inv_1c2d3e4f5a6b4c7d8e9f0a1b2c3d4e5f"},"channel":"email","to":["amani@example.com"],"cc":[],"subject":"Invoice INV-000412 from Example ISP","body":"Dear Amani, please find your October invoice attached.","attachments":[],"status":"sent","permanent_failure":false,"attempts":[{"at":"2026-10-20T09:00:02.000Z","outcome":"sent"}],"next_retry_at":null,"superseded_by":null,"created_at":"2026-10-20T09:00:00.000Z","updated_at":"2026-10-20T09:00:02.000Z"}}}}},"400":{"description":"resource has no printable document, recordId is empty, to is missing, or an address is not valid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Kazee Books is not connected to this CRM organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such document, or you do not have the Books permission to read that collection","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Sending documents by email is not switched on yet, or Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.billing.read","crm.books.comms.send"]}],"x-required-scopes":["crm.billing.read","crm.books.comms.send"],"x-scope-mode":"all"}},"/billing/books/estimates/{recordId}/mark-sent":{"post":{"tags":["Quotes"],"summary":"Mark a Books estimate sent","description":"Marks a draft Kazee Books estimate as sent, in your name, without emailing anything, and moves the CRM quote that follows it to sent in the same request. Books refuses while the estimate waits for approval or is not approved yet.\n\n**Permission:** `crm.quotes.update` (Update quotes) and `crm.books.comms.send`.","operationId":"markBooksEstimateSent","parameters":[{"name":"recordId","in":"path","required":true,"schema":{"type":"string"},"description":"The Books estimate (an est_… ID)","example":"est_2d3e4f5a6b7c4d8e9f0a1b2c3d4e5f6a"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"source":{"type":"string"},"verb":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"number":{"type":"string"},"contact_id":{"type":"string"},"contact_name":{"type":"string"},"date":{"type":"string"},"expiry_date":{"type":"string"},"currency":{"type":"string"},"exchange_rate":{"type":"string"},"status":{"type":"string","description":"draft, sent, accepted, declined, ..."},"approval_status":{"type":"string"},"tax_treatment":{"type":"string"},"is_inclusive_tax":{"type":"boolean"},"sub_total":{"type":"string"},"tax_summary":{"type":"array","items":{"type":"object","properties":{"tax_id":{"type":"string"},"name":{"type":"string"},"rate_percent":{"type":"string"},"base":{"type":"string"},"amount":{"type":"string"}}}},"discount":{},"discount_total":{"type":"string"},"adjustment":{"type":"string"},"total":{"type":"string"},"reference":{"type":"string"},"notes":{"type":"string"},"custom_fields":{"type":"object","properties":{}},"dimension_values":{"type":"object","properties":{}},"converted_so_id":{},"converted_invoice_id":{},"sent_at":{"type":"string","format":"date-time"},"accepted_at":{},"declined_at":{},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"description":"The estimate as Kazee Books now holds it"},"quotes":{"type":"array","items":{"type":"object","properties":{"quoteId":{"type":"string"},"quoteNumber":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"}}},"description":"The CRM quotes that follow this estimate and moved with it: their number and old and new status. Empty when none moved"}}},"example":{"success":true,"source":"kazee-books","verb":"mark-sent","data":{"id":"est_2d3e4f5a6b7c4d8e9f0a1b2c3d4e5f6a","number":"EST-000118","contact_id":"con_0b9e8c1e3f5a4c2d9a7b1e2f3a4b5c6d","contact_name":"Amani Wanjiru","date":"2026-10-18","expiry_date":"2026-11-17","currency":"KES","exchange_rate":"1","status":"sent","approval_status":"approved","tax_treatment":"vat_registered","is_inclusive_tax":true,"sub_total":"3017.24","tax_summary":[{"tax_id":"tax_8d9e0f1a2b3c4d4e5f6a7b8c9d0e1f2a","name":"VAT","rate_percent":"16.0000","base":"3017.24","amount":"482.76"}],"discount":null,"discount_total":"0.00","adjustment":"0.00","total":"3500.00","reference":"Q-2026-0057","notes":"Installation within 5 working days of payment.","custom_fields":{},"dimension_values":{},"converted_so_id":null,"converted_invoice_id":null,"sent_at":"2026-10-20T09:00:00.000Z","accepted_at":null,"declined_at":null,"created_at":"2026-10-18T07:30:00.000Z","updated_at":"2026-10-20T09:00:00.000Z"},"quotes":[{"quoteId":"6e2b9d4f-1a3c-4e5b-8d7f-9a0b1c2d3e4f","quoteNumber":"Q-2026-0057","from":"draft","to":"sent"}]}}}},"400":{"description":"recordId is empty","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You lack a permission this needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such estimate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The estimate is not a draft, or it waits for (or failed) approval","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.quotes.update","crm.books.comms.send"]}],"x-required-scopes":["crm.quotes.update","crm.books.comms.send"],"x-scope-mode":"all"}},"/billing/books/estimates/{recordId}/{verb}":{"post":{"tags":["Quotes"],"summary":"Accept, decline or reopen a Books estimate","description":"Records the customer's answer to a sent Kazee Books estimate (accept or decline), or takes a sent estimate back to draft to be edited and approved again, in your name. The CRM quote that follows the estimate moves with it in the same request.\n\n**Permission:** `crm.quotes.update` (Update quotes) and `crm.books.sales.update`.","operationId":"writeBooksEstimateVerb","parameters":[{"name":"recordId","in":"path","required":true,"schema":{"type":"string"},"description":"The Books estimate (an est_… ID)","example":"est_2d3e4f5a6b7c4d8e9f0a1b2c3d4e5f6a"},{"name":"verb","in":"path","required":true,"schema":{"type":"string","enum":["accept","decline","revert-to-draft"]},"description":"accept or decline records the customer's answer; revert-to-draft takes it back to draft","example":"accept"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"source":{"type":"string"},"verb":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"number":{"type":"string"},"contact_id":{"type":"string"},"contact_name":{"type":"string"},"date":{"type":"string"},"expiry_date":{"type":"string"},"currency":{"type":"string"},"exchange_rate":{"type":"string"},"status":{"type":"string","description":"draft, sent, accepted, declined, ..."},"approval_status":{"type":"string"},"tax_treatment":{"type":"string"},"is_inclusive_tax":{"type":"boolean"},"sub_total":{"type":"string"},"tax_summary":{"type":"array","items":{"type":"object","properties":{"tax_id":{"type":"string"},"name":{"type":"string"},"rate_percent":{"type":"string"},"base":{"type":"string"},"amount":{"type":"string"}}}},"discount":{},"discount_total":{"type":"string"},"adjustment":{"type":"string"},"total":{"type":"string"},"reference":{"type":"string"},"notes":{"type":"string"},"custom_fields":{"type":"object","properties":{}},"dimension_values":{"type":"object","properties":{}},"converted_so_id":{},"converted_invoice_id":{},"sent_at":{"type":"string","format":"date-time"},"accepted_at":{"type":"string","format":"date-time"},"declined_at":{},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"description":"The estimate as Kazee Books now holds it"},"quotes":{"type":"array","items":{"type":"object","properties":{"quoteId":{"type":"string"},"quoteNumber":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"}}},"description":"The CRM quotes that follow this estimate and moved with it: their number and old and new status. Empty when none moved"}}},"example":{"success":true,"source":"kazee-books","verb":"accept","data":{"id":"est_2d3e4f5a6b7c4d8e9f0a1b2c3d4e5f6a","number":"EST-000118","contact_id":"con_0b9e8c1e3f5a4c2d9a7b1e2f3a4b5c6d","contact_name":"Amani Wanjiru","date":"2026-10-18","expiry_date":"2026-11-17","currency":"KES","exchange_rate":"1","status":"accepted","approval_status":"approved","tax_treatment":"vat_registered","is_inclusive_tax":true,"sub_total":"3017.24","tax_summary":[{"tax_id":"tax_8d9e0f1a2b3c4d4e5f6a7b8c9d0e1f2a","name":"VAT","rate_percent":"16.0000","base":"3017.24","amount":"482.76"}],"discount":null,"discount_total":"0.00","adjustment":"0.00","total":"3500.00","reference":"Q-2026-0057","notes":"Installation within 5 working days of payment.","custom_fields":{},"dimension_values":{},"converted_so_id":null,"converted_invoice_id":null,"sent_at":"2026-10-20T09:00:00.000Z","accepted_at":"2026-10-21T08:00:00.000Z","declined_at":null,"created_at":"2026-10-18T07:30:00.000Z","updated_at":"2026-10-21T08:00:00.000Z"},"quotes":[{"quoteId":"6e2b9d4f-1a3c-4e5b-8d7f-9a0b1c2d3e4f","quoteNumber":"Q-2026-0057","from":"sent","to":"accepted"}]}}}},"400":{"description":"verb is not accept, decline or revert-to-draft, or recordId is empty","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You lack a permission this needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such estimate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The estimate is not sent, so it cannot move this way","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.quotes.update","crm.books.sales.update"]}],"x-required-scopes":["crm.quotes.update","crm.books.sales.update"],"x-scope-mode":"all"}},"/users/profiles":{"post":{"tags":["Teams & people"],"summary":"Get people's profile photos","description":"Answers the profile photo of each person in a list of IDs. Repeats and values that are not IDs are dropped quietly.\n\n**Permission:** `crm.users.read` (View users).","operationId":"usersProfiles","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"userIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"CRM user IDs of the people (a JSON array in a string also works)","examples":[["7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","8e2f3a4b-5c6d-4e7f-9a0b-1c2d3e4f5a6b"]]}},"required":["userIds"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"profiles":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"profilePicture":{"type":"string","description":"Link to the person's profile photo (valid about 7 days), or null when they have none"}}}}}},"example":{"success":true,"profiles":[{"userId":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","profilePicture":"https://files.example.com/profile-pictures/amina.jpg?X-Amz-Expires=604800&X-Amz-Signature=abc123"},{"userId":"8e2f3a4b-5c6d-4e7f-9a0b-1c2d3e4f5a6b","profilePicture":null}]}}}},"400":{"description":"userIds is not a list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.users.read"]}],"x-required-scopes":["crm.users.read"]}},"/lookups/resolve":{"post":{"tags":["Search"],"summary":"Get the names of records by ID","description":"Turns up to 500 { module, id } references into the names people read, in one call. You only get names for modules you may read; for the rest the label is null. Entries without a text module and id are skipped.\n\n**Permission:** `crm.customFields.read` (View custom fields).","operationId":"resolveLookups","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"refs":{"type":"array","items":{"type":"object","properties":{"module":{"type":"string"},"id":{"type":"string"}}},"description":"The references, each { module, id }; module is a key from List the record types a reference can point at, e.g. accounts or custom:<slug> (a JSON array in a string also works). At most 500","examples":[[{"module":"accounts","id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"},{"module":"custom:site-surveys","id":"f6a7b8c9-d0e1-4f2a-9b3c-5d6e7f8a9b0c"}]]}},"required":["refs"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"module":{"type":"string"},"id":{"type":"string"},"label":{"type":"string","description":"The record's name; null when it does not exist or you may not read that module"},"deleted":{"type":"boolean","description":"true when the record exists but was deleted (it still gets its name); left out otherwise"}}}}}},"example":{"success":true,"data":[{"module":"accounts","id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","label":"Acme Ltd","deleted":true},{"module":"custom:site-surveys","id":"f6a7b8c9-d0e1-4f2a-9b3c-5d6e7f8a9b0c","label":"Westlands Plaza, 4th floor"}]}}}},"400":{"description":"refs is not a list (or not valid JSON), or holds over 500 entries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customFields.read"]}],"x-required-scopes":["crm.customFields.read"]}},"/createProduct":{"post":{"tags":["Products & plans"],"summary":"Create a product","description":"Add a product to your organisation's catalogue. Its price is before tax; taxId, unit and recurringPeriod let quotes price it without Kazee Books. bundledItems makes it a kit of those products (one of each; use PUT /products/:productId/kit to set quantities).\n\n**Permission:** `crm.products.create` (Create products).","operationId":"createProduct","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"bundledItems":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Product IDs that make up this product as a kit, one of each","examples":[["8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f"]]},"customFields":{"type":"object","description":"Values for your organisation's own product fields, by field key","examples":[{"speed_mbps":50}]},"description":{"type":"string","description":"What it is; the default wording of its quote lines","examples":["Monthly fibre internet, 50 Mbps"]},"name":{"type":"string","description":"The product's name","examples":["50 Mbps Fibre"]},"price":{"type":"number","minimum":0,"description":"Price before tax, 0 or more","examples":[4500]},"recurringPeriod":{"type":"string","enum":["week","month","quarter","half_year","year"],"description":"How often it bills; leave out for a one-off product","examples":["month"]},"sku":{"type":"string","description":"Stock code, unique in your organisation (up to 64 characters)","examples":["FIB-50"]},"status":{"type":"string","enum":["active","inactive","discontinued"],"description":"Default active","examples":["active"]},"taxId":{"type":"string","description":"One of your organisation's own taxes (for quoting without Kazee Books); leave out for none","examples":["460000000038029"]},"type":{"type":"string","enum":["service","item"],"description":"service or item","examples":["service"]},"unit":{"type":"string","maxLength":32,"description":"What a quantity counts, e.g. month or piece","examples":["month"]}},"required":["name","price","sku","type"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string","description":"\"Product created successfully\""},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","description":"service or item"},"price":{"type":"string","description":"Price before tax, a decimal string (two decimals)"},"sku":{"type":"string"},"status":{"type":"string","description":"active, inactive or discontinued"},"description":{"type":"string"},"taxId":{"type":"string","description":"The tax quotes add to it: one of the organisation's taxes, or a Books item's tax for a product mirrored from Books; null for none"},"unit":{"type":"string"},"recurringPeriod":{"type":"string","description":"week, month, quarter, half_year or year; null for a one-off product"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Product created successfully","data":{"id":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"50 Mbps Fibre","type":"service","price":"4500.00","sku":"FIB-50","status":"active","description":"Monthly fibre internet, 50 Mbps","taxId":"460000000038029","unit":"month","recurringPeriod":"month","customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-10-15T14:05:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}}}}},"400":{"description":"type is not service or item, status is unknown, price is not a number of 0 or more, taxId is not one of your organisation's taxes or is switched off, unit is over 32 characters, or recurringPeriod is unknown","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"A product with that SKU already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.products.create"]}],"x-required-scopes":["crm.products.create"]}},"/updateProduct":{"post":{"tags":["Products & plans"],"summary":"Update a product","description":"Change some of a product's fields; only the fields you send change. A product mirrored from Kazee Books keeps Books' price and tax. Sending bundledItems replaces the kit's parts with those products (one of each); an empty value removes them.\n\n**Permission:** `crm.products.update` (Update products).","operationId":"updateProduct","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"bundledItems":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Replace the kit's parts with these product IDs, one of each","examples":[["8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f"]]},"customFields":{"type":"object","description":"ALL your organisation's own product fields, by field key: replaces the whole set (a JSON object in a string also works)","examples":[{"speed_mbps":50}]},"description":{"type":"string","description":"What it is; the default wording of its quote lines","examples":["Monthly fibre internet, 50 Mbps"]},"id":{"type":"string","format":"uuid","description":"The product's ID","examples":["8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f"]},"name":{"type":"string","description":"The product's name","examples":["50 Mbps Fibre"]},"price":{"type":"number","minimum":0,"description":"Price before tax, 0 or more. A product mirrored from Kazee Books keeps Books' price","examples":[4500]},"recurringPeriod":{"type":"string","enum":["week","month","quarter","half_year","year"],"description":"How often it bills; empty makes it a one-off product","examples":["month"]},"sku":{"type":"string","description":"Stock code, unique in your organisation (up to 64 characters)","examples":["FIB-50"]},"status":{"type":"string","enum":["active","inactive","discontinued"],"description":"active, inactive or discontinued","examples":["inactive"]},"taxId":{"type":"string","description":"One of your organisation's own taxes; empty for none. Cannot change on a product mirrored from Books","examples":["460000000038029"]},"type":{"type":"string","enum":["service","item"],"description":"service or item","examples":["service"]},"unit":{"type":"string","maxLength":32,"description":"What a quantity counts, e.g. month or piece; empty clears it","examples":["month"]}},"required":["id"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string","description":"\"Product updated successfully\""},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","description":"service or item"},"price":{"type":"string","description":"Price before tax, a decimal string (two decimals)"},"sku":{"type":"string"},"status":{"type":"string","description":"active, inactive or discontinued"},"description":{"type":"string"},"taxId":{"type":"string","description":"The tax quotes add to it: one of the organisation's taxes, or a Books item's tax for a product mirrored from Books; null for none"},"unit":{"type":"string"},"recurringPeriod":{"type":"string","description":"week, month, quarter, half_year or year; null for a one-off product"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Product updated successfully","data":{"id":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"50 Mbps Fibre","type":"service","price":"4800.00","sku":"FIB-50","status":"active","description":"Monthly fibre internet, 50 Mbps","taxId":"460000000038029","unit":"month","recurringPeriod":"month","customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}}}}},"400":{"description":"type is not service or item, status is unknown, price is not a number of 0 or more, the price or tax of a product mirrored from Books was changed, taxId is not one of your organisation's taxes or is switched off, unit is over 32 characters, or recurringPeriod is unknown","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No product with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Another product already has that SKU","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.products.update"]}],"x-required-scopes":["crm.products.update"]}},"/plans/bulk":{"post":{"tags":["Products & plans"],"summary":"Add or update plans in bulk","description":"Adds or updates many service plans at once, matched by name: a plan whose name exists is updated, any other is created. Plans are saved one by one, so if one fails the ones before it stay saved. Everyone in the organisation is notified.\n\n**Permission:** `crm.plans.create` (Create plans).","operationId":"bulkAddPlans","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"plans":{"type":"array","items":{"type":"object","required":["name","Max_Limit_Down_k","Max_Limit_Up_k","productId"],"properties":{"name":{"type":"string","description":"Plan name; an existing name is updated"},"Max_Limit_Down_k":{"type":"number","description":"Download speed limit, in kbit/s"},"Max_Limit_Up_k":{"type":"number","description":"Upload speed limit, in kbit/s"},"productId":{"type":"string","format":"uuid","description":"The product the plan belongs to"},"serviceTypeId":{"type":"string","format":"uuid","description":"Service type; default Internet"},"installationFee":{"type":"number","description":"Default 0"},"status":{"type":"string","enum":["active","inactive","suspended"],"description":"Default active"},"connectionType":{"type":"array","items":{"type":"string","enum":["Fiber","Wireless"]}},"rateLimit":{"type":"string","description":"The router's rate-limit text"}}},"description":"The plans to add or update. Leaving out installationFee, status, connectionType or rateLimit on an existing plan resets it to the default","examples":[[{"name":"Home Fibre 50 Mbps","Max_Limit_Down_k":51200,"Max_Limit_Up_k":25600,"productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","installationFee":2000,"connectionType":["Fiber"]}]]}},"required":["plans"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"added":{"type":"integer","description":"How many plans were created"},"updated":{"type":"integer","description":"How many existing plans were updated"},"total":{"type":"integer"},"plans":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"limitStr":{"type":"string"},"burstMaxDown":{"description":"Burst settings (burstMax*, burstThresh*, burstTime*): the router's burst speed, threshold and time, or null"},"burstMaxUp":{},"burstThreshDown":{},"burstThreshUp":{},"burstTimeDown":{},"burstTimeUp":{},"connectionType":{"type":"array","items":{"type":"string"},"description":"Fiber, Wireless or both"},"installationFee":{"type":"string","description":"Installation fee, a decimal in a string"},"maxLimitDown":{"type":"integer","description":"Download speed limit, in kbit/s"},"maxLimitUp":{"type":"integer","description":"Upload speed limit, in kbit/s"},"rateLimit":{"type":"string","description":"The router's rate-limit text, e.g. 50M/25M"},"serviceTypeId":{"type":"string"},"statusLevelId":{},"status":{"type":"string","description":"active, inactive or suspended"},"productId":{"type":"string","description":"The product this plan is a speed option of"},"booksItemId":{"type":"string","description":"The Kazee Books item the plan is sold as (the first month of its quotes), or null"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}},"example":{"success":true,"data":{"added":1,"updated":0,"total":1,"plans":[{"id":"9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Home Fibre 50 Mbps","limitStr":"","burstMaxDown":null,"burstMaxUp":null,"burstThreshDown":null,"burstThreshUp":null,"burstTimeDown":null,"burstTimeUp":null,"connectionType":["Fiber"],"installationFee":"2000.00","maxLimitDown":51200,"maxLimitUp":25600,"rateLimit":"50M/25M","serviceTypeId":"e3d02e5a-7b9c-4e6f-9c1d-5a6b7c8d9e0f","statusLevelId":null,"status":"active","productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","booksItemId":"itm_8c1d4e5f60718293a4b5c6d7e8f9a0b1","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}]}}}}},"400":{"description":"plans is empty, a plan lacks name, Max_Limit_Down_k or Max_Limit_Up_k (meta.invalid lists them), or a plan lacks productId","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No serviceTypeId was given and your organisation has no Internet service type","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A new plan's name was taken at the same moment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.plans.create"]}],"x-required-scopes":["crm.plans.create"]}},"/leads/bulk-reassign":{"post":{"tags":["Leads"],"summary":"Reassign leads in bulk","description":"Gives many leads a new owner (and assignee) at once, for admins only (an assistant acting for its owner is refused). Pick the leads by ID or by filters, and how to share them out with mode (static, round_robin, load_balanced or handler_map). By default it is a dry run that only shows the plan; send dryRun=false to write it. Writes are logged per lead but send no notifications and run no automations.\n\n**Permission:** `crm.leads.update` (Update leads).","operationId":"bulkReassignLeads","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"currentLoad":{"type":"object","description":"load_balanced: starting count per pool user, by user ID. Default: each user's current open records","examples":[{"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d":12,"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e":4}]},"dryRun":{"type":"boolean","description":"Default true: only work out and show the plan. Send false to write it","examples":[true]},"echo":{"type":"integer","description":"How many planned assignments and skipped records to list in the reply (default 200, at most 5,000)","examples":[200]},"excludeStatuses":{"type":"array","items":{"type":"string"},"description":"Leave out records at these stages","examples":[["converted"]]},"handledBy":{"type":"string","enum":["any","present","empty"],"description":"Only records whose handled_by custom field is filled in (present), empty (empty), or either (any)","examples":["present"]},"labelMap":{"type":"object","description":"handler_map: which user each handled_by label goes to, e.g. { \"Jane Doe\": \"<user id>\" } (a JSON object in a string also works)","examples":[{"Brian Otieno":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"}]},"limit":{"type":"integer","description":"Reassign at most this many of the matching records","examples":[100]},"mode":{"type":"string","enum":["static","round_robin","load_balanced","handler_map"],"description":"How to pick the new owner: static (everyone to userId), round_robin (take turns through pool), load_balanced (to whoever in pool has the fewest open records) or handler_map (by the record's handled_by label, through labelMap)","examples":["round_robin"]},"ownerIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Only records currently owned by these users","examples":[["b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e"]]},"pool":{"type":"array","items":{"type":"string","format":"uuid"},"description":"The users to share the records between, in order (round_robin, load_balanced, and handler_map's pool fallback). Each must be an active user of your organisation","examples":[["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e"]]},"recordIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"IDs of the records to reassign, at most 5,000 (a JSON array or a comma-separated list in a string also works). Send this or at least one filter","examples":[["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]]},"setAssignee":{"type":"boolean","description":"Also change who the record is assigned to (default true; leads only, ignored for deals)","examples":[true]},"setOwner":{"type":"boolean","description":"Change the owner (default true)","examples":[true]},"statuses":{"type":"array","items":{"type":"string"},"description":"Only records at these stages (lead status or deal stage slugs). A JSON array or comma-separated list in a string also works","examples":[["warm_leads","hot_lead"]]},"unmappedFallback":{"type":"string","enum":["skip","pool"],"description":"handler_map: what to do with a record whose label maps to nobody: skip it (default) or give it to the pool","examples":["skip"]},"userId":{"type":"string","format":"uuid","description":"The new owner, for static mode","examples":["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]}},"required":["mode"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"moduleType":{"type":"string"},"mode":{"type":"string"},"dryRun":{"type":"boolean"},"filter":{"type":"object","properties":{"statuses":{"type":"array","items":{"type":"string"}},"ownerIds":{"type":"array","items":{"type":"string"}}}},"closedStatuses":{"type":"array","items":{"type":"string"},"description":"Stages that do not count as open when load_balanced counts each user's open leads"},"setOwner":{"type":"boolean"},"setAssignee":{"type":"boolean"},"targets":{"type":"integer","description":"How many leads matched"},"planned":{"type":"integer","description":"How many would change (or changed) owner"},"unchanged":{"type":"integer","description":"Leads already with the planned user"},"skipped":{"type":"integer"},"skippedDetail":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"reason":{"type":"string","description":"no_handler (no handled_by label) or unmapped_handler:<labels> (no user for the labels)"}}}},"byUser":{"type":"object","properties":{"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d":{"type":"integer"}}},"currentLoad":{},"assignments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"from":{"type":"object","properties":{"ownerId":{"type":"string"},"assignedTo":{"type":"string"}}},"to":{"type":"string"},"via":{"type":"string"},"label":{"type":"string"}}},"description":"The planned changes, up to echo of them: lead, old owner and assignee, new user, and how it was picked"},"applied":{"type":"integer","description":"Leads written (0 on a dry run)"},"audited":{"type":"integer","description":"Change-log rows written"},"batchId":{"description":"Groups this run's change-log rows; null on a dry run"}}},"example":{"success":true,"moduleType":"leads","mode":"handler_map","dryRun":true,"filter":{"statuses":["warm_leads","hot_lead"],"ownerIds":["b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e"]},"closedStatuses":["lost_lead","junk_lead","cold_lead","not_qualified","converted","pending_hotspot_lead"],"setOwner":true,"setAssignee":true,"targets":3,"planned":1,"unchanged":1,"skipped":1,"skippedDetail":[{"id":"f4e1a2b3-c4d5-4e6f-8a7b-9c0d1e2f3a4b","reason":"no_handler"}],"byUser":{"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d":1},"currentLoad":null,"assignments":[{"id":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","from":{"ownerId":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","assignedTo":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e"},"to":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","via":"handler_map","label":"brian otieno"}],"applied":0,"audited":0,"batchId":null}}}},"400":{"description":"mode is unknown, no recordIds and no filter, over 5,000 recordIds, no target users, a target user is not an active user of your organisation, setOwner and setAssignee both false, or a list, object or boolean input cannot be read","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Your role is not an admin role, or you are an assistant acting for its owner (error.meta.askFirst: the owner must do it)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.update"]}],"x-required-scopes":["crm.leads.update"]}},"/leads/{leadId}/quotes":{"post":{"tags":["Leads"],"summary":"Link a quote to a lead","description":"Links an existing quote to a lead (sets the quote's leadId; a quote has one lead, so this moves it from any other). The lead's stage does not change.\n\n**Permission:** `crm.leads.update` (Update leads).","operationId":"assignQuoteToLead","parameters":[{"name":"leadId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The lead's ID","example":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"assignedBy":{"type":"string","format":"uuid","description":"Your user ID. Required, though the call does not store it","examples":["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]},"quoteId":{"type":"string","format":"uuid","description":"The quote to link","examples":["6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d"]}},"required":["assignedBy","quoteId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"lead":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"partyId":{},"firstName":{"type":"string"},"lastName":{"type":"string"},"salutation":{"type":"string"},"businessName":{},"kraPin":{},"source":{"type":"string"},"entityType":{"type":"string","description":"individual or company"},"contactInfo":{"type":"object","properties":{"email":{"type":"string"},"phone":{"type":"string"}},"description":"The lead's email and phone"},"ownerId":{"type":"string"},"assignedTo":{"type":"string"},"status":{"type":"string","description":"The lead's pipeline stage, as its slug (e.g. warm_leads, hot_lead, converted, not_qualified); the stages are set per organisation"},"documentCompletionStatus":{"type":"object","properties":{"required":{"type":"integer"},"uploaded":{"type":"integer"},"approved":{"type":"integer"}},"description":"How many documents the lead is asked for, has uploaded and has had approved"},"notes":{"type":"string"},"relatedContactIds":{"type":"array"},"relatedAccountIds":{"type":"array"},"primaryContactId":{},"primaryAccountId":{},"convertedAccountId":{},"convertedContactId":{},"customFields":{"type":"object","properties":{"town":{"type":"string"},"connection_type":{"type":"string"}},"description":"Values of the organisation's own lead fields, by field key"},"externalIds":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"externalId":{"type":"string"}}}},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"quote":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"quoteNumber":{"type":"string"},"leadId":{"type":"string"},"accountId":{"type":"string"},"subscriptionId":{},"requestId":{},"dealId":{"type":"string"},"ownerId":{"type":"string"},"type":{"type":"string"},"subtotal":{"type":"string"},"total":{"type":"string"},"status":{"type":"string"},"paidAmount":{"type":"string"},"balance":{"type":"string"},"paidAt":{},"validityStart":{"type":"string","format":"date-time"},"validityEnd":{"type":"string","format":"date-time"},"sentAt":{},"acceptedAt":{},"declinedAt":{},"expiredAt":{},"notes":{"type":"string"},"revisionOf":{},"revisionNumber":{"type":"integer"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"description":"The quote after the change, now pointing at the lead"}}}}},"example":{"success":true,"data":{"lead":{"id":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","partyId":null,"firstName":"Amina","lastName":"Wanjiru","salutation":"Ms.","businessName":null,"kraPin":null,"source":"website","entityType":"individual","contactInfo":{"email":"amina.wanjiru@example.com","phone":"+254700000001"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","assignedTo":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","status":"warm_leads","documentCompletionStatus":{"required":2,"uploaded":1,"approved":0},"notes":"Asked about home fibre in Kilimani","relatedContactIds":[],"relatedAccountIds":[],"primaryContactId":null,"primaryAccountId":null,"convertedAccountId":null,"convertedContactId":null,"customFields":{"town":"Nairobi","connection_type":"Home"},"externalIds":[{"source":"website","externalId":"web-10442"}],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"},"quote":{"id":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","quoteNumber":"QUOTE-00042","leadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","subscriptionId":null,"requestId":null,"dealId":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","type":"quote","subtotal":"6500.00","total":"7540.00","status":"draft","paidAmount":"0.00","balance":"7540.00","paidAt":null,"validityStart":"2026-10-01T08:30:00.000Z","validityEnd":"2026-11-01T00:00:00.000Z","sentAt":null,"acceptedAt":null,"declinedAt":null,"expiredAt":null,"notes":"Includes free router for 12 months","revisionOf":null,"revisionNumber":1,"customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No lead or no quote with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.update"]}],"x-required-scopes":["crm.leads.update"]}},"/leads/{leadId}/convert":{"post":{"tags":["Leads"],"summary":"Convert a lead","description":"Marks a lead as converted to an account (and contact) you have already created: the lead moves to the Converted stage and points at them. It creates nothing itself. Allowed only from a stage the pipeline can move to Converted from.\n\n**Permission:** `crm.leads.update` (Update leads).","operationId":"convertLead","parameters":[{"name":"leadId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The lead's ID","example":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid","description":"The account the lead became; required","examples":["2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"]},"contactId":{"type":"string","format":"uuid","description":"The contact the lead became; required when conversionType is contact","examples":["4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"]},"conversionType":{"type":"string","enum":["account","contact"],"default":"account","description":"account (needs accountId) or contact (needs accountId and contactId)","examples":["account"]},"convertedBy":{"type":"string","format":"uuid","description":"Your user ID; echoed in the reply, not stored","examples":["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]},"subscriptionId":{"type":"string","format":"uuid","description":"The subscription that came of it; echoed in the reply, not stored","examples":["f5a6b7c8-d9e0-4f1a-8b2c-3d4e5f6a7b8c"]}},"required":["convertedBy"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"conversion":{"type":"object","properties":{"leadId":{"type":"string"},"conversionType":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"subscriptionId":{},"convertedAt":{"type":"string","format":"date-time"},"convertedBy":{"type":"string"}},"description":"What was recorded: the account, contact and subscription given, and when"},"lead":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"partyId":{},"firstName":{"type":"string"},"lastName":{"type":"string"},"salutation":{"type":"string"},"businessName":{},"kraPin":{},"source":{"type":"string"},"entityType":{"type":"string","description":"individual or company"},"contactInfo":{"type":"object","properties":{"email":{"type":"string"},"phone":{"type":"string"}},"description":"The lead's email and phone"},"ownerId":{"type":"string"},"assignedTo":{"type":"string"},"status":{"type":"string","description":"The lead's pipeline stage, as its slug (e.g. warm_leads, hot_lead, converted, not_qualified); the stages are set per organisation"},"documentCompletionStatus":{"type":"object","properties":{"required":{"type":"integer"},"uploaded":{"type":"integer"},"approved":{"type":"integer"}},"description":"How many documents the lead is asked for, has uploaded and has had approved"},"notes":{"type":"string"},"relatedContactIds":{"type":"array"},"relatedAccountIds":{"type":"array"},"primaryContactId":{},"primaryAccountId":{},"convertedAccountId":{"type":"string"},"convertedContactId":{"type":"string"},"customFields":{"type":"object","properties":{"town":{"type":"string"},"connection_type":{"type":"string"}},"description":"Values of the organisation's own lead fields, by field key"},"externalIds":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"externalId":{"type":"string"}}}},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}},"example":{"success":true,"data":{"conversion":{"leadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","conversionType":"account","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","subscriptionId":null,"convertedAt":"2026-10-20T09:00:00.000Z","convertedBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"},"lead":{"id":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","partyId":null,"firstName":"Amina","lastName":"Wanjiru","salutation":"Ms.","businessName":null,"kraPin":null,"source":"website","entityType":"individual","contactInfo":{"email":"amina.wanjiru@example.com","phone":"+254700000001"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","assignedTo":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","status":"converted","documentCompletionStatus":{"required":2,"uploaded":1,"approved":0},"notes":"Asked about home fibre in Kilimani","relatedContactIds":[],"relatedAccountIds":[],"primaryContactId":null,"primaryAccountId":null,"convertedAccountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","convertedContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","customFields":{"town":"Nairobi","connection_type":"Home"},"externalIds":[{"source":"website","externalId":"web-10442"}],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}}},"400":{"description":"accountId missing (conversionType account), or accountId or contactId missing (conversionType contact)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No lead with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The lead is already converted, or its pipeline has no move to Converted from its stage","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.update"]}],"x-required-scopes":["crm.leads.update"]}},"/leads/{leadId}/site-sale":{"post":{"tags":["Leads"],"summary":"Start a site sale from a lead","description":"For a lead who is already a customer: opens a deal for the new site on the customer's existing account and contact (stage qualification, named after the site, carrying the site answers the lead holds) and closes the lead as converted against that account. No new account or Books customer is made. The account must be one the lead's phone or email matches, unless anyAccount is true. The organisation's automations for a new deal run afterwards.\n\n**Permission:** `crm.leads.update` (Update leads) and `crm.deals.create` (Create deals).","operationId":"startLeadSiteSale","parameters":[{"name":"leadId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The lead's ID","example":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid","description":"The existing customer's account to add the site to (one the lead matches; see Find the existing customers a lead matches)","examples":["2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"]},"anyAccount":{"type":"boolean","description":"true allows an account the lead's phone and email do not match (the customer called from another number)","examples":[false]},"confirmDuplicate":{"type":"boolean","description":"true starts the sale even though the site already has an open one","examples":[false]},"connectionType":{"type":"string","enum":["Home","Business"],"description":"Home or Business; default: the lead's","examples":["Home"]},"contactId":{"type":"string","format":"uuid","description":"Which of the customer's contacts the deal is for; default: the matched contact, else the account's first","examples":["4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"]},"notes":{"type":"string","description":"Survey notes for the site","examples":["Gate code 4411"]},"servicePlanId":{"type":"string","format":"uuid","description":"Service plan for the site; sets the deal value to its price. Default: the lead's plan","examples":["9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a"]},"siteContactName":{"type":"string","description":"Who is on site, when not the account holder","examples":["Peter Mwangi"]},"siteContactPhone":{"type":"string","description":"Phone of the person on site","examples":["+254700000002"]},"siteLocation":{"type":"string","description":"The site's Google Maps link or coordinates (lat,lng); default: the lead's site_location","examples":["-1.2921,36.8219"]},"siteName":{"type":"string","description":"The new site's name, up to 60 characters; required","examples":["Kijani"]},"town":{"type":"string","description":"Town of the site; default: the lead's","examples":["Nairobi"]}},"required":["accountId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"deal":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{},"leadId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"value":{"type":"string","description":"Deal value, a decimal string in the deal's currency (two decimals)"},"currency":{"type":"string","description":"ISO 4217 code; the organisation's currency unless set"},"recurringValue":{"type":"string"},"stage":{"type":"string","description":"Stage key on the deals pipeline, e.g. qualification, discovery, proposal, negotiation, closed_won, closed_lost (organisations may use their own)"},"probability":{"type":"integer","description":"Chance of winning, 0 to 100"},"expectedCloseDate":{},"actualCloseDate":{},"ownerId":{"type":"string"},"teamId":{},"priority":{"type":"string","description":"low, medium or high"},"source":{"type":"string","description":"Existing customer for a site sale"},"dealType":{"type":"string","description":"additional_site for a site sale"},"lostReason":{},"lostReasonDetails":{},"competitorId":{},"lastActivityDate":{"type":"string","format":"date-time"},"nextActivityDate":{},"nextActivityType":{},"notes":{"type":"string"},"customFields":{"type":"object","properties":{"site_sale":{"type":"string"},"site_name":{"type":"string"},"site_location":{"type":"string"},"town":{"type":"string"},"connection_type":{"type":"string"},"contact_name":{"type":"string"},"phone":{"type":"string"}},"description":"The organisation's own fields, by field key. Fields hidden from you come back masked"},"externalIds":{"type":"array"},"createdBy":{"type":"string"},"updatedBy":{"type":"string"},"version":{"type":"integer","description":"Goes up by one on every change"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"lead":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"partyId":{},"firstName":{"type":"string"},"lastName":{"type":"string"},"salutation":{"type":"string"},"businessName":{},"kraPin":{},"source":{"type":"string"},"entityType":{"type":"string","description":"individual or company"},"contactInfo":{"type":"object","properties":{"email":{"type":"string"},"phone":{"type":"string"}},"description":"The lead's email and phone"},"ownerId":{"type":"string"},"assignedTo":{"type":"string"},"status":{"type":"string","description":"The lead's pipeline stage, as its slug (e.g. warm_leads, hot_lead, converted, not_qualified); the stages are set per organisation"},"documentCompletionStatus":{"type":"object","properties":{"required":{"type":"integer"},"uploaded":{"type":"integer"},"approved":{"type":"integer"}},"description":"How many documents the lead is asked for, has uploaded and has had approved"},"notes":{"type":"string"},"relatedContactIds":{"type":"array"},"relatedAccountIds":{"type":"array"},"primaryContactId":{},"primaryAccountId":{},"convertedAccountId":{"type":"string"},"convertedContactId":{"type":"string"},"customFields":{"type":"object","properties":{"town":{"type":"string"},"connection_type":{"type":"string"}},"description":"Values of the organisation's own lead fields, by field key"},"externalIds":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"externalId":{"type":"string"}}}},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"message":{"type":"string"}}},"example":{"success":true,"data":{"deal":{"id":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Amina Wanjiru – Kijani","description":null,"leadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","value":"4500.00","currency":"KES","recurringValue":"0.00","stage":"qualification","probability":10,"expectedCloseDate":null,"actualCloseDate":null,"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","teamId":null,"priority":"medium","source":"Existing customer","dealType":"additional_site","lostReason":null,"lostReasonDetails":null,"competitorId":null,"lastActivityDate":"2026-10-15T14:05:00.000Z","nextActivityDate":null,"nextActivityType":null,"notes":"","customFields":{"site_sale":"New site for existing customer","site_name":"Kijani","site_location":"-1.2921,36.8219","town":"Nairobi","connection_type":"Home","contact_name":"Amina Wanjiru","phone":"+254700000001"},"externalIds":[],"createdBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","updatedBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","version":1,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"},"lead":{"id":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","partyId":null,"firstName":"Amina","lastName":"Wanjiru","salutation":"Ms.","businessName":null,"kraPin":null,"source":"website","entityType":"individual","contactInfo":{"email":"amina.wanjiru@example.com","phone":"+254700000001"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","assignedTo":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","status":"converted","documentCompletionStatus":{"required":2,"uploaded":1,"approved":0},"notes":"Asked about home fibre in Kilimani","relatedContactIds":[],"relatedAccountIds":[],"primaryContactId":null,"primaryAccountId":null,"convertedAccountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","convertedContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","customFields":{"town":"Nairobi","connection_type":"Home"},"externalIds":[{"source":"website","externalId":"web-10442"}],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}},"message":"Site sale started"}}}},"400":{"description":"No accountId, the account is not one the lead matches (and anyAccount is not true), no siteName or one over 60 characters, siteLocation missing or not a Maps link or coordinates, connectionType not Home or Business, siteContactPhone not a phone number, contactId not one of the customer's contacts, or the customer has no contact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No lead, account or service plan with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The lead is already converted or closed, or the site already has an open sale (send confirmDuplicate=true to start another; meta.dealId names it)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.update","crm.deals.create"]}],"x-required-scopes":["crm.leads.update","crm.deals.create"],"x-scope-mode":"all"}},"/leads/analyzeLead":{"post":{"tags":["Leads"],"summary":"Analyse a lead before converting it","description":"Looks for contacts that already have the lead's email or phone, and their accounts that are not terminated, and says how the lead should be converted: as a new customer, as a new account for an existing contact, or as a new subscription on an existing account. Changes nothing.\n\n**Permission:** `crm.leads.read` (View leads).","operationId":"analyzeLead","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"leadId":{"type":"string","format":"uuid","description":"The lead's ID","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]}},"required":["leadId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"lead":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"partyId":{},"firstName":{"type":"string"},"lastName":{"type":"string"},"salutation":{"type":"string"},"businessName":{},"kraPin":{},"source":{"type":"string"},"entityType":{"type":"string","description":"individual or company"},"contactInfo":{"type":"object","properties":{"email":{"type":"string"},"phone":{"type":"string"}},"description":"The lead's email and phone"},"ownerId":{"type":"string"},"assignedTo":{"type":"string"},"status":{"type":"string","description":"The lead's pipeline stage, as its slug (e.g. warm_leads, hot_lead, converted, not_qualified); the stages are set per organisation"},"documentCompletionStatus":{"type":"object","properties":{"required":{"type":"integer"},"uploaded":{"type":"integer"},"approved":{"type":"integer"}},"description":"How many documents the lead is asked for, has uploaded and has had approved"},"notes":{"type":"string"},"relatedContactIds":{"type":"array"},"relatedAccountIds":{"type":"array"},"primaryContactId":{},"primaryAccountId":{},"convertedAccountId":{},"convertedContactId":{},"customFields":{"type":"object","properties":{"town":{"type":"string"},"connection_type":{"type":"string"}},"description":"Values of the organisation's own lead fields, by field key"},"externalIds":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"externalId":{"type":"string"}}}},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"analysis":{"type":"object","properties":{"existingContactsFound":{"type":"integer"},"associatedAccountsFound":{"type":"integer"},"scenarios":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","description":"new_lead (no matches), new_account_existing_contact (a contact matched) or new_subscription_existing_account (that contact has accounts)"},"title":{"type":"string"},"recommended":{"type":"boolean"},"actions":{"type":"array","items":{"type":"string"},"description":"What converting this way does, in order"},"contacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{}},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{"type":"string"},"cacheDisplayName":{"type":"string"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}},"recommendedScenario":{"type":"object","properties":{"type":{"type":"string"},"title":{"type":"string"},"recommended":{"type":"boolean"},"actions":{"type":"array","items":{"type":"string"}},"contacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{}},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{"type":"string"},"cacheDisplayName":{"type":"string"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"description":"The scenario marked recommended, else the first one"}}},"existingData":{"type":"object","properties":{"contacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{}},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{"type":"string"},"cacheDisplayName":{"type":"string"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"accounts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"accountNumber":{"type":"string"},"status":{"type":"string"}}}}}}}}}},"example":{"success":true,"data":{"lead":{"id":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","partyId":null,"firstName":"Amina","lastName":"Wanjiru","salutation":"Ms.","businessName":null,"kraPin":null,"source":"website","entityType":"individual","contactInfo":{"email":"amina.wanjiru@example.com","phone":"+254700000001"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","assignedTo":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","status":"warm_leads","documentCompletionStatus":{"required":2,"uploaded":1,"approved":0},"notes":"Asked about home fibre in Kilimani","relatedContactIds":[],"relatedAccountIds":[],"primaryContactId":null,"primaryAccountId":null,"convertedAccountId":null,"convertedContactId":null,"customFields":{"town":"Nairobi","connection_type":"Home"},"externalIds":[{"source":"website","externalId":"web-10442"}],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"},"analysis":{"existingContactsFound":1,"associatedAccountsFound":1,"scenarios":[{"type":"new_account_existing_contact","title":"New Account for Existing Contact","recommended":false,"actions":["create_account","link_contact","create_subscription"],"contacts":[{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Amina","lastName":"Wanjiru","email":"amina.wanjiru@example.com","phone":"+254700000001","addressId":null,"status":"active","preferences":{},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Amina Wanjiru","cachePrimaryPhone":"+254700000001","cachePrimaryEmail":"amina.wanjiru@example.com","cachePartyRevision":3,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}]},{"type":"new_subscription_existing_account","title":"New Subscription for Existing Account","recommended":false,"actions":["reuse_account","reuse_contact","create_subscription"],"accounts":[{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","name":"Amina Wanjiru","accountNumber":"ACC-00412","status":"active"}]}],"recommendedScenario":{"type":"new_account_existing_contact","title":"New Account for Existing Contact","recommended":false,"actions":["create_account","link_contact","create_subscription"],"contacts":[{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Amina","lastName":"Wanjiru","email":"amina.wanjiru@example.com","phone":"+254700000001","addressId":null,"status":"active","preferences":{},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Amina Wanjiru","cachePrimaryPhone":"+254700000001","cachePrimaryEmail":"amina.wanjiru@example.com","cachePartyRevision":3,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}]}},"existingData":{"contacts":[{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Amina","lastName":"Wanjiru","email":"amina.wanjiru@example.com","phone":"+254700000001","addressId":null,"status":"active","preferences":{},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Amina Wanjiru","cachePrimaryPhone":"+254700000001","cachePrimaryEmail":"amina.wanjiru@example.com","cachePartyRevision":3,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}],"accounts":[{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","name":"Amina Wanjiru","accountNumber":"ACC-00412","status":"active"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No lead with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The lead has already been converted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.read"]}],"x-required-scopes":["crm.leads.read"]}},"/uploadLeadDocument":{"post":{"tags":["Leads"],"summary":"Upload a lead document","description":"Uploads a file (multipart form) into one of a lead's document slots, replacing any document already in that slot, and recounts the lead's documents. The file type is read from the file itself and must be one the slot accepts (by default PDF, JPEG, PNG, WebP or Word), at most the slot's size limit and never over 25 MB. The new document starts as pending.\n\n**Permission:** `crm.leads.create` (Create leads).","operationId":"uploadLeadDocument","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"document":{"type":"string","format":"binary","description":"The file, sent as a multipart/form-data file field"},"documentType":{"type":"string","description":"The document slot to fill (its key, from the lead's document requirements)","examples":["national_id"]},"entityType":{"type":"string","enum":["individual","company"],"default":"individual","description":"individual or company; only checked and stored with the file (the lead decides which slots exist)","examples":["individual"]},"leadId":{"type":"string","format":"uuid","description":"The lead's ID","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]}},"required":["document","documentType","leadId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"document":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"leadId":{"type":"string"},"documentType":{"type":"string"},"fileName":{"type":"string"},"originalName":{"type":"string"},"fileUrl":{"type":"string","description":"A short-lived download link"},"objectName":{"type":"string"},"fileSize":{"type":"integer"},"mimeType":{"type":"string"},"uploadedAt":{"type":"string","format":"date-time"},"uploadedBy":{},"entityType":{"type":"string"},"isRequired":{"type":"boolean"},"status":{"type":"string","description":"pending until someone approves or rejects it"},"notes":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"documentTypeLabel":{"type":"string","description":"The slot's name"}}}}}}},"example":{"success":true,"data":{"document":{"id":"af9c8a1c-3d5e-4a2b-9e7f-1c2d3e4f5a6b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","leadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","documentType":"national_id","fileName":"national_id-1760000000000.pdf","originalName":"amina-id.pdf","fileUrl":"https://files.example.com/crm/leads/0b9e8c1e/documents/individual/national_id-1760000000000.pdf?X-Amz-Signature=abc123","objectName":"leads/0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d/documents/individual/national_id-1760000000000.pdf","fileSize":248311,"mimeType":"application/pdf","uploadedAt":"2026-10-15T14:05:00.000Z","uploadedBy":null,"entityType":"individual","isRequired":true,"status":"pending","notes":null,"deletedAt":null,"createdAt":"2026-10-15T14:05:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","documentTypeLabel":"National ID"}}}}}},"400":{"description":"entityType is not individual or company, the lead has no slot of that documentType, no file, the file is too big, or its type cannot be read or is not accepted by the slot","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No lead with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.create"]}],"x-required-scopes":["crm.leads.create"]}},"/attachments/upload":{"post":{"tags":["Custom fields & modules"],"summary":"Upload a file for a file field","description":"Stores a file for a file (or files) custom field and answers its reference, sent as a form upload (multipart/form-data). It does not change the record: put the answered reference in the field through the record's normal update. The field's own rules decide the allowed file types and size (at most 25 MB).\n\n**Permission:** `crm.attachments.create` (Upload attachments).","operationId":"uploadAttachment","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"attachment":{"type":"string","format":"binary","description":"The file"},"fieldKey":{"type":"string","description":"The key of the file or files custom field","examples":["id_document"]},"moduleType":{"type":"string","description":"The kind of record the field belongs to, e.g. leads, deals, accounts, contacts","examples":["leads"]},"recordId":{"type":"string","format":"uuid","description":"The record the file is for","examples":["d37e0b8a-5c9f-4ace-8b7d-8f0a2c4e6b79"]}},"required":["attachment","fieldKey","moduleType","recordId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"attachment":{"type":"object","properties":{"bucketName":{"type":"string"},"objectKey":{"type":"string"},"fileName":{"type":"string"},"mimeType":{"type":"string","description":"The file type, read from the file's bytes"},"size":{"type":"integer","description":"Size in bytes"},"uploadedAt":{"type":"string","format":"date-time"},"uploadedBy":{}},"description":"Where the file is stored. Put this object in the field's value through the record's normal update to attach it"}}}}},"example":{"success":true,"data":{"attachment":{"bucketName":"org-65f1a2b3c4d5e6f7a8b9c0d1","objectKey":"leads/d37e0b8a-5c9f-4ace-8b7d-8f0a2c4e6b79/id_document/1729500000000-national_id.pdf","fileName":"national_id.pdf","mimeType":"application/pdf","size":248331,"uploadedAt":"2026-10-20T09:00:00.000Z","uploadedBy":null}}}}}},"400":{"description":"A field is missing, the field is not a file field, the file is too big or of a type the field does not allow, or its type cannot be read","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No active custom field with that key on that module","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.attachments.create"]}],"x-required-scopes":["crm.attachments.create"]}},"/quotes/{quoteId}/accept":{"post":{"tags":["Quotes"],"summary":"Accept a quote","description":"Records that the customer accepted a quote: a sent quote still within its validity moves to accepted. For a quote mirrored to a Kazee Books estimate the estimate is accepted in Books, as you, and the quote follows. Converting the lead is a separate step (POST /leads/:leadId/convert).\n\n**Permission:** `crm.quotes.update` (Update quotes).","operationId":"acceptQuote","parameters":[{"name":"quoteId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The quote the customer accepted","example":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"acceptedBy":{"type":"string","description":"Not used: who accepted is taken from your sign-in. Kept so older callers still work"},"paymentReference":{"type":"string","description":"A payment reference to echo back in the reply (not stored)","examples":["PAY-2026-0042"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"quote":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"quoteNumber":{"type":"string","description":"The quote's number, unique in your organisation (a revision adds -r2, -r3 ...)"},"leadId":{"type":"string"},"accountId":{"type":"string"},"subscriptionId":{},"requestId":{},"dealId":{"type":"string","description":"The deal the quote was made from, if any"},"ownerId":{"type":"string"},"type":{"type":"string","description":"quote (for a lead or account), request (for a service request) or subscription (for a running service)"},"subtotal":{"type":"string","description":"Sum of the lines before tax, a decimal in a string"},"total":{"type":"string","description":"Sum of the lines with tax, a decimal in a string"},"status":{"type":"string","description":"draft, sent, accepted, declined, expired or paid"},"paidAmount":{"type":"string","description":"What the customer has paid so far"},"balance":{"type":"string","description":"What is still to pay: total less paidAmount"},"paidAt":{},"validityStart":{"type":"string","format":"date-time"},"validityEnd":{"type":"string","format":"date-time","description":"The last moment the customer can accept the quote"},"sentAt":{"type":"string","format":"date-time"},"acceptedAt":{"type":"string","format":"date-time"},"declinedAt":{},"expiredAt":{},"notes":{"type":"string"},"revisionOf":{"description":"The quote this one revises, if it is a revision"},"revisionNumber":{"type":"integer"},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array","description":"IDs of this quote in other systems (e.g. its Kazee Books estimate or Zoho quotation)"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"paymentReference":{"type":"string","description":"The payment reference you sent, or null"},"nextSteps":{"type":"object","properties":{"leadConvertible":{"type":"boolean","description":"True when the quote is for a lead that still exists, so it can now be converted"}}}}}}},"example":{"success":true,"message":"Quote accepted","data":{"quote":{"id":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","quoteNumber":"QUOTE-00042","leadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","subscriptionId":null,"requestId":null,"dealId":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","type":"quote","subtotal":"6500.00","total":"7540.00","status":"accepted","paidAmount":"0.00","balance":"7540.00","paidAt":null,"validityStart":"2026-10-01T08:30:00.000Z","validityEnd":"2026-11-01T00:00:00.000Z","sentAt":"2026-10-01T08:30:00.000Z","acceptedAt":"2026-10-15T14:05:00.000Z","declinedAt":null,"expiredAt":null,"notes":"Includes free router for 12 months","revisionOf":null,"revisionNumber":1,"customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"},"paymentReference":"PAY-2026-0042","nextSteps":{"leadConvertible":true}}}}}},"400":{"description":"The quote was raised in Zoho","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No quote with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The quote is not sent (for a quote not in Books), or it has expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.quotes.update"]}],"x-required-scopes":["crm.quotes.update"]}},"/quotes/{quoteId}/revisions":{"post":{"tags":["Quotes"],"summary":"Revise a quote","description":"Makes a new draft quote that revises a draft, declined or expired quote: it copies the original (customer, type, totals, validity, notes) under the original's number plus -r2, -r3 ..., with new lines, validity end or notes if you send them. The original does not change. A quote made from a deal cannot be revised: edit it or add a quote on the deal instead.\n\n**Permission:** `crm.quotes.create` (Create quotes).","operationId":"createQuoteRevision","parameters":[{"name":"quoteId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The quote to revise (from the path)","example":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"lineItems":{"type":"array","items":{"type":"object","required":["quantity"],"properties":{"productId":{"type":["string","null"],"format":"uuid","description":"The product, or null for a line that is not one"},"name":{"type":"string","description":"Line name"},"description":{"type":["string","null"]},"quantity":{"type":"number","minimum":0},"unitPrice":{"type":"number","minimum":0,"description":"Price of one; price is the older name and also works"},"discount":{"type":"number","minimum":0,"description":"Amount off"},"taxAmount":{"type":"number","minimum":0,"description":"Tax on the line; tax is the older name and also works"},"sortOrder":{"type":"number"},"metadata":{"type":"object"}}},"description":"New lines for the revision (else the original's are copied). Each line's discount is set to 0, and the revision keeps the original's totals","examples":[[{"productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","name":"Home Fibre 50 Mbps","description":"First month of service","quantity":1,"unitPrice":4500,"taxAmount":720}]]},"notes":{"type":"string","description":"New notes (else the original's)","examples":["Installation fee reduced"]},"originalQuoteId":{"type":"string","format":"uuid","description":"Older way to name the quote to revise; used instead of the path's quoteId when sent"},"revisionReason":{"type":"string","description":"Why the quote is revised; echoed in the reply, not stored","examples":["Customer asked for a lower installation fee"]},"validityEnd":{"type":"string","format":"date-time","description":"New end of validity, in the future (else the original's)","examples":["2026-11-01T00:00:00.000Z"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"revision":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"quoteNumber":{"type":"string","description":"The quote's number, unique in your organisation (a revision adds -r2, -r3 ...)"},"leadId":{"type":"string"},"accountId":{"type":"string"},"subscriptionId":{},"requestId":{},"dealId":{"description":"The deal the quote was made from, if any"},"ownerId":{"type":"string"},"type":{"type":"string","description":"quote (for a lead or account), request (for a service request) or subscription (for a running service)"},"subtotal":{"type":"string","description":"Sum of the lines before tax, a decimal in a string"},"total":{"type":"string","description":"Sum of the lines with tax, a decimal in a string"},"status":{"type":"string","description":"draft, sent, accepted, declined, expired or paid"},"paidAmount":{"type":"string","description":"What the customer has paid so far"},"balance":{"type":"string","description":"What is still to pay: total less paidAmount"},"paidAt":{},"validityStart":{"type":"string","format":"date-time"},"validityEnd":{"type":"string","format":"date-time","description":"The last moment the customer can accept the quote"},"sentAt":{},"acceptedAt":{},"declinedAt":{},"expiredAt":{},"notes":{"type":"string"},"revisionOf":{"type":"string","description":"The quote this one revises, if it is a revision"},"revisionNumber":{"type":"integer"},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array","description":"IDs of this quote in other systems (e.g. its Kazee Books estimate or Zoho quotation)"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"original":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"quoteNumber":{"type":"string"},"leadId":{"type":"string"},"accountId":{"type":"string"},"subscriptionId":{},"requestId":{},"dealId":{},"ownerId":{"type":"string"},"type":{"type":"string"},"subtotal":{"type":"string"},"total":{"type":"string"},"status":{"type":"string"},"paidAmount":{"type":"string"},"balance":{"type":"string"},"paidAt":{},"validityStart":{"type":"string","format":"date-time"},"validityEnd":{"type":"string","format":"date-time"},"sentAt":{},"acceptedAt":{},"declinedAt":{"type":"string","format":"date-time"},"expiredAt":{},"notes":{"type":"string"},"revisionOf":{},"revisionNumber":{"type":"integer"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"lineItems":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"quoteId":{"type":"string"},"productId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"quantity":{"type":"string"},"unitPrice":{"type":"string"},"discount":{"type":"string"},"taxAmount":{"type":"string"},"subtotal":{"type":"string"},"total":{"type":"string"},"sortOrder":{"type":"integer"},"metadata":{"type":"object","properties":{"kind":{"type":"string"},"treatment":{"type":"string"}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"description":"The quote that was revised, with its lines"},"revisionReason":{"type":"string","description":"The reason you sent, if any (not stored)"}}}}},"example":{"success":true,"data":{"revision":{"id":"38c9d0e1-2f3a-4b4c-8d5e-6f7a8b9c0d1e","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","quoteNumber":"QUOTE-00042-r2","leadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","subscriptionId":null,"requestId":null,"dealId":null,"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","type":"quote","subtotal":"6500.00","total":"7540.00","status":"draft","paidAmount":"0.00","balance":"7540.00","paidAt":null,"validityStart":"2026-10-01T08:30:00.000Z","validityEnd":"2026-11-01T00:00:00.000Z","sentAt":null,"acceptedAt":null,"declinedAt":null,"expiredAt":null,"notes":"Includes free router for 12 months","revisionOf":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","revisionNumber":2,"customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-10-15T14:05:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"},"original":{"id":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","quoteNumber":"QUOTE-00042","leadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","subscriptionId":null,"requestId":null,"dealId":null,"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","type":"quote","subtotal":"6500.00","total":"7540.00","status":"declined","paidAmount":"0.00","balance":"7540.00","paidAt":null,"validityStart":"2026-10-01T08:30:00.000Z","validityEnd":"2026-11-01T00:00:00.000Z","sentAt":null,"acceptedAt":null,"declinedAt":"2026-10-15T14:05:00.000Z","expiredAt":null,"notes":"Includes free router for 12 months","revisionOf":null,"revisionNumber":1,"customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","lineItems":[{"id":"16a7b8c9-0d1e-4f2a-8b3c-4d5e6f7a8b9c","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","quoteId":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","name":"Home Fibre 50 Mbps","description":"First month of service","quantity":"1.00","unitPrice":"4500.00","discount":"0.00","taxAmount":"720.00","subtotal":"4500.00","total":"5220.00","sortOrder":0,"metadata":{"kind":"first_month","treatment":"invoice"},"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-01T08:30:00.000Z"},{"id":"27b8c9d0-1e2f-4a3b-9c4d-5e6f7a8b9c0d","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","quoteId":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","productId":null,"name":"Installation","description":"Fibre installation and router setup","quantity":"1.00","unitPrice":"2000.00","discount":"0.00","taxAmount":"320.00","subtotal":"2000.00","total":"2320.00","sortOrder":1,"metadata":{"kind":"installation","treatment":"invoice"},"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-01T08:30:00.000Z"}]},"revisionReason":"Customer asked for a lower installation fee"}}}}},"400":{"description":"No quote ID, lineItems is not valid JSON, validityEnd is not in the future, the quote was raised in Zoho, or its deal's plan line follows a billing rule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No quote with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The quote belongs to a deal (reason deal_quote_revision), its status does not allow revisions, or that revision number already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.quotes.create"]}],"x-required-scopes":["crm.quotes.create"]}},"/accounts/merge":{"post":{"tags":["Accounts"],"summary":"Merge two accounts","description":"Merge a duplicate (secondary) account into the one to keep (primary): the secondary's contacts move to the primary, its labels are added, its billing and service contacts fill the primary's empty ones, and the secondary is deleted. Optionally keep the secondary's address or join the two descriptions. Subscriptions, quotes and tickets are not moved by this call.\n\n**Permission:** `crm.accounts.update` (Update accounts).","operationId":"mergeAccounts","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mergeData":{"type":"object","properties":{"keepSecondaryAddress":{"type":"boolean"},"combineDescriptions":{"type":"boolean"}},"description":"Options: keepSecondaryAddress (the primary takes the secondary's address), combineDescriptions (the secondary's description is added under the primary's). A JSON string also works","examples":[{"keepSecondaryAddress":false,"combineDescriptions":true}]},"primaryAccountId":{"type":"string","format":"uuid","description":"The account to keep","examples":["2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"]},"secondaryAccountId":{"type":"string","format":"uuid","description":"The duplicate to merge in and delete","examples":["3e2b1f4b-6c8d-4f5a-8d0e-4b5c6d7e8f9a"]},"userId":{"type":"string","format":"uuid","description":"ID of the person merging (for the history)","examples":["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]}},"required":["primaryAccountId","secondaryAccountId","userId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"mergedAccount":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the account is filed under one, else the account's own"},"accountNumber":{"type":"string","description":"Your organisation's account number, unique"},"status":{"type":"string","description":"active, inactive, suspended or terminated"},"description":{"type":"string"},"addressId":{"type":"string"},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string","description":"Credit limit, a decimal number in a string"},"currentBalance":{"type":"string","description":"Current balance, a decimal number in a string"},"originLeadId":{"type":"string","description":"The lead the account was converted from, if any"},"walletId":{},"billingContactId":{"type":"string","description":"The contact who gets the bills"},"serviceContactId":{"type":"string","description":"The contact for service matters"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"}}},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array","description":"IDs of this account in other systems, e.g. { source: zoho, externalId }"},"partyId":{"type":"string","description":"The person or company record in Kazee Contacts the account is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"mergeDetails":{"type":"object","properties":{"deletedAccountId":{"type":"string","description":"The secondary account, now deleted"},"deletedAccountNumber":{"type":"string"},"mergedContacts":{"type":"integer","description":"How many contact links moved"},"mergedAddress":{"type":"boolean"},"combinedDescriptions":{"type":"boolean"},"mergedTags":{"type":"boolean"},"billingContactPromoted":{"type":"boolean"},"serviceContactPromoted":{"type":"boolean"}}}}}}},"example":{"success":true,"message":"Merged account ACC-000124 into ACC-000123","data":{"mergedAccount":{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Baraka Traders Ltd","accountNumber":"ACC-000123","status":"active","description":"Hardware shop on Moi Avenue\n\n[Merged]: Second branch, Kenyatta Avenue","addressId":"f4e13f6b-8c0d-4f7a-9d2e-6b7c8d9e0f1a","onboardingDate":"2026-10-01T08:30:00.000Z","terminationDate":null,"creditLimit":"50000.00","currentBalance":"0.00","originLeadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","walletId":null,"billingContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","serviceContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notificationPreferences":{"email":true,"sms":true},"tags":["retail","wholesale"],"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Baraka Traders Ltd","cachePrimaryPhone":"+254700000101","cachePrimaryEmail":"info@baraka-traders.example","cachePartyRevision":3,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"},"mergeDetails":{"deletedAccountId":"3e2b1f4b-6c8d-4f5a-8d0e-4b5c6d7e8f9a","deletedAccountNumber":"ACC-000124","mergedContacts":2,"mergedAddress":false,"combinedDescriptions":true,"mergedTags":true,"billingContactPromoted":false,"serviceContactPromoted":false}}}}}},"400":{"description":"The two IDs are the same, or mergeData is not valid JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No account with one of those IDs in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The secondary account is terminated and the primary is not","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.accounts.update"]}],"x-required-scopes":["crm.accounts.update"]}},"/accounts/{accountId}/relationships":{"post":{"tags":["Accounts"],"summary":"Link or unlink a contact on an account","description":"Link a contact to an account (as admin) or unlink it. When adding, the contact can also become the account's billing or service contact; when removing, the account stops pointing at it as billing or service contact. Only contacts are supported here: subscriptions, quotes and the like are linked through their own calls.\n\n**Permission:** `crm.accounts.update` (Update accounts).","operationId":"manageAccountRelationships","parameters":[{"name":"accountId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The account's ID","example":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"entityId":{"type":"string","format":"uuid","description":"The contact's ID","examples":["4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"]},"entityType":{"type":"string","enum":["contact"],"description":"What to link; only contact is supported","examples":["contact"]},"operation":{"type":"string","enum":["add","remove"],"description":"Link (add) or unlink (remove) the contact","examples":["add"]},"setAsPrimary":{"type":"string","enum":["billing","service"],"description":"When adding: also make the contact the account's billing or service contact","examples":["billing"]},"userId":{"type":"string","format":"uuid","description":"ID of the person making the change (for the history)","examples":["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]}},"required":["entityId","entityType","operation","userId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the account is filed under one, else the account's own"},"accountNumber":{"type":"string","description":"Your organisation's account number, unique"},"status":{"type":"string","description":"active, inactive, suspended or terminated"},"description":{"type":"string"},"addressId":{"type":"string"},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string","description":"Credit limit, a decimal number in a string"},"currentBalance":{"type":"string","description":"Current balance, a decimal number in a string"},"originLeadId":{"type":"string","description":"The lead the account was converted from, if any"},"walletId":{},"billingContactId":{"type":"string","description":"The contact who gets the bills"},"serviceContactId":{"type":"string","description":"The contact for service matters"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"}}},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array","description":"IDs of this account in other systems, e.g. { source: zoho, externalId }"},"partyId":{"type":"string","description":"The person or company record in Kazee Contacts the account is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"contact added to account","data":{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Baraka Traders Ltd","accountNumber":"ACC-000123","status":"active","description":"Hardware shop on Moi Avenue","addressId":"f4e13f6b-8c0d-4f7a-9d2e-6b7c8d9e0f1a","onboardingDate":"2026-10-01T08:30:00.000Z","terminationDate":null,"creditLimit":"50000.00","currentBalance":"0.00","originLeadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","walletId":null,"billingContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","serviceContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notificationPreferences":{"email":true,"sms":true},"tags":["retail"],"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Baraka Traders Ltd","cachePrimaryPhone":"+254700000101","cachePrimaryEmail":"info@baraka-traders.example","cachePartyRevision":3,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}}}}},"400":{"description":"operation is not add or remove, or entityType is not contact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No account or contact with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.accounts.update"]}],"x-required-scopes":["crm.accounts.update"]}},"/accounts/convert-lead":{"post":{"tags":["Accounts"],"summary":"Convert a lead to an account","description":"Turn a qualified lead (status qualified or documents_received) into a new account: the account takes the lead's business name (or the person's name), owner and notes, and the lead is marked converted and points at the account. No contact is made or linked.\n\n**Permission:** `crm.accounts.create` (Create accounts).","operationId":"convertLeadToAccount","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountData":{"type":"object","description":"More account fields to set, e.g. { accountNumber, creditLimit, tags } (a JSON string also works)","examples":[{"accountNumber":"ACC-000123","tags":["retail"]}]},"accountName":{"type":"string","description":"Name for the account; the lead's business name, else the person's name, when left out","examples":["Baraka Traders Ltd"]},"convertedBy":{"type":"string","format":"uuid","description":"ID of the person converting; also the account's owner when the lead has none","examples":["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]},"leadId":{"type":"string","format":"uuid","description":"The lead to convert","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]}},"required":["convertedBy","leadId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"account":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the account is filed under one, else the account's own"},"accountNumber":{"type":"string","description":"Your organisation's account number, unique"},"status":{"type":"string","description":"active, inactive, suspended or terminated"},"description":{"type":"string"},"addressId":{"type":"string"},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string","description":"Credit limit, a decimal number in a string"},"currentBalance":{"type":"string","description":"Current balance, a decimal number in a string"},"originLeadId":{"type":"string","description":"The lead the account was converted from, if any"},"walletId":{},"billingContactId":{"description":"The contact who gets the bills"},"serviceContactId":{"description":"The contact for service matters"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"}}},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array","description":"IDs of this account in other systems, e.g. { source: zoho, externalId }"},"partyId":{"type":"string","description":"The person or company record in Kazee Contacts the account is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"convertedLead":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"partyId":{},"firstName":{"type":"string"},"lastName":{"type":"string"},"salutation":{"type":"string"},"businessName":{},"kraPin":{},"source":{"type":"string"},"entityType":{"type":"string"},"contactInfo":{"type":"object","properties":{"email":{"type":"string"},"phone":{"type":"string"}}},"ownerId":{"type":"string"},"assignedTo":{"type":"string"},"status":{"type":"string","description":"converted"},"documentCompletionStatus":{"type":"object","properties":{"required":{"type":"integer"},"uploaded":{"type":"integer"},"approved":{"type":"integer"}}},"notes":{"type":"string"},"relatedContactIds":{"type":"array"},"relatedAccountIds":{"type":"array"},"primaryContactId":{},"primaryAccountId":{},"convertedAccountId":{"type":"string","description":"The new account"},"convertedContactId":{},"customFields":{"type":"object","properties":{"town":{"type":"string"},"connection_type":{"type":"string"}}},"externalIds":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"externalId":{"type":"string"}}}},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}},"example":{"success":true,"message":"Lead successfully converted to account ACC-000123","data":{"account":{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Baraka Traders Ltd","accountNumber":"ACC-000123","status":"active","description":"Converted from lead: website - Asked about home fibre","addressId":"f4e13f6b-8c0d-4f7a-9d2e-6b7c8d9e0f1a","onboardingDate":"2026-10-01T08:30:00.000Z","terminationDate":null,"creditLimit":"50000.00","currentBalance":"0.00","originLeadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","walletId":null,"billingContactId":null,"serviceContactId":null,"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notificationPreferences":{"email":true,"sms":true},"tags":["retail"],"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Baraka Traders Ltd","cachePrimaryPhone":"+254700000101","cachePrimaryEmail":"info@baraka-traders.example","cachePartyRevision":3,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"},"convertedLead":{"id":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","partyId":null,"firstName":"Amina","lastName":"Wanjiru","salutation":"Ms.","businessName":null,"kraPin":null,"source":"website","entityType":"individual","contactInfo":{"email":"amina.wanjiru@example.com","phone":"+254700000001"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","assignedTo":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","status":"converted","documentCompletionStatus":{"required":2,"uploaded":1,"approved":0},"notes":"Asked about home fibre in Kilimani","relatedContactIds":[],"relatedAccountIds":[],"primaryContactId":null,"primaryAccountId":null,"convertedAccountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","convertedContactId":null,"customFields":{"town":"Nairobi","connection_type":"Home"},"externalIds":[{"source":"website","externalId":"web-10442"}],"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}}}}}},"400":{"description":"accountData is not a valid JSON object","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No lead with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The lead is not qualified or documents_received, it was already converted, or the account number is taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"description":"Kazee Contacts could not be reached to file the new account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.accounts.create"]}],"x-required-scopes":["crm.accounts.create"]}},"/accounts/{accountId}/site-deals":{"post":{"tags":["Deals"],"summary":"Start a sale for a customer's new site","description":"Start a sale for a new site of an existing customer: a deal on the customer's own account and contact, named after the site, at the qualification stage and priced at the plan's monthly price. No lead, account or Books customer is made. With dryRun it only answers the checks the form shows (contacts, services, Books record, what is owed, an open sale for the same site, another account with the same phone) and creates nothing.\n\n**Permission:** `crm.deals.create` (Create deals).","operationId":"createSiteDeal","parameters":[{"name":"accountId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The customer's account","example":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"confirmDuplicate":{"type":"boolean","description":"Start the sale even though the site already has an open one","examples":[false]},"connectionType":{"type":"string","enum":["Home","Business"],"description":"Kind of connection","examples":["Business"]},"contactId":{"type":"string","format":"uuid","description":"Which of the account's contacts the sale is with; the first one when left out","examples":["4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"]},"dryRun":{"type":"boolean","description":"Only answer the checks; create nothing","examples":[false]},"notes":{"type":"string","description":"Notes for the site survey","examples":["Gate code 4411"]},"servicePlanId":{"type":"string","format":"uuid","description":"The plan to sell; the deal's value is its monthly price","examples":["9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a"]},"siteContactName":{"type":"string","description":"The person to meet on site","examples":["Juma Hassan"]},"siteContactPhone":{"type":"string","description":"Phone of the person on site","examples":["+254700000103"]},"siteLocation":{"type":"string","description":"The site's Google Maps link or coordinates as lat,lng (needed unless dryRun)","examples":["-3.2195,40.1169"]},"siteName":{"type":"string","description":"Name of the new site, up to 60 characters (needed unless dryRun)","examples":["Kijani"]},"town":{"type":"string","description":"Town of the site","examples":["Malindi"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"deal":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{},"leadId":{},"accountId":{"type":"string"},"contactId":{"type":"string"},"value":{"type":"string","description":"Deal value, a decimal string in the deal's currency (two decimals)"},"currency":{"type":"string","description":"ISO 4217 code; the organisation's currency unless set"},"recurringValue":{},"stage":{"type":"string","description":"Stage key on the deals pipeline, e.g. qualification, discovery, proposal, negotiation, closed_won, closed_lost (organisations may use their own)"},"probability":{"type":"integer","description":"Chance of winning, 0 to 100"},"expectedCloseDate":{},"actualCloseDate":{},"ownerId":{"type":"string"},"teamId":{},"priority":{"type":"string","description":"low, medium or high"},"source":{"type":"string"},"dealType":{"type":"string"},"lostReason":{},"lostReasonDetails":{},"competitorId":{},"lastActivityDate":{},"nextActivityDate":{},"nextActivityType":{},"notes":{"type":"string"},"customFields":{"type":"object","properties":{"site_sale":{"type":"string"},"site_name":{"type":"string"},"site_location":{"type":"string"},"town":{"type":"string"},"connection_type":{"type":"string"},"service_plan":{"type":"string"},"contact_name":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"}},"description":"The site answers: site_sale, site_name, site_location, town, connection_type, service_plan, site_contact_name, site_contact_phone, survey_notes, contact_name, phone, email"},"externalIds":{"type":"array"},"createdBy":{"type":"string"},"updatedBy":{"type":"string"},"version":{"type":"integer","description":"Goes up by one on every change"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"description":"The new deal; not there with dryRun (nor is message)"},"check":{"type":"object","properties":{"account":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}},"contact":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"}}},"contacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"},"primary":{"type":"boolean"}}}},"services":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"pppoe":{"type":"string"},"status":{"type":"string"}}}},"duplicate":{"description":"An open sale for the same site, or null"},"books":{"type":"object","properties":{"state":{"type":"string","description":"linked, found_via_services, not_in_books, conflict or unavailable"},"contactId":{"type":"string"},"detail":{"type":"string"}}},"balance":{"type":"object","properties":{"byCurrency":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string"},"cents":{"type":"integer"}}}},"unpaidInvoices":{"type":"integer"},"oldestDue":{"type":"string"},"overdue":{"type":"boolean"}},"description":"What the customer owes in Kazee Books (amounts in cents), or null when it could not be read"},"twins":{"type":"array","description":"Other accounts with the same phone"},"warnings":{"type":"array","items":{"type":"string"},"description":"Things to tell the person before going on"}}}}},"message":{"type":"string"}}},"example":{"success":true,"data":{"deal":{"id":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Baraka Traders Ltd – Kijani","description":null,"leadId":null,"accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","value":"4500.00","currency":"KES","recurringValue":null,"stage":"qualification","probability":10,"expectedCloseDate":null,"actualCloseDate":null,"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","teamId":null,"priority":"medium","source":"Existing customer","dealType":"additional_site","lostReason":null,"lostReasonDetails":null,"competitorId":null,"lastActivityDate":null,"nextActivityDate":null,"nextActivityType":null,"notes":"","customFields":{"site_sale":"New site for existing customer","site_name":"Kijani","site_location":"-3.2195,40.1169","town":"Malindi","connection_type":"Business","service_plan":"9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a","contact_name":"Wanjiru Kamau","phone":"+254700000102","email":"wanjiru.kamau@example.com"},"externalIds":[],"createdBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","updatedBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","version":0,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-01T08:30:00.000Z"},"check":{"account":{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","name":"Baraka Traders Ltd"},"contact":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","name":"Wanjiru Kamau","phone":"+254700000102","email":"wanjiru.kamau@example.com"},"contacts":[{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","name":"Wanjiru Kamau","phone":"+254700000102","email":"wanjiru.kamau@example.com","primary":true}],"services":[{"id":"a7b8c9d0-e1f2-4a3b-8c4d-5e6f7a8b9c0d","name":"Baraka Traders Ltd – Moi Avenue","pppoe":"baraka-moi","status":"active"}],"duplicate":null,"books":{"state":"linked","contactId":"con_6a1d2c3b4e5f4a6b8c7d9e0f1a2b3c4d","detail":"Linked to the customer's Books record."},"balance":{"byCurrency":[{"currency":"KES","cents":348000}],"unpaidInvoices":1,"oldestDue":"2026-10-05","overdue":true},"twins":[],"warnings":["The customer owes KES 3,480.00 on 1 invoice, some of it overdue."]}},"message":"Site sale started"}}}},"400":{"description":"The site name, location, connection type or site phone is missing or not valid, the contact is not one of the account's, or the account has no contact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No account or plan with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The site already has an open sale (send confirmDuplicate to start another); error.meta.dealId is that deal","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.deals.create"]}],"x-required-scopes":["crm.deals.create"]}},"/calls/{callId}/transcript/retry":{"post":{"tags":["Calls"],"summary":"Queue a call's transcript again","description":"Queues a call's transcript for tonight's run, for one that failed or never ran. Needs both calls.update and calls.listen. A call already queued or being transcribed is left as it is and gets the same answer.\n\n**Permission:** `crm.calls.update` (Link calls) and `crm.calls.listen` (Listen to calls).","operationId":"retryCallTranscript","parameters":[{"name":"callId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The call's ID","example":"7d1e4b2a-9c3f-4a6e-8b1d-2f4a6c8e0b13"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"status":{"type":"string","description":"Always queued"},"queuedFor":{"type":"string","description":"Always tonight: transcripts run at midnight (Nairobi time), never during the day"}}}}},"example":{"success":true,"data":{"status":"queued","queuedFor":"tonight"}}}}},"400":{"description":"Transcripts are off for your organisation, or the call has no ready recording","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No call with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.calls.update","crm.calls.listen"]}],"x-required-scopes":["crm.calls.update","crm.calls.listen"],"x-scope-mode":"all"}},"/calls/{callId}/contact":{"post":{"tags":["Calls"],"summary":"Create a contact from a call","description":"Creates a contact for the number of a call (usually an unmatched one), then files every unfiled call from that number under it, this call included. Needs calls.update and contacts.create. The contact is owned by ownerId, or by you.\n\n**Permission:** `crm.calls.update` (Link calls) and `crm.contacts.create` (Create contacts).","operationId":"createContactFromCall","parameters":[{"name":"callId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The call's ID","example":"7d1e4b2a-9c3f-4a6e-8b1d-2f4a6c8e0b13"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","description":"The contact's email; must not belong to another contact","examples":["jane.wanjiku@example.com"]},"firstName":{"type":"string","description":"The contact's first name","examples":["Jane"]},"lastName":{"type":"string","description":"The contact's last name","examples":["Wanjiku"]},"ownerId":{"type":"string","format":"uuid","description":"Who owns the new contact; leave out to own it yourself","examples":["0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac"]}},"required":["firstName"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"contact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{}},"ownerId":{"type":"string"},"notes":{"type":"string"},"customFields":{"type":"object","properties":{"source":{"type":"string"}}},"externalIds":{"type":"object","properties":{}},"partyId":{"type":"string"},"cacheDisplayName":{"type":"string"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"description":"The new contact, with the call's number as its phone"},"call":{"type":"object","properties":{"id":{"type":"string"},"pbxCallId":{"type":"string"},"direction":{"type":"string"},"externalNumber":{"type":"string"},"externalName":{"type":"string"},"internalNumber":{"type":"string"},"extension":{"type":"string"},"agentName":{"type":"string"},"agentUserId":{"type":"string"},"centralParticipants":{"type":"array","items":{"type":"object","properties":{"centralUserId":{"type":"string"},"direction":{"type":"string"},"extensions":{"type":"array","items":{"type":"string"}},"answeredAt":{"type":"string","format":"date-time"}}}},"queue":{"type":"string"},"did":{"type":"string"},"startedAt":{"type":"string","format":"date-time"},"answeredAt":{"type":"string","format":"date-time"},"endedAt":{"type":"string","format":"date-time"},"durationSeconds":{"type":"integer"},"talkSeconds":{"type":"integer"},"outcome":{"type":"string"},"hangupCause":{"type":"string"},"hasRecording":{"type":"boolean"},"recordingStatus":{"type":"string"},"partyId":{"type":"string"},"contactId":{"type":"string"},"contactName":{"type":"string"},"accountId":{"type":"string"},"accountName":{"type":"string"},"leadId":{},"leadName":{},"linkedTo":{"type":"string"},"matchMethod":{"type":"string"},"notes":{"type":"string"},"disposition":{"type":"string"},"metadata":{"type":"object","properties":{}},"transcriptStatus":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"description":"The call, now filed under the new contact"},"callsLinked":{"type":"integer","description":"How many calls were filed under the new contact (at least 1)"}}}}},"example":{"success":true,"data":{"contact":{"id":"b15c8f6e-3a7d-4eac-8f5b-6d8e0a2c4f57","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Jane","lastName":"Wanjiku","email":"jane.wanjiku@example.com","phone":"+254700000123","addressId":null,"status":"active","preferences":{},"ownerId":"0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac","notes":"Created from a PBX call (+254 700 000123)","customFields":{"source":"pbx"},"externalIds":{},"partyId":"f59a2d0c-7e1b-4cea-8d9f-0b2c4e6a8d9b","cacheDisplayName":"Jane Wanjiku","cachePrimaryPhone":"+254700000123","cachePrimaryEmail":"jane.wanjiku@example.com","cachePartyRevision":1,"cacheUpdatedAt":"2026-10-20T09:06:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-20T09:06:00.000Z","updatedAt":"2026-10-20T09:06:00.000Z"},"call":{"id":"7d1e4b2a-9c3f-4a6e-8b1d-2f4a6c8e0b13","pbxCallId":"1729500000.4521","direction":"inbound","externalNumber":"+254700000123","externalName":"JANE WANJIKU","internalNumber":"+254200000100","extension":"204","agentName":"Peter Otieno","agentUserId":"0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac","centralParticipants":[{"centralUserId":"65f1a2b3c4d5e6f7a8b9c0d1","direction":"incoming","extensions":["204"],"answeredAt":"2026-10-20T09:00:12.000Z"}],"queue":"support","did":"+254200000100","startedAt":"2026-10-20T09:00:00.000Z","answeredAt":"2026-10-20T09:00:12.000Z","endedAt":"2026-10-20T09:04:30.000Z","durationSeconds":270,"talkSeconds":258,"outcome":"answered","hangupCause":"NORMAL_CLEARING","hasRecording":true,"recordingStatus":"ready","partyId":"f59a2d0c-7e1b-4cea-8d9f-0b2c4e6a8d9b","contactId":"b15c8f6e-3a7d-4eac-8f5b-6d8e0a2c4f57","contactName":"Jane Wanjiku","accountId":"c26d9a7f-4b8e-4fbd-9a6c-7e9f1b3d5a68","accountName":"Wanjiku Household","leadId":null,"leadName":null,"linkedTo":"contact","matchMethod":"manual","notes":"Router reset; customer will call back if the line drops again","disposition":"resolved","metadata":{},"transcriptStatus":"ready","createdAt":"2026-10-20T09:00:01.000Z","updatedAt":"2026-10-20T09:05:00.000Z"},"callsLinked":3}}}}},"400":{"description":"The call has no outside number, or firstName is empty","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No call with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A contact with that email already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.calls.update","crm.contacts.create"]}],"x-required-scopes":["crm.calls.update","crm.contacts.create"],"x-scope-mode":"all"}},"/accounts/{accountId}/contacts":{"post":{"tags":["Accounts"],"summary":"Link a contact to an account","description":"Link an existing contact to an account, with roles (admin when none). A primary contact also becomes the account's billing and service contact when it has none. Linking a contact that is already linked is refused (409); POST /contacts/{contactId}/link-account allows it.\n\n**Permission:** `crm.accounts.update` (Update accounts).","operationId":"linkExistingContact","parameters":[{"name":"accountId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The account's ID","example":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contactId":{"type":"string","format":"uuid","description":"The contact to link","examples":["4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"]},"isPrimary":{"type":"string","enum":["true","false"],"default":"false","description":"\"true\" to make the contact the account's primary contact (only the text \"true\" counts)","examples":["true"]},"linkedBy":{"type":"string","format":"uuid","description":"ID of the person making the link (for the history)","examples":["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]},"notes":{"type":"string","description":"A note on the link","examples":["Signs off on invoices"]},"preferences":{"type":"object","description":"The person's preferences for this account, e.g. { notifications, billing, technical } (a JSON string also works)"},"roles":{"type":"array","items":{"type":"string"},"description":"What the person does for the account, e.g. admin, billing, technical; admin when left out (a JSON array in a string also works)","examples":[["admin","billing"]]},"setBillingContact":{"type":"string","enum":["true","false"],"default":"false","description":"\"true\" to make the contact the account's billing contact"},"setServiceContact":{"type":"string","enum":["true","false"],"default":"false","description":"\"true\" to make the contact the account's service contact"}},"required":["contactId","linkedBy"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"relationship":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"roles":{"type":"array","items":{"type":"string"},"description":"What the person does for the account: admin, billing, technical, support, readonly or primary"},"isPrimary":{"type":"boolean","description":"True for the account's primary contact (one per account)"},"status":{"type":"string","description":"The link's status, usually active"},"preferences":{"type":"object","properties":{}},"notes":{"type":"string"},"source":{"type":"string","description":"crm when CRM made the link, contacts when it came from Kazee Contacts (another app linked the person)"},"createdBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"account":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the account is filed under one, else the account's own"},"accountNumber":{"type":"string","description":"Your organisation's account number, unique"},"status":{"type":"string","description":"active, inactive, suspended or terminated"},"description":{"type":"string"},"addressId":{"type":"string"},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string","description":"Credit limit, a decimal number in a string"},"currentBalance":{"type":"string","description":"Current balance, a decimal number in a string"},"originLeadId":{"type":"string","description":"The lead the account was converted from, if any"},"walletId":{},"billingContactId":{"type":"string","description":"The contact who gets the bills"},"serviceContactId":{"type":"string","description":"The contact for service matters"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"}}},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array","description":"IDs of this account in other systems, e.g. { source: zoho, externalId }"},"partyId":{"type":"string","description":"The person or company record in Kazee Contacts the account is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"contact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","description":"The email to show (the shared record's when filed under one)"},"phone":{"type":"string","description":"The phone to show (the shared record's when filed under one)"},"addressId":{},"status":{"type":"string","description":"active, invited, inactive or suspended"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}},"description":"Communication preferences: notifications, preferredContactMethod (email, phone or sms), timezone, language"},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array"},"partyId":{"type":"string","description":"The person record in Kazee Contacts the contact is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the contact is filed under one, else first and last name"},"fullName":{"type":"string","description":"Same as displayName"}}},"changes":{"type":"object","properties":{"relationshipCreated":{"type":"boolean"},"setBillingContact":{"type":"boolean"},"setServiceContact":{"type":"boolean"},"setPrimary":{"type":"boolean"}},"description":"What the call did: made the link, set the billing or service contact, made it primary"}}}}},"example":{"success":true,"message":"Contact Wanjiru Kamau linked to account Baraka Traders Ltd","data":{"relationship":{"id":"05f2406c-9d1e-4a8b-8e3f-7c8d9e0f1a2b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","roles":["admin","billing"],"isPrimary":true,"status":"active","preferences":{},"notes":"Linked during account creation","source":"crm","createdBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-01T08:30:00.000Z"},"account":{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Baraka Traders Ltd","accountNumber":"ACC-000123","status":"active","description":"Hardware shop on Moi Avenue","addressId":"f4e13f6b-8c0d-4f7a-9d2e-6b7c8d9e0f1a","onboardingDate":"2026-10-01T08:30:00.000Z","terminationDate":null,"creditLimit":"50000.00","currentBalance":"0.00","originLeadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","walletId":null,"billingContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","serviceContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notificationPreferences":{"email":true,"sms":true},"tags":["retail"],"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Baraka Traders Ltd","cachePrimaryPhone":"+254700000101","cachePrimaryEmail":"info@baraka-traders.example","cachePartyRevision":3,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"},"contact":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau"},"changes":{"relationshipCreated":true,"setBillingContact":true,"setServiceContact":true,"setPrimary":true}}}}}},"400":{"description":"roles or preferences is not valid JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No account or contact with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The contact is already linked to the account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.accounts.update"]}],"x-required-scopes":["crm.accounts.update"]}},"/accounts/{accountId}/manage-contacts":{"post":{"tags":["Accounts"],"summary":"Manage an account's contacts","description":"Change which contacts an account has, one operation per call: add, remove, update (roles, primary, status, preferences, notes of a link), setPrimary, or batchAdd / batchRemove for several contacts at once. Answers with the account and its contacts as they are afterwards.\n\n**Permission:** `crm.accounts.update` (Update accounts).","operationId":"manageAccountContacts","parameters":[{"name":"accountId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The account's ID","example":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contactId":{"type":"string","format":"uuid","description":"The contact, for add, remove, update and setPrimary","examples":["4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"]},"contactIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"The contacts, for batchAdd (linked as admin; already linked ones are skipped) and batchRemove (a JSON array in a string also works)","examples":[["4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","5a4d3b6d-8e0f-4b7c-8f2a-6d7e8f9a0b1c"]]},"isPrimary":{"type":"string","enum":["true","false"],"description":"add and update: \"true\" to make the contact primary. update makes it not primary when this is anything else or left out","examples":["false"]},"notes":{"type":"string","description":"add and update: a note on the link","examples":["Site manager"]},"operation":{"type":"string","enum":["add","remove","update","setPrimary","batchAdd","batchRemove"],"description":"What to do. add, remove, update and setPrimary take contactId; batchAdd and batchRemove take contactIds","examples":["add"]},"preferences":{"type":"object","description":"add and update: the person's preferences for this account (a JSON string also works)"},"roles":{"type":"array","items":{"type":"string"},"description":"add and update: the link's roles, e.g. admin, billing, technical. add uses admin when left out; update replaces the roles (none when left out)","examples":[["admin","billing"]]},"status":{"type":"string","description":"add and update: the link's status; active when left out","examples":["active"]}},"required":["operation"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"operation":{"type":"string"},"changes":{"type":"array","items":{"type":"string"},"description":"What happened, in words"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the account is filed under one, else the account's own"},"accountNumber":{"type":"string","description":"Your organisation's account number, unique"},"status":{"type":"string","description":"active, inactive, suspended or terminated"},"description":{"type":"string"},"addressId":{"type":"string"},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string","description":"Credit limit, a decimal number in a string"},"currentBalance":{"type":"string","description":"Current balance, a decimal number in a string"},"originLeadId":{"type":"string","description":"The lead the account was converted from, if any"},"walletId":{},"billingContactId":{"type":"string","description":"The contact who gets the bills"},"serviceContactId":{"type":"string","description":"The contact for service matters"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"}}},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array","description":"IDs of this account in other systems, e.g. { source: zoho, externalId }"},"partyId":{"type":"string","description":"The person or company record in Kazee Contacts the account is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"phone":{"type":"string","description":"The phone of the account's reach person (its primary contact first)"},"email":{"type":"string","description":"The email of the account's reach person (its primary contact first)"},"primaryContact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}}},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{"type":"string"},"cacheDisplayName":{"type":"string"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string"},"fullName":{"type":"string"}},"description":"The primary contact, or null"},"contacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"roles":{"type":"array","items":{"type":"string"},"description":"What the person does for the account: admin, billing, technical, support, readonly or primary"},"isPrimary":{"type":"boolean","description":"True for the account's primary contact (one per account)"},"status":{"type":"string","description":"The link's status, usually active"},"preferences":{"type":"object","properties":{}},"notes":{"type":"string"},"source":{"type":"string","description":"crm when CRM made the link, contacts when it came from Kazee Contacts (another app linked the person)"},"createdBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"contact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}}},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{"type":"string"},"cacheDisplayName":{"type":"string"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string"},"fullName":{"type":"string"}},"description":"The linked contact"}}}}}}}},"example":{"success":true,"message":"Account contacts managed successfully","operation":"add","changes":["Added contact Wanjiru Kamau"],"data":{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Baraka Traders Ltd","accountNumber":"ACC-000123","status":"active","description":"Hardware shop on Moi Avenue","addressId":"f4e13f6b-8c0d-4f7a-9d2e-6b7c8d9e0f1a","onboardingDate":"2026-10-01T08:30:00.000Z","terminationDate":null,"creditLimit":"50000.00","currentBalance":"0.00","originLeadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","walletId":null,"billingContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","serviceContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notificationPreferences":{"email":true,"sms":true},"tags":["retail"],"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Baraka Traders Ltd","cachePrimaryPhone":"+254700000101","cachePrimaryEmail":"info@baraka-traders.example","cachePartyRevision":3,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","phone":"+254700000102","email":"wanjiru.kamau@example.com","primaryContact":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau"},"contacts":[{"id":"05f2406c-9d1e-4a8b-8e3f-7c8d9e0f1a2b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","roles":["admin","billing"],"isPrimary":true,"status":"active","preferences":{},"notes":"Linked during account creation","source":"crm","createdBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-01T08:30:00.000Z","contact":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau"}}]}}}}},"400":{"description":"operation is unknown, contactId or contactIds is missing for it, or a JSON field is not valid JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No account with that ID, a contact to add, update or make primary does not exist, or (setPrimary) the contact is not linked to the account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"add: the contact is already linked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.accounts.update"]}],"x-required-scopes":["crm.accounts.update"]}},"/contacts/{contactId}/link-account":{"post":{"tags":["Contacts"],"summary":"Link a contact to an account, from a contact or ticket","description":"Link a contact to an account. If they are already linked, the link is saved again with the roles and primary flag sent (no 409, unlike POST /accounts/{accountId}/contacts). Can also put the account on a ticket that has none, for a ticket raised by a contact that had no account.\n\n**Permission:** `crm.contacts.update` (Update contacts).","operationId":"linkContactToAccount","parameters":[{"name":"contactId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The contact's ID","example":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid","description":"The account to link to","examples":["2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"]},"isPrimary":{"type":"boolean","default":"false","description":"Make the contact the account's primary contact; false (the default) also takes primary away on an existing link","examples":[false]},"roles":{"type":"array","items":{"type":"string"},"description":"The link's roles, e.g. admin, billing; none when left out, also on an existing link (a JSON array in a string also works)","examples":[["admin"]]},"ticketId":{"type":"string","format":"uuid","description":"A ticket to put the account on, when the ticket has no account yet"},"userId":{"type":"string","format":"uuid","description":"ID of the person making the link (for the history)","examples":["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]}},"required":["accountId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"relationship":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"roles":{"type":"array","items":{"type":"string"},"description":"What the person does for the account: admin, billing, technical, support, readonly or primary"},"isPrimary":{"type":"boolean","description":"True for the account's primary contact (one per account)"},"status":{"type":"string","description":"The link's status, usually active"},"preferences":{"type":"object","properties":{}},"notes":{},"source":{"type":"string","description":"crm when CRM made the link, contacts when it came from Kazee Contacts (another app linked the person)"},"createdBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"account":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"accountNumber":{"type":"string"},"status":{"type":"string"},"description":{"type":"string"},"addressId":{"type":"string"},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string"},"currentBalance":{"type":"string"},"originLeadId":{"type":"string"},"walletId":{},"billingContactId":{"type":"string"},"serviceContactId":{"type":"string"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"}}},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{"type":"string"},"cacheDisplayName":{"type":"string"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"description":"The account, as stored"},"contact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}}},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{"type":"string"},"cacheDisplayName":{"type":"string"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"description":"The contact, as stored"},"ticket":{"description":"Only with ticketId: the ticket (with the account set when it had none), or null when there is no such ticket you can see"}}}}},"example":{"success":true,"message":"Contact Wanjiru Kamau linked to account Baraka Traders Ltd","data":{"relationship":{"id":"05f2406c-9d1e-4a8b-8e3f-7c8d9e0f1a2b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","roles":["admin"],"isPrimary":false,"status":"active","preferences":{},"notes":null,"source":"crm","createdBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-01T08:30:00.000Z"},"account":{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Baraka Traders Ltd","accountNumber":"ACC-000123","status":"active","description":"Hardware shop on Moi Avenue","addressId":"f4e13f6b-8c0d-4f7a-9d2e-6b7c8d9e0f1a","onboardingDate":"2026-10-01T08:30:00.000Z","terminationDate":null,"creditLimit":"50000.00","currentBalance":"0.00","originLeadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","walletId":null,"billingContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","serviceContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notificationPreferences":{"email":true,"sms":true},"tags":["retail"],"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Baraka Traders Ltd","cachePrimaryPhone":"+254700000101","cachePrimaryEmail":"info@baraka-traders.example","cachePartyRevision":3,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"},"contact":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"},"ticket":null}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No account or contact with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.contacts.update"]}],"x-required-scopes":["crm.contacts.update"]}},"/subscriptions/create":{"post":{"tags":["Subscriptions"],"summary":"Create a subscription","description":"Starts a new subscription (service line) for an account on a service plan, in pending installation. It also queues a welcome reminder (emailed to the account's primary contact when they have an email address) and runs the organisation's \"record created\" automations. A missing account or plan still answers 200, with success false and error.code \"SUBSCRIPTION_CREATE_ERROR\".\n\n**Permission:** `crm.subscriptions.create` (Create subscriptions).","operationId":"createSubscription","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid","description":"The account the line is for","examples":["a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c"]},"billingCycle":{"type":"string","enum":["monthly","quarterly","annually"],"description":"How often the line is billed","examples":["monthly"]},"customFields":{"type":"object","description":"Accepted but not saved yet: set custom fields afterwards","examples":[{"router_model":"HG8145V5"}]},"servicePlanId":{"type":"string","format":"uuid","description":"The service plan the line is on","examples":["5e8b1d3f-7a2c-4c6e-9b4d-8f0a1c3e5d7b"]}},"required":["accountId","billingCycle","servicePlanId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"pppoe":{"type":"string"},"accountId":{"type":"string"},"servicePlanId":{"type":"string"},"status":{"type":"string","description":"A new line starts as pending_installation"},"startDate":{},"billingCycle":{"type":"string"},"lastBilledAt":{},"nextBillingAt":{},"billDate":{"type":"string","format":"date-time"},"renewalDate":{"type":"string","format":"date-time"},"billLeadDays":{"type":"integer"},"disconnectionDate":{},"terminationDate":{},"lastPaymentDate":{},"ownerId":{"type":"string"},"password":{"description":"Masked as { \"secret\": true, \"set\": true } when a password is stored"},"customFields":{"type":"object","properties":{"sm_password":{"type":"object","properties":{"secret":{"type":"boolean"},"set":{"type":"boolean"}}},"router_model":{"type":"string"}}},"externalIds":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"externalId":{"type":"string"}}}},"version":{"type":"integer"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"account":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"accountNumber":{"type":"string"},"status":{"type":"string"},"description":{},"addressId":{},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string"},"currentBalance":{"type":"string"},"originLeadId":{},"walletId":{},"billingContactId":{"type":"string"},"serviceContactId":{"type":"string"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"}}},"tags":{"type":"array"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{},"cacheDisplayName":{},"cachePrimaryPhone":{},"cachePrimaryEmail":{},"cachePartyRevision":{},"cacheUpdatedAt":{},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"contactRelationships":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"roles":{"type":"array","items":{"type":"string"}},"isPrimary":{"type":"boolean"},"status":{"type":"string"},"preferences":{"type":"object","properties":{}},"notes":{},"source":{"type":"string"},"createdBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"contact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{}},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{},"cacheDisplayName":{},"cachePrimaryPhone":{},"cachePrimaryEmail":{},"cachePartyRevision":{},"cacheUpdatedAt":{},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}},"servicePlan":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"limitStr":{"type":"string"},"burstMaxDown":{},"burstMaxUp":{},"burstThreshDown":{},"burstThreshUp":{},"burstTimeDown":{},"burstTimeUp":{},"connectionType":{"type":"array","items":{"type":"string"}},"installationFee":{"type":"string"},"maxLimitDown":{"type":"integer"},"maxLimitUp":{"type":"integer"},"rateLimit":{"type":"string"},"serviceTypeId":{"type":"string"},"statusLevelId":{},"status":{"type":"string"},"productId":{"type":"string"},"booksItemId":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"product":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"price":{"type":"string"},"sku":{"type":"string"},"status":{"type":"string"},"description":{"type":"string"},"taxId":{"type":"string"},"unit":{"type":"string"},"recurringPeriod":{"type":"string"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"serviceType":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}}}}},"radiusStatus":{"type":"string"},"validNextStatuses":{"type":"array","items":{"type":"string"}},"previousStatuses":{"type":"array"},"pipeline":{"type":"string"},"currentStatus":{"type":"string"}}},"message":{"type":"string"}}},"example":{"success":true,"data":{"id":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","name":"Home fibre 20 Mbps","pppoe":"jane.wanjiku.home","accountId":"a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c","servicePlanId":"5e8b1d3f-7a2c-4c6e-9b4d-8f0a1c3e5d7b","status":"pending_installation","startDate":null,"billingCycle":"monthly","lastBilledAt":null,"nextBillingAt":null,"billDate":"2026-10-22T00:00:00.000Z","renewalDate":"2026-11-01T00:00:00.000Z","billLeadDays":10,"disconnectionDate":null,"terminationDate":null,"lastPaymentDate":null,"ownerId":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d","password":null,"customFields":{"sm_password":{"secret":true,"set":true},"router_model":"HG8145V5"},"externalIds":[{"source":"zoho","externalId":"4815000000123457"}],"version":0,"deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z","account":{"id":"a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","name":"Jane Wanjiku","accountNumber":"ACC-00042","status":"active","description":null,"addressId":null,"onboardingDate":"2026-09-01T08:00:00.000Z","terminationDate":null,"creditLimit":"0.00","currentBalance":"0.00","originLeadId":null,"walletId":null,"billingContactId":"e0b6a2d5-7f4c-4dbe-8a91-5f8c1d4e7a0b","serviceContactId":"e0b6a2d5-7f4c-4dbe-8a91-5f8c1d4e7a0b","ownerId":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d","notificationPreferences":{"email":true,"sms":true},"tags":[],"customFields":{},"externalIds":[],"partyId":null,"cacheDisplayName":null,"cachePrimaryPhone":null,"cachePrimaryEmail":null,"cachePartyRevision":null,"cacheUpdatedAt":null,"cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z","contactRelationships":[{"id":"f1c7b3e6-8a5d-4ecf-9ba2-6a9d2e5f8b1c","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","accountId":"a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c","contactId":"e0b6a2d5-7f4c-4dbe-8a91-5f8c1d4e7a0b","roles":["billing","technical"],"isPrimary":true,"status":"active","preferences":{},"notes":null,"source":"crm","createdBy":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d","createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z","contact":{"id":"e0b6a2d5-7f4c-4dbe-8a91-5f8c1d4e7a0b","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","firstName":"Jane","lastName":"Wanjiku","email":"jane.wanjiku@example.com","phone":"+254700000001","addressId":null,"status":"active","preferences":{},"ownerId":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d","notes":null,"customFields":{},"externalIds":[],"partyId":null,"cacheDisplayName":null,"cachePrimaryPhone":null,"cachePrimaryEmail":null,"cachePartyRevision":null,"cacheUpdatedAt":null,"cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}]},"servicePlan":{"id":"5e8b1d3f-7a2c-4c6e-9b4d-8f0a1c3e5d7b","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","name":"Fibre 20 Mbps","limitStr":"20M/20M","burstMaxDown":null,"burstMaxUp":null,"burstThreshDown":null,"burstThreshUp":null,"burstTimeDown":null,"burstTimeUp":null,"connectionType":["fibre"],"installationFee":"2000.00","maxLimitDown":20,"maxLimitUp":20,"rateLimit":"20M/20M","serviceTypeId":"c8f4e0b3-5d2a-4b9c-8e7f-3d6a9b2c5e8f","statusLevelId":null,"status":"active","productId":"b7e3d9a2-4c1f-4a8b-9d6e-2c5f8a1b4d7e","booksItemId":"itm_01JABCDEF0123456789XYZ","deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z","product":{"id":"b7e3d9a2-4c1f-4a8b-9d6e-2c5f8a1b4d7e","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","name":"Fibre 20 Mbps","type":"service","price":"3500.00","sku":"FIB-20","status":"active","description":"Home fibre internet, 20 Mbps down and up","taxId":"tax_vat16","unit":"month","recurringPeriod":"monthly","customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"},"serviceType":{"name":"Home fibre","description":"Fibre internet for homes"}},"radiusStatus":"unknown","validNextStatuses":["active","terminated"],"previousStatuses":[],"pipeline":"subscription","currentStatus":"pending_installation"},"message":"Subscription scaffold created successfully"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.subscriptions.create"]}],"x-required-scopes":["crm.subscriptions.create"]}},"/subscriptions/{subscriptionId}/manage":{"post":{"tags":["Subscriptions"],"summary":"Change a subscription's status","description":"Moves a subscription along its pipeline: activate or reactivate (to active, from pending installation or disconnected; both reset its billing dates), disconnect (from active) or terminate (from any status but terminated). Where the organisation allows suspending (billing.allowSuspend), suspend moves an active line to suspended; otherwise suspend fails. A suspended line can always be reactivated, disconnected or terminated. Any other move is refused. pending_installation is accepted as a word but no line can move there today, so it always fails. Runs the organisation's \"record updated\" automations, and its \"line cut\" and \"line reconnected\" ones, and is written to the audit log. Every failure (no userId, no such line, a move not allowed, no reason) still answers 200, with success false and an error.code such as \"INVALID_TRANSITION\" or \"REASON_REQUIRED\".\n\n**Permission:** `crm.subscriptions.update` (Update subscriptions).","operationId":"manageSubscription","parameters":[{"name":"subscriptionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The subscription's ID","example":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","description":"Why, 2 to 200 characters. Needed for suspend, disconnect and terminate","examples":["Unpaid for 30 days"]},"subscriptionAction":{"type":"string","enum":["activate","suspend","disconnect","reactivate","terminate","pending_installation"],"description":"What to do. suspend works only where the organisation allows it (billing.allowSuspend); pending_installation always fails today (no line can move there)","examples":["disconnect"]},"userId":{"type":"string","format":"uuid","description":"Your own user ID, recorded as who made the change. Not filled in from your sign-in: without it the call fails","examples":["0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d"]}},"required":["subscriptionAction"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"pppoe":{"type":"string"},"accountId":{"type":"string"},"servicePlanId":{"type":"string"},"status":{"type":"string","description":"The line's status after the change"},"startDate":{"type":"string","format":"date-time"},"billingCycle":{"type":"string"},"lastBilledAt":{"type":"string","format":"date-time"},"nextBillingAt":{"type":"string","format":"date-time"},"billDate":{"type":"string","format":"date-time"},"renewalDate":{"type":"string","format":"date-time"},"billLeadDays":{"type":"integer"},"disconnectionDate":{"type":"string","format":"date-time"},"terminationDate":{},"lastPaymentDate":{"type":"string","format":"date-time"},"ownerId":{"type":"string"},"password":{"type":"object","properties":{"secret":{"type":"boolean"},"set":{"type":"boolean"}}},"customFields":{"type":"object","properties":{"sm_password":{"type":"object","properties":{"secret":{"type":"boolean"},"set":{"type":"boolean"}}},"router_model":{"type":"string"}}},"externalIds":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"externalId":{"type":"string"}}}},"version":{"type":"integer"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"account":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"accountNumber":{"type":"string"},"status":{"type":"string"},"description":{},"addressId":{},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string"},"currentBalance":{"type":"string"},"originLeadId":{},"walletId":{},"billingContactId":{"type":"string"},"serviceContactId":{"type":"string"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"}}},"tags":{"type":"array"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{},"cacheDisplayName":{},"cachePrimaryPhone":{},"cachePrimaryEmail":{},"cachePartyRevision":{},"cacheUpdatedAt":{},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"contactRelationships":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"roles":{"type":"array","items":{"type":"string"}},"isPrimary":{"type":"boolean"},"status":{"type":"string"},"preferences":{"type":"object","properties":{}},"notes":{},"source":{"type":"string"},"createdBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"contact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{}},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{},"cacheDisplayName":{},"cachePrimaryPhone":{},"cachePrimaryEmail":{},"cachePartyRevision":{},"cacheUpdatedAt":{},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}},"servicePlan":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"limitStr":{"type":"string"},"burstMaxDown":{},"burstMaxUp":{},"burstThreshDown":{},"burstThreshUp":{},"burstTimeDown":{},"burstTimeUp":{},"connectionType":{"type":"array","items":{"type":"string"}},"installationFee":{"type":"string"},"maxLimitDown":{"type":"integer"},"maxLimitUp":{"type":"integer"},"rateLimit":{"type":"string"},"serviceTypeId":{"type":"string"},"statusLevelId":{},"status":{"type":"string"},"productId":{"type":"string"},"booksItemId":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"product":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"price":{"type":"string"},"sku":{"type":"string"},"status":{"type":"string"},"description":{"type":"string"},"taxId":{"type":"string"},"unit":{"type":"string"},"recurringPeriod":{"type":"string"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"serviceType":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}}}}},"radiusStatus":{"type":"string"},"validNextStatuses":{"type":"array","items":{"type":"string"}},"previousStatuses":{"type":"array","items":{"type":"string"}},"pipeline":{"type":"string"},"currentStatus":{"type":"string"}}},"message":{"type":"string","description":"e.g. \"Subscription disconnected successfully. Reason: Unpaid for 30 days\""}}},"example":{"success":true,"data":{"id":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","name":"Home fibre 20 Mbps","pppoe":"jane.wanjiku.home","accountId":"a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c","servicePlanId":"5e8b1d3f-7a2c-4c6e-9b4d-8f0a1c3e5d7b","status":"disconnected","startDate":"2026-09-01T00:00:00.000Z","billingCycle":"monthly","lastBilledAt":"2026-10-01T00:00:00.000Z","nextBillingAt":"2026-11-01T00:00:00.000Z","billDate":"2026-10-22T00:00:00.000Z","renewalDate":"2026-11-01T00:00:00.000Z","billLeadDays":10,"disconnectionDate":"2026-10-21T08:30:00.000Z","terminationDate":null,"lastPaymentDate":"2026-10-02T10:15:00.000Z","ownerId":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d","password":{"secret":true,"set":true},"customFields":{"sm_password":{"secret":true,"set":true},"router_model":"HG8145V5"},"externalIds":[{"source":"zoho","externalId":"4815000000123457"}],"version":3,"deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z","account":{"id":"a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","name":"Jane Wanjiku","accountNumber":"ACC-00042","status":"active","description":null,"addressId":null,"onboardingDate":"2026-09-01T08:00:00.000Z","terminationDate":null,"creditLimit":"0.00","currentBalance":"0.00","originLeadId":null,"walletId":null,"billingContactId":"e0b6a2d5-7f4c-4dbe-8a91-5f8c1d4e7a0b","serviceContactId":"e0b6a2d5-7f4c-4dbe-8a91-5f8c1d4e7a0b","ownerId":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d","notificationPreferences":{"email":true,"sms":true},"tags":[],"customFields":{},"externalIds":[],"partyId":null,"cacheDisplayName":null,"cachePrimaryPhone":null,"cachePrimaryEmail":null,"cachePartyRevision":null,"cacheUpdatedAt":null,"cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z","contactRelationships":[{"id":"f1c7b3e6-8a5d-4ecf-9ba2-6a9d2e5f8b1c","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","accountId":"a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c","contactId":"e0b6a2d5-7f4c-4dbe-8a91-5f8c1d4e7a0b","roles":["billing","technical"],"isPrimary":true,"status":"active","preferences":{},"notes":null,"source":"crm","createdBy":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d","createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z","contact":{"id":"e0b6a2d5-7f4c-4dbe-8a91-5f8c1d4e7a0b","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","firstName":"Jane","lastName":"Wanjiku","email":"jane.wanjiku@example.com","phone":"+254700000001","addressId":null,"status":"active","preferences":{},"ownerId":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d","notes":null,"customFields":{},"externalIds":[],"partyId":null,"cacheDisplayName":null,"cachePrimaryPhone":null,"cachePrimaryEmail":null,"cachePartyRevision":null,"cacheUpdatedAt":null,"cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}]},"servicePlan":{"id":"5e8b1d3f-7a2c-4c6e-9b4d-8f0a1c3e5d7b","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","name":"Fibre 20 Mbps","limitStr":"20M/20M","burstMaxDown":null,"burstMaxUp":null,"burstThreshDown":null,"burstThreshUp":null,"burstTimeDown":null,"burstTimeUp":null,"connectionType":["fibre"],"installationFee":"2000.00","maxLimitDown":20,"maxLimitUp":20,"rateLimit":"20M/20M","serviceTypeId":"c8f4e0b3-5d2a-4b9c-8e7f-3d6a9b2c5e8f","statusLevelId":null,"status":"active","productId":"b7e3d9a2-4c1f-4a8b-9d6e-2c5f8a1b4d7e","booksItemId":"itm_01JABCDEF0123456789XYZ","deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z","product":{"id":"b7e3d9a2-4c1f-4a8b-9d6e-2c5f8a1b4d7e","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","name":"Fibre 20 Mbps","type":"service","price":"3500.00","sku":"FIB-20","status":"active","description":"Home fibre internet, 20 Mbps down and up","taxId":"tax_vat16","unit":"month","recurringPeriod":"monthly","customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"},"serviceType":{"name":"Home fibre","description":"Fibre internet for homes"}},"radiusStatus":"unknown","validNextStatuses":["active","terminated"],"previousStatuses":["pending_installation","active"],"pipeline":"subscription","currentStatus":"disconnected"},"message":"Subscription disconnected successfully. Reason: Unpaid for 30 days"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.subscriptions.update"]}],"x-required-scopes":["crm.subscriptions.update"]}},"/subscriptions/network-commands/{commandId}/retry":{"post":{"tags":["Subscriptions"],"summary":"Send a failed network command again","description":"Starts over a disconnection or reconnection the network did not take, calling only the targets that have not said yes. The line's status does not change.\n\n**Permission:** `crm.subscriptions.update` (Update subscriptions).","operationId":"retryLineNetworkCommand","parameters":[{"name":"commandId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The command to send again","example":"0a9f3c1e-6b7d-4e2f-8a1b-2c3d4e5f6a7b"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"command":{"type":"object","properties":{"id":{"type":"string"},"subscriptionId":{"type":"string"},"lineName":{"type":"string"},"action":{"type":"string"},"mode":{"type":"string"},"state":{"type":"string"},"networkApplied":{"type":"boolean"},"source":{"type":"string"},"requestId":{"type":"string"},"reason":{"type":"string"},"attempts":{"type":"integer"},"nextAttemptAt":{},"lastError":{},"appliedAt":{},"requests":{"type":"array","items":{"type":"object","properties":{"target":{"type":"string"},"decisive":{"type":"boolean"},"method":{"type":"string"},"url":{"type":"string"},"headers":{"type":"object","properties":{"Content-Type":{"type":"string"}}},"body":{"type":"object","properties":{"name":{"type":"string"},"api_key":{"type":"string"}}}}}},"results":{"type":"object","properties":{"core":{"type":"object","properties":{"ok":{"type":"boolean"},"status":{"type":"integer"},"answer":{"type":"string"},"error":{"type":"string"},"at":{"type":"string","format":"date-time"},"durationMs":{"type":"integer"}}}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"command":{"id":"0a9f3c1e-6b7d-4e2f-8a1b-2c3d4e5f6a7b","subscriptionId":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","lineName":"line0001234","action":"disconnect","mode":"live","state":"pending","networkApplied":false,"source":"approve_list","requestId":"1b2c3d4e-5f6a-4b7c-8d9e-0f1a2b3c4d5e","reason":"Non-payment","attempts":0,"nextAttemptAt":null,"lastError":null,"appliedAt":null,"requests":[{"target":"core","decisive":true,"method":"POST","url":"https://network.example.com/api/line/off","headers":{"Content-Type":"application/json"},"body":{"name":"line0001234","api_key":"***"}}],"results":{"core":{"ok":false,"status":503,"answer":"unavailable","error":"HTTP 503: unavailable","at":"2026-11-04T17:00:02.000Z","durationMs":412}},"createdAt":"2026-11-04T09:00:00.000Z","updatedAt":"2026-11-04T17:00:02.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No such command","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The command is still trying, was only a shadow record, is a plan change waiting for the line to come back, or a newer one for the line has replaced it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.subscriptions.update"]}],"x-required-scopes":["crm.subscriptions.update"]}},"/subscriptions/provision-commands/{commandId}/retry":{"post":{"tags":["Subscriptions"],"summary":"Send a failed new line again","description":"Starts over a new line the network did not take, calling only the targets that have not said yes. Only a command that has stopped trying. The worker makes the calls, so the answer says pending.\n\n**Permission:** `crm.subscriptions.update` (Update subscriptions).","operationId":"retryLineProvisionCommand","parameters":[{"name":"commandId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The command to send again","example":"3c7d9e1f-2a4b-4c6d-8e0f-1a2b3c4d5e6f"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"command":{"type":"object","properties":{"id":{"type":"string"},"subscriptionId":{"type":"string"},"dealId":{"type":"string"},"lineName":{"type":"string"},"mode":{"type":"string"},"state":{"type":"string"},"networkApplied":{"type":"boolean"},"source":{"type":"string"},"attempts":{"type":"integer"},"nextAttemptAt":{},"lastError":{},"appliedAt":{},"requests":{"type":"array","items":{"type":"object","properties":{"target":{"type":"string"},"decisive":{"type":"boolean"},"method":{"type":"string"},"url":{"type":"string"},"headers":{"type":"object","properties":{"Content-Type":{"type":"string"}}},"body":{"type":"object","properties":{"name":{"type":"string"},"pw":{"type":"string"},"plan":{"type":"string"},"api_key":{"type":"string"}}}}}},"results":{"type":"object","properties":{"core":{"type":"object","properties":{"ok":{"type":"boolean"},"status":{"type":"integer"},"answer":{"type":"string"},"error":{"type":"string"},"at":{"type":"string","format":"date-time"},"durationMs":{"type":"integer"}}}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"command":{"id":"3c7d9e1f-2a4b-4c6d-8e0f-1a2b3c4d5e6f","subscriptionId":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","dealId":"5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9","lineName":"line0001234","mode":"live","state":"pending","networkApplied":false,"source":"automation","attempts":0,"nextAttemptAt":null,"lastError":null,"appliedAt":null,"requests":[{"target":"core","decisive":true,"method":"POST","url":"https://network.example.com/api/line","headers":{"Content-Type":"application/json"},"body":{"name":"line0001234","pw":"***","plan":"Home 10","api_key":"***"}}],"results":{"core":{"ok":false,"status":503,"answer":"unavailable","error":"HTTP 503: unavailable","at":"2026-11-04T17:00:02.000Z","durationMs":412}},"createdAt":"2026-11-04T09:00:00.000Z","updatedAt":"2026-11-04T17:00:02.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No such command","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The command is still trying, already reached the network, was cancelled, was only a shadow record, or a newer one for the line has replaced it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.subscriptions.update"]}],"x-required-scopes":["crm.subscriptions.update"]}},"/subscriptions/{subscriptionId}/reveal":{"post":{"tags":["Subscriptions"],"summary":"Reveal a subscription password","description":"Answers one of a line's passwords in full: the PPPoE password or a custom field named like a password (e.g. sm_password). Every other call answers them masked. Each reveal is recorded first on the line's Activity timeline (who, which field, when; never the value), and the value is only answered once that record is written.\n\n**Permission:** `crm.subscriptions.reveal` (Reveal line passwords).","operationId":"revealSubscriptionSecret","parameters":[{"name":"subscriptionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The subscription's ID","example":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"field":{"type":"string","description":"Which password: \"password\" for the PPPoE password, or the key of a custom field named like one, e.g. sm_password","examples":["password"]}},"required":["field"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string","description":"Password for the PPPoE password, else the custom field's label"},"value":{"type":"string"},"revealedAt":{"type":"string","format":"date-time","description":"When the reveal was recorded on the line's Activity timeline"}}}}},"example":{"success":true,"data":{"field":"password","label":"Password","value":"s3cret-Example-42","revealedAt":"2026-10-21T08:30:00.000Z"}}}}},"400":{"description":"field is not \"password\" or a custom field named like a password","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No subscription with that ID, or the line holds nothing in that field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.subscriptions.reveal"]}],"x-required-scopes":["crm.subscriptions.reveal"]}},"/fields/reveal":{"post":{"tags":["Custom fields & modules"],"summary":"Reveal a hidden field","description":"Answers one hidden field of one record in full (Show). Every other answer carries it masked. Allowed for people on the field's who-can-see list and custom fields admins (on subscriptions, also holders of the reveal permission). An assistant acting for its owner is refused (403, askFirst). You need read permission on the record type, and each reveal is recorded on the record's timeline, with who and when, before the value is answered. Revealing a system field also answers data.origin set to native.\n\n**Permission:** `crm.leads.read` (View leads) or `crm.deals.read` (View deals) or `crm.accounts.read` (View accounts) or `crm.contacts.read` (View contacts) or `crm.products.read` (View products) or `crm.quotes.read` (View quotes) or `crm.requests.read` (View requests) or `crm.subscriptions.read` (View subscriptions) or `crm.tickets.read` (View tickets).","operationId":"revealField","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"field":{"type":"string","description":"The hidden custom field's key, or a system field's name with origin native. On subscriptions, password reveals the line's password","examples":["wifi_password"]},"moduleType":{"type":"string","enum":["leads","deals","accounts","contacts","products","quotes","requests","subscriptions","tickets"],"description":"Record type. System fields (origin native) exist on leads, deals, contacts and accounts","examples":["subscriptions"]},"origin":{"type":"string","enum":["custom","native"],"description":"custom (the default) for a custom field, native for a system field","examples":["custom"]},"recordId":{"type":"string","format":"uuid","description":"The record's ID","examples":["c9d0e1f2-a3b4-4c5d-8e6f-8a9b0c1d2e3f"]}},"required":["field","moduleType","recordId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string"},"value":{"type":"string","description":"The field's value in full, as text"},"revealedAt":{"type":"string","format":"date-time","description":"When the reveal was recorded on the record's timeline"}}}}},"example":{"success":true,"data":{"field":"wifi_password","label":"Wi-Fi password","value":"Tk7-fake-Example","revealedAt":"2026-10-21T10:15:00.000Z"}}}}},"400":{"description":"moduleType is not one whose fields can be hidden, field is missing, origin is not custom or native, or the field is not hidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You lack read permission on that record type, you are not on the field's who-can-see list, or you are an assistant acting for its owner (error.meta.askFirst: the owner must do it)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record that you can see, or it holds no value in that field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.read","crm.deals.read","crm.accounts.read","crm.contacts.read","crm.products.read","crm.quotes.read","crm.requests.read","crm.subscriptions.read","crm.tickets.read"]}],"x-required-scopes":["crm.leads.read","crm.deals.read","crm.accounts.read","crm.contacts.read","crm.products.read","crm.quotes.read","crm.requests.read","crm.subscriptions.read","crm.tickets.read"],"x-scope-mode":"any"}},"/reminder-types/add":{"post":{"tags":["Reminders"],"summary":"Create a reminder template","description":"Creates a reminder template (reminder type) in the custom category, active from the start. The message may hold ${name} placeholders; without templateVariables they are read from the message.\n\n**Permission:** `crm.reminders.create` (Create reminders).","operationId":"addReminderType","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"description":{"type":"string","description":"What the template is for, up to 500 characters","examples":["Sent a few days before a bill is due"]},"message":{"type":"string","description":"The reminder's text, up to 1,000 characters; may hold ${name} placeholders","examples":["Hello ${accountName}, your bill of ${amount} is due on ${dueDate}."]},"priority":{"type":"string","enum":["low","medium","high","urgent"],"description":"How urgent reminders made from it are","examples":["medium"]},"templateVariables":{"type":"array","items":{"type":"string"},"default":[],"description":"The placeholder names the message uses. When given, it must name exactly the ${...} placeholders in the message; leave out to read them from the message","examples":[["accountName","amount","dueDate"]]},"title":{"type":"string","description":"The reminder's title, up to 200 characters","examples":["Payment due for ${accountName}"]}},"required":["description","message","priority","title"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"category":{"type":"string","description":"Always custom for a template made here"},"priority":{"type":"string"},"title":{"type":"string"},"message":{"type":"string"},"templateVariables":{"type":"array","items":{"type":"string"},"description":"The ${name} placeholders the message uses, filled in when a reminder is sent"},"isActive":{"type":"boolean"},"description":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"message":{"type":"string"}}},"example":{"success":true,"data":{"id":"3d5a1f7c-2e9b-4c4d-9fe6-0e3b6c9d2f5a","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","category":"custom","priority":"medium","title":"Payment due for ${accountName}","message":"Hello ${accountName}, your bill of ${amount} is due on ${dueDate}.","templateVariables":["accountName","amount","dueDate"],"isActive":true,"description":"Sent a few days before a bill is due","deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"},"message":"Reminder type created successfully"}}}},"400":{"description":"A text is missing, empty or too long, or templateVariables does not match the ${...} placeholders in the message","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.reminders.create"]}],"x-required-scopes":["crm.reminders.create"]}},"/reminder-types/{reminderTypeId}/update":{"post":{"tags":["Reminders"],"summary":"Update a reminder template","description":"Changes the parts of a reminder template you send; the rest stay as they are. A new message resets the placeholder list from the message unless you send templateVariables too.\n\n**Permission:** `crm.reminders.update` (Update reminders).","operationId":"updateReminderType","parameters":[{"name":"reminderTypeId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The reminder template's ID","example":"3d5a1f7c-2e9b-4c4d-9fe6-0e3b6c9d2f5a"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"description":{"type":"string","description":"What the template is for","examples":["Sent a few days before a bill is due"]},"message":{"type":"string","description":"The new text; may hold ${name} placeholders","examples":["Hello ${accountName}, your bill of ${amount} is due on ${dueDate}."]},"priority":{"type":"string","enum":["low","medium","high","urgent"],"description":"How urgent reminders made from it are","examples":["high"]},"templateVariables":{"type":"array","items":{"type":"string"},"description":"The placeholder names. With a new message it must name exactly the message's ${...} placeholders (an empty list reads them from the message); without one it replaces the list as given","examples":[["accountName","amount","dueDate"]]},"title":{"type":"string","description":"The new title","examples":["Payment due for ${accountName}"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"category":{"type":"string"},"priority":{"type":"string"},"title":{"type":"string"},"message":{"type":"string"},"templateVariables":{"type":"array","items":{"type":"string"}},"isActive":{"type":"boolean"},"description":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"message":{"type":"string"}}},"example":{"success":true,"data":{"id":"3d5a1f7c-2e9b-4c4d-9fe6-0e3b6c9d2f5a","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","category":"custom","priority":"medium","title":"Payment due for ${accountName}","message":"Hello ${accountName}, your bill of ${amount} is due on ${dueDate}.","templateVariables":["accountName","amount","dueDate"],"isActive":true,"description":"Sent a few days before a bill is due","deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"},"message":"Reminder type updated successfully"}}}},"400":{"description":"templateVariables does not name exactly the ${...} placeholders in the new message","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No reminder template with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.reminders.update"]}],"x-required-scopes":["crm.reminders.update"]}},"/reminders/send":{"post":{"tags":["Reminders"],"summary":"Send a reminder for a subscription","description":"Saves a reminder for a subscription and emails it at once to the account's primary contact (or another active contact with an email address). The text comes from an active template in the chosen template's category, unless you send your own message. Needs the reminders admin permission.\n\n**Permission:** `crm.reminders.admin` (Administer reminders).","operationId":"sendReminder","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Your own text, used instead of the template's (placeholders are not filled in)","examples":["Your bill is due on 1 November."]},"reminderTypeId":{"type":"string","format":"uuid","description":"The reminder template to use. Only its category counts: the text comes from an active template in that category, which may be another one when several share it","examples":["3d5a1f7c-2e9b-4c4d-9fe6-0e3b6c9d2f5a"]},"subscriptionId":{"type":"string","format":"uuid","description":"The subscription the reminder is for","examples":["3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e"]}},"required":["reminderTypeId","subscriptionId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"reminderId":{"type":"string"},"category":{"type":"string"},"sentTo":{"type":"string","description":"The email address it went to; left out when the account has no contact with an email address (the reminder is still saved)"},"__notify":{"type":"object","properties":{"module":{"type":"string"},"entity":{"type":"string"},"action":{"type":"string"},"outcome":{"type":"string"},"eventType":{"type":"string"},"message":{"type":"string"},"metadata":{"type":"object","properties":{"reminderId":{"type":"string"},"reminderTypeId":{"type":"string"},"subscriptionId":{"type":"string"},"subscriptionName":{"type":"string"},"category":{"type":"string"},"sentTo":{"type":"string"},"customMessage":{"type":"string"}}}},"description":"The notification this send describes; answered as is today"}}},"example":{"success":true,"reminderId":"4e6b2a8d-3f0c-4d5e-8af7-1f4c7d0e3a6b","category":"payment_due","sentTo":"jane.wanjiku@example.com","__notify":{"module":"reminders","entity":"reminder","action":"create","outcome":"success","eventType":"reminder_sent","message":"Reminder \"Your bill is due on 1 November.\" sent successfully for subscription Home fibre 20 Mbps","metadata":{"reminderId":"4e6b2a8d-3f0c-4d5e-8af7-1f4c7d0e3a6b","reminderTypeId":"3d5a1f7c-2e9b-4c4d-9fe6-0e3b6c9d2f5a","subscriptionId":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","subscriptionName":"Home fibre 20 Mbps","category":"payment_due","sentTo":"jane.wanjiku@example.com","customMessage":"Your bill is due on 1 November."}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No active reminder template with that ID, or no subscription with that ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.reminders.admin"]}],"x-required-scopes":["crm.reminders.admin"]}},"/requests/{requestId}/assign":{"post":{"tags":["Requests"],"summary":"Assign a request","description":"Hands a new or assigned service request to a person. A \"new\" request becomes \"assigned\". Giving it to the person who already has it changes nothing. The change is written to the audit log.\n\n**Permission:** `crm.requests.update` (Update requests).","operationId":"assignRequest","parameters":[{"name":"requestId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The request to assign","example":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"assignedBy":{"type":"string","format":"uuid","description":"Your user ID, recorded as who assigned it","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]},"assignedTo":{"type":"string","format":"uuid","description":"Person to hand it to; left out, it goes to you when your sign-in carries your user","examples":["1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"]},"notes":{"type":"string","description":"A note on the hand-over, kept in the audit log","examples":["Brian covers Kileleshwa"]}},"required":["assignedBy"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"request":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"requestType":{"type":"string","description":"What is asked for, e.g. relocation, upgrade or maintenance"},"title":{"type":"string"},"description":{"type":"string"},"accountId":{"type":"string"},"subscriptionId":{"type":"string"},"quoteId":{},"status":{"type":"string","description":"new, assigned, in_progress, completed, closed or expired (rejected once rejected)"},"ownerId":{"type":"string"},"assignedTo":{"type":"string","description":"The person handling it"},"expiryAt":{"type":"string","format":"date-time","description":"When the request lapses if nobody finishes it"},"lastCustomerUpdate":{},"customerNotified":{"type":"boolean"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Request assigned to user 1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","request":{"id":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","requestType":"relocation","title":"Relocation - Move the fibre line to the new house in Kileleshwa","description":"Move the fibre line to the new house in Kileleshwa from 1 November.","accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","subscriptionId":"b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d","quoteId":null,"status":"assigned","ownerId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","assignedTo":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","expiryAt":"2026-11-19T08:00:00.000Z","lastCustomerUpdate":null,"customerNotified":false,"customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-10-20T08:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z"}}}}},"400":{"description":"No assignee was given, or the request is not new or assigned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No request with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"The assignment could not be saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.requests.update"]}],"x-required-scopes":["crm.requests.update"]}},"/requests/{requestId}/notes":{"post":{"tags":["Requests"],"summary":"Add a work note to a request","description":"Adds a work note to a service request and answers with the request, the new note and the five newest notes. Notes are internal unless isInternal is false; a customer-visible note also marks the request as customer notified.\n\n**Permission:** `crm.requests.update` (Update requests).","operationId":"addWorkNote","parameters":[{"name":"requestId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The request to add the note to","example":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"addedBy":{"type":"string","format":"uuid","description":"Your user ID, recorded as the note's author","examples":["1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"]},"isInternal":{"type":"string","description":"The text \"false\" makes the note visible to the customer; anything else (a JSON false too) keeps it internal","examples":["false"]},"note":{"type":"string","description":"The note's text","examples":["Called the customer; the site survey is booked for Thursday."]}},"required":["addedBy","note"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"request":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"requestType":{"type":"string","description":"What is asked for, e.g. relocation, upgrade or maintenance"},"title":{"type":"string"},"description":{"type":"string"},"accountId":{"type":"string"},"subscriptionId":{"type":"string"},"quoteId":{},"status":{"type":"string","description":"new, assigned, in_progress, completed, closed or expired (rejected once rejected)"},"ownerId":{"type":"string"},"assignedTo":{"type":"string","description":"The person handling it"},"expiryAt":{"type":"string","format":"date-time","description":"When the request lapses if nobody finishes it"},"lastCustomerUpdate":{},"customerNotified":{"type":"boolean"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"workNote":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"requestId":{"type":"string"},"authorId":{"type":"string"},"body":{"type":"string"},"metadata":{"type":"object","properties":{"isInternal":{"type":"boolean","description":"Whether only staff see the note"}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"totalWorkNotes":{"type":"integer","description":"How many notes the request has now"},"recentWorkNotes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"requestId":{"type":"string"},"authorId":{"type":"string"},"body":{"type":"string"},"metadata":{"type":"object","properties":{"isInternal":{"type":"boolean"}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"description":"The five newest notes, newest first"}}}},"example":{"success":true,"message":"Internal work note added","request":{"id":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","requestType":"relocation","title":"Relocation - Move the fibre line to the new house in Kileleshwa","description":"Move the fibre line to the new house in Kileleshwa from 1 November.","accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","subscriptionId":"b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d","quoteId":null,"status":"assigned","ownerId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","assignedTo":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","expiryAt":"2026-11-19T08:00:00.000Z","lastCustomerUpdate":null,"customerNotified":false,"customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-10-20T08:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z"},"workNote":{"id":"e0d1e2f3-a4b5-4c6d-9f8a-9b0c1d2e3f4a","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","requestId":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e","authorId":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","body":"Called the customer; the site survey is booked for Thursday.","metadata":{"isInternal":true},"createdAt":"2026-10-20T10:00:00.000Z","updatedAt":"2026-10-20T10:00:00.000Z"},"totalWorkNotes":1,"recentWorkNotes":[{"id":"e0d1e2f3-a4b5-4c6d-9f8a-9b0c1d2e3f4a","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","requestId":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e","authorId":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","body":"Called the customer; the site survey is booked for Thursday.","metadata":{"isInternal":true},"createdAt":"2026-10-20T10:00:00.000Z","updatedAt":"2026-10-20T10:00:00.000Z"}]}}}},"400":{"description":"The note is empty","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No request with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"The note could not be saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.requests.update"]}],"x-required-scopes":["crm.requests.update"]}},"/requests/{requestId}/reopen":{"post":{"tags":["Requests"],"summary":"Reopen a request","description":"Reopens a closed or expired service request, with a reason kept in the audit log. It goes back to \"assigned\" (the default, only when it has an assignee) or \"new\"; an expired request also gets a new expiry date 30 days from now.\n\n**Permission:** `crm.requests.update` (Update requests).","operationId":"reopenRequest","parameters":[{"name":"requestId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The request to reopen","example":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"justification":{"type":"string","description":"Why it is reopened","examples":["Customer says the move date changed to December"]},"newStatus":{"type":"string","enum":["new","assigned"],"description":"Status to reopen it in; default \"assigned\"","examples":["assigned"]},"reopenedBy":{"type":"string","format":"uuid","description":"Your user ID, recorded as who reopened it","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]}},"required":["justification","reopenedBy"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"request":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"requestType":{"type":"string","description":"What is asked for, e.g. relocation, upgrade or maintenance"},"title":{"type":"string"},"description":{"type":"string"},"accountId":{"type":"string"},"subscriptionId":{"type":"string"},"quoteId":{},"status":{"type":"string","description":"new, assigned, in_progress, completed, closed or expired (rejected once rejected)"},"ownerId":{"type":"string"},"assignedTo":{"type":"string","description":"The person handling it"},"expiryAt":{"type":"string","format":"date-time","description":"When the request lapses if nobody finishes it"},"lastCustomerUpdate":{},"customerNotified":{"type":"boolean"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"reopenDetails":{"type":"object","properties":{"previousStatus":{"type":"string"},"newStatus":{"type":"string"},"justification":{"type":"string"},"reopenedBy":{"type":"string"},"reopenedAt":{"type":"string","format":"date-time"},"newExpiryAt":{"type":"string","format":"date-time","description":"The request's expiry date after reopening"}}}}},"example":{"success":true,"message":"Request successfully reopened from expired to assigned","request":{"id":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","requestType":"relocation","title":"Relocation - Move the fibre line to the new house in Kileleshwa","description":"Move the fibre line to the new house in Kileleshwa from 1 November.","accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","subscriptionId":"b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d","quoteId":null,"status":"assigned","ownerId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","assignedTo":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","expiryAt":"2026-11-20T09:30:00.000Z","lastCustomerUpdate":null,"customerNotified":false,"customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-10-20T08:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z"},"reopenDetails":{"previousStatus":"expired","newStatus":"assigned","justification":"Customer says the move date changed to December","reopenedBy":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","reopenedAt":"2026-10-21T09:30:00.000Z","newExpiryAt":"2026-11-20T09:30:00.000Z"}}}}},"400":{"description":"The justification is empty, the request is not closed or expired, newStatus is not new or assigned, or \"assigned\" was asked for a request without an assignee","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No request with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"The request could not be reopened","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.requests.update"]}],"x-required-scopes":["crm.requests.update"]}},"/requests/{requestId}/action":{"post":{"tags":["Requests"],"summary":"Carry out a request","description":"Carries out a subscription request now: runs the handler for its type (scheduled_disconnection, scheduled_reconnection, nonpayment_disconnection, nonpayment_reconnection, plan_change, relocation, termination_request, due_date_change, grant_extension, fiber_shift, subscription_transfer or refund), which changes the subscription. When the request's effective date is still ahead nothing changes yet and the outcome is \"scheduled\"; the request then moves to in_progress, or to completed once done. When there is nothing to do (e.g. the line to disconnect is no longer active, or the arrears behind a non-payment disconnection are already paid) the outcome is \"noop\", the subscription is left alone and the request is closed; details.message says why. Works on new, approved or in_progress requests.\n\n**Permission:** `crm.requests.update` (Update requests).","operationId":"actionRequest","parameters":[{"name":"requestId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The request to carry out","example":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"outcome":{"type":"string","description":"completed (done now), scheduled (its effective date is ahead) or noop (nothing done; the request is closed and details.message says why)"},"details":{"type":"object","properties":{"previousPlanId":{"type":"string"},"newPlanId":{"type":"string"}},"description":"What the handler did or will do, e.g. the old and new plan"}}}}},"example":{"success":true,"data":{"outcome":"completed","details":{"previousPlanId":"a2f3a4b5-c6d7-4e8f-9b0c-1d2e3f4a5b6c","newPlanId":"b3a4b5c6-d7e8-4f9a-8c1d-2e3f4a5b6c7d"}}}}}},"400":{"description":"No handler exists for the request's type, or the request lacks what its type needs (e.g. customFields.newPlanId)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No request with that ID in your organisation, or its subscription is gone","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The request is already completed, rejected or cancelled, or its status cannot be acted on","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.requests.update"]}],"x-required-scopes":["crm.requests.update"]}},"/requests/{requestId}/reject":{"post":{"tags":["Requests"],"summary":"Reject a request","description":"Turns a request down: it moves to \"rejected\" and the reason is kept in its history. Nothing is changed on the subscription.\n\n**Permission:** `crm.requests.update` (Update requests).","operationId":"rejectRequest","parameters":[{"name":"requestId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The request to reject","example":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","description":"Why it is turned down","examples":["The new address is outside our coverage area"]}},"required":["reason"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}},"example":{"success":true,"message":"Request rejected"}}}},"400":{"description":"The reason is empty","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No request with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The request is already completed, rejected or cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.requests.update"]}],"x-required-scopes":["crm.requests.update"]}},"/requests/expire":{"post":{"tags":["Requests"],"summary":"Expire overdue requests","description":"Marks open service requests (new, assigned or in_progress) as expired when their expiry date has passed or nobody touched them for inactiveDays days; force expires every open one. dryRun only lists what would expire. Each change goes to the audit log. With dryRun the reply holds requestsToExpire (the full requests with daysSinceUpdate and reason) instead of expired and errors. A failure answers 200 with success false and the error.\n\n**Permission:** `crm.requests.admin` (Administer requests).","operationId":"expireRequests","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"dryRun":{"type":"boolean","description":"true to only list what would expire, changing nothing","examples":[true]},"executedBy":{"type":"string","format":"uuid","description":"Your user ID for the audit log; default \"system\"","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]},"force":{"type":"boolean","description":"true to expire every open request, overdue or not","examples":[false]},"inactiveDays":{"type":"integer","description":"Days without a change after which a request counts as inactive; default 30","examples":[30]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"expired":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"previousStatus":{"type":"string"},"expireReason":{"type":"string","description":"Why it expired: past its expiry date, inactive, or forced"},"daysSinceUpdate":{"type":"integer","description":"Days since the request last changed"},"expiredAt":{"type":"string","format":"date-time"}}}},"errors":{"type":"array","items":{"type":"object","properties":{"requestId":{"type":"string"},"title":{"type":"string"},"error":{"type":"string"}},"description":"Requests that could not be expired, with the error"}},"summary":{"type":"object","properties":{"totalProcessed":{"type":"integer"},"totalExpired":{"type":"integer"},"totalErrors":{"type":"integer"},"dryRun":{"type":"boolean"},"force":{"type":"boolean"},"inactiveDays":{"type":"integer"},"executedBy":{"type":"string"},"executedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Successfully expired 1 requests","expired":[{"id":"d9c0d1e2-f3a4-4b5c-8e7f-8a9b0c1d2e3f","title":"Upgrade - Move to 100 Mbps","previousStatus":"new","expireReason":"Expired due to passing expiry date","daysSinceUpdate":12,"expiredAt":"2026-10-21T06:00:00.000Z"}],"errors":[{"requestId":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e","title":"Relocation - Move the fibre line to the new house","error":"Request not found"}],"summary":{"totalProcessed":2,"totalExpired":1,"totalErrors":1,"dryRun":false,"force":false,"inactiveDays":30,"executedBy":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","executedAt":"2026-10-21T06:00:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.requests.admin"]}],"x-required-scopes":["crm.requests.admin"]}},"/pipelines/from-blueprint":{"post":{"tags":["Pipelines"],"summary":"Create a pipeline from a blueprint","description":"Creates a new pipeline, with its stages, transitions and automation rules, from one blueprint document. The pipeline is never made the default. Transitions whose stages are not in the blueprint are skipped. Answers the new pipeline's ID.\n\n**Permission:** `crm.pipelines.create` (Create pipelines).","operationId":"createPipelineFromBlueprint","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"blueprint":{"type":"object","description":"The whole pipeline: name, moduleType (and customModuleSlug for a custom module), stages, and optionally transitions, automations and config. Get the allowed values from the pipeline blueprint schema. A JSON string also works","examples":[{"name":"Home fibre sales","description":"Leads for home fibre, from first contact to qualified","moduleType":"leads","stages":[{"id":"new","name":"New","color":"#3b82f6","orderNum":0,"isDefault":true,"positionX":0,"positionY":100},{"id":"qualified","name":"Qualified","color":"#10b981","orderNum":1,"isDefault":false,"positionX":200,"positionY":100}],"transitions":[{"id":"new-to-qualified","name":"Qualify","color":"#22c55e","fromStageId":"new","toStageId":"qualified","isGlobal":false,"fromStageIds":[],"isActive":true,"requiresConfirmation":false,"allowedRoles":[],"allowedUsers":[],"conditions":[{"fieldPath":"phone","operator":"isNotEmpty","logicOperator":"AND"}],"fields":[],"requiredFieldIds":["coverage_confirmed"],"requireComment":false,"actions":[{"id":"new-to-qualified-action-0","actionType":"notification","name":"Tell the owner","isActive":true,"config":{"to":"owner","message":"{{record.name}} is qualified"},"orderNum":0}]}],"automations":[{"id":"welcome","name":"Welcome new leads","isActive":true,"trigger":{"event":"record_created","config":{}},"conditions":[],"actions":[{"id":"welcome-action-0","actionType":"email","name":"Welcome email","isActive":true,"config":{"to":"{{record.email}}","subject":"Thanks for your interest","body":"We will call you soon."},"orderNum":0}],"orderNum":0}]}]}},"required":["blueprint"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"pipelineId":{"type":"string"}}},"example":{"success":true,"pipelineId":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7"}}}},"400":{"description":"blueprint is missing or is not valid JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"The blueprint does not pass validation (the message lists every problem)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.pipelines.create"]}],"x-required-scopes":["crm.pipelines.create"]}},"/pipeline-agent/sessions/{sessionId}/messages":{"post":{"tags":["Automations"],"summary":"Send a message to the pipeline assistant","description":"Sends your message to the AI pipeline assistant. Answers at once with accepted true; the assistant works on its reply afterwards and streams it on the websocket room `pipeline-agent:{sessionId}` (or read the session again). At most 20 messages a minute per person.\n\n**Permission:** `crm.pipelineAgent.update` (Update pipeline agent sessions).","operationId":"sendPipelineAgentMessage","parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The assistant session","example":"28b9c0d1-5f6a-4b7c-9d8e-9f0a1b2c3d4e"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string","description":"Your message","examples":["Add a Lost stage that any stage can move to"]}},"required":["content"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"accepted":{"type":"boolean"}}},"example":{"accepted":true}}}},"400":{"description":"content is empty","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The session is someone else's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No assistant session with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The assistant is still working on its previous reply","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"429":{"description":"Over the message limit (20 a minute by default)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.pipelineAgent.update"]}],"x-required-scopes":["crm.pipelineAgent.update"]}},"/pipeline-agent/sessions/{sessionId}/cancel":{"post":{"tags":["Automations"],"summary":"Stop a pipeline assistant reply","description":"Stops the AI pipeline assistant's reply that is being worked on in your session. Answers cancelled true even when nothing was running.\n\n**Permission:** `crm.pipelineAgent.update` (Update pipeline agent sessions).","operationId":"cancelPipelineAgentTurn","parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The assistant session","example":"28b9c0d1-5f6a-4b7c-9d8e-9f0a1b2c3d4e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"cancelled":{"type":"boolean"}}},"example":{"cancelled":true}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The session is someone else's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No assistant session with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.pipelineAgent.update"]}],"x-required-scopes":["crm.pipelineAgent.update"]}},"/pipeline-agent/sessions/{sessionId}/apply":{"post":{"tags":["Automations"],"summary":"Apply the pipeline assistant's draft","description":"Applies the assistant's draft: in create mode it makes a new pipeline from it, in edit mode it rewrites the session's pipeline to match it (planned stage moves are recorded, but records are not moved). Marks the session applied and answers the pipeline's ID.\n\n**Permission:** `crm.pipelines.create` (Create pipelines).","operationId":"applyPipelineAgentBlueprint","parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The assistant session","example":"28b9c0d1-5f6a-4b7c-9d8e-9f0a1b2c3d4e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"pipelineId":{"type":"string"},"success":{"type":"boolean"}}},"example":{"pipelineId":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","success":true}}}},"400":{"description":"The session has no draft yet, the draft is not valid (validationErrors lists why), or applying it failed (the message says why)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The session is someone else's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No assistant session with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.pipelines.create"]}],"x-required-scopes":["crm.pipelines.create"]}},"/pipelines/{pipelineId}/stages":{"post":{"tags":["Pipelines"],"summary":"Add a stage to a pipeline","description":"Adds a stage to a pipeline. Without orderNum it goes last; without a position it is placed on the editor canvas after the others. Positions are rounded to whole numbers. Records reach it through transitions, so add one to it too. A ticket pipeline's stage gets a statusKey, the ticket status it stands for: the statusKey sent, else its name, in lowercase with underscores, made unique in the pipeline (_2, _3…), which a later rename never changes. Needs pipelines.create, or Desk admin for a ticket pipeline.\n\n**Permission:** `crm.pipelines.create` (Create pipelines) or `crm.desk.admin` (Configure Desk).","operationId":"createStage","parameters":[{"name":"pipelineId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The pipeline","example":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"color":{"type":"string","description":"Colour, a hex code like #8b5cf6 (default #6366f1)","examples":["#8b5cf6"]},"description":{"type":"string","description":"What the stage means","examples":["We have spoken to them"]},"isDefault":{"type":"boolean","description":"New records start in this stage","examples":[false]},"name":{"type":"string","description":"Name of the stage","examples":["Contacted"]},"orderNum":{"type":"integer","description":"Position on the board, from 0; leave out to put it last","examples":[1]},"positionX":{"type":"number","description":"Place on the workflow editor's canvas, left to right (default orderNum × 200)","examples":[200]},"positionY":{"type":"number","description":"Place on the workflow editor's canvas, top to bottom (default 100)","examples":[100]},"stageType":{"type":"string","description":"Kind of stage: active (the default), won or lost","examples":["active"]},"statusKey":{"type":"string","description":"Ticket pipelines only: the ticket status this stage stands for (lowercased, spaces as underscores, at most 24 characters, made unique in the pipeline). Leave out to take it from the name","examples":["escalated"]}},"required":["name"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"stage":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"pipelineId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"color":{"type":"string","description":"Colour of the stage, a hex code like #8b5cf6"},"orderNum":{"type":"integer","description":"Position of the stage on the board, from 0"},"stageType":{"type":"string","description":"Kind of stage: active, won or lost (free text; these are the ones CRM sets up)"},"isDefault":{"type":"boolean","description":"New records start in this stage"},"isTerminal":{"type":"boolean","description":"Records in this stage are finished"},"positionX":{"type":"integer","description":"Where the stage sits on the workflow editor's canvas, left to right"},"positionY":{"type":"integer","description":"Where the stage sits on the workflow editor's canvas, top to bottom"},"statusKey":{"description":"Ticket pipelines: the ticket status this stage stands for, set when the stage is made and kept through a rename; null elsewhere (the status is then the name in lowercase with underscores)"},"createdBy":{"type":"string"},"updatedBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"stage":{"id":"a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b","organizationId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","pipelineId":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","name":"Contacted","description":"We have spoken to them","color":"#8b5cf6","orderNum":1,"stageType":"active","isDefault":false,"isTerminal":false,"positionX":200,"positionY":100,"statusKey":null,"createdBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","updatedBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","deletedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You lack pipelines.create, or you are a Desk admin and the pipeline is not a ticket pipeline","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No pipeline with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"name is empty, color is over 7 characters or stageType over 16 (the message lists the problem)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.pipelines.create","crm.desk.admin"]}],"x-required-scopes":["crm.pipelines.create","crm.desk.admin"],"x-scope-mode":"any"}},"/pipelines/{pipelineId}/transitions":{"post":{"tags":["Pipelines"],"summary":"Add a transition to a pipeline","description":"Adds a transition: a button that moves a record from one stage to another, with optional conditions, a form, a comment, a confirmation, a limit on who may run it, and actions run when it is run. Only one plain (not global) transition may join the same two stages. The new transition is active. Needs pipelines.create, or Desk admin for a ticket pipeline.\n\n**Permission:** `crm.pipelines.create` (Create pipelines) or `crm.desk.admin` (Configure Desk).","operationId":"createTransition","parameters":[{"name":"pipelineId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The pipeline","example":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"actions":{"type":"array","items":{"type":"object"},"description":"Steps run when it is run, in order: each with actionType (email, webhook, notification, update_field, assign_user, create_ticket, start_billing...), name, isActive and config. See the workflow capabilities for what each takes. A JSON string also works","examples":[[{"id":"qualify-action-0","actionType":"notification","name":"Tell the owner","isActive":true,"config":{"to":"owner","title":"Lead qualified","message":"{{record.name}} moved to {{stage.to}}"},"orderNum":0}]]},"allowedRoles":{"type":"array","items":{"type":"string"},"description":"Roles that may run it; with allowedUsers both empty, anyone may. A JSON array or comma-separated text also works","examples":[["sales"]]},"allowedUsers":{"type":"array","items":{"type":"string","format":"uuid"},"description":"People (user IDs) that may run it, besides allowedRoles. A JSON array or comma-separated text also works","examples":[["f6a7b8c9-3d4e-4f5a-9b6c-7d8e9f0a1b2c"]]},"color":{"type":"string","description":"Button colour, a hex code (default: the target stage's colour)","examples":["#22c55e"]},"commentPrompt":{"type":"string","description":"The question shown above the comment box","examples":["What did they say?"]},"confirmationMessage":{"type":"string","description":"The question asked when confirmation is needed","examples":["Mark this lead as qualified?"]},"criteria":{"type":"array","items":{"type":"object"},"description":"Conditions the record must meet for the transition to show and run: each with fieldPath, operator (eq, neq, gt, gte, lt, lte, contains, isEmpty, isNotEmpty, in...), value and logicOperator (AND or OR). A JSON string also works","examples":[[{"id":"cond-0","fieldPath":"phone","fieldLabel":"Phone","operator":"isNotEmpty","logicOperator":"AND"}]]},"description":{"type":"string","description":"What the transition does","examples":["They want fibre and we can reach them"]},"fields":{"type":"array","items":{"type":"object"},"description":"Retired old-style form fields: ignored. Use requiredFieldIds","examples":[[]]},"fromStageId":{"type":"string","format":"uuid","description":"Stage the record leaves. For a global transition you may send fromStageIds instead","examples":["a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b"]},"fromStageIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"For a global transition: the stages it can be run from (at least one). A JSON array or comma-separated text also works","examples":[["a1b2c3d4-0001-4e5f-8a9b-0c1d2e3f4a5b","a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b"]]},"icon":{"type":"string","description":"Icon name for the button","examples":["check-circle"]},"isGlobal":{"type":"boolean","description":"Can be run from any stage in fromStageIds, not just one","examples":[false]},"name":{"type":"string","description":"Button label","examples":["Qualify"]},"requireComment":{"type":"boolean","description":"A comment is needed to run it","examples":[true]},"requiredFieldIds":{"type":"array","items":{"type":"string"},"description":"Record fields (custom field keys) that must be filled to run it; values entered are saved on the record. A JSON array or comma-separated text also works","examples":[["coverage_confirmed"]]},"requiresConfirmation":{"type":"boolean","description":"Ask the person to confirm before it runs","examples":[true]},"toStageId":{"type":"string","format":"uuid","description":"Stage the record moves to","examples":["a1b2c3d4-0003-4e5f-8a9b-0c1d2e3f4a5b"]}},"required":["fromStageId","name","toStageId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"transition":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"pipelineId":{"type":"string"},"fromStageId":{"type":"string","description":"Stage the record leaves. For a global transition, the first of fromStageIds"},"toStageId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"color":{"type":"string"},"icon":{"type":"string"},"isActive":{"type":"boolean"},"requiresConfirmation":{"type":"boolean"},"confirmationMessage":{"type":"string"},"isGlobal":{"type":"boolean","description":"Can be run from any stage in fromStageIds, not just fromStageId"},"fromStageIds":{"type":"array","items":{"type":"string"},"description":"Stages a global transition can be run from (the one source stage otherwise)"},"criteria":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"fieldPath":{"type":"string"},"fieldLabel":{"type":"string"},"operator":{"type":"string"},"logicOperator":{"type":"string"}}},"description":"Conditions the record must meet for the transition to show and run"},"fields":{"type":"array","description":"Old-style form fields. Retired: new and edited transitions keep this as it is and never use it"},"requiredFieldIds":{"type":"array","items":{"type":"string"},"description":"Record fields (custom field keys) that must be filled to run it; values entered are saved on the record"},"requireComment":{"type":"boolean","description":"A comment (notes) is needed to run it"},"commentPrompt":{"type":"string"},"actions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"actionType":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"},"config":{"type":"object","properties":{"to":{"type":"string"},"title":{"type":"string"},"message":{"type":"string"}}},"orderNum":{"type":"integer"}}},"description":"Steps run when it is run: actionType (email, webhook, notification, update_field, assign_user, create_ticket, start_billing...), name, config and order"},"allowedRoles":{"type":"array","items":{"type":"string"},"description":"Roles that may run it. With allowedUsers both empty, anyone may"},"allowedUsers":{"type":"array","items":{"type":"string"},"description":"People (user IDs) that may run it, besides allowedRoles"},"createdBy":{"type":"string"},"updatedBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"transition":{"id":"b2c3d4e5-0001-4f60-9a7b-1c2d3e4f5a6b","organizationId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","pipelineId":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","fromStageId":"a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b","toStageId":"a1b2c3d4-0003-4e5f-8a9b-0c1d2e3f4a5b","name":"Qualify","description":"They want fibre and we can reach them","color":"#22c55e","icon":"check-circle","isActive":true,"requiresConfirmation":true,"confirmationMessage":"Mark this lead as qualified?","isGlobal":false,"fromStageIds":["a1b2c3d4-0002-4e5f-8a9b-0c1d2e3f4a5b"],"criteria":[{"id":"cond-0","fieldPath":"phone","fieldLabel":"Phone","operator":"isNotEmpty","logicOperator":"AND"}],"fields":[],"requiredFieldIds":["coverage_confirmed"],"requireComment":true,"commentPrompt":"What did they say?","actions":[{"id":"qualify-action-0","actionType":"notification","name":"Tell the owner","isActive":true,"config":{"to":"owner","title":"Lead qualified","message":"{{record.name}} moved to {{stage.to}}"},"orderNum":0}],"allowedRoles":["sales"],"allowedUsers":["f6a7b8c9-3d4e-4f5a-9b6c-7d8e9f0a1b2c"],"createdBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","updatedBy":"e5f6a7b8-2c3d-4e4f-8a5b-6c7d8e9f0a1b","deletedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:30:00.000Z"}}}}},"400":{"description":"fromStageId is missing (or, for a global transition, fromStageIds is empty), or criteria or actions is not a valid JSON list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You lack pipelines.create, or you are a Desk admin and the pipeline is not a ticket pipeline","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No pipeline with that ID, or a stage is not in this pipeline","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A transition between these two stages already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"name is empty or color is over 7 characters (the message lists the problem)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.pipelines.create","crm.desk.admin"]}],"x-required-scopes":["crm.pipelines.create","crm.desk.admin"],"x-scope-mode":"any"}},"/pipelines/execute-transition":{"post":{"tags":["Pipelines"],"summary":"Run a transition on a record","description":"Runs a transition on a record: checks the record is in one of its stages, that you may run it, its conditions and its form, saves the form values on the record, runs its blocking actions (such as create_ticket or start_billing), moves the record to the new stage, then runs its other actions and starts the automations that wait for stage changes. A failed blocking action stops the move; other actions may fail without undoing it, so check actionResults.\n\n**Permission:** `crm.workflow.update` (Update record transitions).","operationId":"executeTransition","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fieldValues":{"type":"object","description":"Values for the transition's form, by field key; they are saved on the record. A JSON string also works","examples":[{"coverage_confirmed":true}]},"moduleType":{"type":"string","enum":["leads","deals","subscriptions","tickets","requests","custom"],"description":"The record's type","examples":["leads"]},"notes":{"type":"string","description":"Comment, needed when the transition asks for one; kept in the history","examples":["Wants the 50 Mbps plan"]},"recordId":{"type":"string","format":"uuid","description":"The record to move","examples":["d4e5f6a7-1b2c-4d3e-9f4a-5b6c7d8e9f0a"]},"transitionId":{"type":"string","format":"uuid","description":"The transition to run","examples":["b2c3d4e5-0001-4f60-9a7b-1c2d3e4f5a6b"]}},"required":["moduleType","recordId","transitionId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string","description":"Transitioned to <stage name>"},"data":{"type":"object","properties":{"recordId":{"type":"string"},"fromStage":{"type":"string","description":"Name of the stage the record left"},"toStage":{"type":"string","description":"Name of the stage the record is in now"},"actionResults":{"type":"array","items":{"type":"object","properties":{"actionType":{"type":"string"},"actionName":{"type":"string"},"success":{"type":"boolean"},"message":{"type":"string"},"executedAt":{"type":"string","format":"date-time"}}},"description":"Each action the transition ran, in order, whether it worked, and its message"},"createdRecords":{"type":"object","properties":{"accountId":{"type":"string"},"contactId":{"type":"string"},"dealId":{"type":"string"}},"description":"Only when the transition converted a lead: the account, contact and deal made"}}}}},"example":{"success":true,"message":"Transitioned to Qualified","data":{"recordId":"d4e5f6a7-1b2c-4d3e-9f4a-5b6c7d8e9f0a","fromStage":"Contacted","toStage":"Qualified","actionResults":[{"actionType":"notification","actionName":"Tell the owner","success":true,"message":"Notification sent","executedAt":"2026-10-20T10:15:02.000Z"}],"createdRecords":{"accountId":"39c0d1e2-6a7b-4c8d-8e9f-0a1b2c3d4e5f","contactId":"4ad1e2f3-7b8c-4d9e-9f0a-1b2c3d4e5f60","dealId":"5be2f3a4-8c9d-4e0f-8a1b-2c3d4e5f6071"}}}}}},"400":{"description":"fieldValues is not valid JSON; the transition is inactive; the record is not in the transition's stage; its conditions or form are not met (a required field or comment is missing, or a value breaks its field's rules); the lead is already a customer; or a blocking action failed (the message says which and why)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The transition is limited to roles or people and you are not one of them","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No transition or record with that ID (a ticket you may not see counts as not found)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.workflow.update"]}],"x-required-scopes":["crm.workflow.update"]}},"/pipelines/{pipelineId}/code/test":{"post":{"tags":["Automations"],"summary":"Test-run custom workflow code","description":"Test-runs a custom-code action in the isolated sandbox against a real record (or a stand-in) and answers its log lines, the effects it asked for (field changes, notifications, emails, webhooks, tasks) and its return value. A dry run by default: nothing changes unless applyEffects is true. Needs the pipelines admin permission.\n\n**Permission:** `crm.pipelines.admin` (Administer pipelines).","operationId":"testWorkflowCode","parameters":[{"name":"pipelineId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Pipeline whose stages and transitions the code sees (part of the address in /pipelines/:pipelineId/code/test)","example":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"applyEffects":{"type":"boolean","description":"Really carry out the effects the code asks for (default false: a dry run)","examples":[false]},"automationId":{"type":"string","format":"uuid","description":"An automation rule ID, passed to the code","examples":["17a8b9c0-4e5f-4a6b-8c7d-8e9f0a1b2c3d"]},"customModuleSlug":{"type":"string","description":"For a custom module: its slug, passed to the code","examples":["site-surveys"]},"memoryLimit":{"type":"integer","description":"Memory limit in MB (default 64, at most 128)","examples":[64]},"moduleType":{"type":"string","description":"Record type: leads, deals, subscriptions, tickets, requests or custom. Default: the pipeline's, else custom","examples":["leads"]},"recordId":{"type":"string","format":"uuid","description":"Record to run it on; leave out to use a stand-in record called Test Record","examples":["d4e5f6a7-1b2c-4d3e-9f4a-5b6c7d8e9f0a"]},"sourceCode":{"type":"string","description":"The code to run, up to 128 KB","examples":["log(\"Lead\", ctx.record.title);\nsetField(\"priority\", \"high\");\nreturn { ok: true };"]},"timeout":{"type":"integer","description":"Time limit in milliseconds (default 5000, at most 30000)","examples":[5000]},"transitionId":{"type":"string","format":"uuid","description":"A transition of the pipeline, to run as if from it","examples":["b2c3d4e5-0001-4f60-9a7b-1c2d3e4f5a6b"]}},"required":["sourceCode"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"logs":{"type":"array","items":{"type":"string"},"description":"What the code logged, in order"},"sideEffects":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"field":{"type":"string"},"value":{"type":"string"}}},"description":"Effects the code asked for, each with a type: setField, queueFieldUpdate, notify, email, webhook or task"},"returnValue":{"type":"object","properties":{"ok":{"type":"boolean"}},"description":"What the code returned"},"duration":{"type":"integer","description":"How long it ran, in milliseconds"},"memoryUsed":{"type":"string"},"compiledCode":{"type":"string"},"compilationErrors":{"type":"array","description":"Empty when the code compiled"},"applied":{"type":"boolean","description":"True when the effects were really carried out (applyEffects and a successful run)"},"capabilities":{"type":"object","properties":{"customCodeTestRun":{"type":"boolean"},"customCodeExecution":{"type":"boolean"}}},"sandbox":{"type":"object","properties":{"available":{"type":"boolean"},"reason":{},"executesUserCode":{"type":"boolean"},"appliesEffects":{"type":"boolean"}}},"validation":{"type":"object","properties":{"accepted":{"type":"boolean"},"pipelineId":{"type":"string"},"moduleType":{"type":"string"},"customModuleSlug":{},"recordId":{"type":"string"},"transitionId":{},"automationId":{},"sourceCodeBytes":{"type":"integer"},"applyEffectsRequested":{"type":"boolean"}},"description":"The inputs as the run understood them"}}},"example":{"success":true,"logs":["Lead Jane Doe"],"sideEffects":[{"type":"setField","field":"priority","value":"high"}],"returnValue":{"ok":true},"duration":12,"memoryUsed":"1.4MB","compiledCode":"(async () => { log(\"Lead\", ctx.record.title); setField(\"priority\", \"high\"); return { ok: true }; })();","compilationErrors":[],"applied":false,"capabilities":{"customCodeTestRun":true,"customCodeExecution":true},"sandbox":{"available":true,"reason":null,"executesUserCode":true,"appliesEffects":false},"validation":{"accepted":true,"pipelineId":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","moduleType":"leads","customModuleSlug":null,"recordId":"d4e5f6a7-1b2c-4d3e-9f4a-5b6c7d8e9f0a","transitionId":null,"automationId":null,"sourceCodeBytes":72,"applyEffectsRequested":false}}}}},"400":{"description":"sourceCode is empty or over 128 KB","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No pipeline, record or transition with that ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"The code did not compile, failed when run, or this server cannot run custom code (the message says which)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.pipelines.admin"]}],"x-required-scopes":["crm.pipelines.admin"]}},"/workflow/code/test":{"post":{"tags":["Automations"],"summary":"Test-run custom workflow code","description":"Test-runs a custom-code action in the isolated sandbox against a real record (or a stand-in) and answers its log lines, the effects it asked for (field changes, notifications, emails, webhooks, tasks) and its return value. A dry run by default: nothing changes unless applyEffects is true. Needs the pipelines admin permission.\n\n**Permission:** `crm.pipelines.admin` (Administer pipelines).","operationId":"testWorkflowCode_2","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"applyEffects":{"type":"boolean","description":"Really carry out the effects the code asks for (default false: a dry run)","examples":[false]},"automationId":{"type":"string","format":"uuid","description":"An automation rule ID, passed to the code","examples":["17a8b9c0-4e5f-4a6b-8c7d-8e9f0a1b2c3d"]},"customModuleSlug":{"type":"string","description":"For a custom module: its slug, passed to the code","examples":["site-surveys"]},"memoryLimit":{"type":"integer","description":"Memory limit in MB (default 64, at most 128)","examples":[64]},"moduleType":{"type":"string","description":"Record type: leads, deals, subscriptions, tickets, requests or custom. Default: the pipeline's, else custom","examples":["leads"]},"pipelineId":{"type":"string","format":"uuid","description":"Pipeline whose stages and transitions the code sees (part of the address in /pipelines/:pipelineId/code/test)","examples":["6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7"]},"recordId":{"type":"string","format":"uuid","description":"Record to run it on; leave out to use a stand-in record called Test Record","examples":["d4e5f6a7-1b2c-4d3e-9f4a-5b6c7d8e9f0a"]},"sourceCode":{"type":"string","description":"The code to run, up to 128 KB","examples":["log(\"Lead\", ctx.record.title);\nsetField(\"priority\", \"high\");\nreturn { ok: true };"]},"timeout":{"type":"integer","description":"Time limit in milliseconds (default 5000, at most 30000)","examples":[5000]},"transitionId":{"type":"string","format":"uuid","description":"A transition of the pipeline, to run as if from it","examples":["b2c3d4e5-0001-4f60-9a7b-1c2d3e4f5a6b"]}},"required":["sourceCode"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"logs":{"type":"array","items":{"type":"string"},"description":"What the code logged, in order"},"sideEffects":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"field":{"type":"string"},"value":{"type":"string"}}},"description":"Effects the code asked for, each with a type: setField, queueFieldUpdate, notify, email, webhook or task"},"returnValue":{"type":"object","properties":{"ok":{"type":"boolean"}},"description":"What the code returned"},"duration":{"type":"integer","description":"How long it ran, in milliseconds"},"memoryUsed":{"type":"string"},"compiledCode":{"type":"string"},"compilationErrors":{"type":"array","description":"Empty when the code compiled"},"applied":{"type":"boolean","description":"True when the effects were really carried out (applyEffects and a successful run)"},"capabilities":{"type":"object","properties":{"customCodeTestRun":{"type":"boolean"},"customCodeExecution":{"type":"boolean"}}},"sandbox":{"type":"object","properties":{"available":{"type":"boolean"},"reason":{},"executesUserCode":{"type":"boolean"},"appliesEffects":{"type":"boolean"}}},"validation":{"type":"object","properties":{"accepted":{"type":"boolean"},"pipelineId":{"type":"string"},"moduleType":{"type":"string"},"customModuleSlug":{},"recordId":{"type":"string"},"transitionId":{},"automationId":{},"sourceCodeBytes":{"type":"integer"},"applyEffectsRequested":{"type":"boolean"}},"description":"The inputs as the run understood them"}}},"example":{"success":true,"logs":["Lead Jane Doe"],"sideEffects":[{"type":"setField","field":"priority","value":"high"}],"returnValue":{"ok":true},"duration":12,"memoryUsed":"1.4MB","compiledCode":"(async () => { log(\"Lead\", ctx.record.title); setField(\"priority\", \"high\"); return { ok: true }; })();","compilationErrors":[],"applied":false,"capabilities":{"customCodeTestRun":true,"customCodeExecution":true},"sandbox":{"available":true,"reason":null,"executesUserCode":true,"appliesEffects":false},"validation":{"accepted":true,"pipelineId":"6f1d2c3b-4a5e-4f60-8a71-92b3c4d5e6f7","moduleType":"leads","customModuleSlug":null,"recordId":"d4e5f6a7-1b2c-4d3e-9f4a-5b6c7d8e9f0a","transitionId":null,"automationId":null,"sourceCodeBytes":72,"applyEffectsRequested":false}}}}},"400":{"description":"sourceCode is empty or over 128 KB","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No pipeline, record or transition with that ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"The code did not compile, failed when run, or this server cannot run custom code (the message says which)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.pipelines.admin"]}],"x-required-scopes":["crm.pipelines.admin"]}},"/workflow/webhook/test":{"post":{"tags":["Automations"],"summary":"Test a webhook step","description":"Tests a Call Webhook step: builds its request exactly as a run would, filled in from a record you pick (or a stand-in test record), sends it once from the server and answers what came back. The request carries the header X-Kazee-Test: true. A refused address, a timeout or a non-2xx answer comes back as ok: false with the reason, not as an error. Needs automations.update or pipelines.admin.\n\n**Permission:** `crm.automations.update` (Update automations) or `crm.pipelines.admin` (Administer pipelines).","operationId":"testWorkflowWebhook","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":"string","description":"Body template with {{record.…}} placeholders. Left empty, the body is JSON with event, moduleType, customModuleSlug, organizationId, record and eventConfig","examples":["{\"leadId\":\"{{record.id}}\"}"]},"customModuleSlug":{"type":"string","description":"For a custom module, the module's slug","examples":["site-surveys"]},"event":{"type":"string","description":"Event name the request carries, as a run would (default \"test\")","examples":["record_created"]},"headers":{"type":"object","description":"Headers to send, as a JSON object or JSON text, e.g. an Authorization header","examples":[{"Authorization":"Bearer abc123"}]},"method":{"type":"string","description":"HTTP method (default POST)","examples":["POST"]},"moduleType":{"type":"string","description":"Record type of the record to fill the request from: leads, deals, accounts, contacts, subscriptions, tickets, requests or custom (the default)","examples":["leads"]},"recordId":{"type":"string","format":"uuid","description":"The record to fill {{record.…}} placeholders from. Leave out to use a stand-in { id: \"test-record-id\", title: \"Test record\" }","examples":["d4e5f6a7-1b2c-4d3e-9f4a-5b6c7d8e9f0a"]},"url":{"type":"string","description":"Address to call: https only, never a private address. May hold {{record.…}} placeholders","examples":["https://hooks.example.com/kazee"]}},"required":["url"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"The address answered with a 2xx status"},"status":{"type":"integer","description":"The address's HTTP status; 0 when no answer came"},"durationMs":{"type":"integer"},"answer":{"type":"string","description":"The start of the answer's body (up to 4,096 characters)"},"error":{"description":"Why it did not work (a non-2xx status, no answer within 10 seconds, or a refused address); null when ok"},"sent":{"type":"object","properties":{"url":{"type":"string"},"method":{"type":"string"},"headers":{"type":"object","properties":{"Content-Type":{"type":"string"},"Authorization":{"type":"string"}},"description":"Headers sent; values of secret-looking ones (authorization, token, key, …) show as •••• (set)"},"body":{"type":"string","description":"The body sent (up to 8,192 characters; empty for GET)"}}},"record":{"type":"object","properties":{"id":{"type":"string"},"picked":{"type":"boolean","description":"false when no recordId was given and a stand-in test record was used"}}}}},"example":{"ok":true,"status":200,"durationMs":312,"answer":"{\"received\":true}","error":null,"sent":{"url":"https://hooks.example.com/kazee","method":"POST","headers":{"Content-Type":"application/json","Authorization":"•••• (set)"},"body":"{\"leadId\":\"d4e5f6a7-1b2c-4d3e-9f4a-5b6c7d8e9f0a\"}"},"record":{"id":"d4e5f6a7-1b2c-4d3e-9f4a-5b6c7d8e9f0a","picked":true}}}}},"400":{"description":"The step cannot be built: no url, or headers that are not JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No record of that type with that ID that you can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.automations.update","crm.pipelines.admin"]}],"x-required-scopes":["crm.automations.update","crm.pipelines.admin"],"x-scope-mode":"any"}},"/workflow/webhook/calls/{deliveryId}/resend":{"post":{"tags":["Automations"],"summary":"Resend a webhook call","description":"Sends one logged workflow webhook call again now, with a fresh three-day retry window, and answers the call with its body. Needs automations.update or pipelines.admin.\n\n**Permission:** `crm.automations.update` (Update automations) or `crm.pipelines.admin` (Administer pipelines).","operationId":"resendWorkflowWebhookCall","parameters":[{"name":"deliveryId","in":"path","required":true,"schema":{"type":"string"},"description":"The call to send again: its id from the call list","example":"wfd_019a2b3c4d5e6f708192a3b4c5d6e7f8"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"delivery":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","description":"pending until the new attempt ends, then succeeded, failed or abandoned"},"inFlight":{"type":"boolean"},"attempts":{"type":"integer"},"sourceApp":{"type":"string"},"idempotencyKey":{"type":"string"},"ruleKind":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"stepIndex":{"type":"integer"},"workflowEventId":{"type":"string"},"recordType":{"type":"string"},"recordId":{"type":"string"},"url":{"type":"string"},"method":{"type":"string"},"headerNames":{"type":"array","items":{"type":"string"}},"lastStatusCode":{"type":"integer"},"lastError":{},"lastResponse":{"type":"string"},"lastDurationMs":{"type":"integer"},"createdAt":{"type":"string","format":"date-time"},"firstAttemptAt":{"type":"string","format":"date-time"},"nextAttemptAt":{"type":"string","format":"date-time"},"deliveredAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"body":{"type":"string","description":"The start of the body sent (up to 8 KB)"},"bodyTruncated":{"type":"boolean","description":"The body sent was longer than what is shown"}}}}},"example":{"delivery":{"id":"wfd_019a2b3c4d5e6f708192a3b4c5d6e7f8","status":"pending","inFlight":false,"attempts":2,"sourceApp":"kazee-crm","idempotencyKey":"automation:17a8b9c0-4e5f-4a6b-8c7d-8e9f0a1b2c3d:evt_1:2","ruleKind":"automation","ruleId":"17a8b9c0-4e5f-4a6b-8c7d-8e9f0a1b2c3d","ruleName":"Welcome new fibre leads","stepIndex":2,"workflowEventId":"evt_1","recordType":"lead","recordId":"d4e5f6a7-1b2c-4d3e-9f4a-5b6c7d8e9f0a","url":"https://hooks.example.com/kazee","method":"POST","headerNames":["Content-Type","Authorization"],"lastStatusCode":200,"lastError":null,"lastResponse":"{\"received\":true}","lastDurationMs":312,"createdAt":"2026-10-20T08:15:00.000Z","firstAttemptAt":"2026-10-20T08:15:01.000Z","nextAttemptAt":"2026-10-20T09:00:00.000Z","deliveredAt":"2026-10-20T08:15:01.000Z","updatedAt":"2026-10-20T09:00:00.000Z","body":"{\"leadId\":\"d4e5f6a7-1b2c-4d3e-9f4a-5b6c7d8e9f0a\",\"event\":\"record_created\"}","bodyTruncated":false}}}}},"400":{"description":"The call ID is not valid, or this organisation has no Kazee Central ID, or the webhook service refused the request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Kazee's webhook service is not connected to CRM for this organisation yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No call with that ID for your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"An attempt of this call is in progress","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee's webhook service refused CRM's credential or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee's webhook service cannot be reached right now","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.automations.update","crm.pipelines.admin"]}],"x-required-scopes":["crm.automations.update","crm.pipelines.admin"],"x-scope-mode":"any"}},"/workflow/webhook/signing-secret/rotate":{"post":{"tags":["Automations"],"summary":"Replace the webhook signing secret","description":"Creates or replaces your organisation's workflow webhook signing secret. The answer carries the new secret this one time only; it signs every call from now on. Receivers checking the old one stop accepting calls until they are given the new one.\n\n**Permission:** `crm.connections.update` (Manage connections).","operationId":"rotateWorkflowWebhookSigningSecret","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"secret":{"type":"string","description":"The new secret. Shown only here: keep it now"},"exists":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"rotatedAt":{"type":"string","format":"date-time"},"hint":{"type":"string","description":"Its last four characters"}}},"example":{"secret":"whsec_Zx81QmT4vB7nK2pL9sD3fG6hJ0aWx9Qa","exists":true,"createdAt":"2026-10-02T09:00:00.000Z","rotatedAt":"2026-10-20T09:00:00.000Z","hint":"x9Qa"}}}},"400":{"description":"This organisation has no Kazee Central ID, or the webhook service refused the request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Kazee's webhook service is not connected to CRM for this organisation yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee's webhook service refused CRM's credential or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee's webhook service cannot be reached right now","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.connections.update"]}],"x-required-scopes":["crm.connections.update"]}},"/comments":{"post":{"tags":["Comments"],"summary":"Comment on a record","description":"Adds a comment (or, with parentId, a reply) to a record. The people mentioned, and the author of the comment replied to, get a notification; people tagged on a ticket are told in Kazee Desk too. Answers the new comment with its author filled in. When a bot writes it (a personal bot acting for its owner, or a bot as itself), the comment also names the bot as actedBy.\n\n**Permission:** `crm.comments.create` (Create comments).","operationId":"createComment","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"attachments":{"type":"array","items":{"type":"object"},"description":"Photos or files on the comment, at most 10: each as POST /comments/files answered it for this record (a JSON array in a string also works)"},"authorId":{"type":"string","description":"Your own user ID: the comment's author. Any other ID is refused (403), except from another app's service token, which names the person it writes for","examples":["0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac"]},"content":{"type":"string","description":"The comment, up to 50,000 characters; the web app sends a rich-text (TipTap) document as JSON text","examples":["Called her this morning; she wants the 20 Mbps plan."]},"mentions":{"type":"array","items":{"type":"string"},"default":[],"description":"User IDs of the people tagged (a JSON array in a string also works)","examples":[["1b7c4f2e-9a3d-4eac-8fbb-2d4e6a8cafbd"]]},"parentId":{"type":"string","format":"uuid","description":"The comment this replies to; must be on the same record","examples":["2c8d5a3f-0b4e-4fbd-9acc-3e5f7b9dbace"]},"targetId":{"type":"string","description":"The record's ID; for a Books record its Books ID (e.g. inv_…) or UUID","examples":["d37e0b8a-5c9f-4ace-8b7d-8f0a2c4e6b79"]},"targetType":{"type":"string","description":"The kind of record: lead, deal, account, contact, ticket, call, custom, …, or books:<resource> for a Kazee Books record (e.g. books:invoices)","examples":["lead"]}},"required":["authorId","content","targetId","targetType"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"targetType":{"type":"string","description":"The kind of record the comment is on, e.g. lead, deal, account, contact, ticket, call, custom, or books:<resource> for a Kazee Books record"},"targetId":{"type":"string","description":"The record's ID (a Books record answers with its Books ID, e.g. inv_…)"},"authorId":{"type":"object","properties":{"_id":{"type":"string"},"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"profilePicture":{}},"description":"Who wrote it"},"content":{"type":"string","description":"The text; the web app saves it as a rich-text (TipTap) document in JSON"},"attachments":{"type":"array","description":"Files on the comment (empty: these calls never add any)"},"parentId":{"description":"The comment this one replies to, or null"},"replyCount":{"type":"integer"},"mentions":{"type":"array","items":{"type":"string"},"description":"IDs of the people tagged in it"},"reactions":{"type":"array"},"seenBy":{"type":"array","description":"Who has seen it, and when"},"actedBy":{"description":"The bot that wrote or last edited it, { id, name, ai: true }: a personal bot acting for the author, or a bot as itself (shown with the AI diamond); null when a person did"},"isDeleted":{"type":"boolean"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"2c8d5a3f-0b4e-4fbd-9acc-3e5f7b9dbace","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","targetType":"lead","targetId":"d37e0b8a-5c9f-4ace-8b7d-8f0a2c4e6b79","authorId":{"_id":"0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac","id":"0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac","firstName":"Peter","lastName":"Otieno","email":"peter.otieno@example.com","profilePicture":null},"content":"Called her this morning; she wants the 20 Mbps plan.","attachments":[],"parentId":null,"replyCount":0,"mentions":["1b7c4f2e-9a3d-4eac-8fbb-2d4e6a8cafbd"],"reactions":[],"seenBy":[],"actedBy":null,"isDeleted":false,"deletedAt":null,"createdAt":"2026-10-20T10:00:00.000Z","updatedAt":"2026-10-20T10:00:00.000Z"}}}}},"400":{"description":"targetType, targetId or authorId is missing or not valid, mentions is not a list of user IDs, or the parent comment is on another record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"authorId is not your own user ID (only another app's service token may name the person it writes for)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No parent comment with that ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.comments.create"]}],"x-required-scopes":["crm.comments.create"]}},"/comments/files":{"post":{"tags":["Comments"],"summary":"Upload a file for a comment","description":"Uploads a photo or file for a comment on a record, as a form upload (multipart/form-data). It answers the file to put in attachments when you add the comment; on its own it changes nothing. The type is read from the bytes; files are at most 25 MB.\n\n**Permission:** `crm.comments.create` (Create comments).","operationId":"uploadCommentFile","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"attachment":{"type":"string","format":"binary","description":"The file, as the multipart form field attachment (not file), at most 25 MB"},"targetId":{"type":"string","description":"The record's ID","examples":["d37e0b8a-5c9f-4ace-8b7d-8f0a2c4e6b79"]},"targetType":{"type":"string","description":"The kind of record the comment is on: lead, deal, ticket, …","examples":["ticket"]}},"required":["attachment","targetId","targetType"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"name":{"type":"string","description":"The name made safe for storage"},"objectKey":{"type":"string","description":"Where it is stored; pass the whole object in attachments"},"url":{"type":"string","description":"A short-lived link to show it now"},"type":{"type":"string","description":"The file type, read from the file's bytes"},"size":{"type":"integer","description":"Size in bytes"},"bucketName":{"type":"string"}}}}},"example":{"success":true,"data":{"name":"router-photo.jpg","objectKey":"comments/ticket/d37e0b8a-5c9f-4ace-8b7d-8f0a2c4e6b79/1761720000000-router-photo.jpg","url":"https://files.example/signed-link","type":"image/jpeg","size":48213,"bucketName":"kazee-org-files"}}}}},"400":{"description":"targetType or targetId is missing, no file was sent, it is over 25 MB, or its type cannot be told","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.comments.create"]}],"x-required-scopes":["crm.comments.create"]}},"/comments/{commentId}/reactions":{"post":{"tags":["Comments"],"summary":"React to a comment","description":"Adds your emoji reaction to a comment and tells its author (unless it is yours). Despite the name it only adds: sending the same emoji again answers 409, since taking a reaction off is not supported yet.\n\n**Permission:** `crm.comments.update` (Update comments).","operationId":"toggleCommentReaction","parameters":[{"name":"commentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The comment's ID","example":"2c8d5a3f-0b4e-4fbd-9acc-3e5f7b9dbace"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"emoji":{"type":"string","description":"The emoji","examples":["👍"]},"userId":{"type":"string","description":"Your own user ID (the web app sends it): who reacts","examples":["1b7c4f2e-9a3d-4eac-8fbb-2d4e6a8cafbd"]}},"required":["emoji","userId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"reactions":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"emoji":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}},"description":"Every reaction on the comment now: who, which emoji, when"},"action":{"type":"string","description":"Always added"}}}}},"example":{"success":true,"data":{"reactions":[{"userId":"1b7c4f2e-9a3d-4eac-8fbb-2d4e6a8cafbd","emoji":"👍","createdAt":"2026-10-20T10:15:00.000Z"}],"action":"added"}}}}},"400":{"description":"userId is not a valid user ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"userId is not your own user ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No comment with that ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"You already reacted with that emoji: removing a reaction is not supported yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.comments.update"]}],"x-required-scopes":["crm.comments.update"]}},"/comments/mark-seen":{"post":{"tags":["Comments"],"summary":"Mark comments as seen","description":"Records that you have seen the given comments. IDs of comments that do not exist are skipped.\n\n**Permission:** `crm.comments.update` (Update comments).","operationId":"markCommentsSeen","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"commentIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"IDs of the comments you have seen (a JSON array in a string also works)","examples":[["2c8d5a3f-0b4e-4fbd-9acc-3e5f7b9dbace","3d9e6b4a-1c5f-4ace-8bdd-4f6a8caecbdf"]]},"userId":{"type":"string","description":"Your own user ID (the web app sends it): who has seen them","examples":["0a6b3e1d-8f2c-4dfb-9eaa-1c3d5f7b9eac"]}},"required":["commentIds","userId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}},"example":{"success":true,"message":"Comments marked as seen"}}}},"400":{"description":"userId is not a valid user ID, or commentIds is not a list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"userId is not your own user ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.comments.update"]}],"x-required-scopes":["crm.comments.update"]}},"/notifications/mark-all-read":{"post":{"tags":["Notifications"],"summary":"Mark all my notifications read","description":"Marks every unread notification in your own feed as read, and answers how many.\n\n**Permission:** `crm.notifications.update` (Update notifications).","operationId":"markAllNotificationsAsRead","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string","format":"uuid","description":"Optional: your own user ID. Any other person's ID is refused (403)","examples":["1b3e9d5a-0c7f-4a2b-9dc4-8c1f4a7b0d3e"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"updated":{"type":"integer","description":"How many notifications were marked read"}}}}},"example":{"success":true,"data":{"updated":3}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"userId is not your own","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.notifications.update"]}],"x-required-scopes":["crm.notifications.update"]}},"/notifications/{notificationId}/archive":{"post":{"tags":["Notifications"],"summary":"Archive a notification","description":"Archives one of your own notifications (it stays read or unread as it was) and answers it as saved. Someone else's notification answers 404, the same as one that does not exist.\n\n**Permission:** `crm.notifications.update` (Update notifications).","operationId":"archiveNotification","parameters":[{"name":"notificationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The notification's ID","example":"5f7c3b9e-4a1d-4e6f-9b08-2a5d8e1f4b7c"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string","format":"uuid","description":"Optional: your own user ID. Any other person's ID is refused (403)","examples":["1b3e9d5a-0c7f-4a2b-9dc4-8c1f4a7b0d3e"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"userId":{"type":"string"},"actorId":{"type":"string"},"category":{"type":"string"},"notificationType":{"type":"string"},"title":{"type":"string"},"content":{"type":"string"},"source":{"type":"string"},"sourceEntityId":{"type":"string"},"data":{"type":"object","properties":{"accountId":{"type":"string"},"accountName":{"type":"string"},"subscriptionId":{"type":"string"},"module":{"type":"string"},"entity":{"type":"string"},"action":{"type":"string"},"outcome":{"type":"string"}}},"isRead":{"type":"boolean"},"readAt":{},"isArchived":{"type":"boolean"},"archivedAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"5f7c3b9e-4a1d-4e6f-9b08-2a5d8e1f4b7c","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","userId":"1b3e9d5a-0c7f-4a2b-9dc4-8c1f4a7b0d3e","actorId":"2c4f0e6b-1d8a-4b3c-8ed5-9d2a5b8c1e4f","category":"subscriptions","notificationType":"create","title":"subscription_created","content":"New subscription created for account \"Jane Wanjiku\"","source":"crm:subscriptions:subscription_created","sourceEntityId":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","data":{"accountId":"a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c","accountName":"Jane Wanjiku","subscriptionId":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","module":"subscriptions","entity":"subscription","action":"create","outcome":"success"},"isRead":false,"readAt":null,"isArchived":true,"archivedAt":"2026-10-21T08:30:00.000Z","createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-21T08:30:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"userId is not your own","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No notification with that ID in your own feed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.notifications.update"]}],"x-required-scopes":["crm.notifications.update"]}},"/notifications/archive-all":{"post":{"tags":["Notifications"],"summary":"Archive all my notifications","description":"Archives every notification in your own feed that is not archived yet, and answers how many.\n\n**Permission:** `crm.notifications.update` (Update notifications).","operationId":"archiveAllNotifications","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string","format":"uuid","description":"Optional: your own user ID. Any other person's ID is refused (403)","examples":["1b3e9d5a-0c7f-4a2b-9dc4-8c1f4a7b0d3e"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"archived":{"type":"integer","description":"How many notifications were archived"}}}}},"example":{"success":true,"data":{"archived":12}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"userId is not your own","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.notifications.update"]}],"x-required-scopes":["crm.notifications.update"]}},"/deals/{dealId}/quote":{"post":{"tags":["Deals"],"summary":"Create a quote from a deal","description":"Create a deal's quote from the lines sent (as the quote dialog sends them), priced again on the server, and mirror it to Kazee Books as an estimate when your organisation is connected (so you also need the Books item-read and sales-create permissions). A deal has one open main quote at a time: if it has one, that quote comes back with existing true and nothing new is made. kind extra makes an extra quote beside the main one (other items the customer pays for or declines on their own); it never returns an existing one and leaves the deal's value alone. For a main quote, the deal's value then follows your organisation's value rule. The reply shown is the generic quote engine's (v3); the plan-based engine answers quote, lineItems, pricing (the plan's monthly rate) and dealValue.\n\n**Permission:** `crm.quotes.create` (Create quotes) and `crm.books.items.read` and `crm.books.sales.create`.","operationId":"createQuoteFromDeal","parameters":[{"name":"dealId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The deal's ID","example":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"contractTermLength":{"type":"string","description":"The contract term, one of your organisation's terms; defaults to the deal's","examples":["12 months"]},"fieldValues":{"type":"object","description":"Answers to your organisation's questions before a quote, by field key (plain values); saved on the deal first","examples":[{"installation_type":"Standard"}]},"installationBasis":{"type":"string","enum":["package_total","service_only"],"description":"Older plan-based quotes only: what the installation fee is worked out on","examples":["package_total"]},"kind":{"type":"string","enum":["main","extra"],"description":"main (default) or extra: an extra quote beside the main one","examples":["main"]},"kitId":{"type":"string","format":"uuid","description":"The kit the lines came from, recorded on the quote (generic engine)","examples":["c7b46b9e-1f3a-4bac-8a5b-9e0f1a2b3c4d"]},"lineItems":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","format":"uuid"},"itemRef":{"type":"string","description":"A Kazee Books item picked in the dialog, instead of productId"},"description":{"type":"string"},"quantity":{"type":"number"},"unitPrice":{"type":"number","description":"Only where your quote preferences allow price changes"},"discountType":{"type":"string","enum":["amount","percent"]},"discount":{"type":"number","description":"Amount off the line"},"discountPercent":{"type":"number"},"treatment":{"type":"string","enum":["invoice","retainer"]}}},"description":"The quote's lines (a JSON array in a string also works). Leave out to quote from the draft the deal starts from","examples":[[{"productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","quantity":1,"discountType":"amount","discount":0}]]},"lineVersion":{"type":"integer","enum":[2],"description":"2 for the plan-based Create quote dialog's lines; leave out on the generic engine","examples":[2]},"notes":{"type":"string","description":"Notes printed on the quote; defaults to your organisation's","examples":["Installation within 5 working days of payment"]},"quoteDiscount":{"type":"object","properties":{"type":{"type":"string","enum":["amount","percent"]},"value":{"type":"number"}},"description":"A discount on the whole quote (generic engine)","examples":[{"type":"percent","value":5}]},"templateId":{"type":"string","format":"uuid","description":"The quote template the lines came from, recorded on the quote","examples":["7c6f5d8f-0a2b-4d9e-8b4c-8f9a0b1c2d3e"]},"validityDays":{"type":"integer","minimum":1,"maximum":365,"description":"Days the quote stays valid, 1 to 365; defaults to your quote preferences","examples":[14]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"existing":{"type":"boolean","description":"True when the deal already had an open main quote, which is answered instead"},"data":{"type":"object","properties":{"engine":{"type":"string","description":"v3 on the generic quote engine (absent otherwise)"},"quote":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"quoteNumber":{"type":"string"},"leadId":{"type":"string"},"accountId":{"type":"string"},"subscriptionId":{},"requestId":{},"dealId":{"type":"string"},"ownerId":{"type":"string"},"type":{"type":"string"},"subtotal":{"type":"string"},"total":{"type":"string"},"status":{"type":"string","description":"draft, sent, accepted, declined, expired or paid"},"paidAmount":{"type":"string"},"balance":{"type":"string"},"paidAt":{},"validityStart":{"type":"string","format":"date-time"},"validityEnd":{"type":"string","format":"date-time"},"sentAt":{},"acceptedAt":{},"declinedAt":{},"expiredAt":{},"notes":{"type":"string"},"revisionOf":{},"revisionNumber":{"type":"integer"},"customFields":{"type":"object","properties":{"billing_flow":{"type":"string"},"pricing_source":{"type":"string"},"currency":{"type":"string"},"tax_total":{"type":"string"}}},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"lineItems":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"quoteId":{"type":"string"},"productId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"quantity":{"type":"string"},"unitPrice":{"type":"string"},"discount":{"type":"string"},"taxAmount":{"type":"string"},"subtotal":{"type":"string"},"total":{"type":"string"},"sortOrder":{"type":"integer"},"metadata":{"type":"object","properties":{"tax":{"type":"object","properties":{"kind":{"type":"string"},"id":{"type":"string"},"label":{"type":"string"},"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"ratePercent":{"type":"string"},"type":{"type":"string"}}}}}},"treatment":{"type":"string"}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"pricing":{},"totals":{"type":"object","properties":{"subtotal":{"type":"string"},"total":{"type":"string"},"tax":{"type":"string"},"taxSummary":{"type":"array","items":{"type":"object","properties":{"taxId":{"type":"string"},"name":{"type":"string"},"ratePercent":{"type":"string"},"base":{"type":"string"},"amount":{"type":"string"}}}}},"description":"Subtotal, tax and total as decimal strings, with each tax's base and amount"},"quoteDiscount":{"description":"The discount on the whole quote as stored (type, value and the amount it comes to), or null"},"dealValue":{"type":"string","description":"The deal's value after the value rule, or null when it did not change"}}}}},"example":{"success":true,"existing":false,"data":{"engine":"v3","quote":{"id":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","quoteNumber":"QT-000123","leadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","subscriptionId":null,"requestId":null,"dealId":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","type":"quote","subtotal":"4500.00","total":"5220.00","status":"draft","paidAmount":"0.00","balance":"5220.00","paidAt":null,"validityStart":"2026-10-15T14:05:00.000Z","validityEnd":"2026-10-29T14:05:00.000Z","sentAt":null,"acceptedAt":null,"declinedAt":null,"expiredAt":null,"notes":"","revisionOf":null,"revisionNumber":1,"customFields":{"billing_flow":"deal-quote-v3","pricing_source":"catalogue","currency":"KES","tax_total":"720.00"},"externalIds":[],"deletedAt":null,"createdAt":"2026-10-15T14:05:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"},"lineItems":[{"id":"b6a35a8d-0e2f-4a9b-9f4a-8d9e0f1a2b3c","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","quoteId":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","productId":"8d7a6e9a-1b3c-4e0f-9c5d-9a0b1c2d3e4f","name":"50 Mbps Fibre","description":"Monthly fibre internet, 50 Mbps","quantity":"1.00","unitPrice":"4500.00","discount":"0.00","taxAmount":"720.00","subtotal":"4500.00","total":"5220.00","sortOrder":0,"metadata":{"tax":{"kind":"tax","id":"460000000038029","label":"VAT (16%)","members":[{"id":"460000000038029","name":"VAT","ratePercent":"16","type":"plain"}]},"treatment":"invoice"},"createdAt":"2026-10-15T14:05:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}],"pricing":null,"totals":{"subtotal":"4500.00","total":"5220.00","tax":"720.00","taxSummary":[{"taxId":"460000000038029","name":"VAT","ratePercent":"16","base":"4500.00","amount":"720.00"}]},"quoteDiscount":null,"dealValue":"5220.00"}}}}},"400":{"description":"validityDays is not 1 to 365, lineItems is not valid JSON or a line is not valid (the message names it), kind is not main or extra, a question to confirm is unanswered, or the deal has no account yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No deal with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.quotes.create","crm.books.items.read","crm.books.sales.create"]}],"x-required-scopes":["crm.quotes.create","crm.books.items.read","crm.books.sales.create"],"x-scope-mode":"all"}},"/quotes/deal-links/backfill":{"post":{"tags":["Quotes"],"summary":"Fill older quotes' deal links","description":"Fills the deal link of quotes made before quotes had one, from the deal each quote already names. A dry run unless dryRun is false: it then only counts and shows a sample, writing nothing. Only empty links are filled, so running it again fills nothing new. Needs the Administer quotes permission.\n\n**Permission:** `crm.quotes.admin` (Administer quotes).","operationId":"backfillQuoteDealLinks","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"dryRun":{"type":"boolean","description":"Leave out or true to only count; exactly false to write the links","examples":[true]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"dryRun":{"type":"boolean"},"withoutDeal":{"type":"integer","description":"Quotes with no deal link"},"fillable":{"type":"integer","description":"Of those, the ones naming a deal that exists: what a run fills"},"dealMissing":{"type":"integer","description":"Naming a deal that no longer exists (left empty)"},"unnamed":{"type":"integer","description":"Naming no deal at all (left empty)"},"filled":{"type":"integer","description":"Quotes filled by this call (0 on a dry run)"},"sample":{"type":"array","items":{"type":"object","properties":{"quoteId":{"type":"string"},"quoteNumber":{"type":"string"},"dealId":{"type":"string"},"from":{"type":"string","description":"Where the deal was found: tag (the quote's deal tag) or custom_field"}}},"description":"Up to 10 of the quotes a run fills"}}}}},"example":{"success":true,"data":{"dryRun":true,"withoutDeal":12,"fillable":9,"dealMissing":1,"unnamed":2,"filled":0,"sample":[{"quoteId":"6b5e4c7e-9f1a-4c8d-9a3b-7e8f9a0b1c2d","quoteNumber":"QUOTE-00042","dealId":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","from":"tag"}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.quotes.admin"]}],"x-required-scopes":["crm.quotes.admin"]}},"/products/books-items/{itemId}":{"post":{"tags":["Products & plans"],"summary":"Add a Books item as a product","description":"Makes a Kazee Books item a product in the CRM catalogue, the first time it is picked: the product takes the item's name, price, tax and status, and stays read-only in CRM (Books owns it). Picking the same item again answers the same product with created false.\n\n**Permission:** `crm.products.create` (Create products).","operationId":"mirrorBooksItem","parameters":[{"name":"itemId","in":"path","required":true,"schema":{"type":"string"},"description":"The Kazee Books item (an itm_… ID)","example":"itm_7c8d9e0f1a2b4c3d4e5f6a7b8c9d0e1f"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"created":{"type":"boolean","description":"True when the product was made now; false when it already existed"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","description":"service or item (goods)"},"price":{"type":"string"},"sku":{"type":"string"},"status":{"type":"string"},"description":{"type":"string"},"taxId":{"type":"string","description":"The item's Books tax or tax group, or null"},"unit":{},"recurringPeriod":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"externalId":{"type":"string"}}},"description":"Links the product to its Books item"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"created":true,"data":{"id":"8f3c1a2b-4d5e-4f6a-9b7c-0d1e2f3a4b5c","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","name":"Fibre 50 Mbps","type":"service","price":"5500.00","sku":"FIB-50","status":"active","description":"Home fibre internet, 50 Mbps","taxId":"tax_8d9e0f1a2b3c4d4e5f6a7b8c9d0e1f2a","unit":null,"recurringPeriod":null,"customFields":{},"externalIds":[{"source":"kazee-books-item","externalId":"itm_7c8d9e0f1a2b4c3d4e5f6a7b8c9d0e1f"}],"deletedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"itemId is not a Kazee Books item ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Kazee Books is not connected to this CRM organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Kazee Books has no such item","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.products.create"]}],"x-required-scopes":["crm.products.create"]}},"/products/books-refresh":{"post":{"tags":["Products & plans"],"summary":"Refresh products from Books","description":"Brings every CRM product made from a Kazee Books item up to date with its item: name, price, tax and status (an item no longer sold makes its product inactive). Items nobody picked are not added. Answers how many products changed.\n\n**Permission:** `crm.products.update` (Update products).","operationId":"refreshBooksCatalogue","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"items":{"type":"integer","description":"How many items Kazee Books lists"},"mirrored":{"type":"integer","description":"How many CRM products come from a Books item"},"updated":{"type":"integer","description":"Products changed to match their item"},"unchanged":{"type":"integer","description":"Products that already matched"},"missing":{"type":"integer","description":"Products whose item Books no longer lists"}}}}},"example":{"success":true,"data":{"items":42,"mirrored":12,"updated":3,"unchanged":8,"missing":1}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Kazee Books is not connected to this CRM organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"description":"Kazee Books could not be reached or failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Kazee Books is not set up for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.products.update"]}],"x-required-scopes":["crm.products.update"]}},"/deals/{dealId}/value/recalculate":{"post":{"tags":["Deals"],"summary":"Recalculate a deal's value from its plan","description":"Set a deal's value again from its service plan: the plan's Kazee Books item price plus its tax, as Books will invoice it. This replaces a value typed by hand. Needs read access to Books items as well.\n\n**Permission:** `crm.deals.update` (Update deals) and `crm.books.items.read`.","operationId":"recalculateDealValue","parameters":[{"name":"dealId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The deal's ID","example":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"outcome":{"type":"object","properties":{"updated":{"type":"boolean","description":"True when the deal's value changed. When it was already right this is false and the outcome also holds skipped: \"unchanged\""},"value":{"type":"string","description":"The value worked out, a decimal string"}}},"deal":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"leadId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"value":{"type":"string","description":"Deal value, a decimal string in the deal's currency (two decimals)"},"currency":{"type":"string","description":"ISO 4217 code; the organisation's currency unless set"},"recurringValue":{"type":"string"},"stage":{"type":"string","description":"Stage key on the deals pipeline, e.g. qualification, discovery, proposal, negotiation, closed_won, closed_lost (organisations may use their own)"},"probability":{"type":"integer","description":"Chance of winning, 0 to 100"},"expectedCloseDate":{"type":"string","format":"date-time"},"actualCloseDate":{},"ownerId":{"type":"string"},"teamId":{},"priority":{"type":"string","description":"low, medium or high"},"source":{"type":"string"},"dealType":{"type":"string"},"lostReason":{},"lostReasonDetails":{},"competitorId":{},"lastActivityDate":{"type":"string","format":"date-time"},"nextActivityDate":{},"nextActivityType":{},"notes":{"type":"string"},"customFields":{"type":"object","properties":{"contract_term_length":{"type":"string"},"service_plan":{"type":"string"},"value_source":{"type":"string"}},"description":"The organisation's own fields, by field key. Fields hidden from you come back masked"},"externalIds":{"type":"array"},"createdBy":{"type":"string"},"updatedBy":{"type":"string"},"version":{"type":"integer","description":"Goes up by one on every change"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}},"example":{"success":true,"data":{"outcome":{"updated":true,"value":"5220.00"},"deal":{"id":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Acacia Bakery: 50 Mbps fibre","description":"Fibre for the bakery's main branch","leadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","value":"5220.00","currency":"KES","recurringValue":"4500.00","stage":"proposal","probability":50,"expectedCloseDate":"2026-11-01T00:00:00.000Z","actualCloseDate":null,"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","teamId":null,"priority":"high","source":"website","dealType":"new_business","lostReason":null,"lostReasonDetails":null,"competitorId":null,"lastActivityDate":"2026-10-15T14:05:00.000Z","nextActivityDate":null,"nextActivityType":null,"notes":"","customFields":{"contract_term_length":"12 months","service_plan":"9e8b7f0b-2c4d-4f1a-8d6e-0b1c2d3e4f5a","value_source":"plan"},"externalIds":[],"createdBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","updatedBy":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","version":3,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}}}}}},"400":{"description":"The deal has no service plan, or the plan could not be priced (the message says why)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No deal with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.deals.update","crm.books.items.read"]}],"x-required-scopes":["crm.deals.update","crm.books.items.read"],"x-scope-mode":"all"}},"/deals/bulk-reassign":{"post":{"tags":["Deals"],"summary":"Reassign many deals","description":"Admins only (an assistant acting for its owner is refused): change the owner of many deals at once, picked by ID or by filter (stages, current owners, handled-by label), with the new owners chosen by mode: static, round_robin, load_balanced or handler_map. A dry run by default: it answers the plan and writes nothing until you send dryRun false. Writes one audit row per deal; sends no notifications and runs no automations.\n\n**Permission:** `crm.deals.update` (Update deals).","operationId":"bulkReassignDeals","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"currentLoad":{"type":"object","description":"load_balanced: starting count per pool user, by user ID. Default: each user's current open records","examples":[{"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d":12,"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e":4}]},"dryRun":{"type":"boolean","description":"Default true: only work out and show the plan. Send false to write it","examples":[true]},"echo":{"type":"integer","description":"How many planned assignments and skipped records to list in the reply (default 200, at most 5,000)","examples":[200]},"excludeStatuses":{"type":"array","items":{"type":"string"},"description":"Leave out deals at these stages","examples":[["qualification"]]},"handledBy":{"type":"string","enum":["any","present","empty"],"description":"Only records whose handled_by custom field is filled in (present), empty (empty), or either (any)","examples":["present"]},"labelMap":{"type":"object","description":"handler_map: which user each handled_by label goes to, e.g. { \"Jane Doe\": \"<user id>\" } (a JSON object in a string also works)","examples":[{"Brian Otieno":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"}]},"limit":{"type":"integer","description":"Reassign at most this many of the matching records","examples":[100]},"mode":{"type":"string","enum":["static","round_robin","load_balanced","handler_map"],"description":"How to pick the new owner: static (everyone to userId), round_robin (take turns through pool), load_balanced (to whoever in pool has the fewest open records) or handler_map (by the record's handled_by label, through labelMap)","examples":["round_robin"]},"ownerIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Only records currently owned by these users","examples":[["b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e"]]},"pool":{"type":"array","items":{"type":"string","format":"uuid"},"description":"The users to share the records between, in order (round_robin, load_balanced, and handler_map's pool fallback). Each must be an active user of your organisation","examples":[["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e"]]},"recordIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"IDs of the records to reassign, at most 5,000 (a JSON array or a comma-separated list in a string also works). Send this or at least one filter","examples":[["1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e"]]},"setAssignee":{"type":"boolean","description":"Leads only: ignored for deals","examples":[false]},"setOwner":{"type":"boolean","description":"Change the owner (default true)","examples":[true]},"statuses":{"type":"array","items":{"type":"string"},"description":"Only deals at these stages (a JSON array or comma-separated list in a string also works)","examples":[["proposal","negotiation"]]},"unmappedFallback":{"type":"string","enum":["skip","pool"],"description":"handler_map: what to do with a record whose label maps to nobody: skip it (default) or give it to the pool","examples":["skip"]},"userId":{"type":"string","format":"uuid","description":"The new owner, for static mode","examples":["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]}},"required":["mode"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"moduleType":{"type":"string"},"mode":{"type":"string"},"dryRun":{"type":"boolean"},"filter":{"type":"object","properties":{"statuses":{"type":"array","items":{"type":"string"}},"ownerIds":{"type":"array","items":{"type":"string"}}}},"closedStatuses":{"type":"array","items":{"type":"string"},"description":"Stages that do not count as open when load_balanced counts each person's open deals"},"setOwner":{"type":"boolean"},"setAssignee":{"type":"boolean"},"targets":{"type":"integer","description":"Deals the filter picked"},"planned":{"type":"integer","description":"Deals that would get (or got) a new owner"},"unchanged":{"type":"integer","description":"Deals already with the planned owner"},"skipped":{"type":"integer"},"skippedDetail":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"reason":{"type":"string","description":"no_handler (no handled_by label) or unmapped_handler:<labels> (no person for the labels)"}}},"description":"Deals left alone, with why (up to echo of them)"},"byUser":{"type":"object","properties":{"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d":{"type":"integer"}},"description":"How many deals each new owner gets"},"currentLoad":{},"assignments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"from":{"type":"object","properties":{"ownerId":{"type":"string"},"assignedTo":{"type":"string"}}},"to":{"type":"string"},"via":{"type":"string"},"label":{"type":"string"}}},"description":"The plan, up to echo of them"},"applied":{"type":"integer","description":"Deals written (0 on a dry run)"},"audited":{"type":"integer","description":"Change-log rows written"},"batchId":{"description":"Groups this run's audit rows; null on a dry run"}}},"example":{"success":true,"moduleType":"deals","mode":"handler_map","dryRun":true,"filter":{"statuses":["proposal"],"ownerIds":["b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e"]},"closedStatuses":["completed","not_interested","installation_failed","lost_to_competitor","connected_to_competition","locked"],"setOwner":true,"setAssignee":false,"targets":3,"planned":1,"unchanged":1,"skipped":1,"skippedDetail":[{"id":"f4e1a2b3-c4d5-4e6f-8a7b-9c0d1e2f3a4b","reason":"no_handler"}],"byUser":{"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d":1},"currentLoad":null,"assignments":[{"id":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","from":{"ownerId":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","assignedTo":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e"},"to":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","via":"handler_map","label":"brian otieno"}],"applied":0,"audited":0,"batchId":null}}}},"400":{"description":"mode is unknown, no recordIds and no filter, over 5,000 recordIds, no target users, a target user is not an active user of your organisation, setOwner false (deals have no assignee to set), or a list, object or boolean input cannot be read","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Your role is not an admin role, or you are an assistant acting for its owner (error.meta.askFirst: the owner must do it)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.deals.update"]}],"x-required-scopes":["crm.deals.update"]}},"/deals/fold":{"post":{"tags":["Deals"],"summary":"Fold a twin deal into the imported deal","description":"Migration tool for importer admins: fold a deal made in Kazee into the Zoho-imported deal of the same sale. The lead, the account's contact links, survey fields the kept deal lacks and call logs move to the kept records; the Kazee-made deal and its account (and, with retireQuotes, its draft, expired or declined quotes) are retired: hidden, never removed. All or nothing; refused with every reason listed when the pair is not safe to fold. Send dryRun true to see what would happen without writing (dryRun is false unless sent). No notifications or automations; undo with POST /deals/fold/undo.\n\n**Permission:** `crm.importer.admin` (Administer importer).","operationId":"foldDeal","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"dryRun":{"type":"boolean","description":"Check everything and answer what would happen, writing nothing. Default false","examples":[true]},"keepDealId":{"type":"string","format":"uuid","description":"The Zoho-imported deal that stays","examples":["1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e"]},"retireDealId":{"type":"string","format":"uuid","description":"The Kazee-made twin deal to retire","examples":["f0e79e2b-4c6d-4edf-9d8e-2b3c4d5e6f7a"]},"retireQuotes":{"type":"boolean","description":"Also retire the twin's quotes (only draft, expired or declined ones, none made into anything in Kazee Books). Default false","examples":[false]}},"required":["keepDealId","retireDealId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"dryRun":{"type":"boolean"},"folded":{"type":"boolean","description":"True when written now; false on a dry run or when the pair was folded already"},"foldId":{"type":"string","description":"The fold's record: give it to the undo (null on a dry run)"},"keepDealId":{"type":"string"},"retireDealId":{"type":"string"},"keepAccountId":{"type":"string"},"retireAccountId":{"type":"string"},"lead":{"type":"object","properties":{"id":{"type":"string"},"linkedToDeal":{"type":"boolean","description":"The kept deal now names the lead (it had none)"},"keepDealLeadId":{},"refsMoved":{"type":"boolean"},"originOfKeptAccount":{"type":"boolean"}}},"fields":{"type":"object","properties":{"copied":{"type":"array","items":{"type":"string"},"description":"Custom fields copied from the retired deal (the kept deal had no value)"},"kept":{"type":"array","items":{"type":"string"},"description":"Both had a value: the kept deal's stays"},"notCopied":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"reason":{"type":"string"}}}}}},"contactLinks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"contactId":{"type":"string"},"action":{"type":"string","description":"moved to the kept account, or dropped (the kept account already had that contact)"},"isPrimary":{"type":"boolean"}}}},"callLogIds":{"type":"array","items":{"type":"string"}},"quotes":{"type":"array"},"stays":{"type":"object","properties":{"stageMoves":{"type":"integer"},"auditRows":{"type":"integer"},"comments":{"type":"integer"},"activities":{"type":"integer"},"products":{"type":"integer"}},"description":"History of the retired deal left where it is (stage moves, audit rows, comments, activities, products)"}}}}},"example":{"success":true,"data":{"dryRun":false,"folded":true,"foldId":"e9d68d1a-3b5c-4dce-8c7d-1a2b3c4d5e6f","keepDealId":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","retireDealId":"f0e79e2b-4c6d-4edf-9d8e-2b3c4d5e6f7a","keepAccountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","retireAccountId":"3e2b1f4b-6c8d-4f5a-8d0e-4b5c6d7e8f9a","lead":{"id":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","linkedToDeal":true,"keepDealLeadId":null,"refsMoved":true,"originOfKeptAccount":true},"fields":{"copied":["site_survey_date"],"kept":["contract_term_length"],"notCopied":[{"key":"value_source","reason":"Kazee's own price working; the kept deal keeps Zoho's amount"}]},"contactLinks":[{"id":"0a1b2c3d-4e5f-4a6b-8c7d-8e9f0a1b2c3d","contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","action":"moved","isPrimary":false}],"callLogIds":["1b2c3d4e-5f6a-4b7c-9d8e-9f0a1b2c3d4e"],"quotes":[],"stays":{"stageMoves":2,"auditRows":5,"comments":1,"activities":3,"products":1}}}}}},"400":{"description":"A deal ID is not a UUID, both are the same deal, or retireQuotes / dryRun is not true or false","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"The deal to keep or to retire does not exist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Not safe to fold (the message and meta.refusals list every reason)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.admin"]}],"x-required-scopes":["crm.importer.admin"]}},"/deals/{dealId}/buttons/{ruleId}/check":{"post":{"tags":["Automations"],"summary":"Check a deal's button","description":"Shows what pressing a button on a sale would do, step by step: the Kazee value, what Zoho holds now and what would be written, plus anything that blocks it, and a fingerprint the run call is held to. Reads Kazee and Zoho and writes nothing. Only the people the button names may check it (and they also need importer.admin).\n\n**Permission:** `crm.importer.admin` (Administer importer).","operationId":"checkDealButton","parameters":[{"name":"dealId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The sale (deal)","example":"5be2f3a4-8c9d-4e0f-9a1b-2c3d4e5f6a7b"},{"name":"ruleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The button: an automation rule with the manual trigger","example":"39c0d1e2-6a7b-4c8d-9e0f-0a1b2c3d4e5f"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"ruleId":{"type":"string"},"label":{"type":"string"},"recordId":{"type":"string"},"ready":{"type":"boolean","description":"Nothing blocks a run"},"allowed":{"type":"boolean","description":"You are one of the people the button names"},"steps":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"action":{"type":"string","description":"What the step would do: create, update, linked, adopt, nothing, books or error"},"target":{"type":"string"},"zoho_id":{},"fields":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string"},"kazee":{"type":"string","description":"The value in Kazee"},"zoho_now":{"description":"What Zoho holds now"},"will_write":{"type":"string","description":"What a run would write"}}}},"blockers":{"type":"array"},"warnings":{"type":"array"},"payload":{"type":"object","properties":{"Deal_Name":{"type":"string"}}},"link":{}}}},"blockers":{"type":"array","description":"Why it cannot run, each starting with the step's name"},"warnings":{"type":"array"},"fingerprint":{"type":"string","description":"Pass to the run call: the run is refused if anything changed since"},"checkedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"ruleId":"39c0d1e2-6a7b-4c8d-9e0f-0a1b2c3d4e5f","label":"Send to Zoho","recordId":"5be2f3a4-8c9d-4e0f-9a1b-2c3d4e5f6a7b","ready":true,"allowed":true,"steps":[{"key":"zoho_deal","name":"Create the deal in Zoho","type":"zoho_crm_record","action":"create","target":"Deals","zoho_id":null,"fields":[{"field":"Deal_Name","label":"Deal Name","kazee":"Amina Wanjiru - Home 50","zoho_now":null,"will_write":"Amina Wanjiru - Home 50"}],"blockers":[],"warnings":[],"payload":{"Deal_Name":"Amina Wanjiru - Home 50"},"link":null}],"blockers":[],"warnings":[],"fingerprint":"3f7a9c1e5b2d4f6a8c0e1b3d5f7a9c2e4b6d8f0a1c3e5b7d9f1a3c5e7b9d1f3a","checkedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"The button is switched off or not set up right (its rule fails the button checks)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You are not one of the people the button names","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No button (switched-on manual automation rule) with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.admin"]}],"x-required-scopes":["crm.importer.admin"]}},"/deals/{dealId}/buttons/{ruleId}/run":{"post":{"tags":["Automations"],"summary":"Run a deal's button","description":"Presses a button on a sale: does exactly what the check with this fingerprint showed (e.g. creates or updates records in Zoho). It checks again first and is refused (status refused) if anything changed or blocks; it stops at the first step that does not end as planned, and never sends a create twice. Only the people the button names may run it (and they also need importer.admin); one run per button and sale at a time. The run is logged in the automation run logs and noted on the deal.\n\n**Permission:** `crm.importer.admin` (Administer importer).","operationId":"runDealButton","parameters":[{"name":"dealId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The sale (deal)","example":"5be2f3a4-8c9d-4e0f-9a1b-2c3d4e5f6a7b"},{"name":"ruleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The button: an automation rule with the manual trigger","example":"39c0d1e2-6a7b-4c8d-9e0f-0a1b2c3d4e5f"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fingerprint":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"The fingerprint the check call answered","examples":["3f7a9c1e5b2d4f6a8c0e1b3d5f7a9c2e4b6d8f0a1c3e5b7d9f1a3c5e7b9d1f3a"]}},"required":["fingerprint"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"status":{"type":"string","description":"done: every step worked; stopped: a step did not end as planned (later steps not reached); refused: nothing was done"},"reason":{"description":"Why it stopped or was refused; null when done"},"steps":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"outcome":{"type":"string","description":"already, created, updated, adopted, nothing or sent (worked); refused, in_doubt, mismatch or not_reached (did not)"},"zoho_id":{"type":"string","description":"The Zoho record the step made or found"},"link":{"type":"string"},"message":{}}}},"check":{"type":"object","properties":{"ruleId":{"type":"string"},"label":{"type":"string"},"recordId":{"type":"string"},"ready":{"type":"boolean"},"allowed":{"type":"boolean"},"steps":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"action":{"type":"string"},"target":{"type":"string"},"zoho_id":{},"fields":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string"},"kazee":{"type":"string"},"zoho_now":{},"will_write":{"type":"string"}}}},"blockers":{"type":"array"},"warnings":{"type":"array"},"payload":{"type":"object","properties":{"Deal_Name":{"type":"string"}}},"link":{}}}},"blockers":{"type":"array"},"warnings":{"type":"array"},"fingerprint":{"type":"string"},"checkedAt":{"type":"string","format":"date-time"}},"description":"The fresh check the run was held to (as the check call answers)"}}}}},"example":{"success":true,"data":{"status":"done","reason":null,"steps":[{"key":"zoho_deal","outcome":"created","zoho_id":"5123000004567001","link":"https://crm.zoho.com/crm/org123456789/tab/Potentials/5123000004567001","message":null}],"check":{"ruleId":"39c0d1e2-6a7b-4c8d-9e0f-0a1b2c3d4e5f","label":"Send to Zoho","recordId":"5be2f3a4-8c9d-4e0f-9a1b-2c3d4e5f6a7b","ready":true,"allowed":true,"steps":[{"key":"zoho_deal","name":"Create the deal in Zoho","type":"zoho_crm_record","action":"create","target":"Deals","zoho_id":null,"fields":[{"field":"Deal_Name","label":"Deal Name","kazee":"Amina Wanjiru - Home 50","zoho_now":null,"will_write":"Amina Wanjiru - Home 50"}],"blockers":[],"warnings":[],"payload":{"Deal_Name":"Amina Wanjiru - Home 50"},"link":null}],"blockers":[],"warnings":[],"fingerprint":"3f7a9c1e5b2d4f6a8c0e1b3d5f7a9c2e4b6d8f0a1c3e5b7d9f1a3c5e7b9d1f3a","checkedAt":"2026-10-20T09:00:00.000Z"}}}}}},"400":{"description":"The fingerprint is not 64 hex characters, the button is switched off or not set up right, or it is already running on this sale","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"You are not one of the people the button names","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No button (switched-on manual automation rule) with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.admin"]}],"x-required-scopes":["crm.importer.admin"]}},"/deals/fold/undo":{"post":{"tags":["Deals"],"summary":"Undo a deal fold","description":"Migration tool for importer admins: undo one deal fold. The retired deal, its account and quotes come back and every row the fold moved returns to how it was. Give the fold's ID or the retired deal's ID (one of them). Refused, naming them, when rows the fold wrote on the kept records have changed since. Undoing a fold a second time answers undone false. No notifications or automations.\n\n**Permission:** `crm.importer.admin` (Administer importer).","operationId":"undoDealFold","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"foldId":{"type":"string","format":"uuid","description":"The fold's ID (foldId from the fold's reply)","examples":["e9d68d1a-3b5c-4dce-8c7d-1a2b3c4d5e6f"]},"retireDealId":{"type":"string","format":"uuid","description":"Or the ID of the deal the fold retired","examples":["f0e79e2b-4c6d-4edf-9d8e-2b3c4d5e6f7a"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"undone":{"type":"boolean","description":"False when the fold had been undone already; the reply then also holds reason, saying why nothing was done"},"foldId":{"type":"string"},"keepDealId":{"type":"string"},"retireDealId":{"type":"string"},"undoId":{"type":"string","description":"The record of this undo"},"restored":{"type":"object","properties":{"deal":{"type":"string"},"account":{"type":"string"},"quotes":{"type":"array"},"contactLinks":{"type":"integer"},"callLogs":{"type":"integer"},"fields":{"type":"array","items":{"type":"string"}},"lead":{"type":"string"}},"description":"What came back: the deal, its account, quotes, contact links and call logs moved back, fields removed again and the lead"}}}}},"example":{"success":true,"data":{"undone":true,"foldId":"e9d68d1a-3b5c-4dce-8c7d-1a2b3c4d5e6f","keepDealId":"1c0f9d2f-4a6b-4d3e-8b8c-2f3a4b5c6d7e","retireDealId":"f0e79e2b-4c6d-4edf-9d8e-2b3c4d5e6f7a","undoId":"2c3d4e5f-6a7b-4c8d-8e9f-0a1b2c3d4e5f","restored":{"deal":"f0e79e2b-4c6d-4edf-9d8e-2b3c4d5e6f7a","account":"3e2b1f4b-6c8d-4f5a-8d0e-4b5c6d7e8f9a","quotes":[],"contactLinks":1,"callLogs":1,"fields":["site_survey_date"],"lead":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"}}}}}},"400":{"description":"Neither or both of foldId and retireDealId were sent, or the one sent is not a UUID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No fold with that ID, or none of that deal","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Rows the fold wrote have changed since (the message and meta.changed list them); nothing was undone","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.admin"]}],"x-required-scopes":["crm.importer.admin"]}},"/desk/notifications/read-all":{"post":{"tags":["Desk"],"summary":"Mark all my Desk notifications read","description":"Marks all your unread Desk notifications read and says how many changed.\n\n**Permission:** `crm.desk.read` (Use Desk: own notifications, follows and preferences).","operationId":"markAllDeskNotificationsRead","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"updated":{"type":"integer","description":"How many notifications were unread and are now read"}}},"example":{"success":true,"updated":3}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"description":"The call was not made by a signed-in person","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.desk.read"]}],"x-required-scopes":["crm.desk.read"]}},"/desk/notifications/{notificationId}/read":{"post":{"tags":["Desk"],"summary":"Mark a Desk notification read","description":"Marks one of your Desk notifications read and answers it. Marking it again keeps the first read time. Someone else's notification answers 404.\n\n**Permission:** `crm.desk.read` (Use Desk: own notifications, follows and preferences).","operationId":"markDeskNotificationRead","parameters":[{"name":"notificationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The notification","example":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"ticket.assigned, ticket.customer_reply, ticket.status_changed, ticket.mention, ticket.automation, ticket.sla or channel.linked_device"},"ticketId":{"type":"string","description":"The ticket it is about, if any"},"title":{"type":"string"},"body":{"type":"string"},"data":{"type":"object","properties":{"ticketNumber":{"type":"string"},"subject":{"type":"string"}},"description":"Extra details for the screen, such as the ticket number and subject"},"actorUserId":{"type":"string","description":"Who caused it; null for the system or a customer"},"readAt":{"type":"string","format":"date-time","description":"When it was read; null while unread"},"createdAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"c8b9c0d1-e2f3-4a4b-9d6e-7f8a9b0c1d2e","type":"ticket.assigned","ticketId":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","title":"Ticket TKT-001042 was assigned to you","body":"Internet down since this morning","data":{"ticketNumber":"TKT-001042","subject":"Internet down since this morning"},"actorUserId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","readAt":"2026-10-20T09:00:00.000Z","createdAt":"2026-10-20T08:15:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"description":"The call was not made by a signed-in person","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"You have no notification with that ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.desk.read"]}],"x-required-scopes":["crm.desk.read"]}},"/desk/tickets/{ticketId}/watch":{"post":{"tags":["Desk"],"summary":"Follow a ticket","description":"Follows a ticket: you then get Desk notifications about its customer replies and status changes. Following it again changes nothing and answers created: false.\n\n**Permission:** `crm.desk.read` (Use Desk: own notifications, follows and preferences) and `crm.tickets.read` (View tickets).","operationId":"watchDeskTicket","parameters":[{"name":"ticketId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The ticket to follow","example":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"created":{"type":"boolean","description":"False when you already followed the ticket"},"data":{"type":"object","properties":{"watching":{"type":"boolean","description":"Always true: you follow it now"}}}}},"example":{"success":true,"created":true,"data":{"watching":true}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"description":"The call was not made by a signed-in person","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No ticket with that ID in your organisation that you may see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.desk.read","crm.tickets.read"]}],"x-required-scopes":["crm.desk.read","crm.tickets.read"],"x-scope-mode":"all"},"delete":{"tags":["Desk"],"summary":"Stop following a ticket","description":"Stops following a ticket. Not following it already is not an error: it answers removed: false.\n\n**Permission:** `crm.desk.read` (Use Desk: own notifications, follows and preferences).","operationId":"unwatchDeskTicket","parameters":[{"name":"ticketId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The ticket to stop following","example":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"removed":{"type":"boolean","description":"False when you were not following the ticket"},"data":{"type":"object","properties":{"watching":{"type":"boolean","description":"Always false: you no longer follow it"}}}}},"example":{"success":true,"removed":true,"data":{"watching":false}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"description":"The call was not made by a signed-in person","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.desk.read"]}],"x-required-scopes":["crm.desk.read"]}},"/tickets/{ticketId}/messages":{"post":{"tags":["Tickets"],"summary":"Add a message to a ticket","description":"Adds a reply, a customer message or an internal note to a ticket's conversation, with words, files uploaded onto the ticket, or both. A public agent reply on email, WhatsApp, Facebook, Instagram, TikTok or SMS is sent to the customer and the answer says whether it went (delivery.state sent or delivered) or failed and why; on live chat, phone or web it is stored for the customer to see. An email reply goes out with the organisation's email signature (Desk settings) after the words; the stored message keeps the words only. An internal note never leaves the ticket. The first public reply stops the first-response clock. Sending the same clientMessageId again answers the stored message instead of adding a second.\n\n**Permission:** `crm.tickets.create` (Create tickets).","operationId":"addTicketMessage","parameters":[{"name":"ticketId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The ticket to add the message to","example":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"attachmentIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Unsent files already uploaded onto this ticket, up to 20 (a comma-separated string also works)","examples":[["7c8d9e0f-1a2b-4c3d-8e5f-6a7b8c9d0e1f"]]},"attachments":{"type":"array","items":{"type":"object"},"description":"Not used; send files with attachmentIds","deprecated":true},"channel":{"type":"string","description":"Channel to send on; leave out to use the ticket's own channel","examples":["whatsapp"]},"clientMessageId":{"type":"string","description":"Your own ID for this message, up to 64 characters, so a retried call is not stored twice","examples":["c-1729413720000"]},"content":{"type":"string","description":"The words. May be left out when attachmentIds carries a file. With a template, the rendered preview the agent saw","examples":["Sorry about that. I have restarted your line; please check now."]},"contentType":{"type":"string","enum":["text","html"],"default":"text","description":"Whether content is plain text or HTML","examples":["text"]},"isInternal":{"type":"boolean","default":false,"description":"true: an internal note the customer never sees","examples":[false]},"senderId":{"type":"string","format":"uuid","description":"The person the message is from (shown as its author)","examples":["1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"]},"senderType":{"type":"string","enum":["agent","customer","system"],"description":"Who wrote it: agent (a reply, sent to the customer; the default when left out), customer (logs what the customer said) or system","examples":["agent"]},"templateLanguage":{"type":"string","description":"The template's language code","examples":["en"]},"templateName":{"type":"string","description":"WhatsApp template to send: the only message WhatsApp allows once the customer's 24-hour reply window has closed","examples":["service_update"]},"templateParams":{"type":"array","items":{"type":"string"},"description":"The template's numbered values, in order (a JSON list or comma-separated string also works)","examples":[["Amina","TKT-001042"]]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"duplicate":{"type":"boolean","description":"True when a message with this clientMessageId was already stored; it is answered as it was"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"ticketId":{"type":"string"},"direction":{"type":"string","description":"inbound (from the customer) or outbound (to the customer)"},"authorType":{"type":"string","description":"agent, customer or system"},"authorId":{"type":"string"},"authorName":{"type":"string"},"authorEmail":{"type":"string"},"body":{"type":"string"},"bodyType":{"type":"string"},"isPrivate":{"type":"boolean","description":"An internal note the customer never sees"},"channel":{"type":"string"},"externalMessageId":{"type":"string"},"metadata":{"type":"object","properties":{"attachmentsDelivered":{"type":"boolean"}}},"sentAt":{"type":"string","format":"date-time"},"deliveryState":{"type":"string","description":"stored, internal, queued, sent, delivered, read or failed"},"deliveryError":{},"deliveredAt":{"type":"string","format":"date-time"},"deliveryAttempts":{"type":"integer"},"clientMessageId":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"delivery":{"type":"object","properties":{"state":{"type":"string","description":"internal (a note), stored (kept on the ticket), queued, sent, delivered, read or failed"},"error":{"description":"Why the channel refused it, when it failed"},"attempts":{"type":"integer"},"externalMessageId":{"type":"string"}}},"attachments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"ticketId":{"type":"string"},"messageId":{"type":"string"},"documentId":{"type":"string"},"fileName":{"type":"string"},"originalName":{"type":"string"},"fileUrl":{"type":"string"},"mimeType":{"type":"string"},"fileSize":{"type":"integer"},"uploadedBy":{"type":"string"},"metadata":{"type":"object","properties":{}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"description":"The files this message now carries"},"attachmentsDelivered":{"type":"boolean","description":"True when the channel took every file, false when any stayed on the ticket only, null with no files"}}},"example":{"success":true,"message":"Message added successfully","duplicate":false,"data":{"id":"5a6b7c8d-9e0f-4a1b-8c3d-4e5f6a7b8c9d","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","ticketId":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","direction":"outbound","authorType":"agent","authorId":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","authorName":"Brian Otieno","authorEmail":"brian.otieno@example.com","body":"Sorry about that. I have restarted your line; please check now.","bodyType":"text","isPrivate":false,"channel":"whatsapp","externalMessageId":"wamid.HBgMMjU0NzAwMDAwMDAwFQIAERgSQzY3","metadata":{"attachmentsDelivered":true},"sentAt":"2026-10-20T08:42:00.000Z","deliveryState":"delivered","deliveryError":null,"deliveredAt":"2026-10-20T08:42:03.000Z","deliveryAttempts":1,"clientMessageId":"c-1729413720000","createdAt":"2026-10-20T08:42:00.000Z","updatedAt":"2026-10-20T08:42:03.000Z"},"delivery":{"state":"delivered","error":null,"attempts":1,"externalMessageId":"wamid.HBgMMjU0NzAwMDAwMDAwFQIAERgSQzY3"},"attachments":[{"id":"7c8d9e0f-1a2b-4c3d-8e5f-6a7b8c9d0e1f","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","ticketId":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","messageId":"5a6b7c8d-9e0f-4a1b-8c3d-4e5f6a7b8c9d","documentId":"f5e6f7a8-b9c0-4d1e-8a3b-4c5d6e7f8a9b","fileName":"router-photo.jpg","originalName":"router-photo.jpg","fileUrl":"tickets/3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b/attachments/1792484100000-router-photo.jpg","mimeType":"image/jpeg","fileSize":245760,"uploadedBy":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","metadata":{},"createdAt":"2026-10-20T08:15:00.000Z","updatedAt":"2026-10-20T08:15:00.000Z"}],"attachmentsDelivered":true}}}},"400":{"description":"Neither words nor a file were sent, or the only file was already sent or removed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No ticket with that ID that you may see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.create"]}],"x-required-scopes":["crm.tickets.create"]}},"/tickets/{ticketId}/attachments":{"post":{"tags":["Tickets"],"summary":"Upload a file onto a ticket","description":"Uploads a file onto a ticket as a draft, to be sent with the next message: pass its ID in attachmentIds when adding the message. The file type is read from the bytes, never the name; files are at most 25 MB. An unsent file can be removed again.\n\n**Permission:** `crm.tickets.update` (Update tickets).","operationId":"uploadTicketAttachment","parameters":[{"name":"ticketId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The ticket to put the file on","example":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"attachment":{"type":"string","format":"binary","description":"The file, as the multipart form field attachment (not file), at most 25 MB"}},"required":["attachment"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"ticketId":{"type":"string"},"messageId":{"description":"Empty until a message sends the file"},"documentId":{"type":"string"},"fileName":{"type":"string","description":"The name made safe for storage"},"originalName":{"type":"string","description":"The name it was uploaded with"},"fileUrl":{"type":"string","description":"Where it is stored (not a link; ask for a download link)"},"mimeType":{"type":"string"},"fileSize":{"type":"integer"},"uploadedBy":{"type":"string"},"metadata":{"type":"object","properties":{"bucketName":{"type":"string"}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"7c8d9e0f-1a2b-4c3d-8e5f-6a7b8c9d0e1f","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","ticketId":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","messageId":null,"documentId":"f5e6f7a8-b9c0-4d1e-8a3b-4c5d6e7f8a9b","fileName":"router-photo.jpg","originalName":"router-photo.jpg","fileUrl":"tickets/3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b/attachments/1729413000000-router-photo.jpg","mimeType":"image/jpeg","fileSize":245760,"uploadedBy":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","metadata":{"bucketName":"kazee-org-files"},"createdAt":"2026-10-20T08:15:00.000Z","updatedAt":"2026-10-20T08:15:00.000Z"}}}}},"400":{"description":"No file was sent, it is over 25 MB, or its type cannot be told","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No ticket with that ID that you may see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.update"]}],"x-required-scopes":["crm.tickets.update"]}},"/tickets/{ticketId}/messages/{ticketMessageId}/retry":{"post":{"tags":["Tickets"],"summary":"Retry sending a ticket reply","description":"Sends a public reply to its channel again after it failed (or is still queued). A message the channel already took is refused, so the customer never gets it twice. If the channel takes it this time, it counts as the first reply for the first-response clock.\n\n**Permission:** `crm.tickets.create` (Create tickets).","operationId":"retryTicketMessage","parameters":[{"name":"ticketId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The ticket the message is on","example":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b"},{"name":"ticketMessageId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The failed reply to send again","example":"5a6b7c8d-9e0f-4a1b-8c3d-4e5f6a7b8c9d"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"ticketId":{"type":"string"},"direction":{"type":"string","description":"inbound (from the customer) or outbound (to the customer)"},"authorType":{"type":"string","description":"agent, customer or system"},"authorId":{"type":"string"},"authorName":{"type":"string"},"authorEmail":{"type":"string"},"body":{"type":"string"},"bodyType":{"type":"string"},"isPrivate":{"type":"boolean","description":"An internal note the customer never sees"},"channel":{"type":"string"},"externalMessageId":{"type":"string"},"metadata":{"type":"object","properties":{}},"sentAt":{"type":"string","format":"date-time"},"deliveryState":{"type":"string","description":"stored, internal, queued, sent, delivered, read or failed"},"deliveryError":{},"deliveredAt":{"type":"string","format":"date-time"},"deliveryAttempts":{"type":"integer"},"clientMessageId":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"delivery":{"type":"object","properties":{"state":{"type":"string","description":"sent, delivered or failed after this try"},"error":{"description":"Why the channel refused it, when it failed again"},"attempts":{"type":"integer","description":"How many times it was handed to the channel"},"externalMessageId":{"type":"string"}}}}},"example":{"success":true,"data":{"id":"5a6b7c8d-9e0f-4a1b-8c3d-4e5f6a7b8c9d","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","ticketId":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","direction":"outbound","authorType":"agent","authorId":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","authorName":"Brian Otieno","authorEmail":"brian.otieno@example.com","body":"Sorry about that. I have restarted your line; please check now.","bodyType":"text","isPrivate":false,"channel":"whatsapp","externalMessageId":"wamid.HBgMMjU0NzAwMDAwMDAwFQIAERgSQzY3","metadata":{},"sentAt":"2026-10-20T08:42:00.000Z","deliveryState":"sent","deliveryError":null,"deliveredAt":"2026-10-20T08:42:03.000Z","deliveryAttempts":2,"clientMessageId":"c-1729413720000","createdAt":"2026-10-20T08:42:00.000Z","updatedAt":"2026-10-20T08:42:03.000Z"},"delivery":{"state":"sent","error":null,"attempts":2,"externalMessageId":"wamid.HBgMMjU0NzAwMDAwMDAwFQIAERgSQzY3"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No ticket with that ID that you may see, or no such message on it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The message is a note or from the customer, is on a channel that is not sent externally, or was already sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.create"]}],"x-required-scopes":["crm.tickets.create"]}},"/tickets/{ticketId}/assign":{"post":{"tags":["Tickets"],"summary":"Assign a ticket","description":"Assigns a ticket to a person, a department, or both, and notes it in the ticket's history. The new assignee gets a Desk notification; a ticket moved unassigned into a department that takes turns goes to that department's next member. Ticket-updated automations run when something changed.\n\n**Permission:** `crm.tickets.update` (Update tickets).","operationId":"assignTicket","parameters":[{"name":"ticketId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The ticket to assign","example":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"assignedTo":{"type":"string","format":"uuid","description":"The person to work on it (this or teamId is needed)","examples":["1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"]},"teamId":{"type":"string","format":"uuid","description":"The department to put it in (this or assignedTo is needed)","examples":["2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"]},"userId":{"type":"string","format":"uuid","description":"Who is assigning, for the ticket's history","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"ticketNumber":{"type":"string","description":"The number people quote, e.g. TKT-001042"},"subject":{"type":"string"},"description":{"type":"string"},"channel":{"type":"string","description":"Where it came in: web, email, whatsapp, phone, chat, telegram and the like"},"status":{"type":"string","description":"open, pending, in_progress, waiting_on_customer, waiting_on_third_party, resolved or closed"},"priority":{"type":"string","description":"low, medium, high or urgent"},"type":{"type":"string"},"category":{"type":"string","description":"One of the organisation's Desk ticket categories"},"accountId":{"type":"string"},"contactId":{"type":"string"},"subscriptionId":{"type":"string"},"leadId":{},"dealId":{},"requesterEmail":{"type":"string"},"requesterName":{"type":"string"},"ownerId":{"type":"string","description":"The person who filed it"},"assignedTo":{"type":"string","description":"The person working on it"},"teamId":{"type":"string","description":"The department it sits with"},"tags":{"type":"array","items":{"type":"string"}},"slaPolicyId":{"type":"string"},"slaStatus":{"type":"object","properties":{"firstResponseDue":{"type":"string","format":"date-time"},"resolutionDue":{"type":"string","format":"date-time"},"firstResponseAt":{"type":"string","format":"date-time"}},"description":"The service-level clocks: when the first reply and the resolution are due, and when they happened"},"customFields":{"type":"object","properties":{}},"mergedInto":{},"relatedTickets":{"type":"array"},"satisfaction":{"type":"object","properties":{}},"firstResponseTime":{"type":"integer","description":"Minutes from opening to the first agent reply"},"resolutionTime":{"description":"Minutes from opening to resolution"},"reopenCount":{"type":"integer"},"holdUntil":{"description":"On hold: when it comes back by itself (null when not on hold)"},"holdMode":{"description":"On hold: timer (for a time) or reply (until the customer replies, back after the organisation's no-reply days at most)"},"holdStartedAt":{"description":"On hold: since when"},"holdBy":{"description":"On hold: who put it on hold"},"holdPreviousStatus":{"description":"On hold: the status before, which Undo restores"},"backFromHoldAt":{"description":"When a hold ended by itself; the ticket tops its assignee's My open until an agent replies, holds or resolves it"},"backFromHoldReason":{"description":"timer (no reply in time) or customer_reply"},"version":{"type":"integer","description":"Goes up by one on every change"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"assignee":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"}}},"team":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}}}}}},"example":{"success":true,"message":"Ticket assignment updated successfully","data":{"id":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","ticketNumber":"TKT-001042","subject":"Internet down since this morning","description":"The router shows a red light and nothing loads since about 7am.","channel":"whatsapp","status":"open","priority":"high","type":"incident","category":"Connectivity","accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","contactId":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","subscriptionId":"b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d","leadId":null,"dealId":null,"requesterEmail":"amina.wanjiru@example.com","requesterName":"Amina Wanjiru","ownerId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","assignedTo":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","teamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","tags":["outage"],"slaPolicyId":"e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a","slaStatus":{"firstResponseDue":"2026-10-20T10:15:00.000Z","resolutionDue":"2026-10-21T08:15:00.000Z","firstResponseAt":"2026-10-20T08:42:00.000Z"},"customFields":{},"mergedInto":null,"relatedTickets":[],"satisfaction":{},"firstResponseTime":27,"resolutionTime":null,"reopenCount":0,"holdUntil":null,"holdMode":null,"holdStartedAt":null,"holdBy":null,"holdPreviousStatus":null,"backFromHoldAt":null,"backFromHoldReason":null,"version":2,"deletedAt":null,"createdAt":"2026-10-20T08:15:00.000Z","updatedAt":"2026-10-20T09:05:00.000Z","assignee":{"id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","firstName":"Brian","lastName":"Otieno"},"team":{"id":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","name":"Technical Support"}}}}}},"400":{"description":"Neither assignedTo nor teamId was sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No ticket with that ID that you may see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.update"]}],"x-required-scopes":["crm.tickets.update"]}},"/tickets/callbacks/installation-completed":{"post":{"tags":["Integrations"],"summary":"Report a finished installation job","description":"Kazee Tickets calls this when an installation job finishes. CRM finds the one deal that raised that job, records when the work finished (and the service identifier, if sent) on the deal, and moves the deal along its installation-completed step, which is what starts billing. Off unless the organisation has switched the Kazee Tickets integration on.\n\n**Permission:** `crm.tickets.update` (Update tickets).","operationId":"completeInstallationCallback","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"completedAt":{"type":"string","format":"date-time","description":"When the work really finished; billing starts from it. Defaults to now","examples":["2026-10-20T15:30:00.000Z"]},"purpose":{"type":"string","default":"installation","description":"Which kind of job the deal raised","examples":["installation"]},"serviceIdentifier":{"type":"string","description":"The service identifier the technician set up (e.g. the PPPoE user name), saved on the deal","examples":["amina.wanjiru@fibre"]},"ticketId":{"type":"string","description":"The Kazee Tickets job that finished, as the deal recorded it when it raised the job","examples":["INS-20431"]}},"required":["ticketId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"dealId":{"type":"string","description":"The deal that raised the job"},"completedAt":{"type":"string","format":"date-time","description":"When the work finished, as recorded"},"advanced":{"type":"boolean","description":"Whether the deal moved to its next stage. When false, a reason field says why (e.g. the deal is at another stage)"}}},"example":{"dealId":"d3c4d5e6-f7a8-4b9c-8e1f-2a3b4c5d6e7f","completedAt":"2026-10-20T15:30:00.000Z","advanced":true}}}},"400":{"description":"No deal (or more than one) is waiting on that job, the integration is off for the organisation, or completedAt is not a date","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.update"]}],"x-required-scopes":["crm.tickets.update"]}},"/tickets/{ticketId}/merge":{"post":{"tags":["Tickets"],"summary":"Merge a ticket into another","description":"Merges a ticket into another one, which is kept: the merged ticket's messages (internal notes too), files, team notes, filed calls and followers move to the kept ticket, its tags join, and it is closed with a link to the kept one. All in one step; both tickets must be ones you may see.\n\n**Permission:** `crm.tickets.update` (Update tickets).","operationId":"mergeTicket","parameters":[{"name":"ticketId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The ticket to merge away (it gets closed)","example":"4f5a6b7c-8d9e-4f0a-9b2c-3d4e5f6a7b8c"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"targetTicketId":{"type":"string","format":"uuid","description":"The ticket to keep","examples":["3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b"]},"userId":{"type":"string","format":"uuid","description":"Who merges, for the history when the sign-in names nobody","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]}},"required":["targetTicketId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"sourceTicketId":{"type":"string","description":"The merged ticket, now closed"},"targetTicket":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"ticketNumber":{"type":"string","description":"The number people quote, e.g. TKT-001042"},"subject":{"type":"string"},"description":{"type":"string"},"channel":{"type":"string","description":"Where it came in: web, email, whatsapp, phone, chat, telegram and the like"},"status":{"type":"string","description":"open, pending, in_progress, waiting_on_customer, waiting_on_third_party, resolved or closed"},"priority":{"type":"string","description":"low, medium, high or urgent"},"type":{"type":"string"},"category":{"type":"string","description":"One of the organisation's Desk ticket categories"},"accountId":{"type":"string"},"contactId":{"type":"string"},"subscriptionId":{"type":"string"},"leadId":{},"dealId":{},"requesterEmail":{"type":"string"},"requesterName":{"type":"string"},"ownerId":{"type":"string","description":"The person who filed it"},"assignedTo":{"type":"string","description":"The person working on it"},"teamId":{"type":"string","description":"The department it sits with"},"tags":{"type":"array","items":{"type":"string"}},"slaPolicyId":{"type":"string"},"slaStatus":{"type":"object","properties":{"firstResponseDue":{"type":"string","format":"date-time"},"resolutionDue":{"type":"string","format":"date-time"},"firstResponseAt":{"type":"string","format":"date-time"}},"description":"The service-level clocks: when the first reply and the resolution are due, and when they happened"},"customFields":{"type":"object","properties":{}},"mergedInto":{},"relatedTickets":{"type":"array"},"satisfaction":{"type":"object","properties":{}},"firstResponseTime":{"type":"integer","description":"Minutes from opening to the first agent reply"},"resolutionTime":{"description":"Minutes from opening to resolution"},"reopenCount":{"type":"integer"},"holdUntil":{"description":"On hold: when it comes back by itself (null when not on hold)"},"holdMode":{"description":"On hold: timer (for a time) or reply (until the customer replies, back after the organisation's no-reply days at most)"},"holdStartedAt":{"description":"On hold: since when"},"holdBy":{"description":"On hold: who put it on hold"},"holdPreviousStatus":{"description":"On hold: the status before, which Undo restores"},"backFromHoldAt":{"description":"When a hold ended by itself; the ticket tops its assignee's My open until an agent replies, holds or resolves it"},"backFromHoldReason":{"description":"timer (no reply in time) or customer_reply"},"version":{"type":"integer","description":"Goes up by one on every change"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"assignee":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"}}},"team":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}}}},"moved":{"type":"object","properties":{"messages":{"type":"integer"},"files":{"type":"integer"},"notes":{"type":"integer"},"calls":{"type":"integer"}},"description":"How many messages, files, internal notes and filed calls moved to the kept ticket"}}}}},"example":{"success":true,"message":"Ticket TKT-001041 merged into TKT-001042","data":{"sourceTicketId":"4f5a6b7c-8d9e-4f0a-9b2c-3d4e5f6a7b8c","targetTicket":{"id":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","ticketNumber":"TKT-001042","subject":"Internet down since this morning","description":"The router shows a red light and nothing loads since about 7am.","channel":"whatsapp","status":"open","priority":"high","type":"incident","category":"Connectivity","accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","contactId":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","subscriptionId":"b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d","leadId":null,"dealId":null,"requesterEmail":"amina.wanjiru@example.com","requesterName":"Amina Wanjiru","ownerId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","assignedTo":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","teamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","tags":["outage","billing"],"slaPolicyId":"e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a","slaStatus":{"firstResponseDue":"2026-10-20T10:15:00.000Z","resolutionDue":"2026-10-21T08:15:00.000Z","firstResponseAt":"2026-10-20T08:42:00.000Z"},"customFields":{},"mergedInto":null,"relatedTickets":[],"satisfaction":{},"firstResponseTime":27,"resolutionTime":null,"reopenCount":0,"holdUntil":null,"holdMode":null,"holdStartedAt":null,"holdBy":null,"holdPreviousStatus":null,"backFromHoldAt":null,"backFromHoldReason":null,"version":3,"deletedAt":null,"createdAt":"2026-10-20T08:15:00.000Z","updatedAt":"2026-10-20T09:05:00.000Z","assignee":{"id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","firstName":"Brian","lastName":"Otieno"},"team":{"id":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","name":"Technical Support"}},"moved":{"messages":4,"files":1,"notes":1,"calls":1}}}}}},"400":{"description":"The two tickets are the same","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Either ticket does not exist or you may not see it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The ticket was already merged, or the kept ticket was itself merged into another","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.update"]}],"x-required-scopes":["crm.tickets.update"]}},"/tickets/{ticketId}/hold/undo":{"post":{"tags":["Tickets"],"summary":"Undo a ticket's hold","description":"Takes a ticket off hold while it is still on hold: the status it had before the hold comes back (open, pending or in progress) and the hold is cleared, noted in the ticket's history. Once the hold has ended (the customer replied, the time ran out or someone moved the ticket) there is nothing to undo.\n\n**Permission:** `crm.tickets.update` (Update tickets).","operationId":"undoTicketHold","parameters":[{"name":"ticketId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The ticket to take off hold","example":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string","format":"uuid","description":"Who undoes it, when the sign-in names nobody","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"ticketNumber":{"type":"string","description":"The number people quote, e.g. TKT-001042"},"subject":{"type":"string"},"description":{"type":"string"},"channel":{"type":"string","description":"Where it came in: web, email, whatsapp, phone, chat, telegram and the like"},"status":{"type":"string","description":"open, pending, in_progress, waiting_on_customer, waiting_on_third_party, resolved or closed"},"priority":{"type":"string","description":"low, medium, high or urgent"},"type":{"type":"string"},"category":{"type":"string","description":"One of the organisation's Desk ticket categories"},"accountId":{"type":"string"},"contactId":{"type":"string"},"subscriptionId":{"type":"string"},"leadId":{},"dealId":{},"requesterEmail":{"type":"string"},"requesterName":{"type":"string"},"ownerId":{"type":"string","description":"The person who filed it"},"assignedTo":{"type":"string","description":"The person working on it"},"teamId":{"type":"string","description":"The department it sits with"},"tags":{"type":"array","items":{"type":"string"}},"slaPolicyId":{"type":"string"},"slaStatus":{"type":"object","properties":{"firstResponseDue":{"type":"string","format":"date-time"},"resolutionDue":{"type":"string","format":"date-time"},"firstResponseAt":{"type":"string","format":"date-time"}},"description":"The service-level clocks: when the first reply and the resolution are due, and when they happened"},"customFields":{"type":"object","properties":{}},"mergedInto":{},"relatedTickets":{"type":"array"},"satisfaction":{"type":"object","properties":{}},"firstResponseTime":{"type":"integer","description":"Minutes from opening to the first agent reply"},"resolutionTime":{"description":"Minutes from opening to resolution"},"reopenCount":{"type":"integer"},"holdUntil":{"description":"On hold: when it comes back by itself (null when not on hold)"},"holdMode":{"description":"On hold: timer (for a time) or reply (until the customer replies, back after the organisation's no-reply days at most)"},"holdStartedAt":{"description":"On hold: since when"},"holdBy":{"description":"On hold: who put it on hold"},"holdPreviousStatus":{"description":"On hold: the status before, which Undo restores"},"backFromHoldAt":{"description":"When a hold ended by itself; the ticket tops its assignee's My open until an agent replies, holds or resolves it"},"backFromHoldReason":{"description":"timer (no reply in time) or customer_reply"},"version":{"type":"integer","description":"Goes up by one on every change"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Hold undone; the ticket is open again","data":{"id":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","ticketNumber":"TKT-001042","subject":"Internet down since this morning","description":"The router shows a red light and nothing loads since about 7am.","channel":"whatsapp","status":"open","priority":"high","type":"incident","category":"Connectivity","accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","contactId":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","subscriptionId":"b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d","leadId":null,"dealId":null,"requesterEmail":"amina.wanjiru@example.com","requesterName":"Amina Wanjiru","ownerId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","assignedTo":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","teamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","tags":["outage"],"slaPolicyId":"e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a","slaStatus":{"firstResponseDue":"2026-10-20T10:15:00.000Z","resolutionDue":"2026-10-21T08:15:00.000Z","firstResponseAt":"2026-10-20T08:42:00.000Z"},"customFields":{},"mergedInto":null,"relatedTickets":[],"satisfaction":{},"firstResponseTime":27,"resolutionTime":null,"reopenCount":0,"holdUntil":null,"holdMode":null,"holdStartedAt":null,"holdBy":null,"holdPreviousStatus":null,"backFromHoldAt":null,"backFromHoldReason":null,"version":5,"deletedAt":null,"createdAt":"2026-10-20T08:15:00.000Z","updatedAt":"2026-10-20T09:05:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No ticket with that ID that you may see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The ticket is no longer on hold","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.update"]}],"x-required-scopes":["crm.tickets.update"]}},"/tickets/{ticketId}/hold":{"post":{"tags":["Tickets"],"summary":"Put a ticket on hold","description":"Puts a ticket on hold: the ticket blueprint's move to waiting_on_customer runs (with no such move from its status, but one to in_progress and one from there, it steps through in_progress), the service-level resolution clock pauses where the policy says so, and it comes back by itself, in progress with the same assignee and tagged back from hold, when the customer replies or the hold runs out. Send one of minutes, until or mode reply (until they reply, which still comes back after the organisation's no-reply wait); send none for the organisation's default hold. When the move asks for fields or a comment, send fieldValues and comment; any still missing refuse it (422, missing, commentRequired and fields, which describes each missing field). A ticket already on hold gets the new time.\n\n**Permission:** `crm.tickets.update` (Update tickets).","operationId":"holdTicket","parameters":[{"name":"ticketId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The ticket to put on hold","example":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"comment":{"type":"string","description":"The hold move's comment, needed when it asks for one; kept in the move's history","examples":["Waiting for the router serial number"]},"fieldValues":{"type":"object","description":"Values for the hold move's required fields, by field id (a JSON string of the object also works); saved on the ticket","examples":[{"category":"Billing"}]},"minutes":{"type":"integer","minimum":1,"maximum":43200,"description":"Hold for this many minutes, 1 to 43200 (30 days)","examples":[240]},"mode":{"type":"string","enum":["timer","reply"],"description":"reply: until the customer replies (comes back after the organisation's no-reply wait at most); timer: for a time (the default)","examples":["timer"]},"until":{"type":"string","format":"date-time","description":"Hold until this time, within 30 days","examples":["2026-10-31T14:30:00.000Z"]},"userId":{"type":"string","format":"uuid","description":"Who puts it on hold, when the sign-in names nobody","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"ticketNumber":{"type":"string","description":"The number people quote, e.g. TKT-001042"},"subject":{"type":"string"},"description":{"type":"string"},"channel":{"type":"string","description":"Where it came in: web, email, whatsapp, phone, chat, telegram and the like"},"status":{"type":"string","description":"open, pending, in_progress, waiting_on_customer, waiting_on_third_party, resolved or closed"},"priority":{"type":"string","description":"low, medium, high or urgent"},"type":{"type":"string"},"category":{"type":"string","description":"One of the organisation's Desk ticket categories"},"accountId":{"type":"string"},"contactId":{"type":"string"},"subscriptionId":{"type":"string"},"leadId":{},"dealId":{},"requesterEmail":{"type":"string"},"requesterName":{"type":"string"},"ownerId":{"type":"string","description":"The person who filed it"},"assignedTo":{"type":"string","description":"The person working on it"},"teamId":{"type":"string","description":"The department it sits with"},"tags":{"type":"array","items":{"type":"string"}},"slaPolicyId":{"type":"string"},"slaStatus":{"type":"object","properties":{"firstResponseDue":{"type":"string","format":"date-time"},"resolutionDue":{"type":"string","format":"date-time"},"firstResponseAt":{"type":"string","format":"date-time"}},"description":"The service-level clocks: when the first reply and the resolution are due, and when they happened"},"customFields":{"type":"object","properties":{}},"mergedInto":{},"relatedTickets":{"type":"array"},"satisfaction":{"type":"object","properties":{}},"firstResponseTime":{"type":"integer","description":"Minutes from opening to the first agent reply"},"resolutionTime":{"description":"Minutes from opening to resolution"},"reopenCount":{"type":"integer"},"holdUntil":{"type":"string","format":"date-time","description":"On hold: when it comes back by itself (null when not on hold)"},"holdMode":{"type":"string","description":"On hold: timer (for a time) or reply (until the customer replies, back after the organisation's no-reply days at most)"},"holdStartedAt":{"type":"string","format":"date-time","description":"On hold: since when"},"holdBy":{"type":"string","description":"On hold: who put it on hold"},"holdPreviousStatus":{"type":"string","description":"On hold: the status before, which Undo restores"},"backFromHoldAt":{"description":"When a hold ended by itself; the ticket tops its assignee's My open until an agent replies, holds or resolves it"},"backFromHoldReason":{"description":"timer (no reply in time) or customer_reply"},"version":{"type":"integer","description":"Goes up by one on every change"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"message":"Ticket on hold until 2026-10-31T14:30:00.000Z","data":{"id":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","organizationId":"5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f","ticketNumber":"TKT-001042","subject":"Internet down since this morning","description":"The router shows a red light and nothing loads since about 7am.","channel":"whatsapp","status":"waiting_on_customer","priority":"high","type":"incident","category":"Connectivity","accountId":"9e0f1a2b-3c4d-4e5f-8a7b-8c9d0e1f2a3b","contactId":"a0f1a2b3-c4d5-4e6f-9b8c-9d0e1f2a3b4c","subscriptionId":"b1a2b3c4-d5e6-4f7a-8c9d-0e1f2a3b4c5d","leadId":null,"dealId":null,"requesterEmail":"amina.wanjiru@example.com","requesterName":"Amina Wanjiru","ownerId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","assignedTo":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","teamId":"2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a","tags":["outage"],"slaPolicyId":"e4d5e6f7-a8b9-4c0d-9f2a-3b4c5d6e7f8a","slaStatus":{"firstResponseDue":"2026-10-20T10:15:00.000Z","resolutionDue":"2026-10-21T08:15:00.000Z","firstResponseAt":"2026-10-20T08:42:00.000Z"},"customFields":{},"mergedInto":null,"relatedTickets":[],"satisfaction":{},"firstResponseTime":27,"resolutionTime":null,"reopenCount":0,"holdUntil":"2026-10-31T14:30:00.000Z","holdMode":"timer","holdStartedAt":"2026-10-31T10:30:00.000Z","holdBy":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","holdPreviousStatus":"open","backFromHoldAt":null,"backFromHoldReason":null,"version":4,"deletedAt":null,"createdAt":"2026-10-20T08:15:00.000Z","updatedAt":"2026-10-31T10:30:00.000Z"}}}}},"400":{"description":"More than one of minutes, until and mode reply; minutes not a whole number from 1 to 43200; until not a date, not in the future or more than 30 days ahead; mode not timer or reply","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The hold move is limited to roles or people and you are not one of them","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No ticket with that ID that you may see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The ticket blueprint has no move to waiting on customer from the ticket's status (resolved or closed in the default one)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The hold move needs fields or a comment that are missing: missing lists the field ids, commentRequired says the comment is wanted, fields describes each missing field as {id, label, type, options?} (error.code MISSING_DETAILS)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.update"]}],"x-required-scopes":["crm.tickets.update"]}},"/teams/{teamId}/members":{"post":{"tags":["Teams & people"],"summary":"Add a person to a team","description":"Adds one person to a team (a department in Kazee Desk) and answers with the updated team. Needs the permission to create teams.\n\n**Permission:** `crm.teams.create` (Create teams).","operationId":"addTeamMember","parameters":[{"name":"teamId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The team's ID","example":"3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["leader","member"],"default":"member","description":"Their role in the team","examples":["member"]},"userId":{"type":"string","format":"uuid","description":"The person's CRM user ID (a 24-character Kazee sign-in ID also works)","examples":["8e2f3a4b-5c6d-4e7f-9a0b-1c2d3e4f5a6b"]}},"required":["userId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"_id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"members":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string","description":"The member's CRM user ID"},"role":{"type":"string","description":"leader or member"},"joinedAt":{"type":"string","format":"date-time"}}}},"isActive":{"type":"boolean"},"autoAssign":{"type":"boolean","description":"Whether new tickets are assigned automatically"},"assignmentMethod":{"type":"string","description":"How new tickets are handed out: manual or round_robin"},"currentAssigneeIndex":{"type":"integer"},"specializations":{"type":"array","items":{"type":"string"}},"ticketVisibility":{"type":"string","description":"Who sees the department's tickets in Kazee Desk: members (its members, the assignee and Desk admins) or everyone"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"message":{"type":"string"}}},"example":{"success":true,"data":{"id":"3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f","_id":"3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","name":"Fibre support","description":"First-line help for fibre customers","members":[{"userId":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","role":"leader","joinedAt":"2026-10-01T08:30:00.000Z"},{"userId":"8e2f3a4b-5c6d-4e7f-9a0b-1c2d3e4f5a6b","role":"member","joinedAt":"2026-10-01T08:30:00.000Z"}],"isActive":true,"autoAssign":true,"assignmentMethod":"round_robin","currentAssigneeIndex":0,"specializations":["fibre","billing"],"ticketVisibility":"members","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"},"message":"Member added to team"}}}},"400":{"description":"role is not leader or member, or the person is not in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No team with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"That person is already in the team","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.teams.create"]}],"x-required-scopes":["crm.teams.create"]}},"/macros/{macroId}/execute":{"post":{"tags":["Tickets"],"summary":"Run a macro on a ticket","description":"Runs a macro's steps on a ticket you may see, in order: field changes are saved together, a reply goes out on the ticket's channel like a typed one, and a status change goes through the usual status rules (SLA clocks, notifications, satisfaction survey on close). A step that fails does not stop the others; each step's result is in the reply. Counts one more use of the macro.\n\n**Permission:** `crm.macros.update` (Update macros).","operationId":"executeMacro","parameters":[{"name":"macroId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The macro to run","example":"b9a0b1c2-d3e4-4f5a-8c7d-8e9f0a1b2c3d"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ticketId":{"type":"string","format":"uuid","description":"The ticket to run it on","examples":["3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b"]},"userId":{"type":"string","format":"uuid","description":"Your user ID: the author of its notes and replies and the actor of its changes","examples":["1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"]}},"required":["ticketId","userId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"macroId":{"type":"string"},"macroName":{"type":"string"},"ticketId":{"type":"string"},"actionsApplied":{"type":"integer","description":"Steps that were applied, sent or stored"},"actionsFailed":{"type":"integer"},"actionsSkipped":{"type":"integer"},"results":{"type":"array","items":{"type":"object","properties":{"action":{"type":"string"},"status":{"type":"string","description":"applied, sent (handed to the channel), stored (saved as a reply on a channel that sends nothing), failed or skipped (unknown step type)"},"detail":{"type":"string","description":"The value used, or why it failed or was skipped"}}}}}}}},"example":{"success":true,"message":"Macro \"Line restarted, waiting on customer\" executed on ticket 3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","data":{"macroId":"b9a0b1c2-d3e4-4f5a-8c7d-8e9f0a1b2c3d","macroName":"Line restarted, waiting on customer","ticketId":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","actionsApplied":3,"actionsFailed":0,"actionsSkipped":0,"results":[{"action":"set_status","status":"applied","detail":"waiting_on_customer"},{"action":"add_tag","status":"applied","detail":"line-restart"},{"action":"send_reply","status":"sent"}]}}}}},"400":{"description":"The macro is switched off","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No macro with that ID, or no ticket with that ID that you may see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.macros.update"]}],"x-required-scopes":["crm.macros.update"]}},"/connections/{name}/test":{"post":{"tags":["Integrations"],"summary":"Test a connection","description":"Tests a saved connection: sends one GET to its address (plus an optional path) with its stored headers and answers the HTTP status, so you can tell saved from working. A connection that is missing, switched off or does not answer comes back as ok: false with the reason, not as an error. Needs connections.update.\n\n**Permission:** `crm.connections.update` (Manage connections).","operationId":"testConnection","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"},"description":"The connection's name: 1 to 63 characters of a-z, 0-9, \"_\" or \"-\", starting with a letter or digit (capitals are made small)","example":"billing-api"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"path":{"type":"string","description":"Path to call under the connection's address (default /)","examples":["/health"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"name":{"type":"string"},"ok":{"type":"boolean","description":"The address answered with a 2xx status"},"status":{"type":"integer","description":"The HTTP status; 0 when no answer came"},"error":{"description":"Why it did not work; null when ok"}}}}},"example":{"success":true,"data":{"name":"billing-api","ok":true,"status":200,"error":null}}}}},"400":{"description":"The name is not valid (1 to 63 characters of a-z, 0-9, _ or -)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.connections.update"]}],"x-required-scopes":["crm.connections.update"]}},"/integrations/{source}/test":{"post":{"tags":["Integrations"],"summary":"Test an import connection","description":"Signs in to the system to check a connection. With credentials or settings it tests those and saves nothing; without them it tests the saved connection and, when it works, switches it on and names it after the account it reached. A failed test still answers 200, with success false and the reason in message.\n\n**Permission:** `crm.importer.admin` (Administer importer).","operationId":"testImporterConnection","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"credentials":{"type":"object","description":"Sign-in details to test instead of the saved ones (nothing is saved). For Zoho: clientId, clientSecret and refreshToken","examples":[{"clientId":"1000.ABCDEFGHIJKLMNOPQRSTUVWXYZ","clientSecret":"0123456789abcdef0123456789abcdef9f3a","refreshToken":"1000.0123456789abcdef.0123456789abcdef71c2"}]},"settings":{"type":"object","description":"Settings to test with the credentials above, e.g. Zoho's dataCenter","examples":[{"dataCenter":"com"}]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"true when the sign-in worked"},"displayName":{"type":"string","description":"The account the system reports, e.g. the Zoho organisation's name"},"message":{"type":"string","description":"In words: connected, or why not"}}},"example":{"success":true,"displayName":"Example Telecom Ltd","message":"Connected to Example Telecom Ltd"}}}},"400":{"description":"source is missing or not a known system, or nothing is given and no connection is saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.admin"]}],"x-required-scopes":["crm.importer.admin"]}},"/integrations/{source}/import":{"post":{"tags":["Integrations"],"summary":"Start a one-off import","description":"Starts a one-off import of one module with a saved field-mapping set, without setting up a recurring sync. Answers 202 at once with the import's jobId; it runs in the background, so follow it with GET /integrations/import-jobs/{jobId}. Leads are matched to ones already imported; other records are always created new.\n\n**Permission:** `crm.importer.admin` (Administer importer).","operationId":"startImporterImport","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mappingId":{"type":"string","format":"uuid","description":"The field-mapping set to import with (from the field mappings list)","examples":["c3a9d8e7-1f2b-4c5d-8e9f-0a1b2c3d4e5f"]}},"required":["mappingId"]}}}},"responses":{"202":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"jobId":{"type":"string"},"source":{"type":"string"},"status":{"type":"string"},"remoteModule":{"type":"string"},"crmModule":{"type":"string"}}},"message":{"type":"string"}}},"example":{"success":true,"data":{"jobId":"e9d8c7b6-a5f4-4e3d-9c2b-1a0f9e8d7c6b","source":"zoho","status":"running","remoteModule":"Leads","crmModule":"leads"},"message":"Import started"}}}},"400":{"description":"source or mappingId is missing, or source is not a known system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No field-mapping set with that ID, or no switched-on connection to that system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.admin"]}],"x-required-scopes":["crm.importer.admin"]}},"/integrations/{source}/module-links/batch":{"post":{"tags":["Integrations"],"summary":"Save all module links","description":"Saves a whole set of module links for a system at once: each is updated (by linkId, or by its module and CRM record type) or created, and links to modules not in the set are removed. A link that fails is listed in errors while the rest are saved, so check errors even when success is true.\n\n**Permission:** `crm.importer.update` (Update importer).","operationId":"batchSaveImporterModuleLinks","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"links":{"type":"array","items":{"type":"object","properties":{"linkId":{"type":"string","format":"uuid"},"remoteModule":{"type":"string"},"remoteModuleLabel":{"type":"string"},"crmTarget":{"type":"string"},"crmTargetType":{"type":"string","enum":["builtin","custom"]},"fieldMappings":{"type":"array","items":{"type":"object"}},"skippedFields":{"type":"array","items":{"type":"string"}},"syncEnabled":{"type":"boolean"},"syncMode":{"type":"string","enum":["incremental","full"]},"unmappedFieldAction":{"type":"string","enum":["ignore","create_custom_field"]}},"required":["remoteModule","crmTarget","fieldMappings"]},"description":"Every link the system should have, each like the single save (POST /integrations/{source}/module-links) plus skippedFields (the system's fields left out on purpose). Links to modules not listed are removed. A JSON array in a string also works","examples":[[{"linkId":"7f6e5d4c-3b2a-4190-8f7e-6d5c4b3a2918","remoteModule":"Deals","remoteModuleLabel":"Deals","crmTarget":"deals","fieldMappings":[{"remoteField":"Stage","remoteFieldLabel":"Stage","remoteFieldType":"picklist","crmField":"stage","crmFieldLabel":"Stage","valueMap":{"Interested":"qualification","Closed Won":"won"}}],"skippedFields":["Description"],"syncEnabled":true,"syncMode":"incremental","unmappedFieldAction":"ignore"}]]}},"required":["links"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"source":{"type":"string"},"remoteModule":{"type":"string"},"remoteModuleLabel":{"type":"string"},"crmTarget":{"type":"string"},"crmTargetType":{"type":"string"},"fieldMappings":{"type":"array","items":{"type":"object","properties":{"remoteField":{"type":"string"},"remoteFieldLabel":{"type":"string"},"remoteFieldType":{"type":"string"},"crmField":{"type":"string"},"crmFieldLabel":{"type":"string"}}}},"skippedFields":{"type":"array","items":{"type":"string"}},"syncEnabled":{"type":"boolean"},"syncMode":{"type":"string"},"lastSyncAt":{"type":"string","format":"date-time"},"unmappedFieldAction":{"type":"string"},"createdBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"errors":{"type":"array","items":{"type":"object","properties":{"remoteModule":{"type":"string"},"error":{"type":"string"}}},"description":"Only when some failed: each failed module and why"},"message":{"type":"string"}}},"example":{"success":true,"data":[{"id":"7f6e5d4c-3b2a-4190-8f7e-6d5c4b3a2918","organizationId":"11111111-2222-4333-8444-555555555555","source":"zoho","remoteModule":"Deals","remoteModuleLabel":"Deals","crmTarget":"deals","crmTargetType":"builtin","fieldMappings":[{"remoteField":"Deal_Name","remoteFieldLabel":"Deal Name","remoteFieldType":"text","crmField":"name","crmFieldLabel":"Name"},{"remoteField":"Stage","remoteFieldLabel":"Stage","remoteFieldType":"picklist","crmField":"stage","crmFieldLabel":"Stage","valueMap":{"Interested":"qualification","Closed Won":"won"}}],"skippedFields":["Description"],"syncEnabled":true,"syncMode":"incremental","lastSyncAt":"2026-10-20T09:04:12.000Z","unmappedFieldAction":"ignore","createdBy":"8b1d3f5a-7c9e-4b0d-8f2a-4c6e8a0b2d4f","deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}],"errors":[{"remoteModule":"Leads","error":"fieldMappings[1].valueMap: a value map applies to stage or status only, not \"email\""}],"message":"1 module link(s) saved, 1 failed"}}}},"400":{"description":"source is missing or not a known system, or links is not a non-empty JSON array","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.update"]}],"x-required-scopes":["crm.importer.update"]}},"/integrations/{source}/module-links/{linkId}/apply-value-map":{"post":{"tags":["Integrations"],"summary":"Apply a module link's value map","description":"Moves the deals (stage) or leads (status) imported from the system that still hold a word a module link's value map renames to the mapped value now, instead of at the system's next change. No transition, automation, notification or event runs. Send dryRun true to only see what would move.\n\n**Permission:** `crm.importer.admin` (Administer importer).","operationId":"applyImporterValueMap","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"},{"name":"linkId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The module link whose value map to apply","example":"7f6e5d4c-3b2a-4190-8f7e-6d5c4b3a2918"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"dryRun":{"type":"boolean","description":"true lists what would move and changes nothing","examples":[true]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"linkId":{"type":"string"},"source":{"type":"string"},"target":{"type":"string"},"field":{"type":"string","description":"stage for deals, status for leads"},"dryRun":{"type":"boolean"},"matched":{"type":"integer","description":"Records holding a mapped word (on a dry run: that would move)"},"changed":{"type":"integer","description":"Records moved by this call; 0 on a dry run"},"moves":{"type":"array","items":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"},"ids":{"type":"array","items":{"type":"string"}}}},"description":"Per word: the value it moves to and the records"},"ids":{"type":"array","items":{"type":"string"}}}}}},"example":{"success":true,"data":{"linkId":"7f6e5d4c-3b2a-4190-8f7e-6d5c4b3a2918","source":"zoho","target":"deals","field":"stage","dryRun":false,"matched":2,"changed":2,"moves":[{"from":"interested","to":"qualification","ids":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d"]}],"ids":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d"]}}}}},"400":{"description":"The link ID is not valid, the link writes neither deals nor leads, or its stage or status mapping has no value map","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No module link with that ID for this system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Two mappings on the link map one word to different values","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.admin"]}],"x-required-scopes":["crm.importer.admin"]}},"/integrations/{source}/sync":{"post":{"tags":["Integrations"],"summary":"Sync now","description":"Starts a sync now instead of waiting for the schedule: every switched-on module link of the system, or one. Answers 202; the sync runs in the background, so follow it in the sync runs list. With moduleLinkId it can also catch up from a past moment (replaySince) or re-read named records (remoteIds), without moving the link's last-sync mark.\n\n**Permission:** `crm.importer.admin` (Administer importer).","operationId":"syncImporterNow","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"moduleLinkId":{"type":"string","format":"uuid","description":"Sync only this module link; leave out to sync every switched-on one","examples":["7f6e5d4c-3b2a-4190-8f7e-6d5c4b3a2918"]},"remoteIds":{"type":"array","items":{"type":"string"},"description":"A one-off catch-up of these records only: the system's record IDs (a list or comma-separated, at most 2,000), each read and written as a sync would. Needs moduleLinkId; not with replaySince","examples":[["5123000004567001"]]},"replaySince":{"type":"string","format":"date-time","description":"A one-off catch-up: read the module from this past moment instead of its last sync. Needs moduleLinkId. For Leads it reads only the leads the system converted","examples":["2026-10-01T00:00:00.000Z"]}}}}}},"responses":{"202":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}},"example":{"success":true,"message":"Sync started for selected module"}}}},"400":{"description":"source is missing or not a known system; the link's sync is switched off; replaySince is not a past date; replaySince or remoteIds is sent without moduleLinkId, or both are sent; remoteIds is empty, over 2,000 or holds a bad ID; or the system cannot read records by ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No switched-on connection to that system, or no module link with that ID for it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.admin"]}],"x-required-scopes":["crm.importer.admin"]}},"/integrations/zoho/quotations/import":{"post":{"tags":["Integrations"],"summary":"Copy a Zoho quotation onto its deal","description":"Copies one quotation raised in Zoho, which Kazee Books already holds as an estimate, onto its CRM deal as a read-only quote (no quote event, no notification). The same snapshot again changes nothing; a newer snapshot with the same lines refreshes it. Needs both importer admin and billing read permissions.\n\n**Permission:** `crm.importer.admin` (Administer importer) and `crm.billing.read` (View billing documents).","operationId":"importZohoQuotation","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"booksEstimateId":{"type":"string","description":"The Kazee Books estimate holding the Zoho quotation: est_ and 32 hex characters","examples":["est_0123456789abcdef0123456789abcdef"]},"dealId":{"type":"string","format":"uuid","description":"The CRM deal that came from the quotation's Zoho sale","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]}},"required":["booksEstimateId","dealId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"quoteId":{"type":"string"},"created":{"type":"boolean","description":"A new quote was made"},"updated":{"type":"boolean","description":"An already-copied quote took a newer snapshot"},"booksEstimateId":{"type":"string"}}}}},"example":{"success":true,"data":{"quoteId":"1f2e3d4c-5b6a-4978-8d7c-6b5a4f3e2d1c","created":true,"updated":false,"booksEstimateId":"est_0123456789abcdef0123456789abcdef"}}}}},"400":{"description":"The Books estimate ID is not valid, the estimate carries no valid Zoho quotation details or no customer, its dates are not valid, or the deal is not this quotation's Zoho sale or has no account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No deal with that ID, or its account is gone","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The quotation is already in CRM with different details or links, is already copied without its estimate, or the deal's account is linked to another Books customer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"description":"Kazee Books is not set up for your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.importer.admin","crm.billing.read"]}],"x-required-scopes":["crm.importer.admin","crm.billing.read"],"x-scope-mode":"all"}},"/integrations/zoho/quotations/undo":{"post":{"tags":["Integrations"],"summary":"Undo a copied Zoho quotation","description":"Takes a quotation copied from Zoho off its CRM deal (the quote is deleted and gives up its number, so it can be copied again). Name it by CRM quote ID or Zoho quotation ID. Refused for a quote made in Kazee and while anything made from it (an invoice, subscription, payment) remains. Already undone answers undone: false.\n\n**Permission:** `crm.importer.admin` (Administer importer).","operationId":"undoZohoQuotation","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"quoteId":{"type":"string","format":"uuid","description":"The CRM quote; give this or zohoQuotationId","examples":["1f2e3d4c-5b6a-4978-8d7c-6b5a4f3e2d1c"]},"zohoQuotationId":{"type":"string","description":"Zoho's quotation ID; give this or quoteId","examples":["5123000006789003"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"undone":{"type":"boolean","description":"false when it was already undone or deleted (then reason says which)"},"quoteId":{"type":"string"},"quoteNumber":{"type":"string"},"zohoQuotationId":{"type":"string"},"dealId":{"type":"string"}}}}},"example":{"success":true,"data":{"undone":true,"quoteId":"1f2e3d4c-5b6a-4978-8d7c-6b5a4f3e2d1c","quoteNumber":"qtn1117737","zohoQuotationId":"5123000006789003","dealId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"}}}}},"400":{"description":"Neither or both of quoteId and zohoQuotationId given, quoteId is not valid, or the quote was made in Kazee","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No quote with that ID or Zoho quotation ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"More than one live quote carries that Zoho quotation, or something made from the quote still exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.admin"]}],"x-required-scopes":["crm.importer.admin"]}},"/integrations/zoho/quotations/record":{"post":{"tags":["Integrations"],"summary":"Copy a Zoho quotation from its Zoho record","description":"Copies one Zoho quotation from Zoho CRM's own record onto its CRM deal, for a quotation with no Zoho Books estimate (or whose figures disagree with it): a read-only quote with no Books link, event or notification. The same snapshot again changes nothing; a newer one with the same lines refreshes it. Send dryRun true to see what it would do.\n\n**Permission:** `crm.importer.admin` (Administer importer).","operationId":"recordZohoQuotation","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"dealId":{"type":"string","format":"uuid","description":"The CRM deal that came from the quotation's Zoho sale","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]},"dryRun":{"type":"boolean","description":"true checks everything and says what it would do, writing nothing","examples":[true]},"snapshot":{"type":"object","description":"The quotation as read from Zoho CRM (version 1, owner zoho, record_only true): quotation_id, quotation_number, deal_id, status (Zoho's stage), total, amount_paid, currency, modified_at, snapshot_hash, record_only_reason, estimate_id/customer_id/books_status null, record_lines (1 to 200, each quotation_line_id, name, quantity, rate, discount, tax_amount, total; money as text, e.g. \"2500.00\"), and optionally payments, invoices, retainer_invoices, date and expiry_date. Unknown keys are refused. A JSON object in a string also works","examples":[{"version":1,"owner":"zoho","quotation_id":"5123000006789003","quotation_number":"qtn1117737","deal_id":"5123000004567001","status":"Sent","total":"29000.00","amount_paid":"0.00","currency":"KES","modified_at":"2026-10-18T07:30:00.000Z","snapshot_hash":"sha256:9f2c0d4e","record_only":true,"record_only_reason":"No Zoho Books estimate for this quotation","estimate_id":null,"customer_id":null,"books_status":null,"record_lines":[{"quotation_line_id":"5123000006789101","name":"Fibre 50 Mbps installation","quantity":"1.00","rate":"25000.00","discount":"0.00","tax_amount":"4000.00","total":"29000.00"}]}]}},"required":["dealId","snapshot"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"quoteId":{"description":"The quote; null only on a dry run that would create it"},"created":{"type":"boolean"},"updated":{"type":"boolean"},"dryRun":{"type":"boolean"},"wouldCreate":{"type":"boolean","description":"Dry run only: a real call would create the quote"},"wouldUpdate":{"type":"boolean","description":"Dry run only: a real call would refresh it"},"quote":{"type":"object","properties":{"quoteNumber":{"type":"string"},"status":{"type":"string"},"total":{"type":"string"},"paidAmount":{"type":"string"},"balance":{"type":"string"},"paidAt":{},"lines":{"type":"integer"}},"description":"Dry run only: the quote it would hold (lines is a count)"}}}}},"example":{"success":true,"data":{"quoteId":null,"created":false,"updated":false,"dryRun":true,"wouldCreate":true,"wouldUpdate":false,"quote":{"quoteNumber":"qtn1117737","status":"sent","total":"29000.00","paidAmount":"0.00","balance":"29000.00","paidAt":null,"lines":1}}}}}},"400":{"description":"The deal ID is not valid, the snapshot is not valid (every problem is named), or the deal is not this quotation's Zoho sale or has no account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No deal with that ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The quotation is already in CRM with different details or links, changed in Zoho beyond its status and payments, the deal already has a quote made in Kazee, or a deleted quote holds its number","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.admin"]}],"x-required-scopes":["crm.importer.admin"]}},"/integrations/zoho/subscriptions/tie":{"post":{"tags":["Integrations"],"summary":"Tie Zoho subscriptions to their deals","description":"Ties subscriptions imported from Zoho to their sales, so each deal's card shows its subscription. Each pair is checked on its own: refused pairs are listed with the reason and do not stop the rest. Send dryRun true to only see what would happen.\n\n**Permission:** `crm.importer.admin` (Administer importer).","operationId":"tieZohoSubscriptions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"dryRun":{"type":"boolean","description":"true checks every pair and writes nothing","examples":[false]},"pairs":{"type":"array","items":{"type":"object","properties":{"dealId":{"type":"string","format":"uuid"},"subscriptionId":{"type":"string","format":"uuid"},"zohoSubscriptionId":{"type":"string"}},"required":["dealId"]},"description":"Up to 200 pairs, each a deal (dealId) and a subscription named by one of subscriptionId (CRM) or zohoSubscriptionId (Zoho's digits). A JSON array in a string also works","examples":[[{"dealId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","zohoSubscriptionId":"5123000007890004"}]]}},"required":["pairs"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"dryRun":{"type":"boolean"},"counts":{"type":"object","properties":{"tied":{"type":"integer"},"would_tie":{"type":"integer"},"already_tied":{"type":"integer"},"untied":{"type":"integer"},"would_untie":{"type":"integer"},"refused":{"type":"integer"}},"description":"How many pairs had each outcome"},"results":{"type":"array","items":{"type":"object","properties":{"subscriptionId":{},"zohoSubscriptionId":{"type":"string"},"dealId":{"type":"string"},"outcome":{"type":"string","description":"tied, would_tie (dry run), already_tied or refused"},"reason":{"type":"string","description":"Why it was refused, in words"}}}}}}}},"example":{"success":true,"data":{"dryRun":false,"counts":{"tied":1,"would_tie":0,"already_tied":0,"untied":0,"would_untie":0,"refused":1},"results":[{"subscriptionId":null,"zohoSubscriptionId":"5123000007890999","dealId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","outcome":"refused","reason":"No live subscription carries Zoho id 5123000007890999"},{"subscriptionId":"d4c3b2a1-0f9e-4d8c-9b7a-6f5e4d3c2b1a","zohoSubscriptionId":"5123000007890004","dealId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","outcome":"tied"}]}}}}},"400":{"description":"pairs is not a JSON array of 1 to 200, or a pair has a bad dealId or does not name its subscription by exactly one of subscriptionId or zohoSubscriptionId","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.admin"]}],"x-required-scopes":["crm.importer.admin"]}},"/integrations/zoho/subscriptions/untie":{"post":{"tags":["Integrations"],"summary":"Untie Zoho subscriptions from their deals","description":"Takes back the deal link a Zoho subscription tie added (only one a tie added). Same pairs as the tie; each is checked on its own, and refused pairs are listed with the reason. Send dryRun true to only see what would happen.\n\n**Permission:** `crm.importer.admin` (Administer importer).","operationId":"untieZohoSubscriptions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"dryRun":{"type":"boolean","description":"true checks every pair and writes nothing","examples":[false]},"pairs":{"type":"array","items":{"type":"object","properties":{"dealId":{"type":"string","format":"uuid"},"subscriptionId":{"type":"string","format":"uuid"},"zohoSubscriptionId":{"type":"string"}},"required":["dealId"]},"description":"Up to 200 pairs, each a deal (dealId) and a subscription named by one of subscriptionId (CRM) or zohoSubscriptionId (Zoho's digits). A JSON array in a string also works","examples":[[{"dealId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","zohoSubscriptionId":"5123000007890004"}]]}},"required":["pairs"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"dryRun":{"type":"boolean"},"counts":{"type":"object","properties":{"tied":{"type":"integer"},"would_tie":{"type":"integer"},"already_tied":{"type":"integer"},"untied":{"type":"integer"},"would_untie":{"type":"integer"},"refused":{"type":"integer"}},"description":"How many pairs had each outcome"},"results":{"type":"array","items":{"type":"object","properties":{"subscriptionId":{},"zohoSubscriptionId":{"type":"string"},"dealId":{"type":"string"},"outcome":{"type":"string","description":"untied, would_untie (dry run) or refused"},"reason":{"type":"string","description":"Why it was refused, in words"}}}}}}}},"example":{"success":true,"data":{"dryRun":false,"counts":{"tied":0,"would_tie":0,"already_tied":0,"untied":1,"would_untie":0,"refused":1},"results":[{"subscriptionId":null,"zohoSubscriptionId":"5123000007890999","dealId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","outcome":"refused","reason":"No live subscription carries Zoho id 5123000007890999"},{"subscriptionId":"d4c3b2a1-0f9e-4d8c-9b7a-6f5e4d3c2b1a","zohoSubscriptionId":"5123000007890004","dealId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","outcome":"untied"}]}}}}},"400":{"description":"pairs is not a JSON array of 1 to 200, or a pair has a bad dealId or does not name its subscription by exactly one of subscriptionId or zohoSubscriptionId","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.admin"]}],"x-required-scopes":["crm.importer.admin"]}},"/integrations/zoho/relations/fill":{"post":{"tags":["Integrations"],"summary":"Fill a Zoho deal's relations","description":"Fills in what one Zoho sale's CRM deal is missing: ties its account and contact, gives the deal, account and contact Zoho's creation dates, and finds (or makes) the lead it came from and closes it as converted. Recorded under runId so it can be undone; the same runId again is refused.\n\n**Permission:** `crm.importer.admin` (Administer importer).","operationId":"fillZohoRelations","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"dealId":{"type":"string","format":"uuid","description":"The CRM deal that came from the Zoho sale","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]},"runId":{"type":"string","description":"Names this fill so it can be undone: 1-100 letters, digits, '.', ':', '_' or '-'","examples":["zoho-history:2026-10-20T09:00"]},"zoho":{"type":"object","description":"What Zoho says: dealCreatedAt (required), accountCreatedAt and contactCreatedAt (past date-times), and lead { zohoId?, submissionId?, create? } to find the lead it came from (create: { firstName, createdAt, lastName?, businessName?, phone?, email?, source?, ownerName? } makes one when none is found). A JSON object in a string also works","examples":[{"dealCreatedAt":"2025-03-14T09:21:00+03:00","accountCreatedAt":"2025-03-12T13:02:00+03:00","lead":{"zohoId":"5123000003456002"}}]}},"required":["dealId","runId","zoho"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"dealId":{"type":"string"},"runId":{"type":"string"},"changed":{"type":"boolean","description":"Anything was written"},"tie":{"type":"object","properties":{"created":{"type":"boolean"},"id":{"type":"string"},"primary":{"type":"boolean"}},"description":"The account-contact tie: made now, its ID, and whether it is the primary one"},"dates":{"type":"object","properties":{"deal":{"type":"string","format":"date-time"},"account":{"type":"string","format":"date-time"},"contact":{}},"description":"The creation date each record took from Zoho; null = kept its own"},"lead":{"type":"object","properties":{"id":{"type":"string"},"how":{"type":"string","description":"How the lead was found: zoho (by Zoho lead ID), submission (by submission ID), created, linked (already on the deal) or none"},"wasStatus":{"type":"string","description":"Its status before this call"},"closedAsConverted":{"type":"boolean"},"zohoIdRecorded":{"type":"boolean"}}},"dealLinked":{"type":"boolean","description":"The deal was linked to the lead now"},"runRecorded":{"type":"boolean","description":"The run was recorded, so it can be undone"}}}}},"example":{"success":true,"data":{"dealId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","runId":"zoho-history:2026-10-20T09:00","changed":true,"tie":{"created":true,"id":"c0d1e2f3-a4b5-4c6d-8e7f-9a0b1c2d3e4f","primary":true},"dates":{"deal":"2025-03-14T06:21:00.000Z","account":"2025-03-12T10:02:00.000Z","contact":null},"lead":{"id":"6d5c4b3a-2f1e-4d0c-9b8a-7f6e5d4c3b2a","how":"zoho","wasStatus":"qualified","closedAsConverted":true,"zohoIdRecorded":false},"dealLinked":true,"runRecorded":true}}}}},"400":{"description":"dealId is not a uuid, runId is not 1-100 letters, digits, '.', ':', '_' or '-', zoho or zoho.dealCreatedAt is missing, a date is not a past date-time, a lead detail is malformed, the deal has no Zoho sale ID, or it has no account to convert a lead onto","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No deal with that ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"That runId is already recorded on the deal, more than one lead matches, the lead carries a different Zoho lead ID, or the deal is already linked to another lead (or one that is gone)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.admin"]}],"x-required-scopes":["crm.importer.admin"]}},"/integrations/zoho/relations/undo":{"post":{"tags":["Integrations"],"summary":"Undo a Zoho relations fill","description":"Puts back exactly what one Zoho relations fill changed on a deal (dates, lead link, tie, a lead it made). Only the deal's latest run can be undone. A run that is not recorded (never made, or already undone) answers undone: false.\n\n**Permission:** `crm.importer.admin` (Administer importer).","operationId":"undoZohoRelations","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"dealId":{"type":"string","format":"uuid","description":"The deal the fill was on","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]},"runId":{"type":"string","description":"The runId the fill was sent with","examples":["zoho-history:2026-10-20T09:00"]}},"required":["dealId","runId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"dealId":{"type":"string"},"runId":{"type":"string"},"undone":{"type":"boolean","description":"false when no such run is recorded on the deal"},"tieDeleted":{"type":"boolean"},"leadRestored":{"type":"string","description":"The lead whose status was put back, if any"},"createdLead":{"description":"A lead the fill made: deleted, or kept (keptFor says what now uses it)"}}}}},"example":{"success":true,"data":{"dealId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","runId":"zoho-history:2026-10-20T09:00","undone":true,"tieDeleted":true,"leadRestored":"6d5c4b3a-2f1e-4d0c-9b8a-7f6e5d4c3b2a","createdLead":null}}}}},"400":{"description":"dealId is not a uuid, or runId is not 1-100 letters, digits, '.', ':', '_' or '-'","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"A later relations run on this deal must be undone first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.admin"]}],"x-required-scopes":["crm.importer.admin"]}},"/integrations/zoho/history/import":{"post":{"tags":["Integrations"],"summary":"Import a Zoho deal's history","description":"Brings one Zoho sale's history onto its CRM deal: notes become comments, and stage moves, calls, tasks and technicians become Activity rows. Notes that match comments already in CRM are not copied twice. Items sent before are not added again (a replay writes nothing), and Zoho IDs listed in deleted are hidden. Everything is checked first: one bad item refuses the whole call, naming every problem. Undo it with its runId.\n\n**Permission:** `crm.importer.admin` (Administer importer).","operationId":"importZohoHistory","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"dealId":{"type":"string","format":"uuid","description":"The CRM deal that came from the Zoho sale","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]},"deleted":{"type":"array","items":{"type":"string"},"description":"Up to 5,000 Zoho IDs (as text) of items deleted in Zoho since an earlier import: what they brought in is hidden","examples":[["5123000008901004"]]},"items":{"type":"array","items":{"type":"object"},"description":"Required (may be empty, for a run that only hides). Up to 5,000, each with kind, zohoId and userName (the Zoho user), plus: note { createdAt, modifiedAt?, title?, text }; stage { at, stage, durationDays?, amount?, closingDate? }; call { at, subject, direction?, durationSec?, result?, description?, source: deal, account or contact }; task { createdAt, subject, status, dueDate?, closedAt?, priority?, description? }; technician { at, role, name }. Times are ISO 8601 with an offset; dueDate and closingDate may be a plain date","examples":[[{"kind":"note","zohoId":"5123000008901005","userName":"Amina Otieno","createdAt":"2025-03-15T10:00:00+03:00","title":null,"text":"Customer asked for installation next week.<br>Site survey booked."},{"kind":"stage","zohoId":"5123000008901010","userName":"Amina Otieno","at":"2025-03-20T12:30:00+03:00","stage":"Closed Won","amount":29000}]]},"leadIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Up to 50 CRM leads the deal came from: their comments count when matching notes","examples":[["6d5c4b3a-2f1e-4d0c-9b8a-7f6e5d4c3b2a"]]},"runId":{"type":"string","description":"Names this import so it can be undone: 1-100 letters, digits, '.', '_', ':' or '-'","examples":["zoho-history:2026-10-20T09:00"]},"zohoDealId":{"type":"string","description":"Optional cross-check: the Zoho sale the history was read from; refused when the deal is not that sale","examples":["5123000004567001"]}},"required":["dealId","runId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"dealId":{"type":"string"},"runId":{"type":"string"},"runEventId":{"type":"string","description":"The run's own Activity row, which records it for undo"},"counts":{"type":"object","properties":{"note":{"type":"object","properties":{"inserted":{"type":"integer"},"alreadyPresent":{"type":"integer"},"matched":{"type":"integer"},"nearMatches":{"type":"integer"},"hidden":{"type":"integer"}}},"stage":{"type":"object","properties":{"inserted":{"type":"integer"},"alreadyPresent":{"type":"integer"},"matched":{"type":"integer"},"nearMatches":{"type":"integer"},"hidden":{"type":"integer"}}},"call":{"type":"object","properties":{"inserted":{"type":"integer"},"alreadyPresent":{"type":"integer"},"matched":{"type":"integer"},"nearMatches":{"type":"integer"},"hidden":{"type":"integer"}}},"task":{"type":"object","properties":{"inserted":{"type":"integer"},"alreadyPresent":{"type":"integer"},"matched":{"type":"integer"},"nearMatches":{"type":"integer"},"hidden":{"type":"integer"}}},"technician":{"type":"object","properties":{"inserted":{"type":"integer"},"alreadyPresent":{"type":"integer"},"matched":{"type":"integer"},"nearMatches":{"type":"integer"},"hidden":{"type":"integer"}}}},"description":"Per kind (note, stage, call, task, technician): inserted, alreadyPresent (sent before), matched (a CRM comment already says it), nearMatches (looks like one; copied anyway) and hidden (deleted in Zoho)"},"matched":{"type":"array","items":{"type":"object","properties":{"zohoId":{"type":"string"},"matchedCommentId":{"type":"string"},"similarity":{"type":"number"}}},"description":"Zoho notes not copied, and the CRM comment that already says it"},"nearMatches":{"type":"array","items":{"type":"object","properties":{"zohoId":{"type":"string"},"commentId":{"type":"string"},"similarity":{"type":"number"}}},"description":"Zoho notes copied though a CRM comment looks alike, to check by hand"}}}}},"example":{"success":true,"data":{"dealId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","runId":"zoho-history:2026-10-20T09:00","runEventId":"f0e1d2c3-b4a5-4697-8879-6a5b4c3d2e1f","counts":{"note":{"inserted":3,"alreadyPresent":0,"matched":1,"nearMatches":1,"hidden":0},"stage":{"inserted":4,"alreadyPresent":0,"matched":0,"nearMatches":0,"hidden":0},"call":{"inserted":2,"alreadyPresent":0,"matched":0,"nearMatches":0,"hidden":0},"task":{"inserted":1,"alreadyPresent":0,"matched":0,"nearMatches":0,"hidden":0},"technician":{"inserted":1,"alreadyPresent":0,"matched":0,"nearMatches":0,"hidden":0}},"matched":[{"zohoId":"5123000008901005","matchedCommentId":"b6a5f4e3-d2c1-4b0a-9f8e-7d6c5b4a3f2e","similarity":0.97}],"nearMatches":[{"zohoId":"5123000008901006","commentId":"e7f8a9b0-c1d2-4e3f-8a4b-5c6d7e8f9a0b","similarity":0.81}]}}}}},"400":{"description":"A field is malformed (every problem is named), the deal has no Zoho sale ID or is not zohoDealId, or the zoho-crm-bot user is missing from your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No deal with that ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.admin"]}],"x-required-scopes":["crm.importer.admin"]}},"/integrations/zoho/history/undo":{"post":{"tags":["Integrations"],"summary":"Undo a Zoho history import","description":"Takes back one Zoho history import on a deal: the comments and Activity rows it added are deleted, and what it hid is shown again. Refused when staff replied to an imported note. A run that is not recorded (never ran, or already undone) answers found: false.\n\n**Permission:** `crm.importer.admin` (Administer importer).","operationId":"undoZohoHistory","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"dealId":{"type":"string","format":"uuid","description":"The deal the import was on","examples":["0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"]},"runId":{"type":"string","description":"The runId the import was sent with","examples":["zoho-history:2026-10-20T09:00"]}},"required":["dealId","runId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"dealId":{"type":"string"},"runId":{"type":"string"},"found":{"type":"boolean","description":"false when no such run is recorded on the deal"},"deleted":{"type":"object","properties":{"comments":{"type":"integer"},"audit":{"type":"integer"}},"description":"Comments and Activity rows the import had added, now deleted"},"restored":{"type":"object","properties":{"comments":{"type":"integer"},"audit":{"type":"integer"}},"description":"Comments and Activity rows it had hidden, now shown again"}}}}},"example":{"success":true,"data":{"dealId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","runId":"zoho-history:2026-10-20T09:00","found":true,"deleted":{"comments":3,"audit":8},"restored":{"comments":0,"audit":1}}}}}},"400":{"description":"dealId is not a uuid, or runId is not 1-100 letters, digits, '.', '_', ':' or '-'","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"Staff replied to a note the import added; nothing was changed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.admin"]}],"x-required-scopes":["crm.importer.admin"]}},"/integrations/{source}/write-probe":{"post":{"tags":["Integrations"],"summary":"Check write access to Zoho","description":"Checks whether your organisation's Zoho connection may create records in Zoho, by sending an empty record Zoho refuses (nothing is created). The answer is in words; if Zoho created the empty record after all, probe.createdId names it, to delete by hand. Zoho only for now.\n\n**Permission:** `crm.importer.update` (Update importer).","operationId":"probeImporterWriteAccess","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string","enum":["zoho"]},"description":"The connected system; only \"zoho\" can be checked","example":"zoho"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"probe":{"type":"object","properties":{"connected":{"type":"boolean","description":"An active Zoho connection exists"},"allowed":{"type":"boolean","description":"true: writes allowed; false: refused for permission; null: could not tell"},"code":{"type":"string","description":"Zoho's code for the answer, when it gave one"},"message":{"type":"string"}}}}},"example":{"success":true,"probe":{"connected":true,"allowed":true,"code":"MANDATORY_NOT_FOUND","message":"Zoho CRM accepts writes from this connection: it refused the empty record as expected and created nothing."}}}}},"400":{"description":"source is not \"zoho\"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.update"]}],"x-required-scopes":["crm.importer.update"]}},"/integrations/{source}/preview":{"post":{"tags":["Integrations"],"summary":"Preview a sync","description":"Reads a few records (5 by default, at most 10) from one of the system's modules and shows how the given field mappings would turn them into CRM fields. Writes nothing in CRM.\n\n**Permission:** `crm.importer.read` (View importer).","operationId":"previewImporterSync","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"crmTarget":{"type":"string","default":"","description":"The CRM record type the link would write (not used by the preview yet)","examples":["leads"]},"fieldMappings":{"type":"array","items":{"type":"object"},"default":[],"description":"The mappings to try: each { remoteField, crmField }. Send a JSON array (a string is not read)","examples":[[{"remoteField":"Last_Name","remoteFieldLabel":"Last Name","remoteFieldType":"text","crmField":"lastName","crmFieldLabel":"Last name"}]]},"limit":{"type":"integer","default":5,"description":"How many records to read, at most 10","examples":[5]},"moduleName":{"type":"string","description":"The system's module to read (apiName from the modules list)","examples":["Leads"]},"skippedFields":{"type":"array","items":{"type":"string"},"default":[],"description":"The system's fields to leave out of autoCreated","examples":[["Description"]]},"unmappedFieldAction":{"type":"string","enum":["ignore","create_custom_field"],"default":"ignore","description":"create_custom_field also shows the custom fields unmapped fields would become","examples":["create_custom_field"]}},"required":["moduleName"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"remoteRecords":{"type":"array","items":{"type":"object","properties":{"Last_Name":{"type":"string"},"Email":{"type":"string"},"Lead_Source":{"type":"string"}}},"description":"The records as the system holds them, without its system fields"},"transformedRecords":{"type":"array","items":{"type":"object","properties":{"mapped":{"type":"object","properties":{"lastName":{"type":"string"}},"description":"CRM field to value, under the given mappings"},"autoCreated":{"type":"object","properties":{"zoho_email":{"type":"string"},"zoho_lead_source":{"type":"string"}},"description":"With unmappedFieldAction create_custom_field: the custom fields that would be made, keyed <source>_<field>"},"_externalId":{"type":"string","description":"The record's ID in the system"}}}},"totalAvailable":{"type":"integer","description":"Records in the module, when the system can count them"}}}}},"example":{"success":true,"data":{"remoteRecords":[{"Last_Name":"Mwangi","Email":"j.mwangi@example.com","Lead_Source":"Web"}],"transformedRecords":[{"mapped":{"lastName":"Mwangi"},"autoCreated":{"zoho_email":"j.mwangi@example.com","zoho_lead_source":"Web"},"_externalId":"5123000003456002"}],"totalAvailable":412}}}}},"400":{"description":"source or moduleName is missing, or source is not a known system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No switched-on connection to that system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.read"]}],"x-required-scopes":["crm.importer.read"]}},"/integrations/{source}/sync/retry":{"post":{"tags":["Integrations"],"summary":"Retry a sync run","description":"Runs again the module link of an earlier sync run (usually one that failed). The new run starts in the background; follow it in the sync runs list.\n\n**Permission:** `crm.importer.admin` (Administer importer).","operationId":"retryImporterSync","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"syncLogId":{"type":"string","format":"uuid","description":"The earlier sync run (its ID from the sync runs list)","examples":["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]}},"required":["syncLogId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}},"example":{"success":true,"message":"Retry sync enqueued"}}}},"400":{"description":"The run has no module link, or that link's sync is switched off","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No sync run with that ID, or its module link is gone","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.admin"]}],"x-required-scopes":["crm.importer.admin"]}},"/integrations/{source}/restore-values":{"post":{"tags":["Integrations"],"summary":"Restore values an import overwrote","description":"Puts back values an import wrote over newer ones in CRM, from an exact list: each item names a lead or deal, a field, the value it must hold now, and the value to restore. An item whose field holds something else is left alone. Only a dry run unless dryRun is false. No transition, automation or notification runs; each restore is logged with the reason.\n\n**Permission:** `crm.importer.admin` (Administer importer).","operationId":"restoreImportedValues","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"dryRun":{"type":"boolean","description":"Restores only when false; anything else (or left out) is a dry run","examples":[false]},"items":{"type":"array","items":{"type":"object","properties":{"module":{"type":"string","enum":["leads","deals"]},"recordId":{"type":"string","format":"uuid"},"field":{"type":"string"},"expected":{},"value":{}},"required":["module","recordId","field","expected","value"]},"description":"Up to 500 items: module (leads or deals), recordId, field (a column such as \"status\" or \"owner_id\", or \"custom_fields.<key>\" / \"contact_info.<key>\"), expected (what it must hold now) and value (what to put back; null empties it). A JSON array in a string also works","examples":[[{"module":"leads","recordId":"6d5c4b3a-2f1e-4d0c-9b8a-7f6e5d4c3b2a","field":"status","expected":"new","value":"qualified"}]]},"reason":{"type":"string","description":"Why, in words (at least 5 characters); logged with each restore","examples":["Zoho import of 20 Oct overwrote statuses changed in Kazee"]}},"required":["items","reason"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"dryRun":{"type":"boolean"},"reason":{"type":"string"},"counts":{"type":"object","properties":{"restored":{"type":"integer"},"would_restore":{"type":"integer"},"already":{"type":"integer"},"mismatch":{"type":"integer"},"missing":{"type":"integer"},"refused":{"type":"integer"}},"description":"How many items had each outcome: restored, would_restore (dry run), already (holds the value), mismatch (holds neither), missing (no such record) or refused"},"items":{"type":"array","items":{"type":"object","properties":{"module":{"type":"string"},"recordId":{"type":"string"},"field":{"type":"string"},"expected":{"type":"string"},"value":{"type":"string"},"outcome":{"type":"string","description":"What happened to this item (see counts)"},"current":{"type":"string","description":"What the field held when checked"}}}}}}}},"example":{"success":true,"data":{"dryRun":true,"reason":"Zoho import of 20 Oct overwrote statuses changed in Kazee","counts":{"restored":0,"would_restore":1,"already":0,"mismatch":1,"missing":0,"refused":0},"items":[{"module":"leads","recordId":"6d5c4b3a-2f1e-4d0c-9b8a-7f6e5d4c3b2a","field":"status","expected":"new","value":"qualified","outcome":"would_restore","current":"new"},{"module":"deals","recordId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","field":"custom_fields.follow_up_date","expected":"2026-10-01","value":"2026-10-25","outcome":"mismatch","current":"2026-10-28"}]}}}}},"400":{"description":"source is missing; items is not a JSON array of 1 to 500, or an item has a bad module, record ID or field, misses expected or value, or is listed twice; or reason is under 5 characters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.admin"]}],"x-required-scopes":["crm.importer.admin"]}},"/integrations/{source}/link-relations":{"post":{"tags":["Integrations"],"summary":"Link synced records","description":"Starts linking the records synced from a system to each other (e.g. contacts to their accounts) from the system's related lists. Answers 202; it runs in the background and may take minutes.\n\n**Permission:** `crm.importer.update` (Update importer).","operationId":"linkImporterRelations","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"}],"responses":{"202":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}},"example":{"success":true,"message":"Relationship linking started. This may take several minutes for large datasets."}}}},"400":{"description":"source is missing or not a known system, or the system has no related lists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No switched-on connection to that system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.update"]}],"x-required-scopes":["crm.importer.update"]}},"/integrations/{source}/sync/cancel":{"post":{"tags":["Integrations"],"summary":"Stop running syncs","description":"Stops running sync runs: those of one module link, or, without moduleLinkId, every running sync of your organisation (of any system, whatever :source says). The worker stops at its next page of records.\n\n**Permission:** `crm.importer.admin` (Administer importer).","operationId":"cancelImporterSync","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"moduleLinkId":{"type":"string","format":"uuid","description":"Only this module link's runs; leave out to stop all","examples":["7f6e5d4c-3b2a-4190-8f7e-6d5c4b3a2918"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"cancelled":{"type":"integer","description":"How many running syncs were stopped"}}},"example":{"success":true,"message":"Cancelled 1 running sync(s). The worker will stop at the next page boundary.","cancelled":1}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.admin"]}],"x-required-scopes":["crm.importer.admin"]}},"/integrations/{source}/migrate-fields":{"post":{"tags":["Integrations"],"summary":"Rename imported custom fields","description":"Starts a one-time rename of the custom fields a sync made with the system's prefix (e.g. \"zoho_lead_source\") to clean names (\"lead_source\"). Answers 202; it runs in the background.\n\n**Permission:** `crm.importer.update` (Update importer).","operationId":"migrateImporterFieldKeys","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"}],"responses":{"202":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}},"example":{"success":true,"message":"Field key migration started. This renames \"zoho_\" prefixed fields to clean names."}}}},"400":{"description":"source is missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.update"]}],"x-required-scopes":["crm.importer.update"]}},"/integrations/{source}/user-mappings/auto-match":{"post":{"tags":["Integrations"],"summary":"Suggest user mappings by email","description":"Suggests user mappings: each of the system's users is matched to the active CRM user with the same email. Nothing is saved: keep it with POST /integrations/{source}/user-mappings.\n\n**Permission:** `crm.importer.update` (Update importer).","operationId":"autoMatchImporterUsers","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"},"description":"The connected system, e.g. \"zoho\" (the list of systems comes from GET /integrations)","example":"zoho"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"remoteUserId":{"type":"string"},"remoteUserName":{"type":"string"},"remoteUserEmail":{"type":"string"},"crmUserId":{"type":"string","description":"Empty when there is no match"},"crmUserName":{"type":"string"},"matched":{"type":"boolean","description":"A CRM user with the same email was found"}}}},"stats":{"type":"object","properties":{"totalRemoteUsers":{"type":"integer"},"totalCrmUsers":{"type":"integer"},"matched":{"type":"integer"},"unmatched":{"type":"integer"}}}}},"example":{"success":true,"data":[{"remoteUserId":"5123000000123006","remoteUserName":"Amina Otieno","remoteUserEmail":"amina@example.com","crmUserId":"8b1d3f5a-7c9e-4b0d-8f2a-4c6e8a0b2d4f","crmUserName":"Amina Otieno","matched":true},{"remoteUserId":"5123000000123007","remoteUserName":"Sales Team","remoteUserEmail":"sales@example.com","crmUserId":"","crmUserName":"","matched":false}],"stats":{"totalRemoteUsers":2,"totalCrmUsers":14,"matched":1,"unmatched":1}}}}},"400":{"description":"source is missing or not a known system, or the system has no user list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No switched-on connection to that system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.update"]}],"x-required-scopes":["crm.importer.update"]}},"/custom-fields/reorder":{"post":{"tags":["Custom fields & modules"],"summary":"Reorder custom fields","description":"Sets the display position of several custom fields at once and answers with each changed field. Fields are saved one by one, so a bad ID part-way stops the rest.\n\n**Permission:** `crm.customFields.update` (Update custom fields).","operationId":"reorderCustomFieldDefinitions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fieldOrders":{"type":"array","items":{"type":"object","properties":{"fieldId":{"type":"string","format":"uuid"},"displayOrder":{"type":"integer"}}},"description":"Each field's ID and new position, smallest first (a JSON array in a string also works)","examples":[[{"fieldId":"e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8a9b","displayOrder":1},{"fieldId":"f6a7b8c9-d0e1-4f2a-9b3c-5d6e7f8a9b0c","displayOrder":2}]]},"moduleType":{"type":"string","description":"Record type whose fields are reordered, e.g. leads. Required, but the fields are found by fieldId alone","examples":["leads"]}},"required":["fieldOrders","moduleType"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"moduleType":{"type":"string","description":"Record type the field sits on: leads, deals, accounts, contacts, tickets, requests, subscriptions, quotes or products"},"fieldKey":{"type":"string","description":"The key the value is stored under in each record's customFields"},"label":{"type":"string"},"type":{"type":"string","description":"text, textarea, email, phone, url, number, decimal, currency, date, datetime, select, multiselect, checkbox, lookup, multilookup, user, file or files"},"description":{"type":"string"},"required":{"type":"boolean"},"defaultValue":{},"options":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"},"color":{"type":"string"}}},"description":"The choices of a select or multiselect field"},"lookupModule":{"description":"For a lookup or multilookup field: the record type it points at, e.g. users"},"importSource":{},"validation":{"type":"object","properties":{"dependsOn":{"type":"object","properties":{"fieldId":{"type":"string"},"values":{"type":"array","items":{"type":"string"}},"requiredWhenVisible":{"type":"boolean"}}}},"description":"Rules: min, max, minLength, maxLength, pattern, mimeTypes, maxBytes, minCount, maxCount, appliesTo, and the show-when conditions dependsOn, alsoDependsOn and conditionsMatch"},"displayOrder":{"type":"integer"},"showInTable":{"type":"boolean"},"showInFilter":{"type":"boolean"},"isActive":{"type":"boolean","description":"false when the field is retired: hidden from forms, its values kept"},"createdBy":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}},"example":{"success":true,"data":[{"id":"e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8a9b","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","moduleType":"leads","fieldKey":"connection_type","label":"Connection type","type":"select","description":"How the customer wants to be connected","required":false,"defaultValue":null,"options":[{"value":"fibre","label":"Fibre","color":"#22c55e"},{"value":"wireless","label":"Wireless"}],"lookupModule":null,"importSource":null,"validation":{"dependsOn":{"fieldId":"_stage","values":["qualified"],"requiredWhenVisible":true}},"displayOrder":1,"showInTable":true,"showInFilter":true,"isActive":true,"createdBy":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}]}}}},"400":{"description":"fieldOrders is not a list of { fieldId, displayOrder }","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"A fieldId is not a custom field of your organisation (the ones before it are already saved)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customFields.update"]}],"x-required-scopes":["crm.customFields.update"]}},"/custom-fields/{fieldId}/convert-type":{"post":{"tags":["Custom fields & modules"],"summary":"Convert a custom field's type","description":"Turns a text custom field on leads, deals, accounts or contacts into a people field (a multi-lookup of users), with every stored text accounted for. By default it only previews: each distinct text and the people it would become. Send dryRun false (with the preview's expectedUpdatedAt) to write the field and all records in one go; while any text is undecided nothing is written and the answer says applied false. Needs the custom fields admin permission, and the change is recorded with who made it.\n\n**Permission:** `crm.customFields.admin` (Convert custom field types).","operationId":"convertCustomFieldType","parameters":[{"name":"fieldId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The text custom field's ID","example":"e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8a9b"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"decisions":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"userIds":{"type":"array","items":{"type":"string"}},"keep":{"type":"boolean"},"clear":{"type":"boolean"}}},"description":"Your decision per stored text: { value, and exactly one of userIds (people it becomes), keep: true (leave it) or clear: true (remove it) } (a JSON array in a string also works)","examples":[[{"value":"Jane W.","userIds":["7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"]},{"value":"n/a","clear":true}]]},"dryRun":{"type":"boolean","description":"Leave out (or true) to preview; false to apply. Defaults to true","examples":[false]},"expectedUpdatedAt":{"type":"string","format":"date-time","description":"The expectedUpdatedAt of the preview you approved; the apply stops if the field changed since","examples":["2026-10-20T09:00:00.000Z"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"dryRun":{"type":"boolean","description":"true when this was only a preview"},"field":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"fieldKey":{"type":"string"},"moduleType":{"type":"string"},"from":{"type":"object","properties":{"type":{"type":"string"},"lookupModule":{}}},"to":{"type":"object","properties":{"type":{"type":"string"},"lookupModule":{"type":"string"}}}}},"recordsWithValue":{"type":"integer","description":"Records holding a value in the field"},"distinctValues":{"type":"integer","description":"Different texts stored in it"},"values":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"recordIds":{"type":"array","items":{"type":"string"},"description":"Records holding this text"},"outcome":{"type":"string","description":"What becomes of the text: auto_matched (one person's name or email matched), mapped (your decision names people), kept, cleared, undecided or ambiguous (several people nearly match: see candidates)"},"userIds":{"type":"array","items":{"type":"string"},"description":"People the text becomes"}}}},"blockers":{"type":"array","description":"Texts still needing a decision; while there are any nothing is written"},"expectedUpdatedAt":{"type":"string","format":"date-time","description":"Send this back with dryRun false to apply exactly this preview"},"applied":{"type":"boolean","description":"true when the field and the records were changed"},"recordsWritten":{"type":"integer"},"recordsCleared":{"type":"integer"},"recordsKept":{"type":"integer"}}},"example":{"success":true,"dryRun":false,"field":{"id":"e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8a9b","label":"Account manager","fieldKey":"account_manager","moduleType":"deals","from":{"type":"text","lookupModule":null},"to":{"type":"multilookup","lookupModule":"users"}},"recordsWithValue":3,"distinctValues":2,"values":[{"value":"jane@example.com","recordIds":["0f1a2b3c-4d5e-4f6a-9b7c-8d9e0f1a2b3c","1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5e"],"outcome":"auto_matched","userIds":["7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"]},{"value":"Jane W.","recordIds":["2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6f"],"outcome":"mapped","userIds":["7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a"]}],"blockers":[],"expectedUpdatedAt":"2026-10-20T09:00:00.000Z","applied":true,"recordsWritten":3,"recordsCleared":0,"recordsKept":0}}}},"400":{"description":"The field is not a text field, its record type is not leads, deals, accounts or contacts, decisions is not valid, or the field changed since the preview","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No custom field with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customFields.admin"]}],"x-required-scopes":["crm.customFields.admin"]}},"/channel-configs/{configId}/test":{"post":{"tags":["Help desk setup"],"summary":"Test a channel","description":"Tries the channel's connection with its provider (e.g. logs in to the mailbox) and says whether it worked. A failed test answers 200 with success false and the reason in message. Some providers add details (Zoho Desk: the connected agent, departments and webhook).\n\n**Permission:** `crm.channelConfigs.admin` (Administer channel configs).","operationId":"testChannelConfig","parameters":[{"name":"configId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The channel to test","example":"b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the connection worked"},"message":{"type":"string","description":"What happened, in words"},"channelType":{"type":"string"},"configName":{"type":"string"}}},"example":{"success":true,"message":"Connected to mail.example.com","channelType":"email","configName":"Support mailbox"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No channel with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.channelConfigs.admin"]}],"x-required-scopes":["crm.channelConfigs.admin"]}},"/channel-configs/{configId}/forwarding-test":{"post":{"tags":["Help desk setup"],"summary":"Send a forwarding test email","description":"For an email channel that receives by forwarding: Kazee Desk emails the channel's public address through Kazee Mail, and the email should travel the forwarding path back to Desk, which marks the channel verified (no ticket is opened). Check the result with the forwarding status call.\n\n**Permission:** `crm.channelConfigs.admin` (Administer channel configs).","operationId":"sendChannelForwardingTest","parameters":[{"name":"configId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The forwarding email channel","example":"b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"sentAt":{"type":"string","format":"date-time","description":"When the test email went out"},"sendAs":{"type":"string","description":"public (sent as the public address) or desk (as the Kazee forwarding address, while Kazee Mail may not send as the public one)"}}},"example":{"success":true,"sentAt":"2026-10-21T09:00:00.000Z","sendAs":"public"}}}},"400":{"description":"The channel is not a forwarding address, forwarding is not available to your organisation, or the channel is switched off","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No channel with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.channelConfigs.admin"]}],"x-required-scopes":["crm.channelConfigs.admin"]}},"/desk/chat-bot/test":{"post":{"tags":["Help desk setup"],"summary":"Send a test message from the Kazee Chat bot","description":"Sends you a test message in Kazee Chat from the connected bot, so you can see it arrives.\n\n**Permission:** `crm.desk.admin` (Configure Desk).","operationId":"testDeskChatBot","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"sent":{"type":"boolean","description":"true: Kazee Chat took the message"}}}}},"example":{"success":true,"data":{"sent":true}}}}},"400":{"description":"No bot is connected, or you have no Kazee Chat account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"The call was not made by a signed-in person","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.desk.admin"]}],"x-required-scopes":["crm.desk.admin"]}},"/desk/channels/{configId}/logout":{"post":{"tags":["Help desk setup"],"summary":"Unlink a WhatsApp linked-device channel's phone","description":"Unlinks the phone from a WhatsApp linked-device channel: WhatsApp forgets the device and messages stop flowing until it is paired again. The channel itself stays. Needs channel admin permission.\n\n**Permission:** `crm.channelConfigs.admin` (Administer channel configs).","operationId":"logoutDeskChannel","parameters":[{"name":"configId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The WhatsApp linked-device channel","example":"b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"account":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"enabled":{"type":"boolean"},"status":{"type":"string","description":"unpaired, pairing, starting, connected, disconnected, logged_out or stopped"},"paired":{"type":"boolean","description":"Whether a phone is linked"},"lastEventAt":{"type":"string","format":"date-time"},"lastEvent":{"type":"string"},"lastError":{"description":"The bridge's last error, if any"},"restarts":{"type":"integer","description":"How many times the bridge restarted this connection"},"pairing":{"description":"The open pairing attempt: mode code (type the code on the phone under Linked devices) or qr (scan qr); null when none is open"}}}}},"example":{"success":true,"account":{"id":"b7a8b9c0-d1e2-4f3a-8c5d-6e7f8a9b0c1d","label":"Support WhatsApp","enabled":true,"status":"logged_out","paired":false,"lastEventAt":"2026-10-20T08:30:00.000Z","lastEvent":"logged_out","lastError":null,"restarts":0,"pairing":null}}}}},"400":{"description":"The channel is not a WhatsApp linked-device channel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No channel with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"description":"The linked-device bridge is not set up, unreachable or refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"kazee":["crm.channelConfigs.admin"]}],"x-required-scopes":["crm.channelConfigs.admin"]}},"/helpdesk/tickets/{ticketId}/csat/resend":{"post":{"tags":["Help desk setup"],"summary":"Resend a ticket's satisfaction survey","description":"Queues the satisfaction survey email for a ticket you may see to go out again, with a new link (an unrated earlier survey is dropped). A ticket that was already rated cannot be surveyed again.\n\n**Permission:** `crm.csat.admin` (Administer CSAT).","operationId":"resendCsatSurvey","parameters":[{"name":"ticketId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The ticket to survey again","example":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}},"example":{"success":true,"message":"Survey re-dispatch queued"}}}},"400":{"description":"The ticket has already been rated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No ticket with that ID that you may see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.csat.admin"]}],"x-required-scopes":["crm.csat.admin"]}},"/connections/{name}":{"delete":{"tags":["Integrations"],"summary":"Delete a connection","description":"Deletes a named outbound connection. Workflows that still name it are refused from then on, as if it never existed. The record is kept in the database, marked deleted.\n\n**Permission:** `crm.connections.delete` (Delete connections).","operationId":"deleteConnection","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"},"description":"The connection's name: 1 to 63 characters of a-z, 0-9, \"_\" or \"-\", starting with a letter or digit (capitals are made small)","example":"billing-api"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"name":{"type":"string"},"deleted":{"type":"boolean"}}}}},"example":{"success":true,"data":{"name":"billing-api","deleted":true}}}}},"400":{"description":"The name is not valid (1 to 63 characters of a-z, 0-9, _ or -)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No connection with that name in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.connections.delete"]}],"x-required-scopes":["crm.connections.delete"]}},"/desk/tickets/{ticketId}/calls/{callId}":{"delete":{"tags":["Desk"],"summary":"Take a call off a ticket","description":"Takes a phone call off a ticket. The call and the ticket stay as they are; the ticket's history gets a call.unlinked entry.\n\n**Permission:** `crm.tickets.update` (Update tickets).","operationId":"unlinkDeskTicketCall","parameters":[{"name":"ticketId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The ticket","example":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b"},{"name":"callId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The call to take off it","example":"f1e2f3a4-b5c6-4d7e-8a9b-0c1d2e3f4a5b"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"ticketId":{"type":"string"},"callId":{"type":"string"}}}}},"example":{"success":true,"data":{"ticketId":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b","callId":"f1e2f3a4-b5c6-4d7e-8a9b-0c1d2e3f4a5b"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No ticket with that ID that you may see, or that call is not filed under it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.update"]}],"x-required-scopes":["crm.tickets.update"]}},"/tickets/{ticketId}/attachments/{attachmentId}":{"delete":{"tags":["Tickets"],"summary":"Remove an unsent ticket file","description":"Removes a file uploaded onto a ticket that was never sent with a message, and deletes its stored copy. A file already sent is part of the conversation and stays.\n\n**Permission:** `crm.tickets.update` (Update tickets).","operationId":"deleteTicketAttachment","parameters":[{"name":"ticketId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The ticket the file is on","example":"3e4f5a6b-7c8d-4e9f-8a1b-2c3d4e5f6a7b"},{"name":"attachmentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The unsent file to remove","example":"7c8d9e0f-1a2b-4c3d-8e5f-6a7b8c9d0e1f"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"removed":{"type":"boolean"}}},"example":{"success":true,"removed":true}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No ticket with that ID that you may see, or no such file on it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The file was sent with a message and is part of the conversation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.tickets.update"]}],"x-required-scopes":["crm.tickets.update"]}},"/notifications/clear-all":{"delete":{"tags":["Notifications"],"summary":"Delete all my notifications","description":"Deletes every notification in your own feed for good, archived ones too, and answers how many.\n\n**Permission:** `crm.notifications.delete` (Delete notifications).","operationId":"clearAllNotifications","parameters":[{"name":"userId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional: your own user ID. Any other person's ID is refused (403)","example":"1b3e9d5a-0c7f-4a2b-9dc4-8c1f4a7b0d3e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"deleted":{"type":"integer","description":"How many notifications were deleted"}}}}},"example":{"success":true,"data":{"deleted":27}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"userId is not your own","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.notifications.delete"]}],"x-required-scopes":["crm.notifications.delete"]}},"/notifications/{notificationId}":{"delete":{"tags":["Notifications"],"summary":"Delete a notification","description":"Deletes one of your own notifications for good. Someone else's notification answers 404, the same as one that does not exist.\n\n**Permission:** `crm.notifications.delete` (Delete notifications).","operationId":"deleteNotification","parameters":[{"name":"notificationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The notification's ID","example":"5f7c3b9e-4a1d-4e6f-9b08-2a5d8e1f4b7c"},{"name":"userId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional: your own user ID. Any other person's ID is refused (403)","example":"1b3e9d5a-0c7f-4a2b-9dc4-8c1f4a7b0d3e"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}},"example":{"success":true,"message":"Notification deleted"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"userId is not your own","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No notification with that ID in your own feed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.notifications.delete"]}],"x-required-scopes":["crm.notifications.delete"]}},"/deleteLeadDocument":{"delete":{"tags":["Leads"],"summary":"Delete a lead's document","description":"Deletes a document uploaded on a lead (it is marked deleted) and recounts the lead's documents against its checklist.\n\n**Permission:** `crm.leads.delete` (Delete leads).","operationId":"deleteLeadDocument","parameters":[{"name":"documentId","in":"query","required":true,"schema":{"type":"string","format":"uuid"},"description":"The document's ID","example":"af9c8a1c-3d5e-4a2b-9e7f-1c2d3e4f5a6b"},{"name":"leadId","in":"query","required":true,"schema":{"type":"string","format":"uuid"},"description":"The lead's ID","example":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}},"example":{"success":true,"message":"Document deleted successfully"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No document with that ID on that lead in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.leads.delete"]}],"x-required-scopes":["crm.leads.delete"]}},"/teams/{teamId}/members/{userId}":{"delete":{"tags":["Teams & people"],"summary":"Remove a person from a team","description":"Takes one person out of a team (a department in Kazee Desk) and answers with the updated team. Removing someone who is not a member changes nothing and still succeeds. Needs the permission to delete teams.\n\n**Permission:** `crm.teams.delete` (Delete teams).","operationId":"removeTeamMember","parameters":[{"name":"teamId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The team's ID","example":"3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f"},{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The person's CRM user ID (a 24-character Kazee sign-in ID also works)","example":"8e2f3a4b-5c6d-4e7f-9a0b-1c2d3e4f5a6b"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"_id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"members":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string","description":"The member's CRM user ID"},"role":{"type":"string","description":"leader or member"},"joinedAt":{"type":"string","format":"date-time"}}}},"isActive":{"type":"boolean"},"autoAssign":{"type":"boolean","description":"Whether new tickets are assigned automatically"},"assignmentMethod":{"type":"string","description":"How new tickets are handed out: manual or round_robin"},"currentAssigneeIndex":{"type":"integer"},"specializations":{"type":"array","items":{"type":"string"}},"ticketVisibility":{"type":"string","description":"Who sees the department's tickets in Kazee Desk: members (its members, the assignee and Desk admins) or everyone"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"message":{"type":"string"}}},"example":{"success":true,"data":{"id":"3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f","_id":"3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f","organizationId":"5f0c7a52-8a1e-4d2b-9c3f-1a2b3c4d5e6f","name":"Fibre support","description":"First-line help for fibre customers","members":[{"userId":"7d1e2f3a-4b5c-4d6e-8f9a-0b1c2d3e4f5a","role":"leader","joinedAt":"2026-10-01T08:30:00.000Z"}],"isActive":true,"autoAssign":true,"assignmentMethod":"round_robin","currentAssigneeIndex":0,"specializations":["fibre","billing"],"ticketVisibility":"members","deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"},"message":"Member removed from team"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No team with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.teams.delete"]}],"x-required-scopes":["crm.teams.delete"]}},"/integrations/{source}/module-links/{linkId}":{"delete":{"tags":["Integrations"],"summary":"Remove a module link","description":"Removes a module link, so its module stops syncing. Records already synced stay.\n\n**Permission:** `crm.importer.delete` (Delete importer).","operationId":"deleteImporterModuleLink","parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"}},{"name":"linkId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The module link to remove","example":"7f6e5d4c-3b2a-4190-8f7e-6d5c4b3a2918"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}},"example":{"success":true,"message":"Module link deleted"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No module link with that ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.importer.delete"]}],"x-required-scopes":["crm.importer.delete"]}},"/field-restrictions/{restrictionId}":{"delete":{"tags":["Custom fields & modules"],"summary":"Stop hiding a field","description":"Removes a hidden field's who-can-see list, so a custom or system field is no longer hidden. A custom field named like a credential (password, secret, token…) stays hidden by its name, with only admins able to press Show. Needs the custom fields admin permission; the change is recorded with who made it.\n\n**Permission:** `crm.customFields.admin` (Convert custom field types).","operationId":"deleteFieldRestriction","parameters":[{"name":"restrictionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The rule's ID (id in List hidden fields)","example":"a7b8c9d0-e1f2-4a3b-8c4d-6e7f8a9b0c1d"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"moduleType":{"type":"string"},"fieldKey":{"type":"string"},"origin":{"type":"string"},"deleted":{"type":"boolean"}}}}},"example":{"success":true,"data":{"id":"a7b8c9d0-e1f2-4a3b-8c4d-6e7f8a9b0c1d","moduleType":"leads","fieldKey":"router_password","origin":"custom","deleted":true}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No hidden-field rule with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.customFields.admin"]}],"x-required-scopes":["crm.customFields.admin"]}},"/subscriptions/{subscriptionId}/details":{"patch":{"tags":["Subscriptions"],"summary":"Rename a subscription","description":"Changes a subscription's name and runs the organisation's \"record updated\" automations. Answers the line as stored, with passwords masked.\n\n**Permission:** `crm.subscriptions.update` (Update subscriptions).","operationId":"subscriptionUpdateDetails","parameters":[{"name":"subscriptionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The subscription's ID","example":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"The new name, 2 to 100 characters","examples":["Home fibre 20 Mbps"]}},"required":["name"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"pppoe":{"type":"string"},"accountId":{"type":"string"},"servicePlanId":{"type":"string"},"status":{"type":"string"},"startDate":{"type":"string","format":"date-time"},"billingCycle":{"type":"string"},"lastBilledAt":{"type":"string","format":"date-time"},"nextBillingAt":{"type":"string","format":"date-time"},"billDate":{"type":"string","format":"date-time"},"renewalDate":{"type":"string","format":"date-time"},"billLeadDays":{"type":"integer"},"disconnectionDate":{},"terminationDate":{},"lastPaymentDate":{"type":"string","format":"date-time"},"ownerId":{"type":"string"},"password":{"type":"object","properties":{"secret":{"type":"boolean"},"set":{"type":"boolean"}}},"customFields":{"type":"object","properties":{"sm_password":{"type":"object","properties":{"secret":{"type":"boolean"},"set":{"type":"boolean"}}},"router_model":{"type":"string"}}},"externalIds":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"externalId":{"type":"string"}}}},"version":{"type":"integer"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"message":{"type":"string"}}},"example":{"success":true,"data":{"id":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","name":"Home fibre 20 Mbps","pppoe":"jane.wanjiku.home","accountId":"a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c","servicePlanId":"5e8b1d3f-7a2c-4c6e-9b4d-8f0a1c3e5d7b","status":"active","startDate":"2026-09-01T00:00:00.000Z","billingCycle":"monthly","lastBilledAt":"2026-10-01T00:00:00.000Z","nextBillingAt":"2026-11-01T00:00:00.000Z","billDate":"2026-10-22T00:00:00.000Z","renewalDate":"2026-11-01T00:00:00.000Z","billLeadDays":10,"disconnectionDate":null,"terminationDate":null,"lastPaymentDate":"2026-10-02T10:15:00.000Z","ownerId":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d","password":{"secret":true,"set":true},"customFields":{"sm_password":{"secret":true,"set":true},"router_model":"HG8145V5"},"externalIds":[{"source":"zoho","externalId":"4815000000123457"}],"version":3,"deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"},"message":"Subscription name updated from \"Home fibre\" to \"Home fibre 20 Mbps\""}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No subscription with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.subscriptions.update"]}],"x-required-scopes":["crm.subscriptions.update"]}},"/subscriptions/{subscriptionId}/dates":{"patch":{"tags":["Subscriptions"],"summary":"Change a subscription's start date","description":"Moves the start date of a subscription that is still pending installation, and resets its last-billed and next-billing dates to the first billing period from the new date. Answers the line with its account, plan and pipeline place. Any failure (no startDate, no such line, a line past pending installation) still answers 200, with success false and an error.code such as \"NO_DATES_PROVIDED\" or \"SUBSCRIPTION_DATES_UPDATE_ERROR\".\n\n**Permission:** `crm.subscriptions.update` (Update subscriptions).","operationId":"subscriptionUpdateDates","parameters":[{"name":"subscriptionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The subscription's ID","example":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"startDate":{"type":"string","format":"date-time","description":"The new start date. Optional in form, but the call fails without it","examples":["2026-11-01T00:00:00.000Z"]}}}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"subscription":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"pppoe":{"type":"string"},"accountId":{"type":"string"},"servicePlanId":{"type":"string"},"status":{"type":"string"},"startDate":{"type":"string","format":"date-time"},"billingCycle":{"type":"string"},"lastBilledAt":{"type":"string","format":"date-time"},"nextBillingAt":{"type":"string","format":"date-time"},"billDate":{"type":"string","format":"date-time"},"renewalDate":{"type":"string","format":"date-time"},"billLeadDays":{"type":"integer"},"disconnectionDate":{},"terminationDate":{},"lastPaymentDate":{"type":"string","format":"date-time"},"ownerId":{"type":"string"},"password":{"type":"object","properties":{"secret":{"type":"boolean"},"set":{"type":"boolean"}}},"customFields":{"type":"object","properties":{"sm_password":{"type":"object","properties":{"secret":{"type":"boolean"},"set":{"type":"boolean"}}},"router_model":{"type":"string"}}},"externalIds":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"externalId":{"type":"string"}}}},"version":{"type":"integer"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"account":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"accountNumber":{"type":"string"},"status":{"type":"string"},"description":{},"addressId":{},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string"},"currentBalance":{"type":"string"},"originLeadId":{},"walletId":{},"billingContactId":{"type":"string"},"serviceContactId":{"type":"string"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"}}},"tags":{"type":"array"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{},"cacheDisplayName":{},"cachePrimaryPhone":{},"cachePrimaryEmail":{},"cachePartyRevision":{},"cacheUpdatedAt":{},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"contactRelationships":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"accountId":{"type":"string"},"contactId":{"type":"string"},"roles":{"type":"array","items":{"type":"string"}},"isPrimary":{"type":"boolean"},"status":{"type":"string"},"preferences":{"type":"object","properties":{}},"notes":{},"source":{"type":"string"},"createdBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"contact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"addressId":{},"status":{"type":"string"},"preferences":{"type":"object","properties":{}},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"partyId":{},"cacheDisplayName":{},"cachePrimaryPhone":{},"cachePrimaryEmail":{},"cachePartyRevision":{},"cacheUpdatedAt":{},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}},"servicePlan":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"limitStr":{"type":"string"},"burstMaxDown":{},"burstMaxUp":{},"burstThreshDown":{},"burstThreshUp":{},"burstTimeDown":{},"burstTimeUp":{},"connectionType":{"type":"array","items":{"type":"string"}},"installationFee":{"type":"string"},"maxLimitDown":{"type":"integer"},"maxLimitUp":{"type":"integer"},"rateLimit":{"type":"string"},"serviceTypeId":{"type":"string"},"statusLevelId":{},"status":{"type":"string"},"productId":{"type":"string"},"booksItemId":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"product":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"price":{"type":"string"},"sku":{"type":"string"},"status":{"type":"string"},"description":{"type":"string"},"taxId":{"type":"string"},"unit":{"type":"string"},"recurringPeriod":{"type":"string"},"customFields":{"type":"object","properties":{}},"externalIds":{"type":"array"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"serviceType":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}}}}},"radiusStatus":{"type":"string"},"validNextStatuses":{"type":"array","items":{"type":"string"}},"previousStatuses":{"type":"array"},"pipeline":{"type":"string"},"currentStatus":{"type":"string"}}},"summary":{"type":"object","properties":{"oldStartDate":{"type":"string","format":"date-time"},"newStartDate":{"type":"string","format":"date-time"},"billingPeriod":{"type":"object","properties":{"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"}},"description":"The first billing period from the new start date (start, end); left out when the date did not change"}}}}},"message":{"type":"string","description":"Today it reads \"undefined draft bill(s) updated\": no draft bills are changed"}}},"example":{"success":true,"data":{"subscription":{"id":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","name":"Home fibre 20 Mbps","pppoe":"jane.wanjiku.home","accountId":"a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c","servicePlanId":"5e8b1d3f-7a2c-4c6e-9b4d-8f0a1c3e5d7b","status":"pending_installation","startDate":"2026-11-01T00:00:00.000Z","billingCycle":"monthly","lastBilledAt":"2026-11-01T00:00:00.000Z","nextBillingAt":"2026-12-01T00:00:00.000Z","billDate":"2026-10-22T00:00:00.000Z","renewalDate":"2026-11-01T00:00:00.000Z","billLeadDays":10,"disconnectionDate":null,"terminationDate":null,"lastPaymentDate":"2026-10-02T10:15:00.000Z","ownerId":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d","password":{"secret":true,"set":true},"customFields":{"sm_password":{"secret":true,"set":true},"router_model":"HG8145V5"},"externalIds":[{"source":"zoho","externalId":"4815000000123457"}],"version":3,"deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z","account":{"id":"a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","name":"Jane Wanjiku","accountNumber":"ACC-00042","status":"active","description":null,"addressId":null,"onboardingDate":"2026-09-01T08:00:00.000Z","terminationDate":null,"creditLimit":"0.00","currentBalance":"0.00","originLeadId":null,"walletId":null,"billingContactId":"e0b6a2d5-7f4c-4dbe-8a91-5f8c1d4e7a0b","serviceContactId":"e0b6a2d5-7f4c-4dbe-8a91-5f8c1d4e7a0b","ownerId":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d","notificationPreferences":{"email":true,"sms":true},"tags":[],"customFields":{},"externalIds":[],"partyId":null,"cacheDisplayName":null,"cachePrimaryPhone":null,"cachePrimaryEmail":null,"cachePartyRevision":null,"cacheUpdatedAt":null,"cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z","contactRelationships":[{"id":"f1c7b3e6-8a5d-4ecf-9ba2-6a9d2e5f8b1c","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","accountId":"a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c","contactId":"e0b6a2d5-7f4c-4dbe-8a91-5f8c1d4e7a0b","roles":["billing","technical"],"isPrimary":true,"status":"active","preferences":{},"notes":null,"source":"crm","createdBy":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d","createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z","contact":{"id":"e0b6a2d5-7f4c-4dbe-8a91-5f8c1d4e7a0b","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","firstName":"Jane","lastName":"Wanjiku","email":"jane.wanjiku@example.com","phone":"+254700000001","addressId":null,"status":"active","preferences":{},"ownerId":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d","notes":null,"customFields":{},"externalIds":[],"partyId":null,"cacheDisplayName":null,"cachePrimaryPhone":null,"cachePrimaryEmail":null,"cachePartyRevision":null,"cacheUpdatedAt":null,"cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}]},"servicePlan":{"id":"5e8b1d3f-7a2c-4c6e-9b4d-8f0a1c3e5d7b","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","name":"Fibre 20 Mbps","limitStr":"20M/20M","burstMaxDown":null,"burstMaxUp":null,"burstThreshDown":null,"burstThreshUp":null,"burstTimeDown":null,"burstTimeUp":null,"connectionType":["fibre"],"installationFee":"2000.00","maxLimitDown":20,"maxLimitUp":20,"rateLimit":"20M/20M","serviceTypeId":"c8f4e0b3-5d2a-4b9c-8e7f-3d6a9b2c5e8f","statusLevelId":null,"status":"active","productId":"b7e3d9a2-4c1f-4a8b-9d6e-2c5f8a1b4d7e","booksItemId":"itm_01JABCDEF0123456789XYZ","deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z","product":{"id":"b7e3d9a2-4c1f-4a8b-9d6e-2c5f8a1b4d7e","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","name":"Fibre 20 Mbps","type":"service","price":"3500.00","sku":"FIB-20","status":"active","description":"Home fibre internet, 20 Mbps down and up","taxId":"tax_vat16","unit":"month","recurringPeriod":"monthly","customFields":{},"externalIds":[],"deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"},"serviceType":{"name":"Home fibre","description":"Fibre internet for homes"}},"radiusStatus":"unknown","validNextStatuses":["active","terminated"],"previousStatuses":[],"pipeline":"subscription","currentStatus":"pending_installation"},"summary":{"oldStartDate":"2026-10-25T00:00:00.000Z","newStartDate":"2026-11-01T00:00:00.000Z","billingPeriod":{"start":"2026-11-01T00:00:00.000Z","end":"2026-12-01T00:00:00.000Z"}}},"message":"Subscription start date updated to 2026-11-01T00:00:00.000Z. undefined draft bill(s) updated with new billing periods."}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.subscriptions.update"]}],"x-required-scopes":["crm.subscriptions.update"]}},"/subscriptions/{subscriptionId}/service-plan":{"patch":{"tags":["Subscriptions"],"summary":"Change a subscription's plan","description":"Moves a subscription to another service plan. Only the plan changes: bills are not prorated or changed, and the line's products stay as they are. Where the organisation sends plan changes to its network, a line that is on gets the new plan straight away, and one that is off waits for its reconnection (a plan call would switch it back on). Answers the line as stored, with passwords masked.\n\n**Permission:** `crm.subscriptions.update` (Update subscriptions).","operationId":"subscriptionUpdateServicePlan","parameters":[{"name":"subscriptionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The subscription's ID","example":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"servicePlanId":{"type":"string","format":"uuid","description":"The plan to move the line to","examples":["6f9c2e4a-8b3d-4d7f-a05e-9a1b2d4f6e8c"]}},"required":["servicePlanId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string"},"pppoe":{"type":"string"},"accountId":{"type":"string"},"servicePlanId":{"type":"string"},"status":{"type":"string"},"startDate":{"type":"string","format":"date-time"},"billingCycle":{"type":"string"},"lastBilledAt":{"type":"string","format":"date-time"},"nextBillingAt":{"type":"string","format":"date-time"},"billDate":{"type":"string","format":"date-time"},"renewalDate":{"type":"string","format":"date-time"},"billLeadDays":{"type":"integer"},"disconnectionDate":{},"terminationDate":{},"lastPaymentDate":{"type":"string","format":"date-time"},"ownerId":{"type":"string"},"password":{"type":"object","properties":{"secret":{"type":"boolean"},"set":{"type":"boolean"}}},"customFields":{"type":"object","properties":{"sm_password":{"type":"object","properties":{"secret":{"type":"boolean"},"set":{"type":"boolean"}}},"router_model":{"type":"string"}}},"externalIds":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"externalId":{"type":"string"}}}},"version":{"type":"integer"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"message":{"type":"string"},"meta":{"type":"object","properties":{"userId":{"type":"string"}},"description":"userId: who made the change"},"__notify":{"type":"object","properties":{"module":{"type":"string"},"entity":{"type":"string"},"action":{"type":"string"},"outcome":{"type":"string"},"eventType":{"type":"string"},"message":{"type":"string"},"metadata":{"type":"object","properties":{"subscriptionId":{"type":"string"},"oldPlanId":{"type":"string"},"oldPlanName":{"type":"string"},"newPlanId":{"type":"string"},"newPlanName":{"type":"string"},"accountId":{"type":"string"},"userId":{"type":"string"}}}},"description":"The notification this change describes; answered as is today"}}},"example":{"success":true,"data":{"id":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","name":"Home fibre 20 Mbps","pppoe":"jane.wanjiku.home","accountId":"a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c","servicePlanId":"6f9c2e4a-8b3d-4d7f-a05e-9a1b2d4f6e8c","status":"active","startDate":"2026-09-01T00:00:00.000Z","billingCycle":"monthly","lastBilledAt":"2026-10-01T00:00:00.000Z","nextBillingAt":"2026-11-01T00:00:00.000Z","billDate":"2026-10-22T00:00:00.000Z","renewalDate":"2026-11-01T00:00:00.000Z","billLeadDays":10,"disconnectionDate":null,"terminationDate":null,"lastPaymentDate":"2026-10-02T10:15:00.000Z","ownerId":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d","password":{"secret":true,"set":true},"customFields":{"sm_password":{"secret":true,"set":true},"router_model":"HG8145V5"},"externalIds":[{"source":"zoho","externalId":"4815000000123457"}],"version":3,"deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"},"message":"Service plan updated successfully","meta":{"userId":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d"},"__notify":{"module":"subscriptions","entity":"subscription","action":"update","outcome":"success","eventType":"plan_change","message":"Subscription plan changed from \"Fibre 20 Mbps\" to \"Fibre 50 Mbps\"","metadata":{"subscriptionId":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","oldPlanId":"5e8b1d3f-7a2c-4c6e-9b4d-8f0a1c3e5d7b","oldPlanName":"Fibre 20 Mbps","newPlanId":"6f9c2e4a-8b3d-4d7f-a05e-9a1b2d4f6e8c","newPlanName":"Fibre 50 Mbps","accountId":"a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c","userId":"0a2d8c4f-9b6e-4f1a-8cb3-7b0e3f6a9c2d"}}}}}},"400":{"description":"The subscription has no current service plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No subscription, or no plan, with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.subscriptions.update"]}],"x-required-scopes":["crm.subscriptions.update"]}},"/reminder-types/{id}/title":{"patch":{"tags":["Reminders"],"summary":"Change a reminder template's title","description":"Replaces a reminder template's title and its list of placeholder names (the list then covers the whole template, so include the message's placeholders too). Answers the template as saved.\n\n**Permission:** `crm.reminders.update` (Update reminders).","operationId":"patchReminderTypeTitle","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The reminder template's ID","example":"3d5a1f7c-2e9b-4c4d-9fe6-0e3b6c9d2f5a"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"templateVars":{"type":"array","items":{"type":"string"},"description":"The placeholder names the template may use; every ${...} in the title must be one of them. Saved as the template's templateVariables","examples":[["accountName","amount","dueDate"]]},"title":{"type":"string","description":"The new title; may hold ${name} placeholders","examples":["Payment due for ${accountName}"]}},"required":["templateVars","title"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"category":{"type":"string"},"priority":{"type":"string"},"title":{"type":"string"},"message":{"type":"string"},"templateVariables":{"type":"array","items":{"type":"string"}},"isActive":{"type":"boolean"},"description":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"3d5a1f7c-2e9b-4c4d-9fe6-0e3b6c9d2f5a","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","category":"custom","priority":"medium","title":"Payment due for ${accountName}","message":"Hello ${accountName}, your bill of ${amount} is due on ${dueDate}.","templateVariables":["accountName","amount","dueDate"],"isActive":true,"description":"Sent a few days before a bill is due","deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"templateVars is not a list, or a ${...} placeholder in the title is not in templateVars","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No reminder template with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.reminders.update"]}],"x-required-scopes":["crm.reminders.update"]}},"/reminder-types/{id}/message":{"patch":{"tags":["Reminders"],"summary":"Change a reminder template's message","description":"Replaces a reminder template's message and its list of placeholder names. Answers the template as saved.\n\n**Permission:** `crm.reminders.update` (Update reminders).","operationId":"patchReminderTypeMessage","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The reminder template's ID","example":"3d5a1f7c-2e9b-4c4d-9fe6-0e3b6c9d2f5a"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"The new text; may hold ${name} placeholders","examples":["Hello ${accountName}, your bill of ${amount} is due on ${dueDate}."]},"templateVars":{"type":"array","items":{"type":"string"},"description":"The placeholder names the template may use; every ${...} in the message must be one of them. Saved as the template's templateVariables","examples":[["accountName","amount","dueDate"]]}},"required":["message","templateVars"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"category":{"type":"string"},"priority":{"type":"string"},"title":{"type":"string"},"message":{"type":"string"},"templateVariables":{"type":"array","items":{"type":"string"}},"isActive":{"type":"boolean"},"description":{"type":"string"},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}},"example":{"success":true,"data":{"id":"3d5a1f7c-2e9b-4c4d-9fe6-0e3b6c9d2f5a","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","category":"custom","priority":"medium","title":"Payment due for ${accountName}","message":"Hello ${accountName}, your bill of ${amount} is due on ${dueDate}.","templateVariables":["accountName","amount","dueDate"],"isActive":true,"description":"Sent a few days before a bill is due","deletedAt":null,"createdAt":"2026-09-01T08:00:00.000Z","updatedAt":"2026-10-20T09:00:00.000Z"}}}}},"400":{"description":"templateVars is not a list, or a ${...} placeholder in the message is not in templateVars","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No reminder template with that ID in your organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.reminders.update"]}],"x-required-scopes":["crm.reminders.update"]}},"/accounts/{accountId}/contacts/{contactId}/primary":{"patch":{"tags":["Contacts"],"summary":"Make a contact an account's primary contact","description":"Make a contact the primary contact of an account it is linked to (the previous primary stops being primary). Only an active contact can be primary. By default it also becomes the account's billing and service contact where the account has none.\n\n**Permission:** `crm.contacts.update` (Update contacts).","operationId":"setPrimaryContact","parameters":[{"name":"accountId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The account to make it primary on","example":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"},{"name":"contactId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The contact's ID","example":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"updateAccountContacts":{"type":"string","enum":["true","false"],"default":"true","description":"\"true\" (the default): also make the contact the account's billing and service contact where it has none","examples":["true"]},"userId":{"type":"string","format":"uuid","description":"ID of the person making the change (for the history)","examples":["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]}},"required":["userId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"contact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","description":"The email to show (the shared record's when filed under one)"},"phone":{"type":"string","description":"The phone to show (the shared record's when filed under one)"},"addressId":{},"status":{"type":"string","description":"active, invited, inactive or suspended"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}},"description":"Communication preferences: notifications, preferredContactMethod (email, phone or sms), timezone, language"},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array"},"partyId":{"type":"string","description":"The person record in Kazee Contacts the contact is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the contact is filed under one, else first and last name"},"fullName":{"type":"string","description":"Same as displayName"}}},"account":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the account is filed under one, else the account's own"},"accountNumber":{"type":"string","description":"Your organisation's account number, unique"},"status":{"type":"string","description":"active, inactive, suspended or terminated"},"description":{"type":"string"},"addressId":{"type":"string"},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string","description":"Credit limit, a decimal number in a string"},"currentBalance":{"type":"string","description":"Current balance, a decimal number in a string"},"originLeadId":{"type":"string","description":"The lead the account was converted from, if any"},"walletId":{},"billingContactId":{"type":"string","description":"The contact who gets the bills"},"serviceContactId":{"type":"string","description":"The contact for service matters"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"}}},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array","description":"IDs of this account in other systems, e.g. { source: zoho, externalId }"},"partyId":{"type":"string","description":"The person or company record in Kazee Contacts the account is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}},"example":{"success":true,"message":"Wanjiru Kamau set as primary contact for Baraka Traders Ltd","data":{"contact":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau"},"account":{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Baraka Traders Ltd","accountNumber":"ACC-000123","status":"active","description":"Hardware shop on Moi Avenue","addressId":"f4e13f6b-8c0d-4f7a-9d2e-6b7c8d9e0f1a","onboardingDate":"2026-10-01T08:30:00.000Z","terminationDate":null,"creditLimit":"50000.00","currentBalance":"0.00","originLeadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","walletId":null,"billingContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","serviceContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notificationPreferences":{"email":true,"sms":true},"tags":["retail"],"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Baraka Traders Ltd","cachePrimaryPhone":"+254700000101","cachePrimaryEmail":"info@baraka-traders.example","cachePartyRevision":3,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No contact or account with that ID in your organisation, or the contact is not linked to the account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The contact is not active","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.contacts.update"]}],"x-required-scopes":["crm.contacts.update"]}},"/contacts/{contactId}/primary":{"patch":{"tags":["Contacts"],"summary":"Make a contact an account's primary contact","description":"Make a contact the primary contact of an account it is linked to (the previous primary stops being primary). Only an active contact can be primary. By default it also becomes the account's billing and service contact where the account has none.\n\n**Permission:** `crm.contacts.update` (Update contacts).","operationId":"setPrimaryContact_2","parameters":[{"name":"contactId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The contact's ID","example":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid","description":"The account to make it primary on","examples":["2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"]},"updateAccountContacts":{"type":"string","enum":["true","false"],"default":"true","description":"\"true\" (the default): also make the contact the account's billing and service contact where it has none","examples":["true"]},"userId":{"type":"string","format":"uuid","description":"ID of the person making the change (for the history)","examples":["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]}},"required":["accountId","userId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"contact":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","description":"The email to show (the shared record's when filed under one)"},"phone":{"type":"string","description":"The phone to show (the shared record's when filed under one)"},"addressId":{},"status":{"type":"string","description":"active, invited, inactive or suspended"},"preferences":{"type":"object","properties":{"preferredContactMethod":{"type":"string"}},"description":"Communication preferences: notifications, preferredContactMethod (email, phone or sms), timezone, language"},"ownerId":{"type":"string"},"notes":{},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array"},"partyId":{"type":"string","description":"The person record in Kazee Contacts the contact is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"displayName":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the contact is filed under one, else first and last name"},"fullName":{"type":"string","description":"Same as displayName"}}},"account":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"name":{"type":"string","description":"The name to show: the shared Kazee Contacts record's name when the account is filed under one, else the account's own"},"accountNumber":{"type":"string","description":"Your organisation's account number, unique"},"status":{"type":"string","description":"active, inactive, suspended or terminated"},"description":{"type":"string"},"addressId":{"type":"string"},"onboardingDate":{"type":"string","format":"date-time"},"terminationDate":{},"creditLimit":{"type":"string","description":"Credit limit, a decimal number in a string"},"currentBalance":{"type":"string","description":"Current balance, a decimal number in a string"},"originLeadId":{"type":"string","description":"The lead the account was converted from, if any"},"walletId":{},"billingContactId":{"type":"string","description":"The contact who gets the bills"},"serviceContactId":{"type":"string","description":"The contact for service matters"},"ownerId":{"type":"string"},"notificationPreferences":{"type":"object","properties":{"email":{"type":"boolean"},"sms":{"type":"boolean"}}},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","properties":{},"description":"Your organisation's own fields, by field key"},"externalIds":{"type":"array","description":"IDs of this account in other systems, e.g. { source: zoho, externalId }"},"partyId":{"type":"string","description":"The person or company record in Kazee Contacts the account is filed under"},"cacheDisplayName":{"type":"string","description":"Copy of the Kazee Contacts record's name (read only; the cache* fields are kept in step by CRM)"},"cachePrimaryPhone":{"type":"string"},"cachePrimaryEmail":{"type":"string"},"cachePartyRevision":{"type":"integer"},"cacheUpdatedAt":{"type":"string","format":"date-time"},"cachePartyDeletedAt":{},"deletedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}},"example":{"success":true,"message":"Wanjiru Kamau set as primary contact for Baraka Traders Ltd","data":{"contact":{"id":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","firstName":"Wanjiru","lastName":"Kamau","email":"wanjiru.kamau@example.com","phone":"+254700000102","addressId":null,"status":"active","preferences":{"preferredContactMethod":"email"},"ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notes":null,"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Wanjiru Kamau","cachePrimaryPhone":"+254700000102","cachePrimaryEmail":"wanjiru.kamau@example.com","cachePartyRevision":2,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z","displayName":"Wanjiru Kamau","fullName":"Wanjiru Kamau"},"account":{"id":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","organizationId":"5f0c2b8e-1a3d-4e6f-8a9b-0c1d2e3f4a5b","name":"Baraka Traders Ltd","accountNumber":"ACC-000123","status":"active","description":"Hardware shop on Moi Avenue","addressId":"f4e13f6b-8c0d-4f7a-9d2e-6b7c8d9e0f1a","onboardingDate":"2026-10-01T08:30:00.000Z","terminationDate":null,"creditLimit":"50000.00","currentBalance":"0.00","originLeadId":"0b9e8c1e-3f5a-4c2d-9a7b-1e2f3a4b5c6d","walletId":null,"billingContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","serviceContactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","ownerId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","notificationPreferences":{"email":true,"sms":true},"tags":["retail"],"customFields":{},"externalIds":[],"partyId":"c1be0c3e-5f7a-4c4d-9a9b-3e4f5a6b7c8d","cacheDisplayName":"Baraka Traders Ltd","cachePrimaryPhone":"+254700000101","cachePrimaryEmail":"info@baraka-traders.example","cachePartyRevision":3,"cacheUpdatedAt":"2026-10-15T14:05:00.000Z","cachePartyDeletedAt":null,"deletedAt":null,"createdAt":"2026-10-01T08:30:00.000Z","updatedAt":"2026-10-15T14:05:00.000Z"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No contact or account with that ID in your organisation, or the contact is not linked to the account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The contact is not active","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.contacts.update"]}],"x-required-scopes":["crm.contacts.update"]}},"/contacts/{contactId}/roles":{"patch":{"tags":["Contacts"],"summary":"Change a contact's roles on an account","description":"Change the roles a contact has on one account: add one role, remove one, or set the whole list. A contact always keeps at least one role, and a primary contact keeps admin when the list is set.\n\n**Permission:** `crm.contacts.update` (Update contacts).","operationId":"manageContactRoles","parameters":[{"name":"contactId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The contact's ID","example":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid","description":"The account the roles are on","examples":["2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f"]},"operation":{"type":"string","enum":["add","remove","set"],"description":"add or remove one role (role), or set the whole list (roles)","examples":["add"]},"role":{"type":"string","enum":["admin","billing","technical","support","readonly","primary"],"description":"The role to add or remove","examples":["billing"]},"roles":{"type":"array","items":{"type":"string","enum":["admin","billing","technical","support","readonly","primary"]},"description":"For set: the whole list; empty gives admin (a JSON array in a string also works)","examples":[["admin","billing"]]},"userId":{"type":"string","format":"uuid","description":"ID of the person making the change (for the history)","examples":["a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]}},"required":["accountId","operation","userId"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"object","properties":{"contactId":{"type":"string"},"accountId":{"type":"string"},"roles":{"type":"array","items":{"type":"string"},"description":"The contact's roles on the account now"}}}}},"example":{"success":true,"message":"Contact roles added successfully","data":{"contactId":"4f3c2a5c-7d9e-4a6b-9e1f-5c6d7e8f9a0b","accountId":"2d1a0e3a-5b7c-4e4f-9c9d-3a4b5c6d7e8f","roles":["admin","billing"]}}}}},"400":{"description":"operation is not add, remove or set, a role is unknown, roles is not valid JSON, or remove would take away the last role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No contact with that ID, or the contact is not linked to the account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.contacts.update"]}],"x-required-scopes":["crm.contacts.update"]}},"/notifications/{notificationId}/read":{"patch":{"tags":["Notifications"],"summary":"Mark a notification read","description":"Marks one of your own notifications as read and answers it as saved. Someone else's notification answers 404, the same as one that does not exist.\n\n**Permission:** `crm.notifications.update` (Update notifications).","operationId":"updateNotificationRead","parameters":[{"name":"notificationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The notification's ID","example":"5f7c3b9e-4a1d-4e6f-9b08-2a5d8e1f4b7c"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"userId":{"type":"string"},"actorId":{"type":"string"},"category":{"type":"string"},"notificationType":{"type":"string"},"title":{"type":"string"},"content":{"type":"string"},"source":{"type":"string"},"sourceEntityId":{"type":"string"},"data":{"type":"object","properties":{"accountId":{"type":"string"},"accountName":{"type":"string"},"subscriptionId":{"type":"string"},"module":{"type":"string"},"entity":{"type":"string"},"action":{"type":"string"},"outcome":{"type":"string"}}},"isRead":{"type":"boolean"},"readAt":{"type":"string","format":"date-time"},"isArchived":{"type":"boolean"},"archivedAt":{},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"message":{"type":"string"}}},"example":{"success":true,"data":{"id":"5f7c3b9e-4a1d-4e6f-9b08-2a5d8e1f4b7c","organizationId":"7d1e4c2a-5b3f-4a8e-9c6d-2f1a0b9e8c7d","userId":"1b3e9d5a-0c7f-4a2b-9dc4-8c1f4a7b0d3e","actorId":"2c4f0e6b-1d8a-4b3c-8ed5-9d2a5b8c1e4f","category":"subscriptions","notificationType":"create","title":"subscription_created","content":"New subscription created for account \"Jane Wanjiku\"","source":"crm:subscriptions:subscription_created","sourceEntityId":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","data":{"accountId":"a4f2c8e1-6b3d-4e9a-8c7f-1d2e3f4a5b6c","accountName":"Jane Wanjiku","subscriptionId":"3c9a7e51-2d4b-4f6a-8e1c-5b7d9f0a2c4e","module":"subscriptions","entity":"subscription","action":"create","outcome":"success"},"isRead":true,"readAt":"2026-10-21T08:30:00.000Z","isArchived":false,"archivedAt":null,"createdAt":"2026-10-20T09:00:00.000Z","updatedAt":"2026-10-21T08:30:00.000Z"},"message":"Notification marked as read successfully"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No notification with that ID in your own feed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/InvalidInput"},"500":{"$ref":"#/components/responses/ServerError"}},"security":[{"kazee":["crm.notifications.update"]}],"x-required-scopes":["crm.notifications.update"]}}},"components":{"schemas":{"Error":{"type":"object","description":"Every error reply. The HTTP status says what kind of error it is.","properties":{"error":{"type":"string","description":"What went wrong, in words"}},"required":["error"]}},"responses":{"BadRequest":{"description":"The request was refused, for example an ID in the wrong form","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"No sign-in, or the token has expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"Signed in, but without the permission or access this needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Nothing found with that ID, or you cannot see it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InvalidInput":{"description":"A required input is missing or an input is not valid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something went wrong on our side","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"securitySchemes":{"kazee":{"type":"http","scheme":"bearer","bearerFormat":"Kazee token","description":"Send `Authorization: Bearer <token>` with a token from Kazee sign-in."}}},"security":[{"kazee":[]}],"x-tagGroups":[{"name":"Sales","tags":["Leads","Deals","Accounts","Contacts","Quotes","Products & plans","Pipelines"]},{"name":"Billing","tags":["Subscriptions","Billing"]},{"name":"Help desk","tags":["Tickets","Requests","Desk","Knowledge base","Live chat","Help desk setup"]},{"name":"Calls & messages","tags":["Calls","Comments","Notifications","Reminders"]},{"name":"Find","tags":["Search","Saved filters","Dashboards","Exports","Activity"]},{"name":"Setup","tags":["Custom fields & modules","Layouts","Automations","Integrations","Teams & people","Settings"]}],"servers":[{"url":"https://api.crm.kazee.africa/api"}]}