{"openapi":"3.0.0","info":{"title":"CazVid CRM Public API","version":"1.0.0","description":"Read and write your CazVid CRM contacts, upload resumes, and log interactions. Requires a Platinum, Diamond, or Enterprise plan. See https://cazvid.com/api/crm/v1 for guides and examples."},"servers":[{"url":"https://aio-backend-prod.cazvid.app"}],"paths":{"/api/v3.0/crm/public/contacts":{"get":{"operationId":"crmPublicListContacts","summary":"List or search your CRM contacts.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan. Omit search to list contacts newest-first; pass search for a full-text match. Paginated on hasMore (no total is returned). List items carry a summary shape - use GET /public/contacts/{id} for a contact's emails and phones. Limited to 30 requests per minute per organization, a per-IP backstop of 60 per minute, plus a daily quota of 500 per UTC day by default.","parameters":[{"name":"workspaceId","required":false,"in":"query","description":"Workspace to read from. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}},{"name":"search","required":false,"in":"query","description":"Free-text search across name, company, job title, and email/phone (CazVid Atlas Search). Omit to list all contacts newest-first.","schema":{"maxLength":200,"type":"string"}},{"name":"page","required":false,"in":"query","description":"1-based page number (max 10000).","schema":{"minimum":1,"maximum":10000,"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Results per page (max 50).","schema":{"minimum":1,"maximum":50,"default":10,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicContactListResponseDto"}}}},"400":{"description":"A query parameter failed validation (page, pageSize, search) or a malformed workspaceId was supplied (not a 24-character hex id). details lists each offending field, and is an empty array for the malformed-query-parameter case surfaced by the auth chain.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"pageSize above the maximum","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"pageSize must be at most 50","path":"/api/v3.0/crm/public/contacts","details":[{"field":"pageSize","message":"pageSize must be at most 50"}]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/contacts"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/contacts"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/contacts"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/contacts"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:read scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/contacts"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:read scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/contacts"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/contacts"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/contacts"}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/contacts","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/contacts","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/contacts","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/contacts"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]},"post":{"operationId":"crmPublicCreateContact","summary":"Create a CRM contact.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan and the crm:write scope. The owning workspace and owner are taken from the key - never send them. IDEMPOTENT BY DEDUP: if the email or phone (or a linked CazVid user) already matches a contact in this workspace, the request resolves to that existing contact (merging new emails/phones/tags) and returns it with deduplicated:true instead of inserting a duplicate. An email/phone that points at two different existing contacts returns 409 CONTACT_CONFLICT. Consumes one call from the same daily quota as the reads.","parameters":[{"name":"workspaceId","required":false,"in":"query","description":"Workspace to read from. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePublicContactDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicContactWriteResponseDto"}}}},"400":{"description":"A request parameter failed validation: a malformed contact id in the path, a body field (name is required on create; a malformed email; an over-long array), or a malformed workspaceId query parameter. details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Missing required name on create","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"name is required","path":"/api/v3.0/crm/public/contacts","details":[{"field":"name","message":"name is required"}]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/contacts"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/contacts"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/contacts"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/contacts"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:write scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/contacts"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:write scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/contacts"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/contacts"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/contacts"}}}}}},"409":{"description":"The contact's email or phone matches more than one existing contact in this workspace (or would overwrite a strong CazVid-user/global-contact link), so it cannot be de-duplicated automatically. details carries a single entry, `{ field: \"contact\", message: <reason> }`, where reason is the resolver's verdict: weak_key_split, strong_key_split, or strong_link_overwrite.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"CONTACT_CONFLICT":{"summary":"Email and phone point at different contacts","value":{"statusCode":409,"errorCode":"CONTACT_CONFLICT","message":"The contact's email or phone matches more than one existing contact in this workspace, so it cannot be resolved automatically. Reconcile the duplicates and retry.","path":"/api/v3.0/crm/public/contacts","details":[{"field":"contact","message":"weak_key_split"}]}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/contacts","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/contacts","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/contacts","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/contacts"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]}},"/api/v3.0/crm/public/contacts/{id}":{"get":{"operationId":"crmPublicGetContact","summary":"Get one CRM contact by id.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan. Returns the full contact including emails and phones (index 0 is the primary of each). A contact's own privacy settings can still block a linked CazVid user's contact fields. Same rate limits as the list endpoint.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to read from. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicContactDto"}}}},"400":{"description":"A malformed workspaceId query parameter was supplied (not a 24-character hex id). details lists the offending field, or is empty when the auth chain rejects it first.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Malformed workspaceId query parameter","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"workspaceId is invalid","path":"/api/v3.0/crm/public/contacts/{id}","details":[]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/contacts/{id}"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/contacts/{id}"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/contacts/{id}"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/contacts/{id}"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:read scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/contacts/{id}"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:read scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/contacts/{id}"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/contacts/{id}"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/contacts/{id}"}}}}}},"404":{"description":"No contact with that id exists in the resolved workspace (or it was deleted).","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"NOT_FOUND":{"summary":"Contact not found","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No contacts found for the given workspace","path":"/api/v3.0/crm/public/contacts/{id}"}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/contacts/{id}","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/contacts/{id}","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/contacts/{id}","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/contacts/{id}"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]},"patch":{"operationId":"crmPublicUpdateContact","summary":"Update a CRM contact by id.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan and the crm:write scope. A partial update - only the fields you send change. emails/phones write the primary (index 0) slot. A contact in another workspace is reported as 404 NOT_FOUND, indistinguishable from a missing id. Consumes one call from the same daily quota as the reads.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to read from. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePublicContactDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicContactDto"}}}},"400":{"description":"A request parameter failed validation: a malformed contact id in the path, a body field (name is required on create; a malformed email; an over-long array), or a malformed workspaceId query parameter. details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Missing required name on create","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"name is required","path":"/api/v3.0/crm/public/contacts/{id}","details":[{"field":"name","message":"name is required"}]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/contacts/{id}"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/contacts/{id}"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/contacts/{id}"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/contacts/{id}"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:write scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/contacts/{id}"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:write scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/contacts/{id}"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/contacts/{id}"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/contacts/{id}"}}}}}},"404":{"description":"No contact with that id exists in the resolved workspace (or it was deleted).","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"NOT_FOUND":{"summary":"Contact not found","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No contacts found for the given workspace","path":"/api/v3.0/crm/public/contacts/{id}"}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/contacts/{id}","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/contacts/{id}","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/contacts/{id}","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/contacts/{id}"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]},"delete":{"operationId":"crmPublicDeleteContact","summary":"Delete a CRM contact by id.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan and the crm:write scope. SOFT delete: the contact is marked deleted (retained), and its interactions and list memberships are soft-deleted too. A contact in another workspace is reported as 404 NOT_FOUND. Consumes one call from the same daily quota as the reads.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to read from. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicContactDeletedResponseDto"}}}},"400":{"description":"A request parameter failed validation: a malformed contact id in the path, a body field (name is required on create; a malformed email; an over-long array), or a malformed workspaceId query parameter. details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Missing required name on create","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"name is required","path":"/api/v3.0/crm/public/contacts/{id}","details":[{"field":"name","message":"name is required"}]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/contacts/{id}"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/contacts/{id}"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/contacts/{id}"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/contacts/{id}"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:write scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/contacts/{id}"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:write scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/contacts/{id}"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/contacts/{id}"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/contacts/{id}"}}}}}},"404":{"description":"No contact with that id exists in the resolved workspace (or it was deleted).","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"NOT_FOUND":{"summary":"Contact not found","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No contacts found for the given workspace","path":"/api/v3.0/crm/public/contacts/{id}"}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/contacts/{id}","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/contacts/{id}","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/contacts/{id}","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/contacts/{id}"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]}},"/api/v3.0/crm/public/contacts/{id}/resume":{"post":{"operationId":"crmPublicUploadContactResume","summary":"Upload a resume (base64) for a CRM contact and queue a parse.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan and the crm:write scope. Send the file inline as { fileName, mimeType, contentBase64 } - a single call, no separate S3 step. Allowed types are PDF, DOC and DOCX (max 10MB decoded); the file's real type is verified from its magic bytes against the declared mimeType (400 MIME_MISMATCH otherwise). PARSING IS ASYNChronous: the response is 202 with parseStatus 'queued'; the parsed fields (name, email, phone, location, job title, skills, LinkedIn) are written onto the contact later, filling only its EMPTY fields. DEDUPE CAVEAT - that enrichment is best-effort on FIRST upload only: re-uploading bytes this key's owner already uploaded reuses the existing resume (the SAME resumeId is returned) and attaches it to this contact, but queues NO new parse job, and enrichment runs only off that job - so the parsed fields are NOT written onto this contact even though the attachment looks healthy (resumes[] reports parseStatus 'done' and resumeCount counts it). No response field marks a reused resume, so read the contact back to confirm rather than inferring enrichment from the 202. Note: legacy binary .doc files are stored and attached but NOT auto-parsed (use PDF or DOCX for parsing). Poll GET /public/contacts/{id} - its hasResume/resumeCount reflect the attached resume once processing completes, and its resumes[] array carries the per-resume parseStatus and lastError, so a file that will NEVER parse is diagnosable rather than merely counted. MERGE CAVEAT: if the parsed email/phone matches a DIFFERENT existing contact in the workspace, parsing MERGES this contact into that one and soft-deletes this id - after which GET /public/contacts/{id} for the uploaded id returns 404 (the data lives on the surviving contact). A contact in another workspace is reported as 404 NOT_FOUND, indistinguishable from a missing id. Consumes one general daily call AND one of a separate per-key daily upload quota (default 50; see GET /public/usage -> resumeUpload).","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to read from. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadContactResumeDto"}}}},"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactResumeUploadResponseDto"}}}},"400":{"description":"The upload body failed validation: a missing/invalid field (fileName, mimeType, contentBase64), a mimeType outside PDF/DOC/DOCX, a payload that is not valid standard base64 (INVALID_BASE64), an empty decoded file (EMPTY_FILE), or a file whose real magic bytes do not match the declared mimeType (MIME_MISMATCH). details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Missing required contentBase64","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"contentBase64 is required","path":"/api/v3.0/crm/public/contacts/{id}","details":[{"field":"contentBase64","message":"contentBase64 is required"}]}},"MIME_MISMATCH":{"summary":"Declared mimeType does not match the file's magic bytes","value":{"statusCode":400,"errorCode":"MIME_MISMATCH","message":"The file contents do not match the declared mimeType, or the file is not an allowed resume type (PDF, DOC, DOCX).","path":"/api/v3.0/crm/public/contacts/{id}","details":[{"field":"contentBase64","message":"file signature does not match the declared mimeType"}]}},"INVALID_BASE64":{"summary":"Payload is not valid standard base64","value":{"statusCode":400,"errorCode":"INVALID_BASE64","message":"contentBase64 is not valid standard base64.","path":"/api/v3.0/crm/public/contacts/{id}","details":[{"field":"contentBase64","message":"contentBase64 is not valid standard base64"}]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/contacts/{id}"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/contacts/{id}"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/contacts/{id}"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/contacts/{id}"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:write scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/contacts/{id}"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:write scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/contacts/{id}"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/contacts/{id}"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/contacts/{id}"}}}}}},"404":{"description":"No contact with that id exists in the resolved workspace (or it was deleted).","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"NOT_FOUND":{"summary":"Contact not found","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No contacts found for the given workspace","path":"/api/v3.0/crm/public/contacts/{id}"}}}}}},"413":{"description":"The resume payload is too large. The base64 body is rejected before decoding when it is longer than the encoded form of a 10MB file, and the decoded file is rejected when it exceeds 10MB.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PAYLOAD_TOO_LARGE":{"summary":"Decoded resume exceeds 10MB","value":{"statusCode":413,"errorCode":"PAYLOAD_TOO_LARGE","message":"Resume file exceeds the 10485760-byte limit.","path":"/api/v3.0/crm/public/contacts/{id}"}}}}}},"429":{"description":"A rate limit was exceeded. Four independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), a general daily quota (default 500 per UTC day per organization), and a SEPARATE per-key daily upload quota (default 50 per UTC day, details.scope:'resume_upload'). An upload consumes a general daily slot AND an upload slot. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip","resume_upload"],"description":"Present on the per-IP backstop ('ip') and the per-key upload-quota ('resume_upload') blocks."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"resume_upload_quota":{"summary":"Per-key daily upload quota of 50 exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM resume-upload quota exceeded.","path":"/api/v3.0/crm/public/contacts/{id}","retryAfter":3600,"details":{"limit":50,"period":"utc_day","scope":"resume_upload"}}},"daily_quota":{"summary":"General daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/contacts/{id}","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/contacts/{id}","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/contacts/{id}","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/contacts/{id}"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]}},"/api/v3.0/crm/public/usage":{"get":{"operationId":"crmPublicUsage","summary":"Current daily quota usage for the calling organization.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Does not consume daily quota. Throttled to 60 requests per minute per API key, plus a per-IP backstop of 60 per minute (shared with the reads).","parameters":[{"name":"organizationId","required":false,"in":"query","description":"Organization whose usage to report. Optional; defaults to the key's organization. Must be an organization the key can access.","schema":{}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to scope the usage read to. Optional when the API key grants exactly one workspace; must belong to the key's organization or the request fails 400/403.","schema":{}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CrmUsageDto"}}}},"400":{"description":"A malformed workspaceId query parameter was supplied (not a 24-character hex id). details lists the offending field, or is empty when the auth chain rejects it first.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Malformed workspaceId query parameter","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"workspaceId is invalid","path":"/api/v3.0/crm/public/usage","details":[]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/usage"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/usage"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/usage"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/usage"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:read scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/usage"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:read scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/usage"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/usage"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/usage"}}}}}},"429":{"description":"A rate limit was exceeded. The usage endpoint is throttled to 60 requests per minute per API key, plus a per-IP backstop of 60 per minute per client IP (carrying details.scope:'ip'). retryAfter is the whole number of seconds until the next minute.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"per_key":{"summary":"More than 60 usage checks in a minute for one key","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Too many usage checks. This endpoint allows 60 requests per minute per API key.","path":"/api/v3.0/crm/public/usage","retryAfter":30,"details":{"limit":60,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/usage","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/usage"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]}},"/api/v3.0/crm/public/contacts/{id}/interactions":{"post":{"operationId":"crmPublicLogInteraction","summary":"Log an interaction against a contact.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan and the crm:write scope. Appends a timeline entry to the contact - each call creates a new interaction (never de-duped). type is an interaction-type NAME resolved case-insensitively against the fixed global taxonomy (GET /public/interaction-types lists them); an unknown name is 400 UNKNOWN_INTERACTION_TYPE. A contact in another workspace is 404 NOT_FOUND, indistinguishable from a missing id. Consumes one call from the same daily quota as the reads.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to operate on. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePublicInteractionDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicInteractionDto"}}}},"400":{"description":"A request parameter failed validation: a malformed contact id in the path, a body field (type is required; notes over 5000 chars; a non-ISO date), or a malformed workspaceId query parameter. UNKNOWN_INTERACTION_TYPE is returned when `type` is not one of the valid interaction type names - details carries a `{ field: \"type\", message: \"must be one of: ...\" }` entry listing the valid set (also served by GET /public/interaction-types). details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Missing required type on create","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"type is required","path":"/api/v3.0/crm/public/contacts/{id}/interactions","details":[{"field":"type","message":"type is required"}]}},"UNKNOWN_INTERACTION_TYPE":{"summary":"type is not in the global taxonomy","value":{"statusCode":400,"errorCode":"UNKNOWN_INTERACTION_TYPE","message":"Unknown interaction type \"carrierpigeon\".","path":"/api/v3.0/crm/public/contacts/{id}/interactions","details":[{"field":"type","message":"must be one of: phonecall, email, videocall, ..."}]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/contacts/{id}/interactions"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/contacts/{id}/interactions"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/contacts/{id}/interactions"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/contacts/{id}/interactions"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:write scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/contacts/{id}/interactions"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:write scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/contacts/{id}/interactions"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/contacts/{id}/interactions"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/contacts/{id}/interactions"}}}}}},"404":{"description":"No contact with that id exists in the resolved workspace (or it was deleted).","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"NOT_FOUND":{"summary":"Contact not found","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No contacts found for the given workspace","path":"/api/v3.0/crm/public/contacts/{id}/interactions"}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/contacts/{id}/interactions","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/contacts/{id}/interactions","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/contacts/{id}/interactions","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/contacts/{id}/interactions"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]},"get":{"operationId":"crmPublicListInteractions","summary":"List a contact's interaction timeline (newest first).","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan. Returns the contact's interactions newest-first, each with its resolved type name and tag names. Returns FACTS by default - the immutable evidence log, which is what this endpoint has always returned; pass ?kind=task for the contact's work items or ?kind=all to interleave both. Paginated on hasMore (no total is returned). A contact in another workspace is 404 NOT_FOUND. Same rate limits as the other reads.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to operate on. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}},{"name":"kind","required":false,"in":"query","description":"Which kind of timeline row to return. Defaults to `fact`: the immutable evidence log, which is what this endpoint has always returned. Pass `task` for work items only, or `all` to interleave both. Task rows carry their work fields on the /public/tasks routes, not here.","schema":{"default":"fact","enum":["fact","task","all"],"type":"string"}},{"name":"page","required":false,"in":"query","description":"1-based page number (max 10000).","schema":{"minimum":1,"maximum":10000,"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Results per page (max 50).","schema":{"minimum":1,"maximum":50,"default":20,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicInteractionListResponseDto"}}}},"400":{"description":"A query parameter failed validation (page, pageSize) or a malformed workspaceId was supplied (not a 24-character hex id). details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"pageSize above the maximum","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"pageSize must be at most 50","path":"/api/v3.0/crm/public/contacts/{id}/interactions","details":[{"field":"pageSize","message":"pageSize must be at most 50"}]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/contacts/{id}/interactions"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/contacts/{id}/interactions"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/contacts/{id}/interactions"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/contacts/{id}/interactions"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:read scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/contacts/{id}/interactions"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:read scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/contacts/{id}/interactions"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/contacts/{id}/interactions"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/contacts/{id}/interactions"}}}}}},"404":{"description":"No contact with that id exists in the resolved workspace (or it was deleted).","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"NOT_FOUND":{"summary":"Contact not found","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No contacts found for the given workspace","path":"/api/v3.0/crm/public/contacts/{id}/interactions"}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/contacts/{id}/interactions","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/contacts/{id}/interactions","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/contacts/{id}/interactions","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/contacts/{id}/interactions"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]}},"/api/v3.0/crm/public/interaction-types":{"get":{"operationId":"crmPublicListInteractionTypes","summary":"List the valid interaction type names.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan. Returns the fixed global interaction taxonomy (the same values the log endpoint accepts as `type`), grouped by category plus a flat union. The taxonomy is global, not per-workspace. Same rate limits as the other reads.","parameters":[{"name":"workspaceId","required":false,"in":"query","description":"Workspace to operate on. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicInteractionTypesResponseDto"}}}},"400":{"description":"A malformed workspaceId query parameter was supplied (not a 24-character hex id). details lists the offending field, or is empty when the auth chain rejects it first.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Malformed workspaceId query parameter","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"workspaceId is invalid","path":"/api/v3.0/crm/public/interaction-types","details":[]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/interaction-types"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/interaction-types"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/interaction-types"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/interaction-types"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:read scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/interaction-types"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:read scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/interaction-types"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/interaction-types"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/interaction-types"}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/interaction-types","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/interaction-types","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/interaction-types","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/interaction-types"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]}},"/api/v3.0/crm/public/lists":{"get":{"operationId":"crmPublicListLists","summary":"List your CRM pipelines (lists), newest first.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan. Returns every list in the workspace, newest-first, each with its active card count. Paginated on hasMore (no total is returned). Use GET /public/lists/{id} for a list's stages and cards. Consumes one call from the same daily quota as every other CRM route (default 500 per UTC day per organization, 30 per minute burst, plus a per-IP backstop of 60 per minute).","parameters":[{"name":"workspaceId","required":false,"in":"query","description":"Workspace to operate on. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","description":"1-based page number (max 10000).","schema":{"minimum":1,"maximum":10000,"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Results per page (max 50).","schema":{"minimum":1,"maximum":50,"default":10,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicListListResponseDto"}}}},"400":{"description":"A query parameter failed validation (page, pageSize) or a malformed workspaceId was supplied (not a 24-character hex id). details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Malformed id in the path","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"id must be a 24-character hex id","path":"/api/v3.0/crm/public/lists","details":[{"field":"id","message":"id must be a 24-character hex id"}]}},"VALIDATION_ERROR_PAGE_SIZE":{"summary":"pageSize above the maximum","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"pageSize must be at most 50","path":"/api/v3.0/crm/public/lists","details":[{"field":"pageSize","message":"pageSize must be at most 50"}]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/lists"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/lists"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/lists"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/lists"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:read scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/lists"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:read scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/lists"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/lists"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/lists"}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/lists","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/lists"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]},"post":{"operationId":"crmPublicCreateList","summary":"Create a CRM pipeline (list).","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan. Requires the crm:write scope. Send name (plus an optional color) to get the default candidate pipeline; the list is stamped templateType \"CANDIDATE_PIPELINE_EN\". Pass stages to seed your own stage set in board order, or copyFromListId to copy an existing list's stage skeleton - the two are mutually exclusive (400 VALIDATION_ERROR when both are sent). copyFromListId only resolves lists in THIS key's workspace that were created by THIS key's own user; anything else is 404 NOT_FOUND. Optionally send an Idempotency-Key header: a repeat of the SAME key and the SAME body within 24 hours returns the first result (with an Idempotency-Replayed: true header) instead of creating a second list. While the first create is still running a repeat is 409 IDEMPOTENCY_IN_PROGRESS; the same key with a DIFFERENT body is 422 IDEMPOTENCY_KEY_REUSED. Consumes one call from the same daily quota as every other CRM route (default 500 per UTC day per organization, 30 per minute burst, plus a per-IP backstop of 60 per minute).","parameters":[{"name":"idempotency-key","required":true,"in":"header","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to operate on. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","description":"Optional. An opaque string of your choosing that makes this create safe to retry: a repeat with the SAME key and the SAME body within 24 hours returns the first result (carrying Idempotency-Replayed: true) instead of creating a second list. Remembered per organization AND resolved workspace. A create that FAILS is not remembered, so retrying it really does create the list.","required":false,"schema":{"type":"string","example":"weekly-pipeline-2026-W32"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePublicListDto"}}}},"responses":{"201":{"headers":{"Idempotency-Replayed":{"description":"Present and \"true\" only when this body was replayed from an earlier create with the same Idempotency-Key. Absent means a list was created by THIS request.","schema":{"type":"string","enum":["true"]}}},"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicListDto"}}}},"400":{"description":"A body field failed validation (name is required and must be words of letters and numbers, in any script, separated by single spaces; a stage name over 60 characters; more than 30 stages; a color outside the hex/token rule) or stages and copyFromListId were sent together. details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Malformed id in the path","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"id must be a 24-character hex id","path":"/api/v3.0/crm/public/lists","details":[{"field":"id","message":"id must be a 24-character hex id"}]}},"VALIDATION_ERROR_NAME":{"summary":"Missing required name","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"name is required","path":"/api/v3.0/crm/public/lists","details":[{"field":"name","message":"name is required"}]}},"VALIDATION_ERROR_MUTUALLY_EXCLUSIVE":{"summary":"stages and copyFromListId sent together","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"error.stagesAndCopyFromListIdMutuallyExclusive","path":"/api/v3.0/crm/public/lists","details":[]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/lists"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/lists"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/lists"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/lists"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:write scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/lists"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:write scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/lists"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/lists"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/lists"}}}}}},"404":{"description":"A referenced record does not exist in the resolved workspace (or was deleted). A record belonging to another workspace returns exactly this response, so the id's existence never leaks across tenants.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"NOT_FOUND":{"summary":"copyFromListId cannot be resolved","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No list with that id exists in the resolved workspace, or it has no stages to copy.","path":"/api/v3.0/crm/public/lists","details":[{"field":"copyFromListId","message":"must be an existing list in this workspace with at least one stage, created by this API key's user"}]}}}}}},"409":{"description":"A create with this Idempotency-Key is still in flight. The first request has not committed yet, so this one was neither queued nor duplicated - retry in a moment and you will get the replayed result.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"IDEMPOTENCY_IN_PROGRESS":{"summary":"The first create with this key has not finished","value":{"statusCode":409,"errorCode":"IDEMPOTENCY_IN_PROGRESS","message":"A request with this Idempotency-Key is still being processed. Retry shortly.","path":"/api/v3.0/crm/public/lists"}}}}}},"422":{"description":"This Idempotency-Key was already used with a DIFFERENT request body. The key is bound to the body it was first sent with, so replaying it with new content would hand you back a list you did not ask for. Use a new key for a new list; retrying this one will keep failing.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"Same key, different body","value":{"statusCode":422,"errorCode":"IDEMPOTENCY_KEY_REUSED","message":"Idempotency-Key was already used with a different request body.","path":"/api/v3.0/crm/public/lists"}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/lists","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/lists"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]}},"/api/v3.0/crm/public/lists/{id}":{"get":{"operationId":"crmPublicGetList","summary":"Read one pipeline: its stages, each with a page of cards.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan. Returns the list with its stages in board order (zorder ascending), each carrying its total card count and one page of cards. The same page / pageSize window is applied to EVERY stage, and hasMore is true when any stage has more. A card carries ids only - pair its contactId with GET /public/contacts/{id} for the person's name, email, and phone. A list in another workspace is 404 NOT_FOUND. Consumes one call from the same daily quota as every other CRM route (default 500 per UTC day per organization, 30 per minute burst, plus a per-IP backstop of 60 per minute).","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to operate on. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","description":"1-based page number for the ITEMS inside each stage (max 10000). The same window is applied to every stage of the board.","schema":{"minimum":1,"maximum":10000,"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Items per stage per page (max 50).","schema":{"minimum":1,"maximum":50,"default":10,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicListDetailDto"}}}},"400":{"description":"A request parameter failed validation: a malformed id in the path, a body field, or a malformed workspaceId query parameter. details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Malformed id in the path","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"id must be a 24-character hex id","path":"/api/v3.0/crm/public/lists/{id}","details":[{"field":"id","message":"id must be a 24-character hex id"}]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/lists/{id}"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/lists/{id}"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/lists/{id}"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/lists/{id}"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:read scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/lists/{id}"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:read scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/lists/{id}"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/lists/{id}"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/lists/{id}"}}}}}},"404":{"description":"A referenced record does not exist in the resolved workspace (or was deleted). A record belonging to another workspace returns exactly this response, so the id's existence never leaks across tenants.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"NOT_FOUND":{"summary":"List not found","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No list with that id exists in the resolved workspace.","path":"/api/v3.0/crm/public/lists/{id}"}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/lists/{id}","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists/{id}","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists/{id}","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/lists/{id}"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]},"patch":{"operationId":"crmPublicUpdateList","summary":"Rename or recolor a pipeline.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan. Requires the crm:write scope. Partial update: only the fields you send change, and an empty body is a successful no-op. Stages are edited through the /stages routes, not here. Writes are WORKSPACE-WIDE: a key scoped to this workspace can manage any list in it, no matter which app user created it. A list in another workspace is 404 NOT_FOUND, indistinguishable from a missing id. Consumes one call from the same daily quota as every other CRM route (default 500 per UTC day per organization, 30 per minute burst, plus a per-IP backstop of 60 per minute).","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to operate on. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePublicListDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicListDto"}}}},"400":{"description":"A request parameter failed validation: a malformed id in the path, a body field, or a malformed workspaceId query parameter. details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Malformed id in the path","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"id must be a 24-character hex id","path":"/api/v3.0/crm/public/lists/{id}","details":[{"field":"id","message":"id must be a 24-character hex id"}]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/lists/{id}"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/lists/{id}"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/lists/{id}"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/lists/{id}"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:write scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/lists/{id}"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:write scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/lists/{id}"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/lists/{id}"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/lists/{id}"}}}}}},"404":{"description":"A referenced record does not exist in the resolved workspace (or was deleted). A record belonging to another workspace returns exactly this response, so the id's existence never leaks across tenants.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"NOT_FOUND":{"summary":"List not found","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No list with that id exists in the resolved workspace.","path":"/api/v3.0/crm/public/lists/{id}"}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/lists/{id}","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists/{id}","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists/{id}","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/lists/{id}"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]},"delete":{"operationId":"crmPublicDeleteList","summary":"Soft-delete a pipeline and everything on it.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan. Requires the crm:write scope. Soft-deletes the list, its stages, and its cards, and unlinks job postings pointing at it. The response's effects block reports what the cascade soft-deleted (stagesDeleted, itemsDeleted) so the blast radius is visible before you act on it. Contacts themselves are NOT deleted. Writes are WORKSPACE-WIDE: a key scoped to this workspace can manage any list in it, no matter which app user created it. A list in another workspace is 404 NOT_FOUND, indistinguishable from a missing id. Consumes one call from the same daily quota as every other CRM route (default 500 per UTC day per organization, 30 per minute burst, plus a per-IP backstop of 60 per minute).","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to operate on. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicListDeletedResponseDto"}}}},"400":{"description":"A request parameter failed validation: a malformed id in the path, a body field, or a malformed workspaceId query parameter. details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Malformed id in the path","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"id must be a 24-character hex id","path":"/api/v3.0/crm/public/lists/{id}","details":[{"field":"id","message":"id must be a 24-character hex id"}]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/lists/{id}"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/lists/{id}"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/lists/{id}"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/lists/{id}"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:write scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/lists/{id}"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:write scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/lists/{id}"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/lists/{id}"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/lists/{id}"}}}}}},"404":{"description":"A referenced record does not exist in the resolved workspace (or was deleted). A record belonging to another workspace returns exactly this response, so the id's existence never leaks across tenants.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"NOT_FOUND":{"summary":"List not found","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No list with that id exists in the resolved workspace.","path":"/api/v3.0/crm/public/lists/{id}"}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/lists/{id}","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists/{id}","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists/{id}","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/lists/{id}"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]}},"/api/v3.0/crm/public/lists/{id}/stages":{"post":{"operationId":"crmPublicCreateListStage","summary":"Add a stage at the end of a pipeline.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan. Requires the crm:write scope. The new stage is appended AFTER every existing stage and the whole board is renumbered contiguously from 1, so the landing stage new candidates enter never moves. A list is capped at 30 active stages (400 VALIDATION_ERROR beyond that). Returns the list's full stage array in board order. Writes are WORKSPACE-WIDE: a key scoped to this workspace can manage any list in it, no matter which app user created it. A list in another workspace is 404 NOT_FOUND, indistinguishable from a missing id. Consumes one call from the same daily quota as every other CRM route (default 500 per UTC day per organization, 30 per minute burst, plus a per-IP backstop of 60 per minute).","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to operate on. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePublicListStageDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PublicListStageDto"}}}}},"400":{"description":"A body field failed validation (name is required, max 60 characters; color must be a hex code or plain token), a malformed list id, or the list already holds the maximum of 30 active stages. details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Malformed id in the path","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"id must be a 24-character hex id","path":"/api/v3.0/crm/public/lists/{id}/stages","details":[{"field":"id","message":"id must be a 24-character hex id"}]}},"VALIDATION_ERROR_STAGE_LIMIT":{"summary":"The list already holds the maximum 30 active stages","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"error.stageLimitExceeded","path":"/api/v3.0/crm/public/lists/{id}/stages","details":[]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/lists/{id}/stages"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/lists/{id}/stages"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/lists/{id}/stages"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/lists/{id}/stages"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:write scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/lists/{id}/stages"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:write scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/lists/{id}/stages"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/lists/{id}/stages"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/lists/{id}/stages"}}}}}},"404":{"description":"A referenced record does not exist in the resolved workspace (or was deleted). A record belonging to another workspace returns exactly this response, so the id's existence never leaks across tenants.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"NOT_FOUND":{"summary":"List not found","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No list with that id exists in the resolved workspace.","path":"/api/v3.0/crm/public/lists/{id}/stages"}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/lists/{id}/stages","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists/{id}/stages","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists/{id}/stages","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/lists/{id}/stages"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]}},"/api/v3.0/crm/public/lists/{id}/stages/{stageId}":{"patch":{"operationId":"crmPublicUpdateListStage","summary":"Rename or recolor one stage.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan. Requires the crm:write scope. Send name, color, or both - sending neither is 400 VALIDATION_ERROR. Stage order is untouched. Returns the list's full stage array in board order. A stage that does not belong to this list is 404 NOT_FOUND. Writes are WORKSPACE-WIDE: a key scoped to this workspace can manage any list in it, no matter which app user created it. A list in another workspace is 404 NOT_FOUND, indistinguishable from a missing id. Consumes one call from the same daily quota as every other CRM route (default 500 per UTC day per organization, 30 per minute burst, plus a per-IP backstop of 60 per minute).","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"stageId","required":true,"in":"path","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to operate on. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePublicListStageDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PublicListStageDto"}}}}},"400":{"description":"A request parameter failed validation: a malformed list or stage id, a name over 60 characters, a color outside the hex/token rule, or a body carrying neither name nor color. details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Malformed id in the path","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"id must be a 24-character hex id","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}","details":[{"field":"id","message":"id must be a 24-character hex id"}]}},"VALIDATION_ERROR_NAME_OR_COLOR":{"summary":"Neither name nor color supplied","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"error.nameOrColorRequired","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}","details":[]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:write scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:write scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}"}}}}}},"404":{"description":"A referenced record does not exist in the resolved workspace (or was deleted). A record belonging to another workspace returns exactly this response, so the id's existence never leaks across tenants.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"NOT_FOUND":{"summary":"List not found","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No list with that id exists in the resolved workspace.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}"}},"NOT_FOUND_2":{"summary":"Stage does not belong to this list","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No stage with that id exists on this list.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}","details":[{"field":"stageId","message":"must be an active stage of this list"}]}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]},"delete":{"operationId":"crmPublicDeleteListStage","summary":"Delete a stage, moving its cards somewhere first.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan. Requires the crm:write scope. Cards are never silently lost: when the stage holds at least one card you MUST pass ?moveToStageId, another active stage of the same list, and every card is moved there first (400 MOVE_TO_STAGE_REQUIRED when it is missing). A list's last remaining stage cannot be deleted (400 LAST_STAGE_UNDELETABLE). The surviving stages are renumbered contiguously from 1. Writes are WORKSPACE-WIDE: a key scoped to this workspace can manage any list in it, no matter which app user created it. A list in another workspace is 404 NOT_FOUND, indistinguishable from a missing id. Consumes one call from the same daily quota as every other CRM route (default 500 per UTC day per organization, 30 per minute burst, plus a per-IP backstop of 60 per minute).","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"stageId","required":true,"in":"path","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to operate on. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}},{"name":"moveToStageId","required":false,"in":"query","description":"Stage to move this stage's cards to before it is deleted. REQUIRED when the stage holds at least one active item (400 MOVE_TO_STAGE_REQUIRED otherwise) - cards are never silently lost. Must be a different, active stage of the SAME list.","example":"6a3ff55406ae2ed06ddb5fe4","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicListStageDeletedResponseDto"}}}},"400":{"description":"The delete was rejected. Besides ordinary validation (a malformed list, stage, or moveToStageId), three business rules produce a stable errorCode: MOVE_TO_STAGE_REQUIRED (the stage holds cards and no moveToStageId was supplied - cards are never silently deleted), LAST_STAGE_UNDELETABLE (a list must keep at least one stage), and TOO_MANY_ITEMS_TO_REASSIGN (the stage holds more cards than one request may reassign). error.invalidMoveToStageId is returned as VALIDATION_ERROR when moveToStageId is malformed or equal to the stage being deleted.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"MOVE_TO_STAGE_REQUIRED":{"summary":"The stage holds cards and no moveToStageId was supplied","value":{"statusCode":400,"errorCode":"MOVE_TO_STAGE_REQUIRED","message":"This stage holds cards, so moveToStageId is required - cards are never silently deleted.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}","details":[{"field":"moveToStageId","message":"required when the stage holds at least one card"}]}},"LAST_STAGE_UNDELETABLE":{"summary":"The list's only remaining stage","value":{"statusCode":400,"errorCode":"LAST_STAGE_UNDELETABLE","message":"A list must keep at least one stage, so its last stage cannot be deleted.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}","details":[{"field":"stageId","message":"this is the list's only stage"}]}},"TOO_MANY_ITEMS_TO_REASSIGN":{"summary":"The stage holds too many cards to move in one request","value":{"statusCode":400,"errorCode":"TOO_MANY_ITEMS_TO_REASSIGN","message":"This stage holds too many cards to reassign in one request. Move some of them first, then delete the stage.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}","details":[{"field":"stageId","message":"too many cards to reassign at once"}]}},"VALIDATION_ERROR":{"summary":"Malformed moveToStageId","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"moveToStageId must be a 24-character hex id","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}","details":[{"field":"moveToStageId","message":"moveToStageId must be a 24-character hex id"}]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:write scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:write scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}"}}}}}},"404":{"description":"A referenced record does not exist in the resolved workspace (or was deleted). A record belonging to another workspace returns exactly this response, so the id's existence never leaks across tenants.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"NOT_FOUND":{"summary":"List not found","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No list with that id exists in the resolved workspace.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}"}},"NOT_FOUND_2":{"summary":"The stage being deleted does not belong to this list","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No stage with that id exists on this list.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}","details":[{"field":"stageId","message":"must be an active stage of this list"}]}},"NOT_FOUND_3":{"summary":"The moveToStageId target does not belong to this list","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No stage with that id exists on this list.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}","details":[{"field":"moveToStageId","message":"must be an active stage of this list"}]}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/lists/{id}/stages/{stageId}"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]}},"/api/v3.0/crm/public/lists/{id}/stages/order":{"put":{"operationId":"crmPublicReorderListStages","summary":"Reorder a pipeline's stages.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan. Requires the crm:write scope. Send stageIds as the FULL desired order - exactly the list's current active stage ids, no missing, extra, foreign, or repeated entry (400 VALIDATION_ERROR otherwise). Stage order is renumbered contiguously from 1 in the order you send. Returns the list's full stage array in the new order. Writes are WORKSPACE-WIDE: a key scoped to this workspace can manage any list in it, no matter which app user created it. A list in another workspace is 404 NOT_FOUND, indistinguishable from a missing id. Consumes one call from the same daily quota as every other CRM route (default 500 per UTC day per organization, 30 per minute burst, plus a per-IP backstop of 60 per minute).","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to operate on. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReorderListStagesDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PublicListStageDto"}}}}},"400":{"description":"A request parameter failed validation: a malformed list id or stage id, an empty stageIds array, a repeated id (error.duplicateStageIdInReorder), or a set that is not exactly the list's current active stages (error.stageSetMismatch). details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Malformed id in the path","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"id must be a 24-character hex id","path":"/api/v3.0/crm/public/lists/{id}/stages/order","details":[{"field":"id","message":"id must be a 24-character hex id"}]}},"VALIDATION_ERROR_SET_MISMATCH":{"summary":"stageIds is not exactly the list's active stage set","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"error.stageSetMismatch","path":"/api/v3.0/crm/public/lists/{id}/stages/order","details":[]}},"VALIDATION_ERROR_DUPLICATE":{"summary":"A stage id appears twice in stageIds","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"error.duplicateStageIdInReorder","path":"/api/v3.0/crm/public/lists/{id}/stages/order","details":[]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/lists/{id}/stages/order"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/lists/{id}/stages/order"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/lists/{id}/stages/order"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/lists/{id}/stages/order"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:write scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/lists/{id}/stages/order"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:write scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/lists/{id}/stages/order"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/lists/{id}/stages/order"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/lists/{id}/stages/order"}}}}}},"404":{"description":"A referenced record does not exist in the resolved workspace (or was deleted). A record belonging to another workspace returns exactly this response, so the id's existence never leaks across tenants.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"NOT_FOUND":{"summary":"List not found","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No list with that id exists in the resolved workspace.","path":"/api/v3.0/crm/public/lists/{id}/stages/order"}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/lists/{id}/stages/order","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists/{id}/stages/order","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists/{id}/stages/order","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/lists/{id}/stages/order"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]}},"/api/v3.0/crm/public/lists/{id}/items":{"post":{"operationId":"crmPublicAddListItem","summary":"Add an existing contact to a pipeline as a card.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan. Requires the crm:write scope. The contact must ALREADY exist in this workspace (create it first with POST /public/contacts); a missing or foreign-workspace contactId is 404 NOT_FOUND. The card lands on the list's first stage unless you pass stageId, which must be an active stage of this list. Adding the same contact twice never duplicates the card - but a repeat WITH a different stageId MOVES it, which is a real write, so treat stage placement as intentional rather than idempotent. Writes are WORKSPACE-WIDE: a key scoped to this workspace can manage any list in it, no matter which app user created it. A list in another workspace is 404 NOT_FOUND, indistinguishable from a missing id. Consumes one call from the same daily quota as every other CRM route (default 500 per UTC day per organization, 30 per minute burst, plus a per-IP backstop of 60 per minute).","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to operate on. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddPublicListItemDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicListItemDto"}}}},"400":{"description":"A request parameter failed validation: a malformed list id, a missing or malformed contactId, or a malformed stageId. details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Malformed id in the path","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"id must be a 24-character hex id","path":"/api/v3.0/crm/public/lists/{id}/items","details":[{"field":"id","message":"id must be a 24-character hex id"}]}},"VALIDATION_ERROR_CONTACT_ID":{"summary":"Missing required contactId","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"contactId is required","path":"/api/v3.0/crm/public/lists/{id}/items","details":[{"field":"contactId","message":"contactId is required"}]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/lists/{id}/items"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/lists/{id}/items"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/lists/{id}/items"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/lists/{id}/items"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:write scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/lists/{id}/items"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:write scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/lists/{id}/items"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/lists/{id}/items"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/lists/{id}/items"}}}}}},"404":{"description":"A referenced record does not exist in the resolved workspace (or was deleted). A record belonging to another workspace returns exactly this response, so the id's existence never leaks across tenants.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"NOT_FOUND":{"summary":"List not found","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No list with that id exists in the resolved workspace.","path":"/api/v3.0/crm/public/lists/{id}/items"}},"NOT_FOUND_2":{"summary":"Contact missing, or in another workspace","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No contact with that id exists in the resolved workspace.","path":"/api/v3.0/crm/public/lists/{id}/items","details":[{"field":"contactId","message":"must be an existing contact in this workspace"}]}},"NOT_FOUND_3":{"summary":"stageId does not belong to this list","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No stage with that id exists on this list.","path":"/api/v3.0/crm/public/lists/{id}/items","details":[{"field":"stageId","message":"must be an active stage of this list"}]}}}}}},"409":{"description":"The list exists but has no ACTIVE stage for the card to land on, so nothing was added. Deliberately NOT a 404: the list id you sent is valid, and the fix is to add a stage rather than to hunt for a different list. Only reachable on legacy pipelines - every stage delete keeps a list's last stage.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"LIST_HAS_NO_STAGES":{"summary":"The list has zero active stages","value":{"statusCode":409,"errorCode":"LIST_HAS_NO_STAGES","message":"This list has no active stages, so a card has nowhere to land. Add a stage with POST /public/lists/{id}/stages, then retry.","path":"/api/v3.0/crm/public/lists/{id}/items","details":[{"field":"id","message":"the list has no active stages"}]}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/lists/{id}/items","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists/{id}/items","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists/{id}/items","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/lists/{id}/items"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]}},"/api/v3.0/crm/public/lists/{id}/items/{itemId}":{"patch":{"operationId":"crmPublicUpdateListItemStage","summary":"Move a card to another stage of the same pipeline.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan. Requires the crm:write scope. stageId must be an active stage of THIS list (404 NOT_FOUND otherwise) - a card can never be moved onto another list's stage. The move is recorded in the card's stage history exactly as a drag-and-drop in the app would be. Writes are WORKSPACE-WIDE: a key scoped to this workspace can manage any list in it, no matter which app user created it. A list in another workspace is 404 NOT_FOUND, indistinguishable from a missing id. Consumes one call from the same daily quota as every other CRM route (default 500 per UTC day per organization, 30 per minute burst, plus a per-IP backstop of 60 per minute).","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"itemId","required":true,"in":"path","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to operate on. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePublicListItemDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicListItemDto"}}}},"400":{"description":"A request parameter failed validation: a malformed list or item id, or a missing/malformed stageId. details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Malformed id in the path","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"id must be a 24-character hex id","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}","details":[{"field":"id","message":"id must be a 24-character hex id"}]}},"VALIDATION_ERROR_STAGE_ID":{"summary":"Missing required stageId","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"stageId is required","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}","details":[{"field":"stageId","message":"stageId is required"}]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:write scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:write scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}}}}}},"404":{"description":"A referenced record does not exist in the resolved workspace (or was deleted). A record belonging to another workspace returns exactly this response, so the id's existence never leaks across tenants.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"NOT_FOUND":{"summary":"List not found","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No list with that id exists in the resolved workspace.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}},"NOT_FOUND_2":{"summary":"Card not found on this list","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No list item with that id exists on this list.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}},"NOT_FOUND_3":{"summary":"stageId does not belong to this list","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No stage with that id exists on this list.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}","details":[{"field":"stageId","message":"must be an active stage of this list"}]}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]},"delete":{"operationId":"crmPublicDeleteListItem","summary":"Remove a card from a pipeline.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan. Requires the crm:write scope. Soft-deletes the card and drops the list from the contact's own list memberships. The CONTACT itself is not deleted - use DELETE /public/contacts/{id} for that. A card that does not belong to this list is 404 NOT_FOUND. Writes are WORKSPACE-WIDE: a key scoped to this workspace can manage any list in it, no matter which app user created it. A list in another workspace is 404 NOT_FOUND, indistinguishable from a missing id. Consumes one call from the same daily quota as every other CRM route (default 500 per UTC day per organization, 30 per minute burst, plus a per-IP backstop of 60 per minute).","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"itemId","required":true,"in":"path","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to operate on. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicListItemDeletedResponseDto"}}}},"400":{"description":"A request parameter failed validation: a malformed id in the path, a body field, or a malformed workspaceId query parameter. details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Malformed id in the path","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"id must be a 24-character hex id","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}","details":[{"field":"id","message":"id must be a 24-character hex id"}]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:write scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:write scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}}}}}},"404":{"description":"A referenced record does not exist in the resolved workspace (or was deleted). A record belonging to another workspace returns exactly this response, so the id's existence never leaks across tenants.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"NOT_FOUND":{"summary":"List not found","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No list with that id exists in the resolved workspace.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}},"NOT_FOUND_2":{"summary":"Card not found on this list","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No list item with that id exists on this list.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/lists/{id}/items/{itemId}"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]}},"/api/v3.0/crm/public/contacts/{id}/tasks":{"post":{"operationId":"crmPublicCreateTask","summary":"Create a task against a contact.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan and the crm:write scope. A task is the WORK half of the contact timeline - a proposal, a follow-up, a todo - and it always starts in status open. Send dedupeKey to make the create idempotent: a second call with the same key returns 409 TASK_DUPLICATE carrying the existing task's id in details, so a retry adopts the first task rather than writing a second. A contact in another workspace is 404 NOT_FOUND, indistinguishable from a missing id. Consumes one call from the same daily quota as the reads.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to operate on. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePublicTaskDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTaskDto"}}}},"400":{"description":"A request parameter failed validation: a malformed contact or task id in the path, a body field (taskType is required on create; title over 200 chars; notes over 5000; a non-ISO dueAt or date; a non-boolean done; an interactionTypeId that is not a 24-character hex id; an assigneeId that is neither a 24-character hex id nor the empty string that means unassigned), or a malformed workspaceId query parameter. UNKNOWN_INTERACTION_TYPE is returned when `interactionTypeId` is not a category or type id from the global interaction taxonomy (GET /public/interaction-types lists it). details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Missing required taskType on create","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"taskType is required","path":"/api/v3.0/crm/public/contacts/{id}/tasks","details":[{"field":"taskType","message":"taskType is required"}]}},"UNKNOWN_INTERACTION_TYPE":{"summary":"interactionTypeId is not in the global taxonomy","value":{"statusCode":400,"errorCode":"UNKNOWN_INTERACTION_TYPE","message":"Unknown interaction type id \"aaaaaaaaaaaaaaaaaaaaaaaa\".","path":"/api/v3.0/crm/public/contacts/{id}/tasks","details":[{"field":"interactionTypeId","message":"must be a category or type id from the global interaction taxonomy - read GET /api/v3.0/crm/public/interaction-types for the valid set"}]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/contacts/{id}/tasks"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/contacts/{id}/tasks"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/contacts/{id}/tasks"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/contacts/{id}/tasks"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:write scope, it cannot access the resolved organization or workspace, or the id resolves to a FACT interaction rather than a task (NOT_A_TASK) - facts are append-only and can never be edited or deleted through this API.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"NOT_A_TASK":{"summary":"The id belongs to a fact interaction, not a task","value":{"statusCode":403,"errorCode":"NOT_A_TASK","message":"This interaction is a fact, not a task. Facts are append-only: they can never be edited or deleted through this API.","path":"/api/v3.0/crm/public/contacts/{id}/tasks"}},"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/contacts/{id}/tasks"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:write scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/contacts/{id}/tasks"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/contacts/{id}/tasks"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/contacts/{id}/tasks"}}}}}},"404":{"description":"No task (or, on the create route, no contact) with that id exists in the resolved workspace, or it was deleted. A row in another workspace is reported identically, so existence never leaks across tenants.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"NOT_FOUND":{"summary":"Task not found","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No task with that id exists in the resolved workspace.","path":"/api/v3.0/crm/public/contacts/{id}/tasks"}}}}}},"409":{"description":"A live (non-deleted) task in this workspace already holds the supplied dedupeKey. This is the idempotent-create response: details carries the existing task's id in taskId - adopt it rather than retrying. Deleting that task releases the key.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"},"taskId":{"type":"string","nullable":true,"description":"Id of the live task already holding this dedupeKey. Null only if it was removed between the failed write and this lookup."}}}}}},"examples":{"TASK_DUPLICATE":{"summary":"dedupeKey already held by a live task","value":{"statusCode":409,"errorCode":"TASK_DUPLICATE","message":"A task with this dedupeKey already exists in this workspace. Use the existing task rather than creating another.","path":"/api/v3.0/crm/public/contacts/{id}/tasks","details":[{"field":"dedupeKey","message":"a live task already holds this dedupeKey","taskId":"6a3ff55306ae2ed06ddb5fd8"}]}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/contacts/{id}/tasks","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/contacts/{id}/tasks","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/contacts/{id}/tasks","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/contacts/{id}/tasks"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]},"get":{"operationId":"crmPublicListContactTasks","summary":"List a contact's tasks (newest first).","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan. Returns the contact's tasks newest-first, optionally filtered by status. Facts are NOT returned here - read the interaction timeline for those. Paginated on hasMore (no total is returned). A contact in another workspace is 404 NOT_FOUND. Same rate limits as the other reads.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to operate on. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","description":"1-based page number (max 10000).","schema":{"minimum":1,"maximum":10000,"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Results per page (max 50).","schema":{"minimum":1,"maximum":50,"default":20,"type":"number"}},{"name":"status","required":false,"in":"query","description":"Only return tasks in this status. Omit to return every non-deleted task on the contact.","schema":{"enum":["open","approved","sent","done","rejected","expired"],"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTaskListResponseDto"}}}},"400":{"description":"A query parameter failed validation (status, taskType, dueBefore, page, pageSize) or a malformed workspaceId was supplied (not a 24-character hex id). details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"status outside the task status set","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"status must be one of: open, approved, sent, done, rejected, expired","path":"/api/v3.0/crm/public/contacts/{id}/tasks","details":[{"field":"status","message":"status must be one of: open, approved, sent, done, rejected, expired"}]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/contacts/{id}/tasks"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/contacts/{id}/tasks"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/contacts/{id}/tasks"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/contacts/{id}/tasks"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:read scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/contacts/{id}/tasks"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:read scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/contacts/{id}/tasks"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/contacts/{id}/tasks"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/contacts/{id}/tasks"}}}}}},"404":{"description":"No task (or, on the create route, no contact) with that id exists in the resolved workspace, or it was deleted. A row in another workspace is reported identically, so existence never leaks across tenants.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"NOT_FOUND":{"summary":"Task not found","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No task with that id exists in the resolved workspace.","path":"/api/v3.0/crm/public/contacts/{id}/tasks"}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/contacts/{id}/tasks","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/contacts/{id}/tasks","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/contacts/{id}/tasks","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/contacts/{id}/tasks"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]}},"/api/v3.0/crm/public/tasks":{"post":{"operationId":"crmPublicCreateTaskAtAnchor","summary":"Create an activity about a contact, candidate, job, or application.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan and the crm:write scope. Send anchor:{type,id} for what the activity is about: contact, candidate (a CazVid user), job (a job post), application (a pipeline card), or none. The anchor is resolved in your workspace BEFORE anything is written - an id that names nothing you can reach is 404 NOT_FOUND (missing and someone else's are the same answer), and an anchor missing its id, or carrying one its type does not allow, is 400 INVALID_ANCHOR. An application anchor DERIVES its jobPostId from the application, so do not send one. A candidate anchor REFUSES call and meeting activity types with 400 CALL_ACTIVITY_NEEDS_CONTACT: those record an outcome on a contact timeline, and a candidate has none. The anchor is frozen once written. dedupeKey works exactly as it does on the contact-scoped create. Consumes one call from the same daily quota as the reads.","parameters":[{"name":"workspaceId","required":false,"in":"query","description":"Workspace to operate on. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePublicTaskAtAnchorDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTaskDto"}}}},"400":{"description":"A request parameter failed validation: a malformed contact or task id in the path, a body field (taskType is required on create; title over 200 chars; notes over 5000; a non-ISO dueAt or date; a non-boolean done; an interactionTypeId that is not a 24-character hex id; an assigneeId that is neither a 24-character hex id nor the empty string that means unassigned), or a malformed workspaceId query parameter. UNKNOWN_INTERACTION_TYPE is returned when `interactionTypeId` is not a category or type id from the global interaction taxonomy (GET /public/interaction-types lists it). details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Missing required taskType on create","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"taskType is required","path":"/api/v3.0/crm/public/tasks","details":[{"field":"taskType","message":"taskType is required"}]}},"UNKNOWN_INTERACTION_TYPE":{"summary":"interactionTypeId is not in the global taxonomy","value":{"statusCode":400,"errorCode":"UNKNOWN_INTERACTION_TYPE","message":"Unknown interaction type id \"aaaaaaaaaaaaaaaaaaaaaaaa\".","path":"/api/v3.0/crm/public/tasks","details":[{"field":"interactionTypeId","message":"must be a category or type id from the global interaction taxonomy - read GET /api/v3.0/crm/public/interaction-types for the valid set"}]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/tasks"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/tasks"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/tasks"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/tasks"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:write scope, it cannot access the resolved organization or workspace, or the id resolves to a FACT interaction rather than a task (NOT_A_TASK) - facts are append-only and can never be edited or deleted through this API.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"NOT_A_TASK":{"summary":"The id belongs to a fact interaction, not a task","value":{"statusCode":403,"errorCode":"NOT_A_TASK","message":"This interaction is a fact, not a task. Facts are append-only: they can never be edited or deleted through this API.","path":"/api/v3.0/crm/public/tasks"}},"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/tasks"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:write scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/tasks"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/tasks"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/tasks"}}}}}},"404":{"description":"No task (or, on the create route, no contact) with that id exists in the resolved workspace, or it was deleted. A row in another workspace is reported identically, so existence never leaks across tenants.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"NOT_FOUND":{"summary":"Task not found","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No task with that id exists in the resolved workspace.","path":"/api/v3.0/crm/public/tasks"}}}}}},"409":{"description":"A live (non-deleted) task in this workspace already holds the supplied dedupeKey. This is the idempotent-create response: details carries the existing task's id in taskId - adopt it rather than retrying. Deleting that task releases the key.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"},"taskId":{"type":"string","nullable":true,"description":"Id of the live task already holding this dedupeKey. Null only if it was removed between the failed write and this lookup."}}}}}},"examples":{"TASK_DUPLICATE":{"summary":"dedupeKey already held by a live task","value":{"statusCode":409,"errorCode":"TASK_DUPLICATE","message":"A task with this dedupeKey already exists in this workspace. Use the existing task rather than creating another.","path":"/api/v3.0/crm/public/contacts/{id}/tasks","details":[{"field":"dedupeKey","message":"a live task already holds this dedupeKey","taskId":"6a3ff55306ae2ed06ddb5fd8"}]}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/tasks","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/tasks","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/tasks","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/tasks"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]},"get":{"operationId":"crmPublicListTasks","summary":"List the workspace's tasks, soonest due first.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan. THE QUEUE: every task in the resolved workspace across all contacts. DATED WORK COMES FIRST, sorted by dueAt ascending then createdAt; tasks with NO dueAt sort LAST, oldest first. Filter with status (pass status=open for the working queue - omitting it returns terminal tasks too), taskType, and dueBefore. dueBefore excludes undated tasks entirely (there is no due date to compare). Paginated on hasMore. Same rate limits as the other reads.","parameters":[{"name":"workspaceId","required":false,"in":"query","description":"Workspace to operate on. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","description":"1-based page number (max 10000).","schema":{"minimum":1,"maximum":10000,"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Results per page (max 50).","schema":{"minimum":1,"maximum":50,"default":20,"type":"number"}},{"name":"status","required":false,"in":"query","description":"Only return tasks in this status. Omit to return every non-deleted task in the workspace, terminal ones included - pass status=open for the working queue.","schema":{"enum":["open","approved","sent","done","rejected","expired"],"type":"string"}},{"name":"taskType","required":false,"in":"query","description":"Only return tasks of this type.","schema":{"enum":["proposal","followup","todo"],"type":"string"}},{"name":"dueBefore","required":false,"in":"query","description":"Only return tasks due at or before this ISO 8601 timestamp. Tasks with no dueAt are excluded by this filter (they have no due date to compare).","example":"2026-08-11T00:00:00.000Z","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTaskListResponseDto"}}}},"400":{"description":"A query parameter failed validation (status, taskType, dueBefore, page, pageSize) or a malformed workspaceId was supplied (not a 24-character hex id). details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"status outside the task status set","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"status must be one of: open, approved, sent, done, rejected, expired","path":"/api/v3.0/crm/public/tasks","details":[{"field":"status","message":"status must be one of: open, approved, sent, done, rejected, expired"}]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/tasks"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/tasks"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/tasks"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/tasks"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:read scope, or it cannot access the resolved organization or workspace.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/tasks"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:read scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/tasks"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/tasks"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/tasks"}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/tasks","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/tasks","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/tasks","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/tasks"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]}},"/api/v3.0/crm/public/tasks/{id}":{"patch":{"operationId":"crmPublicUpdateTask","summary":"Update a task: a status move, field edits, or both.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan and the crm:write scope. A partial update - only the fields you send change. THIS ROUTE ONLY TOUCHES TASKS: an id that resolves to a fact interaction is 403 NOT_A_TASK, because facts are append-only and can never be edited through this API. A task in a terminal status (sent, done, rejected, expired) is frozen and returns 409 TASK_TERMINAL. ONE exception: a done task can be REOPENED by sending {\"status\":\"open\"} and nothing else, which moves it back to open with its history intact and RELEASES any claim it carries, so it returns to the queue as unclaimed work. Any other field in that body makes it a field edit on a frozen task and it stays 409 - reopen first, then edit. A sent, rejected or expired task can never be un-frozen. An illegal status move is 409 INVALID_STATUS_TRANSITION with the allowed moves in details. Field edits are recorded as a revision (prior values retained); status moves and claims are recorded in the task's events. Consumes one call from the same daily quota as the reads.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to operate on. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePublicTaskDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTaskDto"}}}},"400":{"description":"A request parameter failed validation: a malformed contact or task id in the path, a body field (taskType is required on create; title over 200 chars; notes over 5000; a non-ISO dueAt or date; a non-boolean done; an interactionTypeId that is not a 24-character hex id; an assigneeId that is neither a 24-character hex id nor the empty string that means unassigned), or a malformed workspaceId query parameter. UNKNOWN_INTERACTION_TYPE is returned when `interactionTypeId` is not a category or type id from the global interaction taxonomy (GET /public/interaction-types lists it). details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Missing required taskType on create","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"taskType is required","path":"/api/v3.0/crm/public/tasks/{id}","details":[{"field":"taskType","message":"taskType is required"}]}},"UNKNOWN_INTERACTION_TYPE":{"summary":"interactionTypeId is not in the global taxonomy","value":{"statusCode":400,"errorCode":"UNKNOWN_INTERACTION_TYPE","message":"Unknown interaction type id \"aaaaaaaaaaaaaaaaaaaaaaaa\".","path":"/api/v3.0/crm/public/tasks/{id}","details":[{"field":"interactionTypeId","message":"must be a category or type id from the global interaction taxonomy - read GET /api/v3.0/crm/public/interaction-types for the valid set"}]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/tasks/{id}"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/tasks/{id}"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/tasks/{id}"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/tasks/{id}"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:write scope, it cannot access the resolved organization or workspace, or the id resolves to a FACT interaction rather than a task (NOT_A_TASK) - facts are append-only and can never be edited or deleted through this API.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"NOT_A_TASK":{"summary":"The id belongs to a fact interaction, not a task","value":{"statusCode":403,"errorCode":"NOT_A_TASK","message":"This interaction is a fact, not a task. Facts are append-only: they can never be edited or deleted through this API.","path":"/api/v3.0/crm/public/tasks/{id}"}},"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/tasks/{id}"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:write scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/tasks/{id}"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/tasks/{id}"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/tasks/{id}"}}}}}},"404":{"description":"No task (or, on the create route, no contact) with that id exists in the resolved workspace, or it was deleted. A row in another workspace is reported identically, so existence never leaks across tenants.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"NOT_FOUND":{"summary":"Task not found","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No task with that id exists in the resolved workspace.","path":"/api/v3.0/crm/public/tasks/{id}"}}}}}},"409":{"description":"The task's current state forbids the request. TASK_TERMINAL: the task is sent, done, rejected or expired, so it is frozen. A done task has one way out - REOPEN it by sending {\"status\":\"open\"} and nothing else, which also releases any claim on it so it returns to the queue as unclaimed work - and a body carrying any other field is a field edit on a frozen task, so it stays 409; sent, rejected and expired can never be un-frozen. INVALID_STATUS_TRANSITION: the requested status is not reachable from the current one (open -> approved | done | rejected | expired, though a proposal must be approved before it can be done; approved -> sent | done | rejected | open; done -> open); details lists what IS reachable. TASK_CLAIMED: a different worker holds the claim, so claiming it is refused rather than silently stolen; details names the holder. Re-claiming a task you already hold succeeds, and releasing (claimedBy: \"\") is always allowed.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"},"claimedBy":{"type":"string","nullable":true,"description":"On TASK_CLAIMED only: the worker that currently holds the task."},"claimedAt":{"type":"string","nullable":true,"description":"On TASK_CLAIMED only: when the holder took it."}}}}}},"examples":{"TASK_CLAIMED":{"summary":"Another worker already holds this task","value":{"statusCode":409,"errorCode":"TASK_CLAIMED","message":"This task is already claimed by cold-sales-agent. Work only what you hold - release the claim first if it is stale.","path":"/api/v3.0/crm/public/tasks/{id}","details":[{"field":"claimedBy","message":"another worker already holds this task","claimedBy":"cold-sales-agent","claimedAt":"2026-08-10T10:00:00.000Z"}]}},"TASK_TERMINAL":{"summary":"The task is already in a terminal status","value":{"statusCode":409,"errorCode":"TASK_TERMINAL","message":"This task is sent and can no longer be changed. Create a new task instead.","path":"/api/v3.0/crm/public/tasks/{id}","details":[{"field":"status","message":"a sent task is frozen"}]}},"INVALID_STATUS_TRANSITION":{"summary":"The status move is not on the machine","value":{"statusCode":409,"errorCode":"INVALID_STATUS_TRANSITION","message":"A task cannot move from open to sent.","path":"/api/v3.0/crm/public/tasks/{id}","details":[{"field":"status","message":"from open the allowed statuses are: approved, rejected, expired"}]}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/tasks/{id}","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/tasks/{id}","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/tasks/{id}","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/tasks/{id}"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]},"delete":{"operationId":"crmPublicDeleteTask","summary":"Delete a task.","description":"Authenticate with an x-api-key header or Authorization: Bearer <key>. Requires a paid plan and the crm:write scope. SOFT delete: the task is marked deleted and retained, and its dedupeKey is released so the same key can be created again. TASK-KIND ONLY: an id that resolves to a fact interaction is 403 NOT_A_TASK - facts are undeletable through this API, forever. A task in another workspace is 404 NOT_FOUND. Consumes one call from the same daily quota as the reads.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"workspaceId","required":false,"in":"query","description":"Workspace to operate on. Optional when the API key grants a single workspace (defaults to it); pass it when the key grants several. Must be one of the key's workspaces.","example":"6a3ff55406ae2ed06ddb5fda","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTaskDeletedResponseDto"}}}},"400":{"description":"A request parameter failed validation: a malformed contact id in the path, a body field (name is required on create; a malformed email; an over-long array), or a malformed workspaceId query parameter. details lists each offending field.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"array","items":{"type":"object","required":["field","message"],"properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"examples":{"VALIDATION_ERROR":{"summary":"Missing required name on create","value":{"statusCode":400,"errorCode":"VALIDATION_ERROR","message":"name is required","path":"/api/v3.0/crm/public/tasks/{id}","details":[{"field":"name","message":"name is required"}]}}}}}},"401":{"description":"Authentication failed: no API key was presented, or the presented key is invalid, expired, or revoked.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"API_KEY_MISSING":{"summary":"No API key presented","value":{"statusCode":401,"errorCode":"API_KEY_MISSING","message":"Missing API key. Send it with x-api-key or Authorization: Bearer <key>.","path":"/api/v3.0/crm/public/tasks/{id}"}},"API_KEY_INVALID":{"summary":"Key not recognized","value":{"statusCode":401,"errorCode":"API_KEY_INVALID","message":"API key is invalid.","path":"/api/v3.0/crm/public/tasks/{id}"}},"API_KEY_EXPIRED":{"summary":"Key past its expiry","value":{"statusCode":401,"errorCode":"API_KEY_EXPIRED","message":"API key has expired.","path":"/api/v3.0/crm/public/tasks/{id}"}},"API_KEY_REVOKED":{"summary":"Key revoked","value":{"statusCode":401,"errorCode":"API_KEY_REVOKED","message":"API key has been revoked.","path":"/api/v3.0/crm/public/tasks/{id}"}}}}}},"403":{"description":"The key authenticated but is not allowed to run this request: the organization is not on a paid plan (PLAN_REQUIRED), the key is missing the crm:write scope, it cannot access the resolved organization or workspace, or the id resolves to a FACT interaction rather than a task (NOT_A_TASK) - facts are append-only and can never be edited or deleted through this API.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"NOT_A_TASK":{"summary":"The id belongs to a fact interaction, not a task","value":{"statusCode":403,"errorCode":"NOT_A_TASK","message":"This interaction is a fact, not a task. Facts are append-only: they can never be edited or deleted through this API.","path":"/api/v3.0/crm/public/tasks/{id}"}},"PLAN_REQUIRED":{"summary":"Organization is not on a paid plan","value":{"statusCode":403,"errorCode":"PLAN_REQUIRED","message":"CRM API access requires a Platinum, Diamond, or Enterprise plan.","path":"/api/v3.0/crm/public/tasks/{id}"}},"SCOPE_FORBIDDEN":{"summary":"Key lacks the crm:write scope","value":{"statusCode":403,"errorCode":"SCOPE_FORBIDDEN","message":"API key scope is not allowed","path":"/api/v3.0/crm/public/tasks/{id}"}},"ORG_ACCESS_DENIED":{"summary":"Key cannot access the resolved organization","value":{"statusCode":403,"errorCode":"ORG_ACCESS_DENIED","message":"API key cannot access this organization","path":"/api/v3.0/crm/public/tasks/{id}"}},"WORKSPACE_ACCESS_DENIED":{"summary":"Key cannot access the resolved workspace","value":{"statusCode":403,"errorCode":"WORKSPACE_ACCESS_DENIED","message":"API key cannot access this workspace","path":"/api/v3.0/crm/public/tasks/{id}"}}}}}},"404":{"description":"No task (or, on the create route, no contact) with that id exists in the resolved workspace, or it was deleted. A row in another workspace is reported identically, so existence never leaks across tenants.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"NOT_FOUND":{"summary":"Task not found","value":{"statusCode":404,"errorCode":"NOT_FOUND","message":"No task with that id exists in the resolved workspace.","path":"/api/v3.0/crm/public/tasks/{id}"}}}}}},"429":{"description":"A rate limit was exceeded. Three independent limits apply: a per-IP backstop (60 per minute per client IP), a per-minute burst limit (30 per organization), and a daily quota (default 500 per UTC day per organization). The per-IP block carries details.scope:'ip'. retryAfter is the whole number of seconds to wait.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"},"details":{"type":"object","required":["limit","period"],"properties":{"limit":{"type":"integer"},"period":{"type":"string","enum":["utc_day","minute"]},"scope":{"type":"string","enum":["ip"],"description":"Present only on the per-IP backstop block (a shared-key/scraping guard)."}}},"retryAfter":{"type":"integer","description":"Whole seconds to wait before retrying."}}},"examples":{"daily_quota":{"summary":"Daily quota for this organization exhausted","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"Daily CRM API quota exceeded.","path":"/api/v3.0/crm/public/tasks/{id}","retryAfter":3600,"details":{"limit":500,"period":"utc_day"}}},"burst":{"summary":"Burst limit of 30 per minute per organization exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API burst limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/tasks/{id}","retryAfter":30,"details":{"limit":30,"period":"minute"}}},"ip":{"summary":"Per-IP backstop of 60 per minute exceeded","value":{"statusCode":429,"errorCode":"RATE_LIMIT_EXCEEDED","message":"CRM API per-IP limit exceeded. Slow down and retry shortly.","path":"/api/v3.0/crm/public/tasks/{id}","retryAfter":15,"details":{"limit":60,"period":"minute","scope":"ip"}}}}}}},"500":{"description":"An unexpected error occurred (e.g. the datastore was unreachable). The underlying cause is intentionally hidden from the response body.","content":{"application/json":{"schema":{"type":"object","required":["statusCode","errorCode","message","path"],"properties":{"statusCode":{"type":"integer"},"errorCode":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}}},"examples":{"CRM_API_ERROR":{"summary":"Unexpected server error","value":{"statusCode":500,"errorCode":"CRM_API_ERROR","message":"CRM API request failed.","path":"/api/v3.0/crm/public/tasks/{id}"}}}}}}},"tags":["CRM API V3.0"],"security":[{"apiKey":[]},{"bearer":[]}]}}},"components":{"schemas":{"PublicContactListResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicContactDto"}},"page":{"type":"number","description":"Echoed 1-based page number."},"pageSize":{"type":"number","description":"Echoed page size."},"hasMore":{"type":"boolean","description":"Whether another page exists. This API paginates on hasMore - no total count is returned."}},"required":["items","page","pageSize","hasMore"]},"CreatePublicContactDto":{"type":"object","properties":{"firstName":{"type":"string","nullable":true,"example":"Ada"},"lastName":{"type":"string","nullable":true,"example":"Lovelace"},"jobTitle":{"example":["Software Engineer"],"description":"Job titles. Index 0 is treated as the primary title.","type":"array","items":{"type":"string"}},"companyName":{"type":"string","nullable":true,"example":"Analytical Engines"},"linkedinUrl":{"type":"string","nullable":true,"example":"https://www.linkedin.com/in/adalovelace"},"locationName":{"type":"string","nullable":true,"example":"London, UK"},"locationGeoNameId":{"type":"number","nullable":true,"example":2643743,"description":"GeoNames id for the contact's location, when known."},"emails":{"description":"Email addresses. Re-POSTing a create with an email already on a contact in this workspace resolves to that contact (see the create endpoint's idempotency note).","type":"array","items":{"$ref":"#/components/schemas/PublicContactEmailInputDto"}},"phones":{"description":"Phone numbers.","type":"array","items":{"$ref":"#/components/schemas/PublicContactPhoneInputDto"}},"tags":{"example":["priority","referral"],"description":"Tag names. Existing tags are matched by name; unknown names are created in the workspace. Send names, not ids.","type":"array","items":{"type":"string"}},"name":{"type":"string","description":"Display name of the contact.","example":"Ada Lovelace","maxLength":100}},"required":["name"]},"PublicContactWriteResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Contact id."},"name":{"type":"string","nullable":true,"description":"Display name."},"firstName":{"type":"string","nullable":true,"description":"First name, when stored separately from the display name."},"lastName":{"type":"string","nullable":true,"description":"Last name, when stored separately from the display name."},"jobTitle":{"description":"Job titles. Array; the app treats index 0 as the primary title.","type":"array","items":{"type":"string"}},"companyName":{"type":"string","nullable":true},"companyId":{"type":"string","nullable":true,"description":"Linked Company id, when the contact is associated with one."},"emails":{"description":"Email addresses (index 0 is the primary). Returned in full for PLAIN workspace contacts on every endpoint. For a contact linked to a CazVid user they are returned ONLY by the detail (get-by-id) endpoint, which runs the linked candidate's own privacy check - the create, update, and list endpoints omit them (empty array) for linked contacts, so fetch the detail endpoint for those.","type":"array","items":{"$ref":"#/components/schemas/PublicContactEmailDto"}},"phones":{"description":"Phone numbers (index 0 is the primary). Same visibility rule as emails: full for plain contacts everywhere; for a CazVid-linked contact only the detail endpoint returns them (create/update/list omit them).","type":"array","items":{"$ref":"#/components/schemas/PublicContactPhoneDto"}},"linkedinUrl":{"type":"string","nullable":true,"description":"LinkedIn URL. Same visibility rule as emails/phones: returned for plain contacts everywhere; for a CazVid-linked contact only the detail endpoint returns it (create/update/list return null)."},"locationName":{"type":"string","nullable":true},"locationGeoNameId":{"type":"number","nullable":true,"description":"GeoNames id for the contact's location, when available."},"tags":{"type":"array","items":{"$ref":"#/components/schemas/PublicContactTagDto"}},"hasResume":{"type":"boolean","description":"Whether the contact has at least one resume on file."},"resumeCount":{"type":"number","description":"Number of resumes on file for the contact. It can legitimately exceed `resumes.length`: the count also sees legacy embedded resume entries, which carry no per-resume detail, while `resumes` lists only canonical resume records. Treat resumeCount as the count and `resumes` as the detail available for it. The list read and the single-contact read count the same resumes, so on a plain workspace contact (linkedCazvidUser false) the value does not change between them - note that a contact you created through this API is not automatically plain, since create's de-dup resolver can auto-link it to an existing CazVid user. For a contact linked to a CazVid user this can read 0 while a resume IS on file, because a resume that has not finished parsing is not resolved for a linked contact at all - and on such a contact the single-contact read can report a HIGHER count than the list, because it additionally sees a resume attached to the candidate's most recent application. See Resume Ingestion in the guide."},"resumes":{"nullable":true,"description":"Per-resume ingestion detail, ordered with the contact's primary resume at index 0. This is how you diagnose a resume that never produced data: `parseStatus: permanently_failed` with a `lastError` means the file will never parse and must be re-uploaded. NULL vs EMPTY is meaningful. `null` means this response did not resolve resumes at all - the list read and the create/update echo ALWAYS return null, because their read shapes do not load canonical resume records; fetch the single-contact (get-by-id) endpoint for the detail. `[]` comes only from the single-contact endpoint and means it looked and there genuinely are none - which can still happen when a contact's only resumes are legacy embedded entries, or, for a contact linked to a CazVid user, when its resumes have not finished parsing (see Resume Ingestion in the guide). Never read `null` as `no resumes`: check `resumeCount`.","type":"array","items":{"$ref":"#/components/schemas/PublicContactResumeDto"}},"cazvidUserId":{"type":"string","nullable":true,"description":"Linked CazVid platform user id, when the contact is a CazVid user."},"linkedCazvidUser":{"type":"boolean","description":"Whether the contact is linked to a CazVid platform user."},"createdAt":{"type":"string","nullable":true,"description":"ISO 8601 timestamp the contact was created."},"updatedAt":{"type":"string","nullable":true,"description":"ISO 8601 timestamp the contact was last updated."},"deduplicated":{"type":"boolean","description":"True when the create request resolved to an EXISTING contact instead of inserting a new one. CazVid de-duplicates within a workspace by CazVid-user link, then by email, then by phone: re-POSTing a contact whose email or phone already exists returns that same contact (its id and original createdAt), with any new emails/phones/tags merged in - that resolution is the create endpoint's idempotency behavior. A name-only create carries no de-duplication key, so retrying one after a timeout can insert a duplicate. This flag itself is ADVISORY: it is derived from the returned contact's createdAt (a best-effort heuristic, not a resolver-reported fact) and can misreport on a same-millisecond createdAt or on legacy records with a missing or skewed createdAt. Present only on the create response; omitted on update.","example":false}},"required":["id","jobTitle","emails","phones","tags","hasResume","resumeCount","resumes","linkedCazvidUser","deduplicated"]},"PublicContactDto":{"type":"object","properties":{"id":{"type":"string","description":"Contact id."},"name":{"type":"string","nullable":true,"description":"Display name."},"firstName":{"type":"string","nullable":true,"description":"First name, when stored separately from the display name."},"lastName":{"type":"string","nullable":true,"description":"Last name, when stored separately from the display name."},"jobTitle":{"description":"Job titles. Array; the app treats index 0 as the primary title.","type":"array","items":{"type":"string"}},"companyName":{"type":"string","nullable":true},"companyId":{"type":"string","nullable":true,"description":"Linked Company id, when the contact is associated with one."},"emails":{"description":"Email addresses (index 0 is the primary). Returned in full for PLAIN workspace contacts on every endpoint. For a contact linked to a CazVid user they are returned ONLY by the detail (get-by-id) endpoint, which runs the linked candidate's own privacy check - the create, update, and list endpoints omit them (empty array) for linked contacts, so fetch the detail endpoint for those.","type":"array","items":{"$ref":"#/components/schemas/PublicContactEmailDto"}},"phones":{"description":"Phone numbers (index 0 is the primary). Same visibility rule as emails: full for plain contacts everywhere; for a CazVid-linked contact only the detail endpoint returns them (create/update/list omit them).","type":"array","items":{"$ref":"#/components/schemas/PublicContactPhoneDto"}},"linkedinUrl":{"type":"string","nullable":true,"description":"LinkedIn URL. Same visibility rule as emails/phones: returned for plain contacts everywhere; for a CazVid-linked contact only the detail endpoint returns it (create/update/list return null)."},"locationName":{"type":"string","nullable":true},"locationGeoNameId":{"type":"number","nullable":true,"description":"GeoNames id for the contact's location, when available."},"tags":{"type":"array","items":{"$ref":"#/components/schemas/PublicContactTagDto"}},"hasResume":{"type":"boolean","description":"Whether the contact has at least one resume on file."},"resumeCount":{"type":"number","description":"Number of resumes on file for the contact. It can legitimately exceed `resumes.length`: the count also sees legacy embedded resume entries, which carry no per-resume detail, while `resumes` lists only canonical resume records. Treat resumeCount as the count and `resumes` as the detail available for it. The list read and the single-contact read count the same resumes, so on a plain workspace contact (linkedCazvidUser false) the value does not change between them - note that a contact you created through this API is not automatically plain, since create's de-dup resolver can auto-link it to an existing CazVid user. For a contact linked to a CazVid user this can read 0 while a resume IS on file, because a resume that has not finished parsing is not resolved for a linked contact at all - and on such a contact the single-contact read can report a HIGHER count than the list, because it additionally sees a resume attached to the candidate's most recent application. See Resume Ingestion in the guide."},"resumes":{"nullable":true,"description":"Per-resume ingestion detail, ordered with the contact's primary resume at index 0. This is how you diagnose a resume that never produced data: `parseStatus: permanently_failed` with a `lastError` means the file will never parse and must be re-uploaded. NULL vs EMPTY is meaningful. `null` means this response did not resolve resumes at all - the list read and the create/update echo ALWAYS return null, because their read shapes do not load canonical resume records; fetch the single-contact (get-by-id) endpoint for the detail. `[]` comes only from the single-contact endpoint and means it looked and there genuinely are none - which can still happen when a contact's only resumes are legacy embedded entries, or, for a contact linked to a CazVid user, when its resumes have not finished parsing (see Resume Ingestion in the guide). Never read `null` as `no resumes`: check `resumeCount`.","type":"array","items":{"$ref":"#/components/schemas/PublicContactResumeDto"}},"cazvidUserId":{"type":"string","nullable":true,"description":"Linked CazVid platform user id, when the contact is a CazVid user."},"linkedCazvidUser":{"type":"boolean","description":"Whether the contact is linked to a CazVid platform user."},"createdAt":{"type":"string","nullable":true,"description":"ISO 8601 timestamp the contact was created."},"updatedAt":{"type":"string","nullable":true,"description":"ISO 8601 timestamp the contact was last updated."}},"required":["id","jobTitle","emails","phones","tags","hasResume","resumeCount","resumes","linkedCazvidUser"]},"UpdatePublicContactDto":{"type":"object","properties":{"firstName":{"type":"string","nullable":true,"example":"Ada"},"lastName":{"type":"string","nullable":true,"example":"Lovelace"},"jobTitle":{"example":["Software Engineer"],"description":"Job titles. Index 0 is treated as the primary title.","type":"array","items":{"type":"string"}},"companyName":{"type":"string","nullable":true,"example":"Analytical Engines"},"linkedinUrl":{"type":"string","nullable":true,"example":"https://www.linkedin.com/in/adalovelace"},"locationName":{"type":"string","nullable":true,"example":"London, UK"},"locationGeoNameId":{"type":"number","nullable":true,"example":2643743,"description":"GeoNames id for the contact's location, when known."},"emails":{"description":"Replaces the contact's PRIMARY email (index 0). Update accepts exactly one entry; additional emails can only be added through create's de-dup merge.","maxItems":1,"type":"array","items":{"$ref":"#/components/schemas/PublicContactEmailInputDto"}},"phones":{"description":"Replaces the contact's PRIMARY phone (index 0). Update accepts exactly one entry.","maxItems":1,"type":"array","items":{"$ref":"#/components/schemas/PublicContactPhoneInputDto"}},"tags":{"example":["priority","referral"],"description":"Tag names. Existing tags are matched by name; unknown names are created in the workspace. Send names, not ids.","type":"array","items":{"type":"string"}},"name":{"type":"string","example":"Ada Lovelace","maxLength":100,"description":"Display name of the contact."}}},"PublicContactDeletedResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Id of the deleted contact."},"deleted":{"type":"boolean","description":"Always true. The contact is SOFT-deleted (marked deleted, retained), and its interactions and list memberships are soft-deleted too.","example":true}},"required":["id","deleted"]},"UploadContactResumeDto":{"type":"object","properties":{"fileName":{"type":"string","description":"Original file name, used for the stored resume's display name and to derive the file extension. Path components are stripped server-side.","example":"ada-lovelace-resume.pdf","maxLength":255},"mimeType":{"type":"string","description":"Declared MIME type. Strictly one of PDF, DOC or DOCX - the file's real type is verified against this from its magic bytes after decode (MIME_MISMATCH otherwise).","enum":["application/pdf","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/msword"],"example":"application/pdf"},"contentBase64":{"type":"string","description":"The resume file, standard base64-encoded (no data: URI prefix). Must decode to at most 10MB; oversized or non-base64 payloads are rejected before any S3 work. Supply EITHER contentBase64 OR sourceUrl, never both. Prefer sourceUrl for anything but a tiny file: base64 inflates the payload by a third, and an AI agent cannot reliably emit a real resume inline (an 81KB PDF is roughly 108,000 characters).","example":"JVBERi0xLjQKJcfs..."},"sourceUrl":{"type":"string","description":"HTTPS URL the server fetches the resume from, as an alternative to contentBase64. Supply EITHER contentBase64 OR sourceUrl, never both. The URL must be https, must not embed credentials, must resolve to a public address, must return 200 with the file body (redirects are NOT followed), and must deliver at most 10MB. The fetched bytes go through exactly the same magic-byte check against mimeType as an inline upload, so a URL is only a transport and never a reason to trust the payload.","example":"https://files.example.com/resumes/ada-lovelace.pdf","maxLength":2048}},"required":["fileName","mimeType"]},"ContactResumeUploadResponseDto":{"type":"object","properties":{"resumeId":{"type":"string","description":"Id of the created (or deduplicated) canonical resume.","example":"6a3ff55406ae2ed06ddb5fda"},"parseStatus":{"type":"string","description":"Always 'queued'. Parsing runs ASYNChronously: the resume is uploaded and a parse job is enqueued. Parsed fields (name, email, phone, location, job title, skills, LinkedIn) are written onto the contact later, filling only its EMPTY fields. Poll GET /public/contacts/{id} - hasResume/resumeCount reflect the attached resume once processing completes, and its `resumes` array reports the real per-resume outcome (`parseStatus` plus a `lastError` reason), so a file that will never parse is diagnosable rather than merely counted. DEDUPE CAVEAT - contact enrichment is best-effort on FIRST upload only: uploading bytes this API key's owner has already uploaded reuses the existing resume (the SAME resumeId comes back) and attaches it to this contact, but enqueues NO new parse job, and the enrichment step runs only off that job. The parsed fields are therefore NOT written onto this contact, even though the attachment looks healthy - `resumes` shows `parseStatus: done` and `resumeCount` counts it. No field on this response distinguishes a reused resume from a freshly queued one, so read the contact back and confirm the fields you needed rather than inferring enrichment from this 202.","enum":["queued"],"example":"queued"},"contactId":{"type":"string","description":"Id of the contact the resume was attached to.","example":"6a3ff55406ae2ed06ddb5fd8"}},"required":["resumeId","parseStatus","contactId"]},"CrmUsageDto":{"type":"object","properties":{"dailyQuota":{"type":"number","description":"Maximum quota-consuming CRM API calls allowed per organization per UTC day.","example":500},"used":{"type":"number","description":"Quota-consuming CRM calls made with this organization's API keys today (UTC).","example":12},"remaining":{"type":"number","description":"Calls still available today: max(0, dailyQuota - used).","example":488},"resetsAt":{"type":"string","description":"ISO-8601 timestamp of the next UTC midnight, when used resets to 0.","example":"2026-07-15T00:00:00.000Z"},"period":{"type":"string","description":"Quota window. Always utc_day in v1.","enum":["utc_day"],"example":"utc_day"},"resumeUpload":{"description":"The presented key's SEPARATE per-key daily resume-upload quota. A resume upload consumes one general daily call (above) AND one upload slot here.","allOf":[{"$ref":"#/components/schemas/CrmResumeUploadUsageDto"}]}},"required":["dailyQuota","used","remaining","resetsAt","period","resumeUpload"]},"CreatePublicInteractionDto":{"type":"object","properties":{"type":{"type":"string","description":"Interaction type NAME (case-insensitive) - the granular kind of the interaction, e.g. \"email\", \"phonecall\", \"videointerview\". Resolved against the fixed global taxonomy; GET /public/interaction-types lists every valid name. An unknown name is rejected 400 UNKNOWN_INTERACTION_TYPE with the valid names in details.","example":"email","maxLength":100},"notes":{"type":"string","nullable":true,"maxLength":5000,"description":"Free-text note for the interaction (max 5000 characters).","example":"Left a voicemail about the Senior Backend role; will follow up Friday."},"date":{"type":"string","description":"ISO 8601 timestamp of when the interaction happened. Defaults to now when omitted. Future dates are allowed (e.g. a scheduled follow-up).","example":"2026-07-14T15:30:00.000Z"},"tags":{"example":["priority","follow-up"],"description":"Tag names. Existing tags are matched by name in the workspace; unknown names are created. Send names, not ids.","type":"array","items":{"type":"string"}}},"required":["type"]},"PublicInteractionDto":{"type":"object","properties":{"id":{"type":"string","description":"Interaction id."},"contactId":{"type":"string","description":"Id of the contact this interaction belongs to."},"type":{"type":"string","nullable":true,"description":"Resolved interaction type name (e.g. \"email\"). Null on a legacy interaction that carries no type."},"notes":{"type":"string","nullable":true,"description":"Free-text note, returned as PLAIN TEXT. Storage is always verbatim; the conversion happens on read. A note whose text BEGINS with an HTML block tag (`<p>`, `<div>`, `<h1>`-`<h6>`, `<ul>`, `<ol>`, `<li>`, `<figure>`, `<blockquote>`, `<table>`, `<pre>`, `<script>`, `<style>`) - which is what the CazVid app rich-text editor writes - is converted: block boundaries and `<br>` become newlines, markup is removed, `<script>` and `<style>` elements are dropped with their content, and HTML entities are decoded. Any other note is returned byte-for-byte as stored, so plain text you POST round-trips exactly; if you POST HTML it is stored as HTML and echoed back converted. The result is TEXT, not HTML: it can contain `<`, `>` and `&` that a user typed, so escape it before rendering it as HTML."},"date":{"type":"string","nullable":true,"description":"ISO 8601 timestamp of when the interaction happened."},"tags":{"description":"Tag names associated with the interaction.","type":"array","items":{"type":"string"}},"createdAt":{"type":"string","nullable":true,"description":"ISO 8601 timestamp the interaction was logged."}},"required":["id","contactId","tags"]},"PublicInteractionListResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicInteractionTimelineItemDto"}},"page":{"type":"number","description":"Echoed 1-based page number."},"pageSize":{"type":"number","description":"Echoed page size."},"hasMore":{"type":"boolean","description":"Whether another page exists. This API paginates on hasMore - no total count is returned."}},"required":["items","page","pageSize","hasMore"]},"PublicInteractionTypesResponseDto":{"type":"object","properties":{"categories":{"description":"The fixed global interaction taxonomy, grouped by category. Any name in any category is a valid `type` when logging an interaction.","type":"array","items":{"$ref":"#/components/schemas/PublicInteractionTypeCategoryDto"}},"types":{"description":"Flat list of every valid interaction type name (the union of all categories' types).","type":"array","items":{"type":"string"}}},"required":["categories","types"]},"PublicListListResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicListDto"}},"page":{"type":"number","description":"Echoed 1-based page number."},"pageSize":{"type":"number","description":"Echoed page size."},"hasMore":{"type":"boolean","description":"Whether another page exists. This API paginates on hasMore - no total count is returned."}},"required":["items","page","pageSize","hasMore"]},"CreatePublicListDto":{"type":"object","properties":{"name":{"type":"string","maxLength":100,"description":"List name. Words of letters and numbers separated by single spaces (the same rule the CazVid app enforces). Letters in any script are accepted, so an accented name such as Selección de personal is valid.","example":"Warehouse Hiring 2026"},"color":{"type":"string","maxLength":20,"description":"List color: a hex code (with or without the leading #) or a plain alphanumeric color token.","example":"#0EA5E9"},"stages":{"description":"Custom stages to seed the list with, in board order (first entry becomes the landing stage new candidates enter). Omit to get the default candidate-pipeline template. Mutually exclusive with copyFromListId. Max 30 stages.","type":"array","items":{"$ref":"#/components/schemas/CreatePublicListStageInputDto"}},"copyFromListId":{"type":"string","description":"Copy the stage skeleton (names, colors, order - never the cards) from an existing list. Mutually exclusive with stages. The source list must live in the workspace this request resolves to; a list in another workspace is 404 NOT_FOUND, indistinguishable from a missing id. LIMITATION: it must also have been created by the SAME app user your API key is issued for; a list another member of your workspace created is reported as 404 NOT_FOUND even though it exists in your workspace. A source list with zero active stages is likewise 404.","example":"6a3ff55406ae2ed06ddb5fe0"}},"required":["name"]},"PublicListDto":{"type":"object","properties":{"id":{"type":"string","description":"List id."},"name":{"type":"string","nullable":true,"description":"List name."},"color":{"type":"string","nullable":true,"description":"List color, exactly as stored."},"templateType":{"type":"string","nullable":true,"description":"Which stage template the list was seeded from. Lists created through this API are always stamped \"CANDIDATE_PIPELINE_EN\"; lists auto-created for a job posting carry a JOBSEARCH_PIPELINE_* value. Informational only - it does not change after creation and it does not describe the list's CURRENT stages once they are edited."},"associatedPosts":{"description":"Ids of the job postings linked to this list. Empty for a list created through this API (posting links are made by the CazVid app, not by this API).","type":"array","items":{"type":"string"}},"itemCount":{"type":"number","description":"Number of active cards (list items) on the list, across every stage."},"createdAt":{"type":"string","nullable":true,"description":"ISO 8601 timestamp the list was created."}},"required":["id","associatedPosts","itemCount"]},"PublicListDetailDto":{"type":"object","properties":{"id":{"type":"string","description":"List id."},"name":{"type":"string","nullable":true,"description":"List name."},"color":{"type":"string","nullable":true,"description":"List color, exactly as stored."},"templateType":{"type":"string","nullable":true,"description":"Which stage template the list was seeded from. Lists created through this API are always stamped \"CANDIDATE_PIPELINE_EN\"; lists auto-created for a job posting carry a JOBSEARCH_PIPELINE_* value. Informational only - it does not change after creation and it does not describe the list's CURRENT stages once they are edited."},"associatedPosts":{"description":"Ids of the job postings linked to this list. Empty for a list created through this API (posting links are made by the CazVid app, not by this API).","type":"array","items":{"type":"string"}},"itemCount":{"type":"number","description":"Number of active cards (list items) on the list, across every stage."},"createdAt":{"type":"string","nullable":true,"description":"ISO 8601 timestamp the list was created."},"updatedAt":{"type":"string","nullable":true,"description":"ISO 8601 timestamp the list last changed."},"stages":{"description":"The board's stages in board order (zorder ascending).","type":"array","items":{"$ref":"#/components/schemas/PublicListStageWithItemsDto"}},"page":{"type":"number","description":"Echoed 1-based page number."},"pageSize":{"type":"number","description":"Echoed items-per-stage page size."},"hasMore":{"type":"boolean","description":"True when ANY stage has more cards beyond this page. The same page window is applied to every stage."}},"required":["id","associatedPosts","itemCount","stages","page","pageSize","hasMore"]},"UpdatePublicListDto":{"type":"object","properties":{"name":{"type":"string","maxLength":100,"description":"New list name. Words of letters and numbers separated by single spaces, in any script. Omit the field to leave the name unchanged; sending null is rejected.","example":"Warehouse Hiring Q3"},"color":{"type":"string","maxLength":20,"description":"New list color: a hex code (with or without the leading #) or a plain alphanumeric color token. Omit the field to leave the color unchanged; sending null is rejected.","example":"#0EA5E9"}}},"PublicListDeletedResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Id of the deleted list."},"deleted":{"type":"boolean","description":"Always true.","example":true},"effects":{"description":"What the soft-delete cascade removed.","allOf":[{"$ref":"#/components/schemas/PublicListDeleteEffectsDto"}]}},"required":["id","deleted","effects"]},"CreatePublicListStageDto":{"type":"object","properties":{"name":{"type":"string","maxLength":60,"description":"Stage name (max 60 characters). The new stage is appended at the END of the board.","example":"Offer Sent"},"color":{"type":"string","maxLength":20,"description":"Stage color: a hex code (with or without the leading #) or a plain alphanumeric color token.","example":"#16A34A"}},"required":["name"]},"PublicListStageDto":{"type":"object","properties":{"id":{"type":"string","description":"Stage id."},"name":{"type":"string","nullable":true,"description":"Stage name as shown on the board."},"color":{"type":"string","nullable":true,"description":"Stage color, exactly as stored. Null when never set."},"zorder":{"type":"number","nullable":true,"description":"Board position, 1-based and contiguous over the list's active stages. Every stage mutation renumbers the whole list, so zorder 1 is always the landing stage new candidates enter."}},"required":["id"]},"UpdatePublicListStageDto":{"type":"object","properties":{"name":{"type":"string","maxLength":60,"description":"New stage name (max 60 characters). Omit the field to leave the name unchanged; sending null is rejected.","example":"Phone Screen"},"color":{"type":"string","maxLength":20,"description":"New stage color: a hex code (with or without the leading #) or a plain alphanumeric color token. Omit the field to leave the color unchanged; sending null is rejected.","example":"#F59E0B"}}},"PublicListStageDeletedResponseDto":{"type":"object","properties":{"stagesRemaining":{"description":"The list's remaining active stages after the delete, renumbered contiguously and in board order.","type":"array","items":{"$ref":"#/components/schemas/PublicListStageDto"}}},"required":["stagesRemaining"]},"ReorderListStagesDto":{"type":"object","properties":{"stageIds":{"description":"The list's stage ids in the desired board order. Must be EXACTLY the list's current active stage ids - no missing, extra, foreign, or repeated id. Stage order (zorder) is renumbered contiguously from 1 in the order you send.","example":["6a3ff55406ae2ed06ddb5fe1","6a3ff55406ae2ed06ddb5fe2","6a3ff55406ae2ed06ddb5fe3"],"type":"array","items":{"type":"string"}}},"required":["stageIds"]},"AddPublicListItemDto":{"type":"object","properties":{"contactId":{"type":"string","description":"Id of an EXISTING CRM contact in this workspace (create it first with POST /public/contacts). A contact in another workspace, or one that does not exist, is 404 NOT_FOUND.","example":"6a3ff55306ae2ed06ddb5fd8"},"stageId":{"type":"string","description":"Stage to place the card in. Omit to use the list's first stage. Must be an active stage of THIS list (404 NOT_FOUND otherwise). Adding a contact that is already on the list does not duplicate the card - it MOVES the existing card when a different stageId is supplied.","example":"6a3ff55406ae2ed06ddb5fe2"}},"required":["contactId"]},"PublicListItemDto":{"type":"object","properties":{"id":{"type":"string","description":"List item (card) id."},"listId":{"type":"string","description":"Id of the list this card belongs to."},"stageId":{"type":"string","nullable":true,"description":"Id of the stage the card currently sits in."},"contactId":{"type":"string","nullable":true,"description":"Id of the CRM contact behind this card. Pair it with GET /public/contacts/{id} to read the person's name, email, and phone - this object deliberately carries no personal data."},"authorId":{"type":"string","nullable":true,"description":"Id of the CazVid platform user behind this card, set when the applicant is a CazVid user; may lack a Contact record, in which case contactId is null."},"rating":{"type":"string","nullable":true,"enum":["good_fit","maybe","not_a_fit"],"description":"Recruiter rating on the card, when one was set."},"applicationStatus":{"type":"string","nullable":true,"enum":["invited","in_progress","completed","disqualified"],"description":"Application status of the card, when one was set."},"disqualified":{"type":"boolean","description":"Whether the card was disqualified."},"disqualifiedReason":{"type":"string","nullable":true,"description":"Free-text reason recorded when the card was disqualified."},"source":{"type":"string","nullable":true,"description":"How the card got onto the list (e.g. \"contact\" for an API/CRM add, or an application source for a job applicant)."},"createdAt":{"type":"string","nullable":true,"description":"ISO 8601 timestamp the card was added to the list."},"updatedAt":{"type":"string","nullable":true,"description":"ISO 8601 timestamp the card last changed."}},"required":["id","listId","disqualified"]},"UpdatePublicListItemDto":{"type":"object","properties":{"stageId":{"type":"string","description":"Stage to move the card to. Must be an active stage of THIS list (404 NOT_FOUND otherwise).","example":"6a3ff55406ae2ed06ddb5fe3"}},"required":["stageId"]},"PublicListItemDeletedResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Id of the removed card."},"deleted":{"type":"boolean","description":"Always true.","example":true}},"required":["id","deleted"]},"CreatePublicTaskDto":{"type":"object","properties":{"taskType":{"type":"string","enum":["proposal","followup","todo"],"description":"What kind of work this is. proposal = a drafted outreach message awaiting approval; followup = a scheduled nudge on an existing thread; todo = anything else.","example":"proposal"},"dueAt":{"type":"string","description":"When the task is due, ISO 8601. Drives the queue sort and the dueBefore filter.","example":"2026-08-12T09:00:00.000Z"},"draft":{"type":"string","maxLength":10000,"description":"The proposed message body. Editable while the task is non-terminal (approve-with-edits); frozen once it is."},"notes":{"type":"string","maxLength":5000,"description":"Free-text context for the task (max 5000 characters)."},"threadRef":{"description":"Ids of the outreach thread this task belongs to. Set at create and immutable afterwards - the thread a task hangs off cannot change.","allOf":[{"$ref":"#/components/schemas/PublicTaskThreadRefDto"}]},"classification":{"type":"string","maxLength":100,"description":"Your own label for the thread state that produced this task."},"lang":{"type":"string","maxLength":10,"description":"Language tag for the draft, e.g. \"en\" or \"es\"."},"observedLastMessageAt":{"type":"string","description":"ISO 8601 timestamp of the last message observed on the thread when this task was drafted - the freshness anchor a sender re-checks before acting."},"dedupeKey":{"type":"string","maxLength":200,"description":"Idempotency token, unique per workspace across live tasks. Retrying a create with the same key returns 409 TASK_DUPLICATE carrying the existing task's id instead of writing a second task. Omit it and every call creates a new task.","example":"heyreach-4711|followup"},"title":{"type":"string","maxLength":200,"example":"Llamar a Ana sobre la propuesta","description":"The activity headline - the one line a person reads in the queue and in the contact's month history. Plain text: any markup is stripped server-side before it is stored."},"assigneeId":{"type":"string","example":"6a3ff55406ae2ed06ddb5fda","description":"Workspace member this activity is for. Defaults to the API key's owning user when omitted. Empty string creates it explicitly UNASSIGNED. Validated on write: a non-empty value must be a member of this workspace (400 INVALID_ASSIGNEE otherwise)."},"interactionTypeId":{"type":"string","example":"66d9eeca1d74a5d57ec5b276","description":"The activity TYPE, from the fixed global interaction taxonomy. Send either a type id or its parent category id - the server resolves and stores both, exactly as a logged interaction does. GET /api/v3.0/crm/public/interaction-types lists the taxonomy. An id outside it is 400 UNKNOWN_INTERACTION_TYPE."},"date":{"type":"string","example":"2026-08-10T16:30:00.000Z","description":"When the activity HAPPENED, ISO 8601 - the timestamp a contact's history groups by. Send it with done:true to log something that already took place; for future work send dueAt instead."},"done":{"type":"boolean","example":false,"description":"Create the activity ALREADY FINISHED - one call to log something that already happened. The task is written with status done, stamped with date (defaulting to now), and its trail carries both a created and a status_changed entry as if it had been completed immediately. This is the ONLY way a create can produce a non-open task: `status` itself is never caller-supplied, so a client still cannot mint an approved or sent one. Note the task is TERMINAL on arrival, so it is frozen to further edits."}},"required":["taskType"]},"PublicTaskDto":{"type":"object","properties":{"id":{"type":"string","description":"Task id."},"contactId":{"type":"string","nullable":true,"description":"Id of the contact this task belongs to. NULL when the activity is anchored to something other than a contact - read anchor for what it is about."},"anchor":{"description":"What this activity is about. Always present.","allOf":[{"$ref":"#/components/schemas/PublicTaskAnchorResponseDto"}]},"kind":{"type":"string","description":"Always \"task\" on this family. Facts are served by the interactions routes."},"taskType":{"type":"string","enum":["proposal","followup","todo"]},"status":{"type":"string","enum":["open","approved","sent","done","rejected","expired"]},"dueAt":{"type":"string","nullable":true},"title":{"type":"string","nullable":true,"description":"The activity headline, plain text. Null on a task created before the activity model shipped."},"interactionTypeId":{"type":"string","nullable":true,"description":"Parent CATEGORY id of the activity type, from the global interaction taxonomy."},"communicationTypeId":{"type":"string","nullable":true,"description":"The specific TYPE id within that category (llamada, correo, reunion...). Null when the activity was typed only at the category level, or not typed at all."},"date":{"type":"string","nullable":true,"description":"When the activity HAPPENED - set on an activity that was logged as already finished, and the timestamp a contact's history groups by. Null on a task that is still work to do (read dueAt for that)."},"draft":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true,"description":"Free-text context, returned as PLAIN TEXT on the same terms as an interaction's notes (HTML written by the CazVid editor is converted on read; text you POST round-trips exactly)."},"classification":{"type":"string","nullable":true},"lang":{"type":"string","nullable":true},"observedLastMessageAt":{"type":"string","nullable":true},"dedupeKey":{"type":"string","nullable":true},"threadRef":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/PublicTaskThreadRefResponseDto"}]},"claimedBy":{"type":"string","nullable":true},"claimedAt":{"type":"string","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true},"events":{"description":"Lifecycle trail, oldest first, capped at the 50 most recent entries. Server-written only.","type":"array","items":{"$ref":"#/components/schemas/PublicTaskEventDto"}}},"required":["id","anchor","kind","taskType","status","events"]},"PublicTaskListResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicTaskListItemDto"}},"page":{"type":"number","description":"Echoed 1-based page number."},"pageSize":{"type":"number","description":"Echoed page size."},"hasMore":{"type":"boolean","description":"Whether another page exists. This API paginates on hasMore - no total count is returned."}},"required":["items","page","pageSize","hasMore"]},"CreatePublicTaskAtAnchorDto":{"type":"object","properties":{"taskType":{"type":"string","enum":["proposal","followup","todo"],"description":"What kind of work this is. proposal = a drafted outreach message awaiting approval; followup = a scheduled nudge on an existing thread; todo = anything else.","example":"proposal"},"dueAt":{"type":"string","description":"When the task is due, ISO 8601. Drives the queue sort and the dueBefore filter.","example":"2026-08-12T09:00:00.000Z"},"draft":{"type":"string","maxLength":10000,"description":"The proposed message body. Editable while the task is non-terminal (approve-with-edits); frozen once it is."},"notes":{"type":"string","maxLength":5000,"description":"Free-text context for the task (max 5000 characters)."},"threadRef":{"description":"Ids of the outreach thread this task belongs to. Set at create and immutable afterwards - the thread a task hangs off cannot change.","allOf":[{"$ref":"#/components/schemas/PublicTaskThreadRefDto"}]},"classification":{"type":"string","maxLength":100,"description":"Your own label for the thread state that produced this task."},"lang":{"type":"string","maxLength":10,"description":"Language tag for the draft, e.g. \"en\" or \"es\"."},"observedLastMessageAt":{"type":"string","description":"ISO 8601 timestamp of the last message observed on the thread when this task was drafted - the freshness anchor a sender re-checks before acting."},"dedupeKey":{"type":"string","maxLength":200,"description":"Idempotency token, unique per workspace across live tasks. Retrying a create with the same key returns 409 TASK_DUPLICATE carrying the existing task's id instead of writing a second task. Omit it and every call creates a new task.","example":"heyreach-4711|followup"},"title":{"type":"string","maxLength":200,"example":"Llamar a Ana sobre la propuesta","description":"The activity headline - the one line a person reads in the queue and in the contact's month history. Plain text: any markup is stripped server-side before it is stored."},"assigneeId":{"type":"string","example":"6a3ff55406ae2ed06ddb5fda","description":"Workspace member this activity is for. Defaults to the API key's owning user when omitted. Empty string creates it explicitly UNASSIGNED. Validated on write: a non-empty value must be a member of this workspace (400 INVALID_ASSIGNEE otherwise)."},"interactionTypeId":{"type":"string","example":"66d9eeca1d74a5d57ec5b276","description":"The activity TYPE, from the fixed global interaction taxonomy. Send either a type id or its parent category id - the server resolves and stores both, exactly as a logged interaction does. GET /api/v3.0/crm/public/interaction-types lists the taxonomy. An id outside it is 400 UNKNOWN_INTERACTION_TYPE."},"date":{"type":"string","example":"2026-08-10T16:30:00.000Z","description":"When the activity HAPPENED, ISO 8601 - the timestamp a contact's history groups by. Send it with done:true to log something that already took place; for future work send dueAt instead."},"done":{"type":"boolean","example":false,"description":"Create the activity ALREADY FINISHED - one call to log something that already happened. The task is written with status done, stamped with date (defaulting to now), and its trail carries both a created and a status_changed entry as if it had been completed immediately. This is the ONLY way a create can produce a non-open task: `status` itself is never caller-supplied, so a client still cannot mint an approved or sent one. Note the task is TERMINAL on arrival, so it is frozen to further edits."},"anchor":{"description":"What this activity is about. Send {type:'contact', id} for the same result as POST /contacts/{id}/tasks.","allOf":[{"$ref":"#/components/schemas/PublicTaskAnchorDto"}]},"jobPostId":{"type":"string","example":"6a3ff55406ae2ed06ddb5fda","description":"Optional job post this activity relates to, on a contact- or candidate-anchored activity (\"this call is about the warehouse role\"). Do NOT send it on a job anchor - there the job IS the anchor - or on an application anchor, where it is derived from the application's own job. Resolved in your workspace like any other id."}},"required":["taskType","anchor"]},"UpdatePublicTaskDto":{"type":"object","properties":{"status":{"type":"string","enum":["open","approved","sent","done","rejected","expired"],"description":"Move the task to this status. Legal moves: open -> done | approved | rejected | expired; approved -> sent | done | rejected | open; done -> open. open -> done is the direct human completion (a followup or todo has nothing to approve); the APPROVAL lane is unchanged, so a proposal still passes through approved before anything can send it. sent means a sending system TRANSMITTED the message; done means a person FINISHED the work - they are different endings, never interchangeable. sent, done, rejected and expired are TERMINAL - a task in any of them is frozen and a PATCH returns 409 TASK_TERMINAL. The ONE way back out is a REOPEN: {\"status\":\"open\"} on a done task, with no other field in the body, moves it to open with its history intact; a body carrying any other field is a field edit on a frozen task and still 409, so reopen first and then edit. Reopening also RELEASES any claim the task carries - it returns to the queue as unclaimed work, free for any worker to claim. An illegal move returns 409 INVALID_STATUS_TRANSITION listing what is allowed from here."},"taskType":{"type":"string","enum":["proposal","followup","todo"]},"dueAt":{"type":"string","nullable":true,"description":"When the task is due, ISO 8601. Send null to CLEAR the due date (an empty string is rejected as a malformed date)."},"draft":{"type":"string","maxLength":10000},"notes":{"type":"string","maxLength":5000},"classification":{"type":"string","maxLength":100},"lang":{"type":"string","maxLength":10},"observedLastMessageAt":{"type":"string","nullable":true,"description":"ISO 8601 timestamp of the last message observed on the thread. Send null to CLEAR it (an empty string is rejected as a malformed date)."},"claimedBy":{"type":"string","maxLength":200,"description":"Who is working this task. Send a value to CLAIM it (the server stamps claimedAt and appends a claimed event); send an empty string to RELEASE it. Claiming is a compare-and-set: if a different worker already holds the task the call is 409 TASK_CLAIMED naming the holder, so two agents polling the same queue can never both believe they own it. Re-claiming with the value you already hold is a no-op, and a release is always allowed so a stale claim can be dropped."},"title":{"type":"string","maxLength":200,"description":"The activity headline. Plain text: markup is stripped before it is stored."},"assigneeId":{"type":"string","description":"Reassign the activity to another workspace member. Send an empty string to clear the assignee - the activity returns to unassigned. Validated on write: a non-empty value must be a member of this workspace (400 INVALID_ASSIGNEE otherwise)."},"interactionTypeId":{"type":"string","description":"Change the activity TYPE. Send a type id or its parent category id; the server resolves and stores both and records the change in revisions[]."},"jobPostId":{"type":"string","nullable":true,"description":"Change the job post this activity relates to. Send null to CLEAR the reference. EDITABLE ONLY on a contact-, candidate- or none-anchored activity: on a job-anchored one the job IS the anchor and on an application-anchored one it is derived from the application, so both return 409 ANCHOR_IMMUTABLE. A non-null value must name a job post in your workspace (404 NOT_FOUND otherwise)."}}},"PublicTaskDeletedResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Id of the deleted task."},"deleted":{"type":"boolean","description":"Always true. SOFT delete: the task is marked deleted and retained, and its dedupeKey is released for reuse. Only task-kind rows can be deleted here - a fact is 403 NOT_A_TASK, forever.","example":true}},"required":["id","deleted"]},"PublicContactEmailInputDto":{"type":"object","properties":{"email":{"type":"string","description":"Email address.","example":"ada@example.com"},"type":{"type":"string","enum":["work","personal","other"],"default":"work","description":"Email label. Defaults to work when omitted."}},"required":["email"]},"PublicContactPhoneInputDto":{"type":"object","properties":{"phone":{"type":"string","description":"Phone number.","example":"+15551234567"},"type":{"type":"string","enum":["mobile","desk","home","other"],"default":"mobile","description":"Phone label. Defaults to mobile when omitted."}},"required":["phone"]},"PublicContactEmailDto":{"type":"object","properties":{"email":{"type":"string","description":"Email address."},"type":{"type":"string","nullable":true,"description":"Email label: work | personal | other."}},"required":["email"]},"PublicContactPhoneDto":{"type":"object","properties":{"phone":{"type":"string","description":"Phone number."},"type":{"type":"string","nullable":true,"description":"Phone label: mobile | desk | home | other."}},"required":["phone"]},"PublicContactTagDto":{"type":"object","properties":{"id":{"type":"string","description":"Tag id."},"name":{"type":"string","nullable":true,"description":"Tag display name."}},"required":["id"]},"PublicContactResumeDto":{"type":"object","properties":{"id":{"type":"string","description":"Resume id."},"fileName":{"type":"string","nullable":true,"description":"Stored file name, including its extension.","example":"ada-lovelace-resume.pdf"},"mimeType":{"type":"string","nullable":true,"description":"MIME type the file was stored as: application/pdf, application/vnd.openxmlformats-officedocument.wordprocessingml.document, or application/msword.","example":"application/pdf"},"fileSize":{"type":"number","nullable":true,"description":"File size in bytes.","example":148213},"parseStatus":{"type":"string","nullable":true,"description":"Text-extraction state: pending | processing | done | failed | retry_scheduled | permanently_failed. `permanently_failed` means the file will never parse (corrupt, or a legacy binary .doc) and no retry is scheduled - re-upload a valid PDF or DOCX. `failed` is transient and may be retried automatically. Read this as an OPEN string, not a closed enum: CazVid content moderation writes a further `rejected` state onto a resume a human moderator has removed. A removed resume is soft-deleted in the same write, and every read behind this API filters soft-deleted resumes, so `rejected` is not expected to appear here - but a client that switches on the six values above should fall through gracefully rather than throw if it ever does.","example":"done"},"overviewStatus":{"type":"string","nullable":true,"description":"AI career-overview state, drawn from the same six-value set as parseStatus (the moderation-only `rejected` state is never written here). Overview runs after parsing, so it stays pending (or goes permanently_failed) when the parse never succeeded.","example":"done"},"lastError":{"type":"string","nullable":true,"description":"Short human-readable reason for the most recent parse or overview failure, prefixed with the phase that produced it (for example: overview: skipped - unsupported file type). Diagnostic prose for a human or an agent to read: it is truncated, has URLs and host addresses stripped, and is NOT a stable enum - do not branch logic on its text. Null when nothing has failed.","example":"parse: Unsupported file type: .doc. Only PDF and DOCX are supported."},"retryAttempt":{"type":"number","nullable":true,"description":"How many times ingestion has been retried. 0 on a first pass.","example":0},"nextRetryAt":{"type":"string","nullable":true,"description":"ISO 8601 timestamp of the next scheduled retry, when parseStatus is retry_scheduled. Null otherwise."},"uploadedAt":{"type":"string","nullable":true,"description":"ISO 8601 timestamp the resume was ingested. For a resume migrated from the older store this is the original upload time, not the migration date."},"source":{"type":"string","nullable":true,"description":"How the resume reached this contact: workspace_contact (uploaded against the contact, which is what this API's upload route creates), user_library (the linked CazVid candidate's own library), or application (attached to an application the candidate submitted).","example":"workspace_contact"},"isPrimary":{"type":"boolean","description":"True for the entry at index 0, the resume CazVid treats as the contact's primary one. For a contact you created through this API that is the most recently ingested resume; for a contact linked to a CazVid user it is the resume attached to their application when there is one, otherwise the first from their library. Exactly one entry is primary when the array is non-empty."}},"required":["id","isPrimary"]},"CrmResumeUploadUsageDto":{"type":"object","properties":{"dailyLimit":{"type":"number","description":"Maximum resume uploads allowed per API key per UTC day. Separate from (and consumed IN ADDITION to) the general dailyQuota.","example":50},"used":{"type":"number","description":"Resume uploads made with THIS API key today (UTC). Per-key, not per-organization.","example":3},"remaining":{"type":"number","description":"Uploads still available today: max(0, dailyLimit - used).","example":47}},"required":["dailyLimit","used","remaining"]},"PublicInteractionTimelineItemDto":{"type":"object","properties":{"id":{"type":"string","description":"Interaction id."},"type":{"type":"string","nullable":true,"description":"Resolved interaction type name (e.g. \"email\")."},"notes":{"type":"string","nullable":true,"description":"Free-text note, returned as PLAIN TEXT. Storage is always verbatim; the conversion happens on read. A note whose text BEGINS with an HTML block tag (`<p>`, `<div>`, `<h1>`-`<h6>`, `<ul>`, `<ol>`, `<li>`, `<figure>`, `<blockquote>`, `<table>`, `<pre>`, `<script>`, `<style>`) - which is what the CazVid app rich-text editor writes - is converted: block boundaries and `<br>` become newlines, markup is removed, `<script>` and `<style>` elements are dropped with their content, and HTML entities are decoded. Any other note is returned byte-for-byte as stored, so plain text you POST round-trips exactly; if you POST HTML it is stored as HTML and echoed back converted. The result is TEXT, not HTML: it can contain `<`, `>` and `&` that a user typed, so escape it before rendering it as HTML."},"date":{"type":"string","nullable":true,"description":"ISO 8601 timestamp of when the interaction happened."},"tags":{"description":"Tag names.","type":"array","items":{"type":"string"}},"createdAt":{"type":"string","nullable":true,"description":"ISO 8601 timestamp the interaction was logged."},"kind":{"type":"string","enum":["fact","task"],"description":"What this row is: \"fact\" (immutable evidence - the default the timeline returns) or \"task\" (a work item, only present when you pass ?kind=task or ?kind=all). Every row written before the work model shipped is a fact."}},"required":["id","tags","kind"]},"PublicInteractionTypeCategoryDto":{"type":"object","properties":{"category":{"type":"string","description":"Category name (e.g. \"communication\", \"interviews\")."},"types":{"description":"Valid interaction type names within this category.","type":"array","items":{"type":"string"}}},"required":["category","types"]},"CreatePublicListStageInputDto":{"type":"object","properties":{"name":{"type":"string","maxLength":60,"description":"Stage name as it appears on the board (max 60 characters). Any printable text is accepted, including slashes and accented characters.","example":"Phone Screen"},"color":{"type":"string","maxLength":20,"description":"Stage color: a hex code (with or without the leading #) or a plain alphanumeric color token. Defaults to null.","example":"#4F46E5"}},"required":["name"]},"PublicListStageWithItemsDto":{"type":"object","properties":{"id":{"type":"string","description":"Stage id."},"name":{"type":"string","nullable":true,"description":"Stage name as shown on the board."},"color":{"type":"string","nullable":true,"description":"Stage color, exactly as stored. Null when never set."},"zorder":{"type":"number","nullable":true,"description":"Board position, 1-based and contiguous over the list's active stages. Every stage mutation renumbers the whole list, so zorder 1 is always the landing stage new candidates enter."},"itemCount":{"type":"number","description":"Total active cards in this stage (not just this page)."},"items":{"description":"This page of the stage's cards.","type":"array","items":{"$ref":"#/components/schemas/PublicListItemDto"}}},"required":["id","itemCount","items"]},"PublicListDeleteEffectsDto":{"type":"object","properties":{"stagesDeleted":{"type":"number","description":"Active stages soft-deleted with the list."},"itemsDeleted":{"type":"number","description":"Active cards soft-deleted with the list."}},"required":["stagesDeleted","itemsDeleted"]},"PublicTaskThreadRefDto":{"type":"object","properties":{"campaignId":{"type":"string","maxLength":200},"leadId":{"type":"string","maxLength":200},"statsId":{"type":"string","maxLength":200},"conversationId":{"type":"string","maxLength":200},"linkedinAccountId":{"type":"string","maxLength":200}}},"PublicTaskAnchorResponseDto":{"type":"object","properties":{"type":{"type":"string","enum":["contact","candidate","job","application","none"],"description":"What this activity is about. \"contact\" on every activity created before anchors shipped."},"id":{"type":"string","nullable":true,"description":"Id of the anchored record, in the collection type names. Null only when type is none."},"jobPostId":{"type":"string","nullable":true,"description":"The job post this activity relates to. On a job anchor this equals id; on an application anchor it is derived from the application; otherwise it is the optional job reference, and null when there is none."}},"required":["type"]},"PublicTaskThreadRefResponseDto":{"type":"object","properties":{"campaignId":{"type":"string","nullable":true},"leadId":{"type":"string","nullable":true},"statsId":{"type":"string","nullable":true},"conversationId":{"type":"string","nullable":true},"linkedinAccountId":{"type":"string","nullable":true}}},"PublicTaskEventDto":{"type":"object","properties":{"event":{"type":"string","description":"What happened: \"created\", \"status_changed\", \"claimed\", \"unclaimed\", or \"deleted\"."},"at":{"type":"string","nullable":true,"description":"ISO 8601 timestamp of the event."},"actorType":{"type":"string","description":"Who did it: user, api_key, or system. An api_key event was written by an integration."},"note":{"type":"string","nullable":true,"description":"Human-readable detail, e.g. \"open -> approved\"."}},"required":["event","actorType"]},"PublicTaskListItemDto":{"type":"object","properties":{"id":{"type":"string","description":"Task id."},"contactId":{"type":"string","nullable":true,"description":"Id of the contact this task belongs to. Null on a non-contact anchor."},"anchor":{"description":"What this activity is about. Always present.","allOf":[{"$ref":"#/components/schemas/PublicTaskAnchorResponseDto"}]},"kind":{"type":"string","description":"Always \"task\"."},"taskType":{"type":"string","enum":["proposal","followup","todo"]},"status":{"type":"string","enum":["open","approved","sent","done","rejected","expired"]},"dueAt":{"type":"string","nullable":true},"title":{"type":"string","nullable":true},"interactionTypeId":{"type":"string","nullable":true},"communicationTypeId":{"type":"string","nullable":true},"date":{"type":"string","nullable":true},"draft":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true},"classification":{"type":"string","nullable":true},"lang":{"type":"string","nullable":true},"observedLastMessageAt":{"type":"string","nullable":true},"dedupeKey":{"type":"string","nullable":true},"threadRef":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/PublicTaskThreadRefResponseDto"}]},"claimedBy":{"type":"string","nullable":true},"claimedAt":{"type":"string","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","anchor","kind","taskType","status"]},"PublicTaskAnchorDto":{"type":"object","properties":{"type":{"type":"string","enum":["contact","candidate","job","application","none"],"example":"job","description":"What this activity is about: contact (a CRM contact - the historical default), candidate (a CazVid user), job (a job post), application (a pipeline card), or none (a standalone activity that is about nothing)."},"id":{"type":"string","example":"6a3ff55406ae2ed06ddb5fda","description":"Id of the record this activity is about, in the collection anchor.type names. Required for every type except none, and refused for none. It is resolved in your workspace before the activity is written: an id that names nothing you can reach is 404 NOT_FOUND."}},"required":["type"]}},"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"x-api-key"},"bearer":{"type":"http","scheme":"bearer"}}}}