{"openapi":"3.1.0","info":{"title":"Co-Create CRM API","version":"1.0.0","description":"## Getting started\n\nMake a personal access token in your CRM profile, under AI assistants and API. Send it on every request as a bearer token. A token acts as you, in the workspace you made it in: it sees the same companies and locations you do, with your role, and nothing more.\n\nA read only token can list and read. A read and write token can also add and change contacts, consent, tags, deals and tasks, and manage webhooks if you're an agency admin or a company owner.\n\nThe API never sends email, text messages or social posts to anyone. There are no send endpoints.\n\nThis reference is also an OpenAPI 3.1 document at /api/v1/openapi.json, which needs no token.\n\n## Responses and pages\n\nOne record comes back as { \"data\": { ... } }. Lists come back as { \"data\": [ ... ], \"next_cursor\": \"...\" }, in the order each list describes. Pass next_cursor as the cursor parameter to get the next page; it is null on the last page. limit sets the page size, up to 200 (50 if you leave it out).\n\nIds are uuids. Times are ISO 8601 in UTC. Money is whole CAD cents. Keys are snake_case. Request bodies are JSON, and fields the API doesn't know are refused rather than ignored.\n\nTo keep a copy in sync, list contacts with updated_since set a minute or two before your last sync started, and page through.\n\n## Errors\n\nErrors come back as { \"error\": { \"code\": \"...\", \"message\": \"...\" } }, with details for validation errors. The message is written for people; the code is for your program.\n\nCodes: unauthorized (401), read_only_token, forbidden, two_factor_required (403), not_found (404), invalid_request, invalid_json, invalid_cursor, invalid_url, consent_evidence_missing, consent_backdating (400), duplicate_contact, consent_on_record, webhook_paused, too_many_webhooks, conflict (409), payload_too_large (413), rate_limited (429) and internal_error (500).\n\n## Rate limits\n\nEach token can make 300 requests a minute. Past that, requests get 429 with a Retry-After header saying how many seconds to wait. Requests with a missing or wrong token are limited to 20 a minute from each IP address, and that address then waits before even a good token works.\n\nRequest bodies can be up to 256 KB.\n\n## Consent (CASL)\n\nConsent on a contact is read only except through POST /contacts/{id}/consent, which uses the same consent record as the CRM itself. Every record is dated the moment the CRM receives it: you can't set recorded_at, so consent can't be backdated. Send it when the person agrees.\n\nExpress consent needs the exact wording the person agreed to and evidence of how: the method, and for a web form the page address. Implied consent from a purchase or an inquiry needs your reference for it, like an order number. Withdrawals (unsubscribes) are always accepted and apply across the whole company.\n\nChanging a contact's email or phone through the API is refused while consent is on record for that address, so consent is never carried over to an address nobody agreed for.\n\n## AI and API access policy\n\nEach company's owners decide what tokens can see about its people, in the company's AI and API access settings. It applies to every token, because the CRM can't tell an AI assistant from any other app. Contacts outside the policy, and everything linked to them, are simply not there: lists leave them out and asking for one by id gets 404.\n\nDetails the policy holds back come back as the text [hidden by policy] in place of the value: email, phone, town and consent addresses, or custom fields. Notes and messages, deals and orders, and form answers can be held back as a whole. When a company turns personal tokens off, its people's tokens get 403 tokens_off until it's turned back on.\n\nEvery request that returns or changes people's records is logged for the company's owners, with the token's name.\n\n## Webhooks\n\nAgency admins and company owners can have the CRM post events to an https address: in Settings, or with /webhooks below. Each event is a POST with a JSON body: { \"id\", \"type\", \"created_at\", \"company_id\", \"data\" }. data is the record, shaped as the API returns it. The id is the same for every webhook that gets the event, so you can drop repeats.\n\nAnswer with any 2xx status within 10 seconds. Anything else, including a redirect, counts as a failure. Failures are tried again after 1, 3, 9, 27 and 81 minutes, then about 4 hours and 12 hours: 8 tries in all, over about 18 hours. After 5 failures in a row the webhook is marked as failing in Settings until a delivery gets through.\n\nEvery delivery is signed. The ccrm-signature header looks like t=1760500000,v1=5257a869..., where v1 is the hex HMAC-SHA256 of the time, a full stop and the raw body, keyed with your signing secret (all of it, including whsec_). Check it against the raw body before you parse it, and refuse deliveries more than 5 minutes old. Other headers: ccrm-event, ccrm-event-id and ccrm-delivery.\n\nEvents: contact.created (a contact is added), contact.updated (a contact's details or tags change), consent.recorded (consent is recorded), consent.withdrawn (someone unsubscribes), form.submitted (a form is submitted), deal.created (a deal is added), deal.moved (a deal moves stage), task.completed (a task is done), appointment.booked (an appointment is booked), appointment.updated (an appointment is moved, confirmed, cancelled or marked done), order.paid (an order is paid at a checkout). contact.updated data also lists the changed fields in changed. The test button sends webhook.test."},"servers":[{"url":"/","description":"This CRM"}],"x-guide":[{"id":"start","title":"Getting started","paragraphs":["Make a personal access token in your CRM profile, under AI assistants and API. Send it on every request as a bearer token. A token acts as you, in the workspace you made it in: it sees the same companies and locations you do, with your role, and nothing more.","A read only token can list and read. A read and write token can also add and change contacts, consent, tags, deals and tasks, and manage webhooks if you're an agency admin or a company owner.","The API never sends email, text messages or social posts to anyone. There are no send endpoints.","This reference is also an OpenAPI 3.1 document at /api/v1/openapi.json, which needs no token."],"code":"curl https://crm.example.com/api/v1/locations \\\n  -H \"Authorization: Bearer ccrm_your_token_here\""},{"id":"responses","title":"Responses and pages","paragraphs":["One record comes back as { \"data\": { ... } }. Lists come back as { \"data\": [ ... ], \"next_cursor\": \"...\" }, in the order each list describes. Pass next_cursor as the cursor parameter to get the next page; it is null on the last page. limit sets the page size, up to 200 (50 if you leave it out).","Ids are uuids. Times are ISO 8601 in UTC. Money is whole CAD cents. Keys are snake_case. Request bodies are JSON, and fields the API doesn't know are refused rather than ignored.","To keep a copy in sync, list contacts with updated_since set a minute or two before your last sync started, and page through."]},{"id":"errors","title":"Errors","paragraphs":["Errors come back as { \"error\": { \"code\": \"...\", \"message\": \"...\" } }, with details for validation errors. The message is written for people; the code is for your program.","Codes: unauthorized (401), read_only_token, forbidden, two_factor_required (403), not_found (404), invalid_request, invalid_json, invalid_cursor, invalid_url, consent_evidence_missing, consent_backdating (400), duplicate_contact, consent_on_record, webhook_paused, too_many_webhooks, conflict (409), payload_too_large (413), rate_limited (429) and internal_error (500)."]},{"id":"limits","title":"Rate limits","paragraphs":["Each token can make 300 requests a minute. Past that, requests get 429 with a Retry-After header saying how many seconds to wait. Requests with a missing or wrong token are limited to 20 a minute from each IP address, and that address then waits before even a good token works.","Request bodies can be up to 256 KB."]},{"id":"consent","title":"Consent (CASL)","paragraphs":["Consent on a contact is read only except through POST /contacts/{id}/consent, which uses the same consent record as the CRM itself. Every record is dated the moment the CRM receives it: you can't set recorded_at, so consent can't be backdated. Send it when the person agrees.","Express consent needs the exact wording the person agreed to and evidence of how: the method, and for a web form the page address. Implied consent from a purchase or an inquiry needs your reference for it, like an order number. Withdrawals (unsubscribes) are always accepted and apply across the whole company.","Changing a contact's email or phone through the API is refused while consent is on record for that address, so consent is never carried over to an address nobody agreed for."]},{"id":"ai-access","title":"AI and API access policy","paragraphs":["Each company's owners decide what tokens can see about its people, in the company's AI and API access settings. It applies to every token, because the CRM can't tell an AI assistant from any other app. Contacts outside the policy, and everything linked to them, are simply not there: lists leave them out and asking for one by id gets 404.","Details the policy holds back come back as the text [hidden by policy] in place of the value: email, phone, town and consent addresses, or custom fields. Notes and messages, deals and orders, and form answers can be held back as a whole. When a company turns personal tokens off, its people's tokens get 403 tokens_off until it's turned back on.","Every request that returns or changes people's records is logged for the company's owners, with the token's name."]},{"id":"webhooks","title":"Webhooks","paragraphs":["Agency admins and company owners can have the CRM post events to an https address: in Settings, or with /webhooks below. Each event is a POST with a JSON body: { \"id\", \"type\", \"created_at\", \"company_id\", \"data\" }. data is the record, shaped as the API returns it. The id is the same for every webhook that gets the event, so you can drop repeats.","Answer with any 2xx status within 10 seconds. Anything else, including a redirect, counts as a failure. Failures are tried again after 1, 3, 9, 27 and 81 minutes, then about 4 hours and 12 hours: 8 tries in all, over about 18 hours. After 5 failures in a row the webhook is marked as failing in Settings until a delivery gets through.","Every delivery is signed. The ccrm-signature header looks like t=1760500000,v1=5257a869..., where v1 is the hex HMAC-SHA256 of the time, a full stop and the raw body, keyed with your signing secret (all of it, including whsec_). Check it against the raw body before you parse it, and refuse deliveries more than 5 minutes old. Other headers: ccrm-event, ccrm-event-id and ccrm-delivery.","Events: contact.created (a contact is added), contact.updated (a contact's details or tags change), consent.recorded (consent is recorded), consent.withdrawn (someone unsubscribes), form.submitted (a form is submitted), deal.created (a deal is added), deal.moved (a deal moves stage), task.completed (a task is done), appointment.booked (an appointment is booked), appointment.updated (an appointment is moved, confirmed, cancelled or marked done), order.paid (an order is paid at a checkout). contact.updated data also lists the changed fields in changed. The test button sends webhook.test."],"code":"// Node: check a delivery before trusting it.\nimport { createHmac, timingSafeEqual } from \"node:crypto\";\n\nfunction verify(secret, header, rawBody) {\n  const parts = Object.fromEntries(header.split(\",\").map((p) => p.split(\"=\")));\n  const t = Number(parts.t);\n  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;\n  const expected = createHmac(\"sha256\", secret).update(`${t}.${rawBody}`).digest();\n  const given = Buffer.from(parts.v1 || \"\", \"hex\");\n  return given.length === expected.length && timingSafeEqual(given, expected);\n}"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Locations","description":"The business locations your token can see."},{"name":"Contacts","description":"People at a location. Consent fields are read only; record consent with the consent endpoint."},{"name":"Consent","description":"Record consent or an unsubscribe for a contact, with the evidence CASL needs."},{"name":"Tags","description":"Short labels on contacts, like vip or newsletter."},{"name":"Forms","description":"Website forms and what people sent through them."},{"name":"Pipeline","description":"Pipelines, their stages, and deals in CAD."},{"name":"Tasks","description":"To-dos at a location."},{"name":"Appointments","description":"Bookings on each location's calendar, soonest first. Read only."},{"name":"Events","description":"Events and who registered. Read only."},{"name":"Webhooks","description":"Where the CRM posts events. Agency admins and company owners only."}],"paths":{"/api/v1/locations":{"get":{"operationId":"getLocations","tags":["Locations"],"summary":"List the locations your token can see","x-token-access":"read","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Location"}},"next_cursor":{"type":["string","null"],"description":"Pass as cursor for the next page; null on the last page"}}}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/contacts":{"get":{"operationId":"getContacts","tags":["Contacts"],"summary":"List contacts, most recently changed first","x-token-access":"read","x-example-query":"?updated_since=2026-10-01T00:00:00Z&limit=50","parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this location"},{"name":"tag","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":50},"description":"A tag. Trimmed and stored in lower case."},{"name":"email","in":"query","required":false,"schema":{"type":"string","maxLength":254},"description":"Only contacts with this email address (any case)"},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only contacts changed at or after this time (ISO 8601)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"How many to return"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","maxLength":200},"description":"next_cursor from the previous page"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Contact"}},"next_cursor":{"type":["string","null"],"description":"Pass as cursor for the next page; null on the last page"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"postContacts","tags":["Contacts"],"summary":"Add a contact","description":"Records no consent and sends nothing. If a contact at that location already has the email, you get 409 duplicate_contact with existing_id.","x-token-access":"read_write","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"location_id":{"type":"string","format":"uuid"},"full_name":{"type":"string","minLength":1,"maxLength":200},"email":{"type":["string","null"],"format":"email","maxLength":254},"phone":{"type":["string","null"],"maxLength":40,"description":"At least 10 digits. Stored as +1 and the number."},"tags":{"type":"array","items":{"type":"string","minLength":1,"maxLength":50,"description":"A tag. Trimmed and stored in lower case."},"maxItems":20}},"required":["location_id","full_name"],"additionalProperties":false},"example":{"location_id":"8d2f4b6a-1c3e-4a5b-9d7f-0e1a2b3c4d5e","full_name":"Avery Example","email":"avery@example.com","phone":"250-555-0142","tags":["newsletter"]}}}},"responses":{"201":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Contact"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Read only token, or your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/contacts/{id}":{"get":{"operationId":"getContactsBy_id","tags":["Contacts"],"summary":"Get one contact, with what the consent check decides right now","x-token-access":"read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The record's id"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ContactDetail"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not in a location you can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"patchContactsBy_id","tags":["Contacts"],"summary":"Change a contact's name, email or phone","description":"Consent can't be changed here. Changing an email or phone that has consent on record is refused with 409 consent_on_record.","x-token-access":"read_write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The record's id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"full_name":{"type":"string","minLength":1,"maxLength":200},"email":{"type":["string","null"],"format":"email","maxLength":254},"phone":{"type":["string","null"],"maxLength":40}},"additionalProperties":false},"example":{"full_name":"Avery Example-Smith"}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Contact"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Read only token, or your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not in a location you can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/contacts/{id}/consent":{"post":{"operationId":"postContactsBy_idConsent","tags":["Consent"],"summary":"Record consent or an unsubscribe","description":"Dated when the CRM receives it; recorded_at can't be set. Express consent needs wording and evidence.method (and evidence.page_url for web_form). Implied consent needs evidence.reference. Withdrawals are always accepted.","x-token-access":"read_write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The record's id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"channel":{"type":"string","enum":["email","sms","dm"]},"type":{"type":"string","enum":["express","implied_purchase","implied_inquiry","implied_donation","implied_volunteer","implied_membership","withdrawn"],"description":"withdrawn records an unsubscribe; implied_donation, implied_volunteer and implied_membership are for charities and nonprofits only"},"wording":{"type":"string","minLength":1,"maxLength":2000,"description":"The exact words the person agreed to. Needed for express consent."},"evidence":{"type":"object","properties":{"method":{"type":"string","enum":["web_form","paper_form","in_person","phone_call","email_reply","point_of_sale","other"],"description":"How the person agreed. Needed for express consent."},"page_url":{"type":"string","format":"uri","maxLength":2000,"description":"The page the form was on. Needed when method is web_form."},"ip":{"type":"string","format":"ip","description":"The person's IP address when they agreed"},"user_agent":{"type":"string","maxLength":300},"reference":{"type":"string","minLength":1,"maxLength":200,"description":"Your record of it: an order, form entry or ticket number. Needed for implied consent."},"notes":{"type":"string","maxLength":1000}},"additionalProperties":false}},"required":["channel","type"],"additionalProperties":false},"example":{"channel":"email","type":"express","wording":"Yes, send me news and offers from Harbour Light Bakery. I can unsubscribe at any time.","evidence":{"method":"web_form","page_url":"https://www.example.ca/newsletter","ip":"203.0.113.24","reference":"signup-1042"}}}}},"responses":{"201":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ConsentRecord"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Read only token, or your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not in a location you can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/tags":{"get":{"operationId":"getTags","tags":["Tags"],"summary":"List the tags in use, with how many contacts have each","x-token-access":"read","parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this location"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Tag"}},"next_cursor":{"type":["string","null"],"description":"Pass as cursor for the next page; null on the last page"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/contacts/{id}/tags":{"post":{"operationId":"postContactsBy_idTags","tags":["Tags"],"summary":"Add tags to a contact","x-token-access":"read_write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The record's id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tags":{"type":"array","items":{"type":"string","minLength":1,"maxLength":50,"description":"A tag. Trimmed and stored in lower case."},"minItems":1,"maxItems":20}},"required":["tags"],"additionalProperties":false},"example":{"tags":["vip","spring open house"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Contact"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Read only token, or your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not in a location you can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/contacts/{id}/tags/{tag}":{"delete":{"operationId":"deleteContactsBy_idTagsBy_tag","tags":["Tags"],"summary":"Take a tag off a contact","x-token-access":"read_write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The contact's id"},{"name":"tag","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":50},"description":"A tag. Trimmed and stored in lower case."}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Contact"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Read only token, or your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not in a location you can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/forms":{"get":{"operationId":"getForms","tags":["Forms"],"summary":"List forms, newest first","x-token-access":"read","parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this location"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"How many to return"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","maxLength":200},"description":"next_cursor from the previous page"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Form"}},"next_cursor":{"type":["string","null"],"description":"Pass as cursor for the next page; null on the last page"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/forms/{id}/submissions":{"get":{"operationId":"getFormsBy_idSubmissions","tags":["Forms"],"summary":"List one form's submissions, newest first","x-token-access":"read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The record's id"},{"name":"created_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only submissions sent at or after this time (ISO 8601)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"How many to return"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","maxLength":200},"description":"next_cursor from the previous page"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Submission"}},"next_cursor":{"type":["string","null"],"description":"Pass as cursor for the next page; null on the last page"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not in a location you can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/pipelines":{"get":{"operationId":"getPipelines","tags":["Pipeline"],"summary":"List pipelines and their stages","x-token-access":"read","parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only this location"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Pipeline"}},"next_cursor":{"type":["string","null"],"description":"Pass as cursor for the next page; null on the last page"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/deals":{"get":{"operationId":"getDeals","tags":["Pipeline"],"summary":"List deals, newest first","x-token-access":"read","parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"pipeline_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"stage_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"contact_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"How many to return"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","maxLength":200},"description":"next_cursor from the previous page"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Deal"}},"next_cursor":{"type":["string","null"],"description":"Pass as cursor for the next page; null on the last page"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"postDeals","tags":["Pipeline"],"summary":"Add a deal","description":"Goes in the first open stage unless you give stage_id. Owned by you unless you give owner_membership_id (null for nobody).","x-token-access":"read_write","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"pipeline_id":{"type":"string","format":"uuid"},"stage_id":{"type":"string","format":"uuid"},"name":{"type":"string","minLength":1,"maxLength":200},"value_cents":{"type":"integer","minimum":0,"maximum":100000000000,"description":"Value in CAD cents"},"contact_id":{"type":["string","null"],"format":"uuid"},"owner_membership_id":{"type":["string","null"],"format":"uuid"}},"required":["pipeline_id","name"],"additionalProperties":false},"example":{"pipeline_id":"5a7c9e1b-3d5f-4a6b-8c0d-2e4f6a8b0c1d","name":"Kitchen refresh quote","value_cents":1250000}}}},"responses":{"201":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Deal"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Read only token, or your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/deals/{id}/move":{"post":{"operationId":"postDealsBy_idMove","tags":["Pipeline"],"summary":"Move a deal to another stage of its pipeline","x-token-access":"read_write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The record's id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"stage_id":{"type":"string","format":"uuid"}},"required":["stage_id"],"additionalProperties":false},"example":{"stage_id":"9b1d3f5a-7c9e-4b0d-8f2a-4c6e8a0b2d4f"}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Deal"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Read only token, or your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not in a location you can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/tasks":{"get":{"operationId":"getTasks","tags":["Tasks"],"summary":"List tasks, newest first","x-token-access":"read","x-example-query":"?status=open","parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["open","done"]}},{"name":"assignee_membership_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"contact_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"How many to return"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","maxLength":200},"description":"next_cursor from the previous page"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Task"}},"next_cursor":{"type":["string","null"],"description":"Pass as cursor for the next page; null on the last page"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"postTasks","tags":["Tasks"],"summary":"Add a task","description":"Assigned to you unless you give assignee_membership_id (null for nobody). The assignee must work at the location.","x-token-access":"read_write","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"location_id":{"type":"string","format":"uuid"},"title":{"type":"string","minLength":1,"maxLength":300},"notes":{"type":"string","maxLength":5000},"due_on":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"A date, like 2026-10-15"},"assignee_membership_id":{"type":["string","null"],"format":"uuid"},"contact_id":{"type":["string","null"],"format":"uuid"}},"required":["location_id","title"],"additionalProperties":false},"example":{"location_id":"8d2f4b6a-1c3e-4a5b-9d7f-0e1a2b3c4d5e","title":"Call back about the quote","due_on":"2026-10-20"}}}},"responses":{"201":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Task"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Read only token, or your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/tasks/{id}/complete":{"post":{"operationId":"postTasksBy_idComplete","tags":["Tasks"],"summary":"Mark a task done","description":"Staff can complete their own tasks only, as in the CRM.","x-token-access":"read_write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The record's id"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Task"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Read only token, or your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not in a location you can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/appointments":{"get":{"operationId":"getAppointments","tags":["Appointments"],"summary":"List appointments, soonest first","x-token-access":"read","x-example-query":"?status=booked","parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Starting at or after this time. Defaults to now."},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Starting before this time"},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["booked","confirmed","done","no_show","cancelled"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"How many to return"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","maxLength":200},"description":"next_cursor from the previous page"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Appointment"}},"next_cursor":{"type":["string","null"],"description":"Pass as cursor for the next page; null on the last page"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/events":{"get":{"operationId":"getEvents","tags":["Events"],"summary":"List events, latest start first","x-token-access":"read","parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["draft","open","closed","cancelled"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"How many to return"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","maxLength":200},"description":"next_cursor from the previous page"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Event"}},"next_cursor":{"type":["string","null"],"description":"Pass as cursor for the next page; null on the last page"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/events/{id}/registrations":{"get":{"operationId":"getEventsBy_idRegistrations","tags":["Events"],"summary":"List one event's registrations, newest first","x-token-access":"read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The record's id"},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["going","waitlist","cancelled"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"How many to return"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","maxLength":200},"description":"next_cursor from the previous page"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Registration"}},"next_cursor":{"type":["string","null"],"description":"Pass as cursor for the next page; null on the last page"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not in a location you can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/webhooks":{"get":{"operationId":"getWebhooks","tags":["Webhooks"],"summary":"List webhooks","description":"Agency admins and company owners only.","x-token-access":"read","x-who":"Agency admins and company owners only.","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}},"next_cursor":{"type":["string","null"],"description":"Pass as cursor for the next page; null on the last page"}}}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"postWebhooks","tags":["Webhooks"],"summary":"Add a webhook","description":"The answer includes the signing secret, this once only. The address must be https and must not point to a private or local network. Agency admins and company owners only.","x-token-access":"read_write","x-who":"Agency admins and company owners only.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":2000,"description":"Where to post events. https only."},"events":{"type":"array","items":{"type":"string","enum":["contact.created","contact.updated","consent.recorded","consent.withdrawn","form.submitted","deal.created","deal.moved","task.completed","appointment.booked","appointment.updated","order.paid"]},"minItems":1,"maxItems":11,"description":"Events to send: contact.created, contact.updated, consent.recorded, consent.withdrawn, form.submitted, deal.created, deal.moved, task.completed, appointment.booked, appointment.updated, order.paid"},"description":{"type":["string","null"],"maxLength":200,"description":"A note to tell webhooks apart"},"company_id":{"type":["string","null"],"format":"uuid","description":"Agency admins: one company, or null for every company. Owners: always their own company."}},"required":["url","events"],"additionalProperties":false},"example":{"url":"https://hooks.example.com/crm","events":["contact.created","form.submitted"],"description":"New leads to our booking tool"}}}},"responses":{"201":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/NewWebhook"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Read only token, or your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/webhooks/{id}":{"get":{"operationId":"getWebhooksBy_id","tags":["Webhooks"],"summary":"Get one webhook","description":"Agency admins and company owners only.","x-token-access":"read","x-who":"Agency admins and company owners only.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The record's id"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Webhook"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not in a location you can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"patchWebhooksBy_id","tags":["Webhooks"],"summary":"Change, pause or resume a webhook","description":"Agency admins and company owners only.","x-token-access":"read_write","x-who":"Agency admins and company owners only.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The record's id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":2000},"events":{"type":"array","items":{"type":"string","enum":["contact.created","contact.updated","consent.recorded","consent.withdrawn","form.submitted","deal.created","deal.moved","task.completed","appointment.booked","appointment.updated","order.paid"]},"minItems":1,"maxItems":11,"description":"Events to send: contact.created, contact.updated, consent.recorded, consent.withdrawn, form.submitted, deal.created, deal.moved, task.completed, appointment.booked, appointment.updated, order.paid"},"description":{"type":["string","null"],"maxLength":200,"description":"A note to tell webhooks apart"},"active":{"type":"boolean","description":"false pauses deliveries; true resumes them and clears a failing streak"}},"additionalProperties":false},"example":{"active":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Webhook"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Read only token, or your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not in a location you can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteWebhooksBy_id","tags":["Webhooks"],"summary":"Delete a webhook","description":"Deliveries still waiting are cancelled. Past deliveries are kept as history. Agency admins and company owners only.","x-token-access":"read_write","x-who":"Agency admins and company owners only.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The record's id"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Read only token, or your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not in a location you can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/webhooks/{id}/deliveries":{"get":{"operationId":"getWebhooksBy_idDeliveries","tags":["Webhooks"],"summary":"List a webhook's deliveries, newest first","description":"Agency admins and company owners only.","x-token-access":"read","x-who":"Agency admins and company owners only.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The record's id"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"How many to return"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","maxLength":200},"description":"next_cursor from the previous page"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Delivery"}},"next_cursor":{"type":["string","null"],"description":"Pass as cursor for the next page; null on the last page"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not in a location you can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/webhooks/{id}/test":{"post":{"operationId":"postWebhooksBy_idTest","tags":["Webhooks"],"summary":"Send a test event","description":"Queues a webhook.test delivery, sent and retried like any other. Agency admins and company owners only.","x-token-access":"read_write","x-who":"Agency admins and company owners only.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The record's id"}],"responses":{"202":{"description":"Done","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Delivery"}}}}}},"400":{"description":"The request didn't pass validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Read only token, or your role can't do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not in a location you can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests. Wait for Retry-After seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"webhooks":{"contact.created":{"post":{"summary":"A contact is added","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["id","type","created_at","company_id","data"],"properties":{"id":{"type":"string","format":"uuid","description":"The event's id. The same for every webhook that gets it."},"type":{"type":"string","const":"contact.created"},"created_at":{"type":"string","format":"date-time"},"company_id":{"type":"string","format":"uuid"},"data":{"$ref":"#/components/schemas/Contact"}}}}}},"parameters":[{"name":"ccrm-signature","in":"header","required":true,"schema":{"type":"string"},"description":"t=<unix time>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\">"},{"name":"ccrm-event","in":"header","required":true,"schema":{"type":"string"}},{"name":"ccrm-event-id","in":"header","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"ccrm-delivery","in":"header","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Any 2xx within 10 seconds counts as delivered"}}}},"contact.updated":{"post":{"summary":"A contact's details or tags change","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["id","type","created_at","company_id","data"],"properties":{"id":{"type":"string","format":"uuid","description":"The event's id. The same for every webhook that gets it."},"type":{"type":"string","const":"contact.updated"},"created_at":{"type":"string","format":"date-time"},"company_id":{"type":"string","format":"uuid"},"data":{"$ref":"#/components/schemas/Contact"}}}}}},"parameters":[{"name":"ccrm-signature","in":"header","required":true,"schema":{"type":"string"},"description":"t=<unix time>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\">"},{"name":"ccrm-event","in":"header","required":true,"schema":{"type":"string"}},{"name":"ccrm-event-id","in":"header","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"ccrm-delivery","in":"header","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Any 2xx within 10 seconds counts as delivered"}}}},"consent.recorded":{"post":{"summary":"Consent is recorded","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["id","type","created_at","company_id","data"],"properties":{"id":{"type":"string","format":"uuid","description":"The event's id. The same for every webhook that gets it."},"type":{"type":"string","const":"consent.recorded"},"created_at":{"type":"string","format":"date-time"},"company_id":{"type":"string","format":"uuid"},"data":{"$ref":"#/components/schemas/ConsentRecord"}}}}}},"parameters":[{"name":"ccrm-signature","in":"header","required":true,"schema":{"type":"string"},"description":"t=<unix time>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\">"},{"name":"ccrm-event","in":"header","required":true,"schema":{"type":"string"}},{"name":"ccrm-event-id","in":"header","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"ccrm-delivery","in":"header","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Any 2xx within 10 seconds counts as delivered"}}}},"consent.withdrawn":{"post":{"summary":"Someone unsubscribes","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["id","type","created_at","company_id","data"],"properties":{"id":{"type":"string","format":"uuid","description":"The event's id. The same for every webhook that gets it."},"type":{"type":"string","const":"consent.withdrawn"},"created_at":{"type":"string","format":"date-time"},"company_id":{"type":"string","format":"uuid"},"data":{"$ref":"#/components/schemas/ConsentRecord"}}}}}},"parameters":[{"name":"ccrm-signature","in":"header","required":true,"schema":{"type":"string"},"description":"t=<unix time>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\">"},{"name":"ccrm-event","in":"header","required":true,"schema":{"type":"string"}},{"name":"ccrm-event-id","in":"header","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"ccrm-delivery","in":"header","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Any 2xx within 10 seconds counts as delivered"}}}},"form.submitted":{"post":{"summary":"A form is submitted","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["id","type","created_at","company_id","data"],"properties":{"id":{"type":"string","format":"uuid","description":"The event's id. The same for every webhook that gets it."},"type":{"type":"string","const":"form.submitted"},"created_at":{"type":"string","format":"date-time"},"company_id":{"type":"string","format":"uuid"},"data":{"$ref":"#/components/schemas/Submission"}}}}}},"parameters":[{"name":"ccrm-signature","in":"header","required":true,"schema":{"type":"string"},"description":"t=<unix time>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\">"},{"name":"ccrm-event","in":"header","required":true,"schema":{"type":"string"}},{"name":"ccrm-event-id","in":"header","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"ccrm-delivery","in":"header","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Any 2xx within 10 seconds counts as delivered"}}}},"deal.created":{"post":{"summary":"A deal is added","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["id","type","created_at","company_id","data"],"properties":{"id":{"type":"string","format":"uuid","description":"The event's id. The same for every webhook that gets it."},"type":{"type":"string","const":"deal.created"},"created_at":{"type":"string","format":"date-time"},"company_id":{"type":"string","format":"uuid"},"data":{"$ref":"#/components/schemas/Deal"}}}}}},"parameters":[{"name":"ccrm-signature","in":"header","required":true,"schema":{"type":"string"},"description":"t=<unix time>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\">"},{"name":"ccrm-event","in":"header","required":true,"schema":{"type":"string"}},{"name":"ccrm-event-id","in":"header","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"ccrm-delivery","in":"header","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Any 2xx within 10 seconds counts as delivered"}}}},"deal.moved":{"post":{"summary":"A deal moves stage","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["id","type","created_at","company_id","data"],"properties":{"id":{"type":"string","format":"uuid","description":"The event's id. The same for every webhook that gets it."},"type":{"type":"string","const":"deal.moved"},"created_at":{"type":"string","format":"date-time"},"company_id":{"type":"string","format":"uuid"},"data":{"$ref":"#/components/schemas/Deal"}}}}}},"parameters":[{"name":"ccrm-signature","in":"header","required":true,"schema":{"type":"string"},"description":"t=<unix time>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\">"},{"name":"ccrm-event","in":"header","required":true,"schema":{"type":"string"}},{"name":"ccrm-event-id","in":"header","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"ccrm-delivery","in":"header","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Any 2xx within 10 seconds counts as delivered"}}}},"task.completed":{"post":{"summary":"A task is done","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["id","type","created_at","company_id","data"],"properties":{"id":{"type":"string","format":"uuid","description":"The event's id. The same for every webhook that gets it."},"type":{"type":"string","const":"task.completed"},"created_at":{"type":"string","format":"date-time"},"company_id":{"type":"string","format":"uuid"},"data":{"$ref":"#/components/schemas/Task"}}}}}},"parameters":[{"name":"ccrm-signature","in":"header","required":true,"schema":{"type":"string"},"description":"t=<unix time>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\">"},{"name":"ccrm-event","in":"header","required":true,"schema":{"type":"string"}},{"name":"ccrm-event-id","in":"header","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"ccrm-delivery","in":"header","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Any 2xx within 10 seconds counts as delivered"}}}},"appointment.booked":{"post":{"summary":"An appointment is booked","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["id","type","created_at","company_id","data"],"properties":{"id":{"type":"string","format":"uuid","description":"The event's id. The same for every webhook that gets it."},"type":{"type":"string","const":"appointment.booked"},"created_at":{"type":"string","format":"date-time"},"company_id":{"type":"string","format":"uuid"},"data":{"$ref":"#/components/schemas/Appointment"}}}}}},"parameters":[{"name":"ccrm-signature","in":"header","required":true,"schema":{"type":"string"},"description":"t=<unix time>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\">"},{"name":"ccrm-event","in":"header","required":true,"schema":{"type":"string"}},{"name":"ccrm-event-id","in":"header","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"ccrm-delivery","in":"header","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Any 2xx within 10 seconds counts as delivered"}}}},"appointment.updated":{"post":{"summary":"An appointment is moved, confirmed, cancelled or marked done","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["id","type","created_at","company_id","data"],"properties":{"id":{"type":"string","format":"uuid","description":"The event's id. The same for every webhook that gets it."},"type":{"type":"string","const":"appointment.updated"},"created_at":{"type":"string","format":"date-time"},"company_id":{"type":"string","format":"uuid"},"data":{"$ref":"#/components/schemas/Appointment"}}}}}},"parameters":[{"name":"ccrm-signature","in":"header","required":true,"schema":{"type":"string"},"description":"t=<unix time>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\">"},{"name":"ccrm-event","in":"header","required":true,"schema":{"type":"string"}},{"name":"ccrm-event-id","in":"header","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"ccrm-delivery","in":"header","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Any 2xx within 10 seconds counts as delivered"}}}},"order.paid":{"post":{"summary":"An order is paid at a checkout","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["id","type","created_at","company_id","data"],"properties":{"id":{"type":"string","format":"uuid","description":"The event's id. The same for every webhook that gets it."},"type":{"type":"string","const":"order.paid"},"created_at":{"type":"string","format":"date-time"},"company_id":{"type":"string","format":"uuid"},"data":{"type":"object"}}}}}},"parameters":[{"name":"ccrm-signature","in":"header","required":true,"schema":{"type":"string"},"description":"t=<unix time>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\">"},{"name":"ccrm-event","in":"header","required":true,"schema":{"type":"string"}},{"name":"ccrm-event-id","in":"header","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"ccrm-delivery","in":"header","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Any 2xx within 10 seconds counts as delivered"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"A personal access token from your CRM profile, starting ccrm_"}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"description":"For validation errors: each problem with its field path."}}}}},"Location":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"company_id":{"type":"string","format":"uuid"},"company_name":{"type":"string"},"business_name":{"type":["string","null"]},"province":{"type":["string","null"],"description":"Two-letter province or territory code"},"timezone":{"type":"string"},"regulated_retail":{"type":"boolean","description":"Cannabis or liquor: marketing only to age-verified contacts"},"sender_missing":{"type":"array","items":{"type":"string"},"description":"Sender details still needed before marketing can go out"},"created_at":{"type":"string","format":"date-time"}},"required":["id","name","company_id","company_name","business_name","province","timezone","regulated_retail","sender_missing","created_at"]},"Contact":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"company_id":{"type":"string","format":"uuid"},"location_id":{"type":"string","format":"uuid"},"full_name":{"type":"string"},"email":{"type":["string","null"]},"phone":{"type":["string","null"],"description":"E.164, like +12505550123"},"age_verified":{"type":"boolean","description":"Read only. Set in the CRM."},"tags":{"type":"array","items":{"type":"string"}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","company_id","location_id","full_name","email","phone","age_verified","tags","created_at","updated_at"]},"ContactDetail":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"company_id":{"type":"string","format":"uuid"},"location_id":{"type":"string","format":"uuid"},"full_name":{"type":"string"},"email":{"type":["string","null"]},"phone":{"type":["string","null"],"description":"E.164, like +12505550123"},"age_verified":{"type":"boolean","description":"Read only. Set in the CRM."},"tags":{"type":"array","items":{"type":"string"}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"consent":{"type":"object","properties":{"email":{"type":"object","properties":{"allowed":{"type":"boolean","description":"Whether a marketing message on this channel would pass the consent check right now"},"basis":{"type":["string","null"],"description":"express, implied_purchase, implied_inquiry, implied_donation, implied_volunteer or implied_membership when allowed"},"expires_at":{"type":["string","null"],"format":"date-time","description":"When implied consent ends"},"reason":{"type":["string","null"],"description":"Why it's held, when it is"},"detail":{"type":["string","null"]}},"required":["allowed","basis","expires_at","reason","detail"]},"sms":{"type":"object","properties":{"allowed":{"type":"boolean","description":"Whether a marketing message on this channel would pass the consent check right now"},"basis":{"type":["string","null"],"description":"express, implied_purchase, implied_inquiry, implied_donation, implied_volunteer or implied_membership when allowed"},"expires_at":{"type":["string","null"],"format":"date-time","description":"When implied consent ends"},"reason":{"type":["string","null"],"description":"Why it's held, when it is"},"detail":{"type":["string","null"]}},"required":["allowed","basis","expires_at","reason","detail"]},"dm":{"type":"object","properties":{"allowed":{"type":"boolean","description":"Whether a marketing message on this channel would pass the consent check right now"},"basis":{"type":["string","null"],"description":"express, implied_purchase, implied_inquiry, implied_donation, implied_volunteer or implied_membership when allowed"},"expires_at":{"type":["string","null"],"format":"date-time","description":"When implied consent ends"},"reason":{"type":["string","null"],"description":"Why it's held, when it is"},"detail":{"type":["string","null"]}},"required":["allowed","basis","expires_at","reason","detail"]}},"required":["email","sms","dm"],"description":"Read only. What the consent check decides right now, per channel."}},"required":["id","company_id","location_id","full_name","email","phone","age_verified","tags","created_at","updated_at","consent"]},"ConsentRecord":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"company_id":{"type":"string","format":"uuid"},"location_id":{"type":"string","format":"uuid"},"contact_id":{"type":"string","format":"uuid"},"channel":{"type":"string","enum":["email","sms","dm"]},"type":{"type":"string","enum":["express","implied_purchase","implied_inquiry","implied_donation","implied_volunteer","implied_membership","withdrawn"]},"recorded_at":{"type":"string","format":"date-time","description":"Always the moment the CRM received it"},"source":{"type":"string"},"wording":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"}},"required":["id","company_id","location_id","contact_id","channel","type","recorded_at","source","wording","created_at"]},"Tag":{"type":"object","properties":{"tag":{"type":"string"},"contacts":{"type":"integer","description":"How many contacts you can see have it"}},"required":["tag","contacts"]},"Form":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"company_id":{"type":"string","format":"uuid"},"location_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"status":{"type":"string","enum":["active","paused"]},"consent_enabled":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","company_id","location_id","name","status","consent_enabled","created_at","updated_at"]},"Submission":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"company_id":{"type":"string","format":"uuid"},"location_id":{"type":"string","format":"uuid"},"form_id":{"type":"string","format":"uuid"},"contact_id":{"type":["string","null"],"format":"uuid"},"answers":{"type":"object","additionalProperties":{"type":"string"},"description":"Field id to answer"},"consent_given":{"type":"boolean"},"page_url":{"type":["string","null"]},"referrer":{"type":["string","null"]},"utm":{"type":"object","additionalProperties":{"type":"string"}},"created_at":{"type":"string","format":"date-time"}},"required":["id","company_id","location_id","form_id","contact_id","answers","consent_given","page_url","referrer","utm","created_at"]},"Pipeline":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"location_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"stages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"position":{"type":"integer"},"outcome":{"type":"string","enum":["open","won","lost"]}},"required":["id","name","position","outcome"]}},"created_at":{"type":"string","format":"date-time"}},"required":["id","location_id","name","stages","created_at"]},"Deal":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"company_id":{"type":"string","format":"uuid"},"location_id":{"type":"string","format":"uuid"},"pipeline_id":{"type":"string","format":"uuid"},"stage_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"value_cents":{"type":"integer"},"currency":{"type":"string","const":"CAD"},"contact_id":{"type":["string","null"],"format":"uuid"},"owner_membership_id":{"type":["string","null"],"format":"uuid"},"stage_entered_at":{"type":"string","format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","company_id","location_id","pipeline_id","stage_id","name","value_cents","currency","contact_id","owner_membership_id","stage_entered_at","created_at","updated_at"]},"Task":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"company_id":{"type":"string","format":"uuid"},"location_id":{"type":"string","format":"uuid"},"title":{"type":"string"},"notes":{"type":["string","null"]},"due_on":{"type":["string","null"],"description":"A date, like 2026-10-15"},"status":{"type":"string","enum":["open","done"]},"kind":{"type":"string","enum":["todo","social_post"]},"assignee_membership_id":{"type":["string","null"],"format":"uuid"},"contact_id":{"type":["string","null"],"format":"uuid"},"done_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","company_id","location_id","title","notes","due_on","status","kind","assignee_membership_id","contact_id","done_at","created_at","updated_at"]},"Appointment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"company_id":{"type":"string","format":"uuid"},"location_id":{"type":"string","format":"uuid"},"type_id":{"type":"string","format":"uuid"},"type_name":{"type":"string"},"contact_id":{"type":"string","format":"uuid"},"staff_membership_id":{"type":["string","null"],"format":"uuid"},"starts_at":{"type":"string","format":"date-time"},"ends_at":{"type":"string","format":"date-time"},"time_zone":{"type":"string","description":"The location's time zone, like America/Vancouver"},"buffer_minutes":{"type":"integer"},"price_cents":{"type":["integer","null"]},"currency":{"type":"string","const":"CAD"},"deposit_required":{"type":"boolean"},"status":{"type":"string","enum":["booked","confirmed","done","no_show","cancelled"]},"notes":{"type":["string","null"]},"source":{"type":"string","enum":["staff","booking_page"]},"created_at":{"type":"string","format":"date-time"}},"required":["id","company_id","location_id","type_id","type_name","contact_id","staff_membership_id","starts_at","ends_at","time_zone","buffer_minutes","price_cents","currency","deposit_required","status","notes","source","created_at"]},"Event":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"company_id":{"type":"string","format":"uuid"},"location_id":{"type":"string","format":"uuid"},"title":{"type":"string"},"description":{"type":["string","null"]},"venue":{"type":["string","null"]},"starts_at":{"type":"string","format":"date-time"},"ends_at":{"type":["string","null"],"format":"date-time"},"capacity":{"type":["integer","null"]},"status":{"type":"string","enum":["draft","open","closed","cancelled"]},"going":{"type":"integer"},"waitlist":{"type":"integer"},"checked_in":{"type":"integer"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","company_id","location_id","title","description","venue","starts_at","ends_at","capacity","status","going","waitlist","checked_in","created_at","updated_at"]},"Registration":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"event_id":{"type":"string","format":"uuid"},"contact_id":{"type":"string","format":"uuid"},"full_name":{"type":"string"},"status":{"type":"string","enum":["going","waitlist","cancelled"]},"source":{"type":"string","enum":["public_form","staff"]},"checked_in_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","event_id","contact_id","full_name","status","source","checked_in_at","created_at","updated_at"]},"Webhook":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"company_id":{"type":["string","null"],"format":"uuid","description":"null: every company in the agency"},"description":{"type":["string","null"]},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"active":{"type":"boolean"},"healthy":{"type":"boolean"},"failure_count":{"type":"integer","description":"Failed attempts in a row"},"unhealthy_since":{"type":["string","null"],"format":"date-time"},"last_success_at":{"type":["string","null"],"format":"date-time"},"last_failure_at":{"type":["string","null"],"format":"date-time"},"secret_prefix":{"type":"string","description":"The first characters of the signing secret"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","company_id","description","url","events","active","healthy","failure_count","unhealthy_since","last_success_at","last_failure_at","secret_prefix","created_at","updated_at"]},"NewWebhook":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"company_id":{"type":["string","null"],"format":"uuid","description":"null: every company in the agency"},"description":{"type":["string","null"]},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"active":{"type":"boolean"},"healthy":{"type":"boolean"},"failure_count":{"type":"integer","description":"Failed attempts in a row"},"unhealthy_since":{"type":["string","null"],"format":"date-time"},"last_success_at":{"type":["string","null"],"format":"date-time"},"last_failure_at":{"type":["string","null"],"format":"date-time"},"secret_prefix":{"type":"string","description":"The first characters of the signing secret"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"secret":{"type":"string","description":"The signing secret. Shown only this once."}},"required":["id","company_id","description","url","events","active","healthy","failure_count","unhealthy_since","last_success_at","last_failure_at","secret_prefix","created_at","updated_at","secret"]},"Delivery":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"endpoint_id":{"type":"string","format":"uuid"},"event_id":{"type":"string","format":"uuid"},"event":{"type":"string"},"status":{"type":"string","enum":["pending","delivered","failed","cancelled"]},"attempts":{"type":"integer"},"next_attempt_at":{"type":["string","null"],"format":"date-time"},"last_attempt_at":{"type":["string","null"],"format":"date-time"},"last_status_code":{"type":["integer","null"]},"last_error":{"type":["string","null"]},"delivered_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"}},"required":["id","endpoint_id","event_id","event","status","attempts","next_attempt_at","last_attempt_at","last_status_code","last_error","delivered_at","created_at"]}}}}