{
  "openapi": "3.1.0",
  "info": {"title":"Plodder external API","version":"1.0.0","description":"Bearer API keys (ctrl_...) made in Settings > Integrations and API > API keys. Each key carries scopes; a route or tool action outside them answers 403 with missing_scopes. Generated by scripts/sdk-openapi.ts."},
  "servers": [{"url":"https://app.useplodder.com"}],
  "security": [{"bearer":[]}],
  "x-ctrl-scopes": {"areas":[{"area":"tasks","label":"Tasks","sub":"Tasks, views, schedules, comments"},{"area":"projects","label":"Projects","sub":"Projects, stages, folders, templates"},{"area":"docs","label":"Docs and databases","sub":"Docs, sheets, slides, designs, discussions, properties"},{"area":"meetings","label":"Meetings","sub":"Meeting notes and transcripts"},{"area":"crm","label":"CRM","sub":"People, businesses, deals, notes, clients"},{"area":"bookings","label":"Calendars and bookings","sub":"Calendar events, booking calendars, bookings"},{"area":"forms","label":"Forms","sub":"Forms and responses"},{"area":"messages","label":"Messages","sub":"Email, texts, chat (reading and organizing)"},{"area":"calls","label":"Calls","sub":"Phone calls, recordings, video calls"},{"area":"automations","label":"Automations and agents","sub":"Workflows, routines, agents"},{"area":"reports","label":"Reports","sub":"Dashboards and activity"},{"area":"files","label":"Files","sub":"Drive, uploads, imports"},{"area":"esign","label":"E-sign","sub":"PDF marks, signature requests, documents and contract templates"},{"area":"payments","label":"Payments","sub":"Stripe, Square, Whop and FanBasis: payment links, invoices, subscriptions, refunds, recorded payments"},{"area":"commissions","label":"Commissions","sub":"Sales team payees, commission lines, monthly statements, QuickBooks bills"},{"area":"instantly","label":"Instantly","sub":"Cold email: Instantly campaigns, adding people to one, taking them out, daily numbers"},{"area":"webhooks","label":"Webhooks","sub":"Outgoing webhooks and their delivery log"},{"area":"users","label":"Users","sub":"The people who log in and the team roster: ids, names, emails, roles (never passwords or sessions)"},{"area":"admin","label":"Admin","sub":"Workspaces, team, settings, everything else"}],"all":["tasks:read","tasks:write","projects:read","projects:write","docs:read","docs:write","meetings:read","meetings:write","crm:read","crm:write","bookings:read","bookings:write","forms:read","forms:write","messages:read","messages:write","calls:read","calls:write","automations:read","automations:write","reports:read","reports:write","files:read","files:write","esign:read","esign:write","payments:read","payments:write","commissions:read","commissions:write","instantly:read","instantly:write","webhooks:read","webhooks:write","users:read","users:write","admin:read","admin:write","messages:send"]},
  "paths": {
    "/api/external/tasks": {"get":{"operationId":"listTasks","summary":"List tasks (filters, offset/limit paging), or one task with ?id.","x-ctrl-scope":"tasks:read","x-ctrl-route":"src/app/api/external/tasks/route.ts","parameters":[{"name":"id","in":"query","required":false,"schema":{"type":["integer","string"],"description":"Numeric id or public id."}},{"name":"workspace_id","in":"query","required":false,"schema":{"type":["integer","string"],"description":"Numeric id or public id."}},{"name":"project_id","in":"query","required":false,"schema":{"type":["integer","string"],"description":"Numeric id or public id."}},{"name":"assignee","in":"query","required":false,"schema":{"type":"string","description":"Team id, login id, email or a name fragment."}},{"name":"status","in":"query","required":false,"schema":{"type":"string"}},{"name":"priority","in":"query","required":false,"schema":{"type":"string"}},{"name":"include_completed","in":"query","required":false,"schema":{"type":"string","description":"\"false\" hides done, cancelled and archived."}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/TaskList"},{"$ref":"#/components/schemas/Task"}]}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}},"post":{"operationId":"createTask","summary":"Create a task.","x-ctrl-scope":"tasks:write","x-ctrl-route":"src/app/api/external/tasks/route.ts","requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/TaskInput"},{"required":["title"]}]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}},"patch":{"operationId":"updateTask","summary":"Update a task (id in the body).","x-ctrl-scope":"tasks:write","x-ctrl-route":"src/app/api/external/tasks/route.ts","requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/TaskInput"},{"type":"object","properties":{"id":{"type":["integer","string"],"description":"Numeric id or public id."}},"required":["id"],"additionalProperties":true}]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}},"delete":{"operationId":"deleteTask","summary":"Delete a task (soft).","x-ctrl-scope":"tasks:write","x-ctrl-route":"src/app/api/external/tasks/route.ts","parameters":[{"name":"id","in":"query","required":true,"schema":{"type":["integer","string"],"description":"Numeric id or public id."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}}},
    "/api/external/activities": {"get":{"operationId":"listTaskActivities","summary":"A task's comment thread (task_id), or many (task_ids=1,2,3).","x-ctrl-scope":"tasks:read","x-ctrl-route":"src/app/api/external/activities/route.ts","parameters":[{"name":"task_id","in":"query","required":false,"schema":{"type":["integer","string"],"description":"Numeric id or public id."}},{"name":"task_ids","in":"query","required":false,"schema":{"type":"string","description":"Comma separated ids."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"activities":{"type":"array","items":{"$ref":"#/components/schemas/TaskActivity"}},"activitiesByTask":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}},"post":{"operationId":"createTaskActivity","summary":"Comment on a task.","x-ctrl-scope":"tasks:write","x-ctrl-route":"src/app/api/external/activities/route.ts","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"task_id":{"type":["integer","string"],"description":"Numeric id or public id."},"message":{"type":"string"},"agent":{"type":"string"},"activity_type":{"type":"string"}},"required":["task_id","message"],"additionalProperties":true}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"activity":{"$ref":"#/components/schemas/TaskActivity"}},"required":["activity"],"additionalProperties":true}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}}},
    "/api/external/projects": {"get":{"operationId":"listProjects","summary":"List projects, or one with ?id (detail=true adds stages, tasks, progress).","x-ctrl-scope":"projects:read","x-ctrl-route":"src/app/api/external/projects/route.ts","parameters":[{"name":"id","in":"query","required":false,"schema":{"type":["integer","string"],"description":"Numeric id or public id."}},{"name":"workspace_id","in":"query","required":false,"schema":{"type":["integer","string"],"description":"Numeric id or public id."}},{"name":"detail","in":"query","required":false,"schema":{"type":"string","description":"\"true\""}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"projects":{"type":"array","items":{"$ref":"#/components/schemas/Project"}},"total":{"type":"integer"}},"required":["projects","total"],"additionalProperties":true},{"$ref":"#/components/schemas/Project"},{"type":"object","properties":{"project":{"$ref":"#/components/schemas/Project"},"stages":{"type":"array","items":{"$ref":"#/components/schemas/Stage"}},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/Task"}},"progress":{}},"required":["project"],"additionalProperties":true}]}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}},"post":{"operationId":"createProject","summary":"Create a project.","x-ctrl-scope":"projects:write","x-ctrl-route":"src/app/api/external/projects/route.ts","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"workspace_id":{"type":["integer","string"],"description":"Numeric id or public id."},"folder_id":{"type":["integer","string"],"description":"Numeric id or public id."},"color":{"type":"string"},"business_id":{"type":"integer"}},"required":["name"],"additionalProperties":true}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}},"patch":{"operationId":"updateProject","summary":"Update a project.","x-ctrl-scope":"projects:write","x-ctrl-route":"src/app/api/external/projects/route.ts","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":["integer","string"],"description":"Numeric id or public id."},"name":{"type":"string"},"color":{"type":"string"},"status":{"type":"string"},"description":{"type":"string"}},"required":["id"],"additionalProperties":true}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}},"delete":{"operationId":"deleteProject","summary":"Soft delete a project and its tasks and stages.","x-ctrl-scope":"projects:write","x-ctrl-route":"src/app/api/external/projects/route.ts","parameters":[{"name":"id","in":"query","required":true,"schema":{"type":["integer","string"],"description":"Numeric id or public id."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}}},
    "/api/external/stages": {"get":{"operationId":"listStages","summary":"A project's stages.","x-ctrl-scope":"projects:read","x-ctrl-route":"src/app/api/external/stages/route.ts","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":["integer","string"],"description":"Numeric id or public id."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"stages":{"type":"array","items":{"$ref":"#/components/schemas/Stage"}},"total":{"type":"integer"}},"required":["stages","total"],"additionalProperties":true}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}},"post":{"operationId":"createStage","summary":"Create a stage.","x-ctrl-scope":"projects:write","x-ctrl-route":"src/app/api/external/stages/route.ts","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"project_id":{"type":["integer","string"],"description":"Numeric id or public id."},"name":{"type":"string"},"color":{"type":"string"}},"required":["project_id","name"],"additionalProperties":true}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Stage"}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}},"patch":{"operationId":"updateStage","summary":"Update a stage.","x-ctrl-scope":"projects:write","x-ctrl-route":"src/app/api/external/stages/route.ts","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":["integer","string"],"description":"Numeric id or public id."},"name":{"type":"string"},"color":{"type":"string"},"sort_order":{"type":"integer"}},"required":["id"],"additionalProperties":true}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Stage"}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}},"delete":{"operationId":"deleteStage","summary":"Delete a stage.","x-ctrl-scope":"projects:write","x-ctrl-route":"src/app/api/external/stages/route.ts","parameters":[{"name":"id","in":"query","required":true,"schema":{"type":["integer","string"],"description":"Numeric id or public id."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"additionalProperties":true}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}}},
    "/api/external/folders": {"get":{"operationId":"listFolders","summary":"List folders.","x-ctrl-scope":"projects:read","x-ctrl-route":"src/app/api/external/folders/route.ts","parameters":[{"name":"workspace_id","in":"query","required":false,"schema":{"type":["integer","string"],"description":"Numeric id or public id."}},{"name":"name","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"folders":{"type":"array","items":{"$ref":"#/components/schemas/Folder"}},"total":{"type":"integer"}},"required":["folders","total"],"additionalProperties":true}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}},"post":{"operationId":"createFolder","summary":"Create a folder (returns the existing one with the same name).","x-ctrl-scope":"projects:write","x-ctrl-route":"src/app/api/external/folders/route.ts","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"workspace_id":{"type":["integer","string"],"description":"Numeric id or public id."},"color":{"type":"string"},"parent_id":{"type":["integer","string"],"description":"Numeric id or public id."}},"required":["name"],"additionalProperties":true}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"folder":{"$ref":"#/components/schemas/Folder"},"existing":{"type":"boolean"}},"required":["folder"],"additionalProperties":true}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}},"patch":{"operationId":"updateFolder","summary":"Update a folder.","x-ctrl-scope":"projects:write","x-ctrl-route":"src/app/api/external/folders/route.ts","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":["integer","string"],"description":"Numeric id or public id."},"name":{"type":"string"},"color":{"type":"string"},"parent_id":{"type":["string","integer","null"]},"sort_order":{"type":"integer"}},"required":["id"],"additionalProperties":true}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"folder":{"$ref":"#/components/schemas/Folder"}},"required":["folder"],"additionalProperties":true}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}},"delete":{"operationId":"deleteFolder","summary":"Delete a folder.","x-ctrl-scope":"projects:write","x-ctrl-route":"src/app/api/external/folders/route.ts","parameters":[{"name":"id","in":"query","required":true,"schema":{"type":["integer","string"],"description":"Numeric id or public id."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}}},
    "/api/external/docs": {"get":{"operationId":"listDocs","summary":"List docs, or one with ?id.","x-ctrl-scope":"docs:read","x-ctrl-route":"src/app/api/external/docs/route.ts","parameters":[{"name":"id","in":"query","required":false,"schema":{"type":["integer","string"],"description":"Numeric id or public id."}},{"name":"workspace_id","in":"query","required":false,"schema":{"type":["integer","string"],"description":"Numeric id or public id."}},{"name":"project_id","in":"query","required":false,"schema":{"type":["integer","string"],"description":"Numeric id or public id."}},{"name":"search","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"docs":{"type":"array","items":{"$ref":"#/components/schemas/Doc"}},"total":{"type":"integer"}},"required":["docs","total"],"additionalProperties":true},{"$ref":"#/components/schemas/Doc"}]}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}},"post":{"operationId":"createDoc","summary":"Create a doc (content is Markdown). No workspace_id = the key owner's primary workspace (agent tools can then edit it); a workspace the owner is not in is 403.","x-ctrl-scope":"docs:write","x-ctrl-route":"src/app/api/external/docs/route.ts","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"content":{"type":"string"},"workspace_id":{"type":"integer"},"project_id":{"type":"integer"},"folder_id":{"type":"integer"}},"required":["title"],"additionalProperties":true}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Doc"}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}},"patch":{"operationId":"updateDoc","summary":"Update a doc. Delete is not offered on the API (docs delete for good).","x-ctrl-scope":"docs:write","x-ctrl-route":"src/app/api/external/docs/route.ts","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":["integer","string"],"description":"Numeric id or public id."},"title":{"type":"string"},"content":{"type":"string"}},"required":["id"],"additionalProperties":true}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Doc"}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}}},
    "/api/external/meetings": {"get":{"operationId":"listMeetings","summary":"List meeting notes, or one with ?id.","x-ctrl-scope":"meetings:read","x-ctrl-route":"src/app/api/external/meetings/route.ts","parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"meetings":{"type":"array","items":{"$ref":"#/components/schemas/MeetingNote"}},"total":{"type":"integer"}},"required":["meetings","total"],"additionalProperties":true},{"$ref":"#/components/schemas/MeetingNote"}]}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}},"post":{"operationId":"createMeeting","summary":"Import a meeting (Plaud, Otter, Zapier, anything).","x-ctrl-scope":"meetings:write","x-ctrl-route":"src/app/api/external/meetings/route.ts","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"transcript":{"type":"string"},"summary":{"type":"string"},"action_items":{"oneOf":[{"type":"array","items":{"type":"string"}},{"type":"string"}]},"attendees":{"oneOf":[{"type":"array","items":{"type":"string"}},{"type":"string"}]},"duration_seconds":{"type":"integer"},"meeting_date":{"type":"string"},"source":{"type":"string"},"client_id":{"type":"integer"},"workspace_id":{"type":"integer"}},"additionalProperties":true}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingNote"}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}},"patch":{"operationId":"updateMeeting","summary":"Update a meeting note (id plus any fields).","x-ctrl-scope":"meetings:write","x-ctrl-route":"src/app/api/external/meetings/route.ts","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"title":{"type":"string"},"transcript":{"type":"string"},"summary":{"type":"string"},"action_items":{"oneOf":[{"type":"array","items":{"type":"string"}},{"type":"string"}]},"attendees":{"oneOf":[{"type":"array","items":{"type":"string"}},{"type":"string"}]},"meeting_date":{"type":"string"}},"required":["id"],"additionalProperties":true}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingNote"}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}},"delete":{"operationId":"deleteMeeting","summary":"Delete a meeting note for good (no undo).","x-ctrl-scope":"meetings:write","x-ctrl-route":"src/app/api/external/meetings/route.ts","parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true}},"additionalProperties":true}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}}},
    "/api/external/calendar": {"get":{"operationId":"getCalendar","summary":"Events in a range (start, end), free slots on a day (mode=free-slots) or the agenda (mode=agenda).","x-ctrl-scope":"calendar:read","x-ctrl-route":"src/app/api/external/calendar/route.ts","parameters":[{"name":"mode","in":"query","required":false,"schema":{"type":"string","enum":["events","free-slots","agenda"]}},{"name":"start","in":"query","required":false,"schema":{"type":"string"}},{"name":"end","in":"query","required":false,"schema":{"type":"string"}},{"name":"calendarId","in":"query","required":false,"schema":{"type":"string"}},{"name":"date","in":"query","required":false,"schema":{"type":"string","description":"YYYY-MM-DD"}},{"name":"duration_minutes","in":"query","required":false,"schema":{"type":"integer"}},{"name":"work_start","in":"query","required":false,"schema":{"type":"string"}},{"name":"work_end","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"events":{"type":"array","items":{"$ref":"#/components/schemas/CalendarEvent"}},"total":{"type":"integer"}},"required":["events"],"additionalProperties":true},{"type":"object","properties":{"date":{"type":"string"},"slots":{"type":"array","items":{"$ref":"#/components/schemas/FreeSlot"}},"total_free_minutes":{"type":"integer"}},"required":["slots"],"additionalProperties":true},{"type":"object","properties":{"date":{"type":"string"},"events":{"type":"array","items":{"$ref":"#/components/schemas/CalendarEvent"}},"scheduled_tasks":{"type":"array","items":{"$ref":"#/components/schemas/Task"}},"overdue_tasks":{"type":"array","items":{"$ref":"#/components/schemas/Task"}}},"required":["scheduled_tasks"],"additionalProperties":true}]}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}}},
    "/api/external/users": {"get":{"operationId":"listUsers","summary":"The people who log in and the team roster.","x-ctrl-scope":"users:read","x-ctrl-route":"src/app/api/external/users/route.ts","parameters":[{"name":"email","in":"query","required":false,"schema":{"type":"string","description":"Exact, any case."}},{"name":"q","in":"query","required":false,"schema":{"type":"string"}},{"name":"include","in":"query","required":false,"schema":{"type":"string","description":"clients, agents, inactive (comma separated; clients needs crm:read)."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"users":{"type":"array","items":{"$ref":"#/components/schemas/User"}}},"required":["users"],"additionalProperties":true}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}}},
    "/api/external/users/{id}": {"get":{"operationId":"getUser","summary":"One person by id, u_<login id>, login id or email.","x-ctrl-scope":"users:read","x-ctrl-route":"src/app/api/external/users/[id]/route.ts","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"include","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"}},"additionalProperties":true}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}}},
    "/api/external/workspaces": {"get":{"operationId":"listWorkspaces","summary":"Workspaces with counts.","x-ctrl-scope":"workspaces:read","x-ctrl-route":"src/app/api/external/workspaces/route.ts","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"workspaces":{"type":"array","items":{"$ref":"#/components/schemas/Workspace"}},"total":{"type":"integer"}},"required":["workspaces","total"],"additionalProperties":true}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}}},
    "/api/external/tools": {"get":{"operationId":"listTools","summary":"The agent tools this key's scopes reach.","x-ctrl-scope":"per tool","x-ctrl-route":"src/app/api/external/tools/route.ts","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"tools":{"type":"array","items":{"$ref":"#/components/schemas/ToolInfo"}}},"required":["tools"],"additionalProperties":true}}}},"401":{"description":"No or bad key"},"403":{"description":"The key lacks the scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited (Retry-After)"}}}},
    "/api/external/tools/add_database_row": {"post":{"operationId":"tool_add_database_row","summary":"Add a database row","description":"Add a row to a database. values maps column names (from list_database_rows) to values.","tags":["tools"],"x-ctrl-tool":"add_database_row","x-ctrl-scopes":{"*":["docs:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"database":{"type":"string","description":"Database id or sheet:<id>."},"values":{"type":"object","description":"Column name -> value."}},"required":["database","values"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/add_task_comment": {"post":{"operationId":"tool_add_task_comment","summary":"Comment on a task","description":"Add a comment to a task thread. It shows as written through your AI client, not as you typing it.","tags":["tools"],"x-ctrl-tool":"add_task_comment","x-ctrl-scopes":{"*":["tasks:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"task_id":{"type":["number","string"]},"comment":{"type":"string"}},"required":["task_id","comment"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/agenda_snapshots": {"post":{"operationId":"tool_agenda_snapshots","summary":"Agenda snapshots","description":"Daily snapshots of every task with its status (/api/agendas). Owner only: snapshots are one table by date.\nActions:\n- list: Dates that have a snapshot.\n- get: The snapshot for a date (YYYY-MM-DD).\n- save: Save today's snapshot from your workspaces' tasks (replaces today's).","tags":["tools"],"x-ctrl-tool":"agenda_snapshots","x-ctrl-scopes":{"list":["tasks:read"],"get":["tasks:read"],"save":["tasks:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get","save"],"description":"What to do."},"date":{"type":"string","description":"YYYY-MM-DD"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/agent_items": {"post":{"operationId":"tool_agent_items","summary":"Agents on items (mention, send to agent, sessions)","description":"Agents on any item (task, project, doc, CRM person or business, client, business, meeting, database row, file ...). mention: post a comment as you that @mentions agents, and they run on the item (its content plus the discussion) and reply in the same thread. dispatch: start an agent conversation with the item and its discussion as context (Send to agent); returns the conversation. prompt: the item and every discussion thread as a markdown prompt (Copy as prompt). sessions: agent conversations, mention runs and assigned work on the item. Items: \"task:12\", \"doc:7\", \"crm_contact:40\", \"client:3\" (ids or public ids). Agents: id, \"ai-<id>\" or @handle (list them with agent_work action agents).\nActions:\n- mention: Mention agents on an item: item, agent (one) or agents (list), text (what you ask), thread_id? (reply inside a discussion thread).\n- dispatch: Send an item to an agent: item, agent? (default: your built-in agent), prompt? (what to do; default: work on it).\n- prompt: The item as a prompt with its discussion threads: item.\n- sessions: Agent sessions on an item: item.","tags":["tools"],"x-ctrl-tool":"agent_items","x-ctrl-scopes":{"mention":["automations:write"],"dispatch":["automations:write"],"prompt":["automations:read"],"sessions":["automations:read"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["mention","dispatch","prompt","sessions"],"description":"What to do."},"item":{"type":"string"},"agent":{"type":["string","number"]},"agents":{"type":"array","items":{"type":["string","number"]}},"text":{"type":"string"},"prompt":{"type":"string"},"thread_id":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/agent_mcp_servers": {"post":{"operationId":"tool_agent_mcp_servers","summary":"Outside MCP servers","description":"Outside MCP servers your agents can use (Settings > Agents > MCP servers). Adding one or turning one on gives agents new tools, so it waits for approval (external). Auth headers and OAuth sign-in are entered by the person in the app.\nActions:\n- list: Servers and their tools.\n- add: Add a server (url, name?). Connects and lists its tools. (waits for approval: external)\n- update: Rename (name) or turn on/off (enabled). Turning on waits for approval. (may wait for approval)\n- refresh: Reconnect and list its tools again.\n- delete: Remove a server. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"agent_mcp_servers","x-ctrl-scopes":{"list":["automations:read"],"add":["automations:write"],"update":["automations:write"],"refresh":["automations:write"],"delete":["automations:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","add","update","refresh","delete"],"description":"What to do."},"server_id":{"type":"number"},"url":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/agent_memory": {"post":{"operationId":"tool_agent_memory","summary":"Agent memory","description":"The memory notes your agents read and write (Settings > AI > Memory, /api/ai-agents/memory).\nActions:\n- list: Every note.\n- add: Add a note by hand (content).\n- delete: Delete a note (note_id). (waits for approval: delete)\n- clear: Delete every note. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"agent_memory","x-ctrl-scopes":{"list":["automations:read"],"add":["automations:write"],"delete":["automations:write"],"clear":["automations:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","add","delete","clear"],"description":"What to do."},"note_id":{"type":"number"},"content":{"type":"string"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/agent_runners": {"post":{"operationId":"tool_agent_runners","summary":"Paired Macs","description":"Macs paired to run agents on their CLIs (Settings > Agents > Runtimes). Pairing a new Mac runs a command on that Mac, so it is done in the app.\nActions:\n- list: Paired Macs, online state and CLIs.\n- rename: Rename a Mac (device_id, name).\n- unpair: Unpair a Mac; its token stops working at once. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"agent_runners","x-ctrl-scopes":{"list":["automations:read"],"rename":["automations:write"],"unpair":["automations:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","rename","unpair"],"description":"What to do."},"device_id":{"type":"number"},"name":{"type":"string"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/agent_runs": {"post":{"operationId":"tool_agent_runs","summary":"Agent runs and routines","description":"Run a routine now, delete a routine, or stop a run (/api/ai-agents/routines/[id], /run, /api/ai-agents/runs/[id]/stop). Create and change routines with create_routine and update_routine.\nActions:\n- run_routine: Run a routine now (routine_id). Starts in the background. A run started this way cannot start further runs, and at most 6 can be started per 10 minutes.\n- delete_routine: Delete a routine (routine_id). (waits for approval: delete)\n- stop_run: Stop a running agent run (run_id).","tags":["tools"],"x-ctrl-tool":"agent_runs","x-ctrl-scopes":{"run_routine":["automations:write"],"delete_routine":["automations:write"],"stop_run":["automations:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["run_routine","delete_routine","stop_run"],"description":"What to do."},"routine_id":{"type":"number"},"run_id":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/agent_skills": {"post":{"operationId":"tool_agent_skills","summary":"Agent skills and instructions","description":"Skills (reusable instructions an agent loads with /slug) and your AI instructions every built-in agent reads (/api/ai-agents/skills, /api/ai-agents/instructions). Read one with read_skill, list with list_skills.\nActions:\n- get: One skill (skill_id).\n- create: A new skill: name, instructions, slug?, description?, share? private|team.\n- duplicate: Copy a skill (skill_id) to edit it (built-in ones too).\n- copy_installed: Copy an installed (legacy) skill in (installed_id). Owner only.\n- update: Change a skill you made (skill_id + any of name, slug, description, instructions, share).\n- delete: Delete a skill you made. (waits for approval: delete)\n- instructions: Your AI instructions.\n- set_instructions: Replace your AI instructions (text).","tags":["tools"],"x-ctrl-tool":"agent_skills","x-ctrl-scopes":{"get":["automations:read"],"create":["automations:write"],"duplicate":["automations:write"],"copy_installed":["automations:write"],"update":["automations:write"],"delete":["automations:write"],"instructions":["automations:read"],"set_instructions":["automations:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["get","create","duplicate","copy_installed","update","delete","instructions","set_instructions"],"description":"What to do."},"skill_id":{"type":"number"},"installed_id":{"type":"number"},"name":{"type":"string"},"slug":{"type":"string"},"description":{"type":"string"},"instructions":{"type":"string"},"share":{"type":"string","enum":["private","team"]},"text":{"type":"string"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/agent_threads": {"post":{"operationId":"tool_agent_threads","summary":"Agent conversations","description":"Rename, archive, compact or delete your agent conversations (/api/ai-agents/threads/[id]), and read how full a conversation's context is and what its agent may do.\nActions:\n- rename: New title (thread_id, title).\n- compact: Compact now: fold every turn but the last exchange into the running summary the agent reads instead of them (/api/ai-agents/threads/[id]/compact). Returns the new context size.\n- context: Context size (tokens used of the model window, what is summarized) and the agent's permissions for this conversation.\n- archive: Archive it.\n- unarchive: Bring it back.\n- mark_read: Mark it read.\n- delete: Delete the conversation for good. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"agent_threads","x-ctrl-scopes":{"rename":["automations:write"],"compact":["automations:write"],"context":["automations:read"],"archive":["automations:write"],"unarchive":["automations:write"],"mark_read":["automations:write"],"delete":["automations:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["rename","compact","context","archive","unarchive","mark_read","delete"],"description":"What to do."},"thread_id":{"type":"number"},"title":{"type":"string"}},"required":["action","thread_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/agent_work": {"post":{"operationId":"tool_agent_work","summary":"Agent work (assign to an agent)","description":"Hand a task, project or doc to an agent as work it owns, and manage that work (Agents > Queue). assign: the agent gets the item with its full context, moves a task to In progress, posts its plan, progress and result as comments, asks questions as comments (the item shows Waiting on you), then moves the task to Review (or Done when finish = done or the task says so) and gives it back to the person who assigned it. Setting a task or project assignee to \"ai-<agent id>\" does the same. Items: \"task:12\", \"project:4\", \"doc:7\" (ids or public ids). Agents: id, \"ai-<id>\" or @handle.\nActions:\n- list: The queue: work you assigned or that runs as you. status (open | closed | queued | running | waiting | paused | review | done | stopped | error), agent, item.\n- get: One piece of work: id.\n- agents: The agents you can assign work to (their assignee values).\n- assign: Give an item to an agent: item, agent, note? (instructions), finish? (review | done), start? (false = hold it until resume; default: the agent's \"start right away\" setting).\n- stop: Stop work (ends it and hands the item back): id.\n- pause: Pause work (a live run stops; resume picks it up): id.\n- resume: Resume paused work, or try again on finished / failed / stopped work with note?: id.\n- answer: Answer the agent's question on waiting work: id, note.","tags":["tools"],"x-ctrl-tool":"agent_work","x-ctrl-scopes":{"list":["automations:read"],"get":["automations:read"],"agents":["automations:read"],"assign":["automations:write"],"stop":["automations:write"],"pause":["automations:write"],"resume":["automations:write"],"answer":["automations:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get","agents","assign","stop","pause","resume","answer"],"description":"What to do."},"id":{"type":"number"},"item":{"type":"string"},"agent":{"type":["string","number"]},"note":{"type":"string"},"finish":{"type":"string","enum":["review","done"]},"status":{"type":"string"},"limit":{"type":"number"},"start":{"type":"boolean"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/agents_admin": {"post":{"operationId":"tool_agents_admin","summary":"Agents","description":"Create, change and delete AI agents like the agent editor (/api/ai-agents). List them with list_agents. fields: name, handle, description, avatar, color, instructions, model, share, channel_scope, channel_ids, use_memory, auto_start, policy, auto_accept, allowed_tools, connections, connections_scope, runtime. Changing policy, auto_accept, allowed_tools, connections, connections_scope, runtime (what an agent may do, or whether it skips approvals) always waits for the person's approval (external), on any agent.\nActions:\n- get: One agent with its settings.\n- create: A new agent: fields = {name, instructions?, model?, ...}. (may wait for approval)\n- update: Change an agent you made (agent_id, fields). (may wait for approval)\n- delete: Delete an agent you made. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"agents_admin","x-ctrl-scopes":{"get":["automations:read"],"create":["automations:write"],"update":["automations:write"],"delete":["automations:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["get","create","update","delete"],"description":"What to do."},"agent_id":{"type":"number"},"fields":{"type":"object"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/append_to_doc": {"post":{"operationId":"tool_append_to_doc","summary":"Append to a doc","description":"Add markdown to the end of a doc without touching what is there.","tags":["tools"],"x-ctrl-tool":"append_to_doc","x-ctrl-scopes":{"*":["docs:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"doc_id":{"type":["number","string"]},"content":{"type":"string","description":"Markdown to append."}},"required":["doc_id","content"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/apps": {"post":{"operationId":"tool_apps","summary":"Connected apps","description":"Your Composio apps (Settings > AI > Connections: Gmail, Slack, GitHub, Notion and more). Connecting an app is your own sign-in in the browser: connect_link returns the link to open. Your agents use connected apps through their tools.\nActions:\n- connected: Apps you connected and their accounts.\n- search: Find an app to connect (q).\n- connect_link: A sign-in link for an app (slug, alias? for a second account). Give it to the person to open; nothing is connected until they finish.\n- check: After the person signed in: the app's accounts now (slug).","tags":["tools"],"x-ctrl-tool":"apps","x-ctrl-scopes":{"connected":["admin:read"],"search":["admin:read"],"connect_link":["admin:write"],"check":["admin:read"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["connected","search","connect_link","check"],"description":"What to do."},"q":{"type":"string"},"slug":{"type":"string","description":"App slug, like gmail or github."},"alias":{"type":"string"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/auto_schedule": {"post":{"operationId":"tool_auto_schedule","summary":"Auto-scheduling","description":"The owner's auto-scheduler: work-hours schedules (blocks = [{day 0-6, start \"09:00\", end \"17:00\"}]), flexible hours for one date, rescheduling now, and the task timer (start/stop).\nActions:\n- list_schedules: Every work-hours schedule.\n- create_schedule: A new schedule (name, blocks, color, is_default).\n- update_schedule: Change a schedule (schedule_id, name, blocks, color, is_default).\n- delete_schedule: Delete a schedule. (waits for approval: delete)\n- get_flexible_hours: The flexible hours set for one date (YYYY-MM-DD).\n- set_flexible_hours: Different hours for one date (date, blocks).\n- clear_flexible_hours: Back to the normal schedule for that date. (waits for approval: delete)\n- reschedule: Run the auto-scheduler now (repacks auto-scheduled tasks around the calendar).\n- toggle_task_timer: Start the timer on a task (status in progress), or stop it when it is running.","tags":["tools"],"x-ctrl-tool":"auto_schedule","x-ctrl-scopes":{"list_schedules":["tasks:read"],"create_schedule":["tasks:write"],"update_schedule":["tasks:write"],"delete_schedule":["tasks:write"],"get_flexible_hours":["tasks:read"],"set_flexible_hours":["tasks:write"],"clear_flexible_hours":["tasks:write"],"reschedule":["tasks:write"],"toggle_task_timer":["tasks:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list_schedules","create_schedule","update_schedule","delete_schedule","get_flexible_hours","set_flexible_hours","clear_flexible_hours","reschedule","toggle_task_timer"],"description":"What to do."},"schedule_id":{"type":"number"},"name":{"type":"string"},"blocks":{"type":"array","items":{"type":"object"}},"color":{"type":"string"},"is_default":{"type":"boolean"},"date":{"type":"string","description":"YYYY-MM-DD."},"task_id":{"type":["number","string"]}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/billing": {"post":{"operationId":"tool_billing","summary":"Billing","description":"Settings > Billing, read only: plan, seats, this month's AI allowance and credits, and (for workspace owners and admins) invoices and the credit ledger. Changing the plan, seats, auto reload or the card stays with the person in the browser.\nActions:\n- overview: Plan, status, seats, credit position, scheduled plan notices (renewal, cancellation, trial end, failed payment) and the workspaces you can see billing for (workspace_id?).\n- preview: Billing lab: the account, AI position and notices Settings > Billing shows in a state (workspace_id?, state: free | pro | renewing | cancel_scheduled | trialing | trial_last_days | past_due | usage_high | out_of_credits | reload_paused | unlimited). Changes nothing; owners and admins.\n- invoices: Recent invoices and credit ledger (owners and admins).\n- unsettled: Owner only: finished AI runs that owe a workspace money and are not on the bill yet (settled by ctrlflow-core every few minutes).","tags":["tools"],"x-ctrl-tool":"billing","x-ctrl-scopes":{"overview":["admin:read"],"preview":["admin:read"],"invoices":["admin:read"],"unsettled":["admin:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["overview","preview","invoices","unsettled"],"description":"What to do."},"workspace_id":{"type":"number"},"state":{"type":"string"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/booking_links": {"post":{"operationId":"tool_booking_links","summary":"Booking pages","description":"Create and edit booking (scheduling) pages people outside the app use to book time on the owner's calendar. A page is public, so creating one or turning one on waits for approval. List them with list_booking_links. Settings go in `fields` with the app's names: name, slug, page_title, page_intro, calendar_title_template, durations [min], slot_interval, custom_hours, daily_limit, start_delay_days, one_time, buffer_before, buffer_after, questions, reminder_minutes, reminder_method (popup|email), booking_mode (single|round_robin|collective|group), round_robin_strategy, capacity, max_days_ahead, timezone, location_type (none|google_meet|zoom|phone|custom|ctrlflow), location_value, cancel_cutoff_minutes, reschedule_cutoff_minutes, min_notice_minutes, phone_field (off|optional|required), notifications, theme, settings (calendar settings, see the calendars tool set_settings), active, hosts [{team_member_id|google_account_id|calendar_id, hours?}]. Per-host hours, date overrides, holidays, notification content, the look and bookings: the calendars tool.\nActions:\n- get: One page with all its settings and hosts.\n- create: A new page (fields.name required; slug optional, random when left out). (waits for approval: external)\n- update: Change settings (link_id, fields). Turning it on or changing its link waits for approval. (may wait for approval)\n- delete: Delete a page. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"booking_links","x-ctrl-scopes":{"get":["bookings:read"],"create":["bookings:write"],"update":["bookings:write"],"delete":["bookings:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["get","create","update","delete"],"description":"What to do."},"link_id":{"type":"number"},"fields":{"type":"object"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/bots": {"post":{"operationId":"tool_bots","summary":"Chat bots","description":"Bot accounts that let an outside program post in chat (Settings > Bots). The bot token is shown only in the app; an outside program gets it with a pairing code.\nActions:\n- list: Your bots.\n- create: A new bot for a runtime agent (agent_id, display_name, username?, description?). Get it running with pair.\n- update: Change display_name, description, avatar_url, webhook_url. A webhook URL sends chat to an outside address (waits for approval). (may wait for approval)\n- pair: A 10-minute pairing code an outside program trades for the bot token. (waits for approval: external)\n- delete: Revoke the bot. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"bots","x-ctrl-scopes":{"list":["automations:read"],"create":["automations:write"],"update":["automations:write"],"pair":["automations:write"],"delete":["automations:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","create","update","pair","delete"],"description":"What to do."},"bot_id":{"type":"number"},"agent_id":{"type":"string"},"display_name":{"type":"string"},"username":{"type":"string"},"description":{"type":"string"},"avatar_url":{"type":["string","null"]},"webhook_url":{"type":["string","null"]}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/calendar_events": {"post":{"operationId":"tool_calendar_events","summary":"Calendar events (more)","description":"More on the owner's Google calendar: search events, the full create (repeat rules, out of office, visibility, travel time, which calendar, project), change a repeat rule, move an event, and sync now. Basic create/update/delete: create_calendar_event, update_calendar_event, delete_calendar_event.\nActions:\n- search: Find events by words (mode title | description | exact).\n- create: Create an event with any field. Guests get invitations, so with guests it waits for approval. (may wait for approval)\n- set_recurrence: Set or clear the repeat rule on an event (whole series). Google emails guests about it, so an event with guests waits for approval. (may wait for approval)\n- move: Move an event to new_start / new_end (ISO). notify_guests emails the guests, so it waits for approval. (may wait for approval)\n- sync: Pull every connected Google calendar now (and the calendar sync links).","tags":["tools"],"x-ctrl-tool":"calendar_events","x-ctrl-scopes":{"search":["bookings:read"],"create":["bookings:write"],"set_recurrence":["bookings:write"],"move":["bookings:write"],"sync":["bookings:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["search","create","set_recurrence","move","sync"],"description":"What to do."},"title":{"type":"string"},"start_time":{"type":"string","description":"ISO 8601 with offset; all-day: YYYY-MM-DD."},"end_time":{"type":"string"},"all_day":{"type":"boolean"},"description":{"type":"string"},"location":{"type":"string"},"guests":{"type":"array","items":{"type":"string"}},"conferencing":{"type":"string","enum":["none","meet","zoom"]},"busy_status":{"type":"string","enum":["busy","free","tentative","out_of_office"]},"visibility":{"type":"string","enum":["default","public","private"]},"recurrence":{"type":"array","items":{"type":"string"},"description":"Repeat rules, e.g. [\"RRULE:FREQ=WEEKLY;BYDAY=MO\"]; [] stops repeating."},"travel_time_before":{"type":"number"},"travel_time_after":{"type":"number"},"calendar_id":{"type":"string"},"project_id":{"type":"number"},"timezone":{"type":"string"},"auto_decline":{"type":"string","enum":["all","new","none"],"description":"Out of office: decline new or all invites."},"decline_message":{"type":"string"},"q":{"type":"string"},"mode":{"type":"string","enum":["title","description","exact"]},"event_id":{"type":"string"},"new_start":{"type":"string"},"new_end":{"type":"string"},"notify_guests":{"type":"boolean"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/calendar_ooo": {"post":{"operationId":"tool_calendar_ooo","summary":"Out of office","description":"Out of office: your auto-decline settings, marking yourself away (shows in Team out of office), and who on the team is away. For an out-of-office event on the Google calendar, use calendar_events create with busy_status out_of_office.\nActions:\n- get: Your out-of-office settings and your upcoming away periods.\n- team: Who is away between start and end (default last 7 days to next 30).\n- settings: auto_decline (all | new | none), message, show_team.\n- add: Mark yourself away: start, end, title, all_day.\n- remove: Remove one of your away periods (ooo_id; the owner can remove anyone's). (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"calendar_ooo","x-ctrl-scopes":{"get":["bookings:read"],"team":["bookings:read"],"settings":["bookings:write"],"add":["bookings:write"],"remove":["bookings:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["get","team","settings","add","remove"],"description":"What to do."},"start":{"type":["string","number"]},"end":{"type":["string","number"]},"title":{"type":"string"},"all_day":{"type":"boolean"},"auto_decline":{"type":"string","enum":["all","new","none"]},"message":{"type":"string"},"show_team":{"type":"boolean"},"ooo_id":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/calendar_sync_links": {"post":{"operationId":"tool_calendar_sync_links","summary":"Calendar sync links","description":"Copy busy time between two connected Google calendars from different accounts (one-way or two-way), so each calendar shows the other's events.\nActions:\n- list: Every sync link with its last sync and error.\n- create: Link source_calendar_id to destination_calendar_id (direction one_way | two_way). It syncs right away.\n- update: Turn a link on or off (enabled) or change direction.\n- delete: Remove a link. (waits for approval: delete)\n- sync_now: Sync one link now.","tags":["tools"],"x-ctrl-tool":"calendar_sync_links","x-ctrl-scopes":{"list":["bookings:read"],"create":["bookings:write"],"update":["bookings:write"],"delete":["bookings:write"],"sync_now":["bookings:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","create","update","delete","sync_now"],"description":"What to do."},"link_id":{"type":"string"},"source_calendar_id":{"type":"string"},"destination_calendar_id":{"type":"string"},"direction":{"type":"string","enum":["one_way","two_way"]},"enabled":{"type":"boolean"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/calendar_team": {"post":{"operationId":"tool_calendar_team","summary":"Team calendars","description":"Teammates' calendars, read only: what each person shares (busy blocks by default, event details if they chose, nothing if they turned it off; private events are always just Busy), team availability for find-a-time, and your own sharing setting. Never changes Google permissions.\nActions:\n- view: Teammates and their shared events between start and end (default this week; 62 days at most). user_ids narrows it.\n- availability: Busy blocks of teammates (emails and/or user_ids) between start and end (31 days at most), plus free_slots of duration_minutes for everyone.\n- get_sharing: What teammates can see of your calendar: busy_only | all | none.\n- set_sharing: Set what teammates can see of your calendar: sharing busy_only (default) | all | none.","tags":["tools"],"x-ctrl-tool":"calendar_team","x-ctrl-scopes":{"view":["admin:read"],"availability":["admin:read"],"get_sharing":["admin:read"],"set_sharing":["admin:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["view","availability","get_sharing","set_sharing"],"description":"What to do."},"start":{"type":["string","number"]},"end":{"type":["string","number"]},"user_ids":{"type":"array","items":{"type":"number"}},"emails":{"type":"array","items":{"type":"string"}},"duration_minutes":{"type":"number"},"sharing":{"type":"string","enum":["busy_only","all","none"]}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/calendars": {"post":{"operationId":"tool_calendars","summary":"Calendars (Automations)","description":"Booking calendars in Automations > Calendars, beyond the page settings (create, rename, durations, hosts, type and limits are the booking_links tool). Types (booking_links booking_mode): single = personal, round_robin, collective (every host free), group = class or event (capacity seats). Open times come from, most specific first: a date override for a host, a date override for the calendar (holidays included), the host's own weekly hours, the calendar's weekly hours (custom_hours), the host's work schedule; minus their Google busy times and their other bookings. Weekly hours look like {\"1\":[{\"start\":\"09:00\",\"end\":\"17:00\"}]} (0 = Sunday). Notification content uses custom values ({{contact.first_name}}, {{booking.start}}, {{booking.location}}, {{calendar.name}}, {{booking.reschedule_url}} in the confirmation). Text messages go only to guests who ticked \"Text me\" and respect STOP and Do not text.\nActions:\n- list: Every calendar with type, hosts, bookings this week and upcoming.\n- get: One calendar: settings, hosts with their own hours, upcoming date overrides, notifications, look, public URL (calendar_id).\n- availability: Open times on a date as the public page shows them (calendar_id, date YYYY-MM-DD, duration?, exclude_booking_id? when moving one).\n- set_host_hours: A host's own weekly hours on this calendar (calendar_id, team_member_id, hours: object, or null to follow the calendar).\n- set_override: Close a date or give it custom hours, for the calendar or one host (calendar_id, date, closed?, hours? [{start,end}], team_member_id?, label?). Replaces that date.\n- delete_override: Remove a date override (calendar_id, override_id). (waits for approval: delete)\n- add_holidays: Close a year's public holidays (calendar_id, region us|ca, year?).\n- get_notifications: The notification matrix (calendar_id): events confirm (Booked, unconfirmed), confirmed (Booked, confirmed), reschedule, cancel, reminders[], follow_up; channels email + sms (guest) and in_app (the host's app notification), each {on, subject, body}; per event team_copy (host heads-up email). WhatsApp: no sender yet, never sent.\n- set_notifications: Change the matrix, merged over what is saved (calendar_id, notifications: { confirm|confirmed|reschedule|cancel: { email|sms|in_app: {on, subject, body}, team_copy }, reminders: [{ minutes, email, sms, in_app }], follow_up: { minutes after the end, after showed|any, email, sms, in_app }, reply_to }). Each cell sends once per booking. (waits for approval: external)\n- get_settings: The calendar settings (GHL parity): booking form, widget order, sticky contacts, prefill, consent box, guests, confirmation page, Meta pixel / Google tag, auto-confirm, payments (stored, no provider yet), owner assignment, guest cancel / reschedule policy, Google invite emails, invite notes. Plus payment_provider and form_problem (calendar_id).\n- set_settings: Change calendar settings, merged (calendar_id, settings). settings = { form_id (a form with its public link on, no screener or booking steps; null = default form), widget_order time_first|form_first, sticky_contacts, prefill_from_url, consent {on, required, text}, guests {on, max 1-10}, confirmation {mode message|redirect, message (custom values: {{contact.first_name}}, {{booking.start}}, {{calendar.name}}), redirect_url https, append_params}, tracking {meta_pixel_id, google_tag_id}, auto_confirm, payments {on, amount, currency, collect full|deposit, deposit_amount, hold_minutes 30-1440} (charged at booking through Stripe Checkout once Stripe is connected; on stays off while it is not), assign_owner {on, skip_if_assigned}, policy {allow_reschedule, allow_cancel} (cut-offs: cancel_cutoff_minutes / reschedule_cutoff_minutes), send_invites (Google invite + update emails to attendees), invite_notes {on, template with {{contact.phone}} {{contact.email}} {{reschedule_link}} {{cancellation_link}}}, recurring {on, frequencies [weekly, biweekly, monthly], max_count 2-52, allow_until} (guests may book a repeating series; each occurrence is its own booking; off while payments are on) }. Merged over what is saved. (waits for approval: external)\n- get_theme: The calendar's public page look (calendar_id).\n- set_theme: Change the public page look, merged (calendar_id, theme: { only the keys to change }), or theme null for the workspace default. Theme keys: mode light|dark|custom, background #hex (page colour in custom mode), brand #hex, text #hex or \"\" (auto), font system|inter|dm-sans|manrope|plus-jakarta|work-sans|lora|merriweather, heading_font \"\" (same as font) or a font key, radius sharp|soft|round, button filled|outline, button_text #hex or \"\" (auto), button_label up to 40 characters or \"\" (default label), button_shape auto (follows radius)|square|pill, button_width auto|full, button_size s|m|l, field_style outlined|filled|underline, field_bg / field_border / label_color #hex or \"\" (auto), show_card true|false, card_bg #hex or \"\" (auto), card_width auto|narrow|medium|wide, card_shadow none|soft|strong, card_border true|false, text_size s|m|l, align left|center (header and intro), page_fill solid|gradient, gradient_from / gradient_to #hex or \"\" (auto), gradient_angle 0-360, page_image / logo_url / cover_url (https or /uploads/ path, \"\" for none), page_overlay 0-80 (darkens page_image), logo_size s|m|l, logo_position left|center; forms: form_layout page|one, progress none|bar|steps, question_numbers true|false, required_color red|brand; calendars: booking_layout month|list, show_host true|false, slot_style outline|filled. A bad value is ignored and the current one stays. (waits for approval: external)\n- bookings: Bookings on a calendar with appt_status and needs_status (calendar_id, scope upcoming|past|cancelled|needs).\n- notices: What was sent for one booking (calendar_id, booking_id).\n- cancel_booking: Cancel a booking: removes the calendar event and sends the cancel notice (calendar_id, booking_id). (waits for approval: send email)\n- reschedule_booking: Move a booking to an open time on the same calendar (calendar_id, booking_id, start YYYY-MM-DDTHH:MM). (waits for approval: send email)\n- book: Book a time on a calendar for someone, once or repeating (calendar_id, name, email, phone?, start and end as the availability slots give them, notes?, recurrence? { frequency weekly|biweekly|monthly, count 2-52 | until YYYY-MM-DD } when the calendar recurring setting is on, skip_conflicts? to book the open dates when some are taken; without it a series with taken dates answers needs_confirm + conflicts). Same path as the public page: calendar event, CRM person, notices. (waits for approval: send email)\n- series: The repeating series a booking belongs to: how often, how many, and every booking in it with its status (calendar_id, booking_id).\n- cancel_series: Cancel a repeating booking: this one, this and the following ones, or every upcoming one (calendar_id, booking_id, series_scope this|following|all). One cancel notice goes to the guest. (waits for approval: send email)\n- external_calendars: Your Outlook / iCloud calendar accounts (each person connects their own in Settings > Calendars): which calendars block booking times (busy), which one gets booking events (write), and the status (ok, reconnect, error).\n- set_external_calendars: Pick an Outlook / iCloud account's busy calendars (busy: calendar ids) and the calendar booking events are written to (write: a calendar id, or null for none) (connection_id).\n- check_external_calendar: Read an Outlook / iCloud account again: refresh the token and its calendars, set the status (connection_id).\n- disconnect_external_calendar: Forget an Outlook / iCloud account (connection_id). Events already written stay on that calendar. (waits for approval: delete)\n- move_series: Move a repeating booking: this one, this and the following ones, or every upcoming one, each by the same amount as this one moves (calendar_id, booking_id, series_scope, start YYYY-MM-DDTHH:MM). Dates whose new time is taken stay and are listed in failed. (waits for approval: send email)\n- get_lead_pipeline: The calendar's CRM lead setting (calendar_id): {on, pipeline_id, stage_id, booked_stage_id, showed_stage_id, no_show_stage_id} or null.\n- set_lead_pipeline: Lead pipelines (calendar_id, setting = {on, pipeline_id, stage_id?, booked_stage_id?, showed_stage_id?, no_show_stage_id?} or null): on = a booking finds or makes the person and adds a deal in that CRM pipeline at stage_id, or, when they already have an open deal there, moves it to booked_stage_id (if set) and logs the booking. showed / no-show marks move it to those stages. One open deal per person per pipeline. A workflow with Create / update deal can do the same.\n- set_outcome: Mark a booking showed or no_show (or null to clear); fires the Showed / No-show workflow triggers (calendar_id, booking_id, outcome). (waits for approval: send email)\n- get_booking_status: One booking with its status (booked, confirmed, showed, no_show, cancelled, invalid), needs_status (ended and still booked / confirmed), calendar, guest, answers, source, and its status history incl. Rescheduled markers (booking_id; calendar_id optional).\n- set_booking_status: Set a booking's status (booking_id, status booked|confirmed|showed|no_show|cancelled|invalid, note?). The same path as the app: fires the status trigger (Appointment confirmed / showed / no-show / cancelled / invalid) and Appointment status changed once; cancelled removes the calendar event and sends the calendar's cancel notice; showed / no-show move the calendar's lead deal. A cancelled booking cannot be reopened. (waits for approval: send email)\n- needs_status: Past bookings still Booked / Confirmed (the meeting ended with no outcome), newest first (calendar_id optional).\n- event_crm: A calendar event's CRM links (people, businesses, deals, the client pages it shows on), its booking + status when it is one of our bookings, and guest suggestions matched by email (event_id, event_calendar_id?).\n- link_event: Link a calendar event to CRM records: a person (contact_id) and, in the same go, businesses (company_ids, e.g. the person's businesses), and / or a deal (deal_id). Clients' pages get the event too (event_id, event_calendar_id?).\n- unlink_event: Remove one CRM link from a calendar event (event_id, event_calendar_id?, link_type crm_contact|crm_company|crm_deal|client|business, link_id).\n- get_default_theme: The workspace default look that calendars and forms without their own follow.\n- set_default_theme: Change the workspace default look (theme, merged; same keys as set_theme: mode light|dark|custom, background #hex (page colour in custom mode), brand #hex, text #hex or \"\" (auto), font system|inter|dm-sans|manrope|plus-jakarta|work-sans|lora|merriweather, heading_font \"\" (same as font) or a font key, radius sharp|soft|round, button filled|outline, button_text #hex or \"\" (auto), button_label up to 40 characters or \"\" (default label), button_shape auto (follows radius)|square|pill, button_width auto|full, button_size s|m|l, field_style outlined|filled|underline, field_bg / field_border / label_color #hex or \"\" (auto), show_card true|false, card_bg #hex or \"\" (auto), card_width auto|narrow|medium|wide, card_shadow none|soft|strong, card_border true|false, text_size s|m|l, align left|center (header and intro), page_fill solid|gradient, gradient_from / gradient_to #hex or \"\" (auto), gradient_angle 0-360, page_image / logo_url / cover_url (https or /uploads/ path, \"\" for none), page_overlay 0-80 (darkens page_image), logo_size s|m|l, logo_position left|center; forms: form_layout page|one, progress none|bar|steps, question_numbers true|false, required_color red|brand; calendars: booking_layout month|list, show_host true|false, slot_style outline|filled. A bad value is ignored and the current one stays.) (waits for approval: external)\n- get_form_theme: A form's public look (form_id).\n- set_form_theme: Change a form's public look, merged (form_id, theme: the set_theme keys, incl. the form ones: form_layout, progress, question_numbers, required_color, button_label), or theme null for the default. (waits for approval: external)\n- list_email_templates: Your email templates.\n- save_email_template: Create or change an email template (template_id?, name, subject, body with custom values).\n- delete_email_template: Delete an email template (template_id). (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"calendars","x-ctrl-scopes":{"list":["bookings:read"],"get":["bookings:read"],"availability":["bookings:read"],"set_host_hours":["bookings:write"],"set_override":["bookings:write"],"delete_override":["bookings:write"],"add_holidays":["bookings:write"],"get_notifications":["bookings:read"],"set_notifications":["bookings:write"],"get_settings":["bookings:read"],"set_settings":["bookings:write"],"get_theme":["bookings:read"],"set_theme":["bookings:write"],"bookings":["bookings:read"],"notices":["bookings:read"],"cancel_booking":["bookings:write"],"reschedule_booking":["bookings:write"],"book":["bookings:write"],"series":["bookings:read"],"cancel_series":["bookings:write"],"external_calendars":["bookings:read"],"set_external_calendars":["bookings:write"],"check_external_calendar":["bookings:write"],"disconnect_external_calendar":["bookings:write"],"move_series":["bookings:write"],"get_lead_pipeline":["bookings:read"],"set_lead_pipeline":["bookings:write"],"set_outcome":["bookings:write"],"get_booking_status":["bookings:read"],"set_booking_status":["bookings:write"],"needs_status":["bookings:read"],"event_crm":["bookings:read"],"link_event":["bookings:write"],"unlink_event":["bookings:write"],"get_default_theme":["bookings:read"],"set_default_theme":["bookings:write"],"get_form_theme":["bookings:read"],"set_form_theme":["bookings:write"],"list_email_templates":["bookings:read"],"save_email_template":["bookings:write"],"delete_email_template":["bookings:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get","availability","set_host_hours","set_override","delete_override","add_holidays","get_notifications","set_notifications","get_settings","set_settings","get_theme","set_theme","bookings","notices","cancel_booking","reschedule_booking","book","series","cancel_series","external_calendars","set_external_calendars","check_external_calendar","disconnect_external_calendar","move_series","get_lead_pipeline","set_lead_pipeline","set_outcome","get_booking_status","set_booking_status","needs_status","event_crm","link_event","unlink_event","get_default_theme","set_default_theme","get_form_theme","set_form_theme","list_email_templates","save_email_template","delete_email_template"],"description":"What to do."},"calendar_id":{"type":"number"},"team_member_id":{"type":"number"},"hours":{"type":["object","array","null"]},"date":{"type":"string"},"closed":{"type":"boolean"},"label":{"type":"string"},"override_id":{"type":"number"},"region":{"type":"string","enum":["us","ca"]},"year":{"type":"number"},"duration":{"type":"number"},"exclude_booking_id":{"type":"number"},"notifications":{"type":"object"},"settings":{"type":"object","description":"set_settings: the calendar settings to change (merged)."},"theme":{"type":["object","null"]},"scope":{"type":"string","enum":["upcoming","past","cancelled","needs"]},"booking_id":{"type":"number"},"start":{"type":"string"},"end":{"type":"string","description":"book: the slot end (from availability)."},"email":{"type":"string","description":"book: the guest's email."},"phone":{"type":"string","description":"book: the guest's phone (optional)."},"notes":{"type":"string","description":"book: notes for the booking."},"recurrence":{"type":"object","description":"book: { frequency: weekly|biweekly|monthly, count: 2-52 } or { frequency, until: YYYY-MM-DD }."},"skip_conflicts":{"type":"boolean","description":"book: book the open dates of a series when some are taken."},"series_scope":{"type":"string","enum":["this","following","all"],"description":"cancel_series / move_series: which bookings of the series."},"connection_id":{"type":"number","description":"An Outlook / iCloud account id (external_calendars)."},"busy":{"type":"array","items":{"type":"string"},"description":"set_external_calendars: calendar ids that block booking times."},"write":{"type":["string","null"],"description":"set_external_calendars: the calendar id booking events go to, or null."},"outcome":{"type":["string","null"],"enum":["showed","no_show",null]},"form_id":{"type":"string","description":"The form id or public id."},"status":{"type":"string","enum":["booked","confirmed","showed","no_show","cancelled","invalid"]},"note":{"type":"string"},"event_id":{"type":"string","description":"Calendar event id (or \"<eventId>|<calendarId>\")."},"event_calendar_id":{"type":"string","description":"The Google calendar id the event is on."},"contact_id":{"type":"number"},"company_ids":{"type":"array","items":{"type":"number"}},"deal_id":{"type":"number"},"link_type":{"type":"string","enum":["crm_contact","crm_company","crm_deal","client","business"]},"link_id":{"type":"number"},"setting":{"type":["object","null"],"description":"set_lead_pipeline: the lead setting, or null to turn it off."},"template_id":{"type":"number"},"name":{"type":"string"},"subject":{"type":"string"},"body":{"type":"string"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/chat_channel_bookmarks": {"post":{"operationId":"tool_chat_channel_bookmarks","summary":"Channel bookmarks","description":"The links bar at the top of a chat channel that everyone in it sees: list, add, remove, reorder.\nActions:\n- list: The channel's bookmarks.\n- add: Add one: title, and a url or a message_id.\n- remove: Remove one (bookmark_id). (waits for approval: delete)\n- reorder: ordered_ids = bookmark ids in order.","tags":["tools"],"x-ctrl-tool":"chat_channel_bookmarks","x-ctrl-scopes":{"list":["messages:read"],"add":["messages:write"],"remove":["messages:write"],"reorder":["messages:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","add","remove","reorder"],"description":"What to do."},"channel_id":{"type":"number"},"bookmark_id":{"type":"number"},"title":{"type":"string"},"url":{"type":"string"},"message_id":{"type":"number"},"ordered_ids":{"type":"array","items":{"type":"number"}}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/chat_channels": {"post":{"operationId":"tool_chat_channels","summary":"Chat channels","description":"Create and manage Plodder chat channels and DMs as you. Read and post with read_chat_messages and post_chat_message; list them with list_chat_channels. Members: chat_members; join links: chat_invites.\nActions:\n- get: One channel: name, topic, type, members, your notification level, starred.\n- create: A new channel (name, optional description, workspace_id, private). You join it.\n- update: Change name, description, topic or type (public|private). Members only.\n- archive: Archive (delete) the channel. Members only; the default channel cannot be archived. (waits for approval: delete)\n- unarchive: Bring an archived channel back. Members only.\n- reorder: Sidebar order: ordered_ids = channel ids in order (channels you are in).\n- star: Star the channel for yourself.\n- unstar: Unstar it.\n- list_starred: Your starred channel ids.\n- set_notifications: Your notification level for the channel: level = all | mentions | nothing.\n- mark_read: Mark the channel read for you.\n- open_dm: Find or open a DM: user_ids (people who share a workspace with you), or with = a teammate's name, a bot's @username, or \"botfather\". Posts nothing.\n- open_bot_dm: Find or open the DM with one of your own bots (bot_id).","tags":["tools"],"x-ctrl-tool":"chat_channels","x-ctrl-scopes":{"get":["messages:read"],"create":["messages:write"],"update":["messages:write"],"archive":["messages:write"],"unarchive":["messages:write"],"reorder":["messages:write"],"star":["messages:write"],"unstar":["messages:write"],"list_starred":["messages:read"],"set_notifications":["messages:write"],"mark_read":["messages:write"],"open_dm":["messages:write"],"open_bot_dm":["messages:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["get","create","update","archive","unarchive","reorder","star","unstar","list_starred","set_notifications","mark_read","open_dm","open_bot_dm"],"description":"What to do."},"channel_id":{"type":"number"},"name":{"type":"string"},"description":{"type":"string"},"topic":{"type":"string"},"type":{"type":"string","enum":["public","private"]},"private":{"type":"boolean","description":"create: a private channel."},"workspace_id":{"type":"number"},"ordered_ids":{"type":"array","items":{"type":"number"}},"level":{"type":"string","enum":["all","mentions","nothing"]},"user_ids":{"type":"array","items":{"type":"number"},"description":"open_dm: the other people (you are added)."},"with":{"type":"string","description":"open_dm: a name, @bot username, or botfather."},"bot_id":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/chat_invites": {"post":{"operationId":"tool_chat_invites","summary":"Chat join links","description":"Join links for a chat channel: anyone with the link can join, so making one waits for approval.\nActions:\n- list: The channel's join links: url, uses, max uses, expiry.\n- create: A new join link (max_uses 1-1000 or none, expires_in_days 1-90, default 7). (waits for approval: external)\n- revoke: Turn a link off (invite_id).","tags":["tools"],"x-ctrl-tool":"chat_invites","x-ctrl-scopes":{"list":["messages:read"],"create":["messages:write"],"revoke":["messages:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","create","revoke"],"description":"What to do."},"channel_id":{"type":"number"},"invite_id":{"type":"number"},"max_uses":{"type":"number"},"expires_in_days":{"type":"number"}},"required":["action","channel_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/chat_labels": {"post":{"operationId":"tool_chat_labels","summary":"Chat sidebar labels","description":"Your own chat sidebar labels (groups of channels and DMs). A label with a rule is a smart label: rule = {kind: name|unread|dms|channels|private|clients|workspace, value?}.\nActions:\n- list: Your labels and which channel sits under which label.\n- create: A new label (name, optional rule).\n- update: Rename, change the rule, or collapse (label_id).\n- reorder: ordered_ids = label ids in order.\n- delete: Delete a label; its channels go back to Channels / DMs. (waits for approval: delete)\n- assign: Move a channel under a label (channel_id, label_id; label_id null = back to default).","tags":["tools"],"x-ctrl-tool":"chat_labels","x-ctrl-scopes":{"list":["messages:read"],"create":["messages:write"],"update":["messages:write"],"reorder":["messages:write"],"delete":["messages:write"],"assign":["messages:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","create","update","reorder","delete","assign"],"description":"What to do."},"label_id":{"type":["number","null"]},"name":{"type":"string"},"rule":{"type":"object"},"collapsed":{"type":"boolean"},"ordered_ids":{"type":"array","items":{"type":"number"}},"channel_id":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/chat_members": {"post":{"operationId":"tool_chat_members","summary":"Chat channel members","description":"Who is in a chat channel, and adding or removing people and agents, as you. Same access as the channel: anyone in a public channel, members of a private one.\nActions:\n- list: Members with names and roles.\n- add: Add a person (user_id) or an agent (agent_id), optional role. Adding a guest from outside the channel's workspace waits for approval. (may wait for approval)\n- remove: Remove a person (user_id) or an agent (agent_id). Waits for approval. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"chat_members","x-ctrl-scopes":{"list":["messages:read"],"add":["messages:write"],"remove":["messages:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","add","remove"],"description":"What to do."},"channel_id":{"type":"number"},"user_id":{"type":"number"},"agent_id":{"type":"string"},"role":{"type":"string","enum":["member","admin"]}},"required":["action","channel_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/chat_messages": {"post":{"operationId":"tool_chat_messages","summary":"Chat message actions","description":"Everything you do to chat messages besides posting: edit or delete your own, reactions, pins, your bookmarks, scheduled messages and reminders, as you.\nActions:\n- edit: Change the text of your own message (message_id, text). Everyone in the channel sees the edit. (waits for approval: post chat)\n- delete: Delete a message: your own, or any as a channel admin or owner. (waits for approval: delete)\n- react: Add an emoji reaction (message_id, emoji).\n- unreact: Remove your reaction.\n- pin: Pin a message in its channel.\n- unpin: Unpin it.\n- list_pins: Pinned messages in a channel (channel_id).\n- bookmark: Save a message to your bookmarks.\n- unbookmark: Remove it from your bookmarks.\n- list_bookmarks: Your bookmarked messages.\n- schedule: Schedule a message (channel_id, text, at = ISO time or epoch seconds in the future). It posts at that time. (waits for approval: post chat)\n- list_scheduled: Your scheduled messages that have not gone out.\n- cancel_scheduled: Cancel one of your scheduled messages (message_id). Nothing was posted yet.\n- remind: Remind you about a message later (message_id, at). Only you see it.\n- list_reminders: Your open reminders (due_only for the ones due now).\n- dismiss_reminder: Dismiss one of your reminders (reminder_id).","tags":["tools"],"x-ctrl-tool":"chat_messages","x-ctrl-scopes":{"edit":["messages:write"],"delete":["messages:write"],"react":["messages:write"],"unreact":["messages:write"],"pin":["messages:write"],"unpin":["messages:write"],"list_pins":["messages:read"],"bookmark":["messages:write"],"unbookmark":["messages:write"],"list_bookmarks":["messages:read"],"schedule":["messages:write"],"list_scheduled":["messages:read"],"cancel_scheduled":["messages:write"],"remind":["messages:write"],"list_reminders":["messages:read"],"dismiss_reminder":["messages:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["edit","delete","react","unreact","pin","unpin","list_pins","bookmark","unbookmark","list_bookmarks","schedule","list_scheduled","cancel_scheduled","remind","list_reminders","dismiss_reminder"],"description":"What to do."},"message_id":{"type":"number"},"channel_id":{"type":"number"},"text":{"type":"string"},"emoji":{"type":"string"},"at":{"type":["string","number"],"description":"ISO time or epoch seconds."},"reminder_id":{"type":"number"},"due_only":{"type":"boolean"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/chat_polls": {"post":{"operationId":"tool_chat_polls","summary":"Chat polls","description":"Polls in chat: ask one (it posts in the channel), read results, vote, close or reopen.\nActions:\n- create: Post a poll (channel_id, question, options 2-10, multi, anonymous). (waits for approval: post chat)\n- get: A poll with its tally and (unless anonymous) who voted what.\n- vote: Your vote: options = option indexes (0-based); an empty list withdraws your vote.\n- close: Close the poll (the person who asked, or the owner).\n- reopen: Reopen it.","tags":["tools"],"x-ctrl-tool":"chat_polls","x-ctrl-scopes":{"create":["messages:write"],"get":["messages:read"],"vote":["messages:write"],"close":["messages:write"],"reopen":["messages:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["create","get","vote","close","reopen"],"description":"What to do."},"channel_id":{"type":"number"},"poll_id":{"type":"number"},"question":{"type":"string"},"options":{"type":"array","items":{"type":["string","number"]},"description":"create: option texts; vote: option indexes."},"multi":{"type":"boolean"},"anonymous":{"type":"boolean"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/chat_share_items": {"post":{"operationId":"tool_chat_share_items","summary":"Share linked items with a channel","description":"Before posting a message that links docs, tasks, files or forms, check which channel members cannot open them, then give those members view access (the same grant as the Share dialog). Only items you can manage can be shared.\nActions:\n- check: Items linked in `content` (entity chips or app links) that some channel members cannot open: type, id, title, missing members, can_share.\n- share: Give every channel member who cannot open these items view access. items = [{type, id}].","tags":["tools"],"x-ctrl-tool":"chat_share_items","x-ctrl-scopes":{"check":["admin:read"],"share":["admin:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["check","share"],"description":"What to do."},"channel_id":{"type":"number"},"content":{"type":"string","description":"check: the message text."},"items":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"id":{"type":"string"}}},"description":"share: the items."}},"required":["action","channel_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/clients_admin": {"post":{"operationId":"tool_clients_admin","summary":"Clients and businesses","description":"Create and delete client profiles and businesses, and manage a client's businesses, as you (/api/clients, /api/businesses, /api/clients/businesses). Change fields with update_client and update_business; list with list_clients and list_businesses.\nActions:\n- create_client: A new client profile (name, workspace_id?, set?).\n- delete_client: Delete a client profile. (waits for approval: delete)\n- create_business: A new business (name, website?, client_id?, workspace_id?, set?). It is backed by a CRM company.\n- delete_business: Delete a business. (waits for approval: delete)\n- client_businesses: A client's businesses.\n- add_client_business: Add a business to a client (client_id, name, website?, set?).\n- update_client_business: Change one of a client's businesses (business_id, set).\n- remove_client_business: Delete one of a client's businesses (business_id). (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"clients_admin","x-ctrl-scopes":{"create_client":["crm:write"],"delete_client":["crm:write"],"create_business":["crm:write"],"delete_business":["crm:write"],"client_businesses":["crm:read"],"add_client_business":["crm:write"],"update_client_business":["crm:write"],"remove_client_business":["crm:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["create_client","delete_client","create_business","delete_business","client_businesses","add_client_business","update_client_business","remove_client_business"],"description":"What to do."},"client_id":{"type":"number"},"business_id":{"type":"number"},"workspace_id":{"type":"number"},"name":{"type":"string"},"website":{"type":"string"},"set":{"type":"object","description":"Other profile / business fields."},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/code_files": {"post":{"operationId":"tool_code_files","summary":"Code files","description":"Read and write code files in Drive, as you: files made in the code editor (id) and code-type uploads (.js .ts .py .json .css .html .md ..., drive_file). Edit with a find and replace (edit) or write the whole text (write).\nActions:\n- list: Code files you can open, newest first.\n- get: A code file: title, type, revision, content, where it sits in Drive.\n- create: A new code file: title (with extension sets the language), file_type, content, workspace_id or folder_id.\n- write: Replace the whole content (base_rev from get; force=true overwrites a newer change).\n- edit: Replace old_text with new_text (exactly once unless all=true).\n- rename: Change the title (an extension also sets the language).\n- set_type: Change the language (file_type, an extension like \"py\").\n- move: Move in Drive: workspace_id and/or folder_id.\n- trash: Move to Drive Trash. (waits for approval: delete)\n- drive_get: A code-type Drive upload (drive_file): content and its sha256.\n- drive_write: Save a Drive upload's text: content (base_sha from drive_get; force=true overwrites), or old_text/new_text.","tags":["tools"],"x-ctrl-tool":"code_files","x-ctrl-scopes":{"list":["docs:read"],"get":["docs:read"],"create":["docs:write"],"write":["docs:write"],"edit":["docs:write"],"rename":["docs:write"],"set_type":["docs:write"],"move":["docs:write"],"trash":["docs:write"],"drive_get":["docs:read"],"drive_write":["docs:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get","create","write","edit","rename","set_type","move","trash","drive_get","drive_write"],"description":"What to do."},"id":{"type":"string","description":"Code file id or public id."},"drive_file":{"type":"string","description":"Drive file id (an uploaded code file)."},"title":{"type":"string"},"file_type":{"type":"string"},"content":{"type":"string"},"old_text":{"type":"string"},"new_text":{"type":"string"},"all":{"type":"boolean"},"base_rev":{"type":"number"},"base_sha":{"type":"string"},"force":{"type":"boolean"},"workspace_id":{"type":"number"},"folder_id":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/coding_sessions": {"post":{"operationId":"tool_coding_sessions","summary":"Coding sessions","description":"Code mode: a coding agent working on a GitHub repository in a sandbox or a hosted runtime (Cursor, Codex cloud, Claude Code), as you (/api/ai-agents/coding*). A turn runs in the background; read progress with get. Review notes are the comments on a session's Changes. Pull requests themselves: github_pulls.\nActions:\n- get: A session (session_id, or thread_id of its conversation) with its change sets.\n- start: A new coding conversation and its first turn: prompt, repository (owner/name or URL), branch? (default main), kind? (runtime), agent_id?, size?.\n- turn: The next turn of a session (session_id, prompt).\n- stop: Stop the session's runtime (session_id). remove=true also deletes the sandbox (waits for approval: delete). (may wait for approval)\n- link_pr: Link a pull request by hand (session_id, pr_url); its diff is read into Changes.\n- changes: The diff of a change set (session_id, set_id? default latest), as file paths with added/removed line counts and hunks.\n- notes: Review notes on the session's changes.\n- add_note: A review note: file_path, body, line?, side? L|R.\n- resolve_note: Mark a note resolved or not (note_id, resolved).\n- delete_note: Delete your own note (note_id). (waits for approval: delete)\n- runtimes: Which coding runtimes are ready for you, and the Codex environment.\n- set_codex_environment: Pick the Codex cloud environment (environment_id, environment_label?). The Cursor API key is entered by you in Settings > Coding agents.\n- clear_codex_environment: Forget the Codex environment choice.\n- link_task: The task a session works on (session_id, task: id, CTRL-TASK-<id> or task link; empty to unlink). The session shows on the task and its pull request counts as the task's.\n- task_pull_requests: Pull requests linked to a task (task), through coding sessions or a reference in their description, with state.\n- preferences: Your coding preferences: track work with tasks, open pull requests.\n- set_preferences: Change coding preferences (create_tasks?, open_pull_requests?).","tags":["tools"],"x-ctrl-tool":"coding_sessions","x-ctrl-scopes":{"get":["automations:read"],"start":["automations:write"],"turn":["automations:write"],"stop":["automations:write"],"link_pr":["automations:write"],"changes":["automations:read"],"notes":["automations:read"],"add_note":["automations:write"],"resolve_note":["automations:write"],"delete_note":["automations:write"],"runtimes":["automations:read"],"set_codex_environment":["automations:write"],"clear_codex_environment":["automations:write"],"link_task":["automations:write"],"task_pull_requests":["automations:read"],"preferences":["automations:read"],"set_preferences":["automations:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["get","start","turn","stop","link_pr","changes","notes","add_note","resolve_note","delete_note","runtimes","set_codex_environment","clear_codex_environment","link_task","task_pull_requests","preferences","set_preferences"],"description":"What to do."},"session_id":{"type":"number"},"thread_id":{"type":"number"},"task":{"type":"string"},"create_tasks":{"type":"boolean"},"open_pull_requests":{"type":"boolean"},"prompt":{"type":"string"},"repository":{"type":"string"},"branch":{"type":"string"},"kind":{"type":"string"},"agent_id":{"type":"number"},"size":{"type":"string"},"remove":{"type":"boolean"},"pr_url":{"type":"string"},"set_id":{"type":"number"},"note_id":{"type":"number"},"file_path":{"type":"string"},"body":{"type":"string"},"line":{"type":"number"},"side":{"type":"string","enum":["L","R"]},"resolved":{"type":"boolean"},"environment_id":{"type":"string"},"environment_label":{"type":"string"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/commissions": {"post":{"operationId":"tool_commissions","summary":"Commissions (closers and setters)","description":"Pay closers and setters (CRM > Commissions). Payees are team members (they see their own lines) or outside people, with roles closer / setter and rates (percent and / or flat per close) over the workspace default. Basis: cash collected (each payment received counts in its month, the default) or deal value when won. Who is paid on a payment: a Closer / Setter picked on the deal wins, else the payment provider's own seller (Whop affiliate, FanBasis rep, Square team member, Stripe metadata) matched to a payee by the payee's provider ids or email (or a Plodder user it names: their email, or in Stripe metadata their user id), else the host of the buyer's closing call (their latest booking, matched by any of their emails, not cancelled or a no-show, before the payment and within the window in settings closing_calls), else the deal owner. Payments whose buyer email matches no CRM person are listed by list_no_person (link_person ties them). A provider seller that matches no payee is listed by list_needs_payee and its payments make no line until link_seller / create_payee_for_seller. Each payment is its own line with a mark: pending (expected, never payable), received, part_refunded, refunded, cancelled; a won deal's unpaid balance is one pending line. Refunds claw back in the month they happen (only what was counted). Months are statements: draft until finalized (lines locked), then sent to QuickBooks Online as a bill per payee; Paid comes back from QuickBooks. Money is in cents in the workspace currency. period = YYYY-MM, a month name, or \"last month\".\nActions:\n- overview: The month (period): per payee earned, adjustments, payable, paid, owed and status, plus totals. Brings the ledger up to date first.\n- list_payees: The sales team with roles, own rules (null = default) and QuickBooks vendor.\n- get_payee: One payee (payee_id): rules in effect, lines (period or the last 12 months), statements, months.\n- create_payee: Add a payee: member_key (a team member) or name + email? (an outside person), contact_id?, roles?, rules?, notes?, provider_refs?.\n- update_payee: Change a payee (payee_id): name, email, contact_id, roles, rules (null = default), active, notes, provider_refs (their ids at Stripe / Square / Whop / FanBasis).\n- remove_payee: Remove a payee (payee_id): gone if they never earned, else made inactive (history stays). (waits for approval: delete)\n- get_deal_roles: Closer and Setter on a deal (deal_id); closer from_owner = the deal owner's payee.\n- set_deal_roles: Set a deal's closer_payee_id and / or setter_payee_id (null clears; closer cleared = the deal owner).\n- list_lines: Ledger lines (payee_id?, period? (or \"open\" = pending and cancelled lines), deal_id?, status?). Each has status (the payment mark), provider, payment_ref and attribution (deal, provider, booking, owner) with attribution_note for a booking (\"from booking <calendar> on <date>\").\n- list_no_person: Paid payments whose buyer email matches no CRM person (email, provider, payments, amount_cents, payment_ids).\n- link_person: Tie every payment with this buyer email (email) to a person (contact_id): the email becomes one of their addresses, then the ledger syncs.\n- list_needs_payee: Payment provider sellers that match no payee (provider, role, ref, name, email, payments, amount_cents). Their payments make no line until linked.\n- link_seller: Link a provider seller (provider, ref) to a payee (payee_id): the ref becomes one of the payee's provider ids, then the ledger syncs and their payments make their lines.\n- create_payee_for_seller: Make a payee from a provider seller (provider, ref, name?, email?, role?, member_key?) with that provider id, then sync.\n- add_adjustment: A bonus or deduction for a payee (payee_id, period, amount, note). Refused when that month is finalized.\n- remove_adjustment: Remove an adjustment (line_id) while its month is a draft. (waits for approval: delete)\n- sync: Bring the ledger up to date now (it also runs every 10 minutes).\n- recalculate: Rebuild a month's automatic lines after a rate change (period). Draft months only; adjustments stay.\n- list_statements: Statements (period?, payee_id?).\n- generate_statements: Draft statements for a month (period), one per payee with lines (all: every active payee too).\n- finalize_statement: Finalize a statement (statement_id): locks its lines and freezes the totals. Without statement_id: every draft of the period.\n- reopen_statement: Unlock a finalized statement (statement_id), only before it is a bill in QuickBooks.\n- get_settings: The workspace default rules, time zone, first month counted, currency and QuickBooks bill settings.\n- update_settings: Change settings (settings: { rules?, timezone?, start_period?, qbo_account_id?, qbo_account_name?, qbo_line_mode?, qbo_due_days? }).\n- quickbooks_status: Is QuickBooks connected (company, sandbox or production, home currency, last error). Never a token.\n- list_vendors: QuickBooks vendors (q? name contains).\n- list_accounts: QuickBooks expense accounts (for qbo_account_id).\n- link_vendor: Link a payee (payee_id) to a QuickBooks vendor (vendor_id; null unlinks).\n- create_vendor: Make a QuickBooks vendor from a payee's name and email (payee_id) and link it (an existing vendor with that name is linked instead). (waits for approval: external)\n- send_to_quickbooks: Send a finalized statement (statement_id) to QuickBooks as a bill for the payee (a second send updates the same unpaid bill). (waits for approval: money)","tags":["tools"],"x-ctrl-tool":"commissions","x-ctrl-scopes":{"overview":["commissions:read"],"list_payees":["commissions:read"],"get_payee":["commissions:read"],"create_payee":["commissions:write"],"update_payee":["commissions:write"],"remove_payee":["commissions:write"],"get_deal_roles":["commissions:read"],"set_deal_roles":["commissions:write"],"list_lines":["commissions:read"],"list_no_person":["commissions:read"],"link_person":["commissions:write"],"list_needs_payee":["commissions:read"],"link_seller":["commissions:write"],"create_payee_for_seller":["commissions:write"],"add_adjustment":["commissions:write"],"remove_adjustment":["commissions:write"],"sync":["commissions:write"],"recalculate":["commissions:write"],"list_statements":["commissions:read"],"generate_statements":["commissions:write"],"finalize_statement":["commissions:write"],"reopen_statement":["commissions:write"],"get_settings":["commissions:read"],"update_settings":["commissions:write"],"quickbooks_status":["commissions:read"],"list_vendors":["commissions:read"],"list_accounts":["commissions:read"],"link_vendor":["commissions:write"],"create_vendor":["commissions:write"],"send_to_quickbooks":["commissions:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["overview","list_payees","get_payee","create_payee","update_payee","remove_payee","get_deal_roles","set_deal_roles","list_lines","list_no_person","link_person","list_needs_payee","link_seller","create_payee_for_seller","add_adjustment","remove_adjustment","sync","recalculate","list_statements","generate_statements","finalize_statement","reopen_statement","get_settings","update_settings","quickbooks_status","list_vendors","list_accounts","link_vendor","create_vendor","send_to_quickbooks"],"description":"What to do."},"period":{"type":"string","description":"YYYY-MM, a month name (september) or \"last month\". Default: this month."},"payee_id":{"type":"number"},"deal_id":{"type":"number"},"statement_id":{"type":"number"},"line_id":{"type":"number"},"member_key":{"type":"string","description":"create_payee: a team member: their id from list_team_members, their user_id or their email. Their login sees their own commissions."},"name":{"type":"string"},"email":{"type":"string"},"contact_id":{"type":"number","description":"Optional CRM person for the payee."},"roles":{"type":"array","items":{"type":"string","enum":["closer","setter"]}},"rules":{"type":"object","description":"{ basis?: cash | won, closer?: { pct, flat_cents }, setter?: { pct, flat_cents }, pipelines?: [ids] | null, product?: \"contains\" }. On a payee: only what differs from the default (null = all default)."},"active":{"type":"boolean"},"notes":{"type":"string"},"closer_payee_id":{"type":["number","null"],"description":"set_deal_roles: null = the deal owner's payee."},"setter_payee_id":{"type":["number","null"]},"amount":{"type":["number","string"],"description":"Adjustment in the currency: 250 = bonus, -100 = deduction."},"amount_cents":{"type":"number"},"note":{"type":"string"},"all":{"type":"boolean","description":"generate_statements: also a statement for active payees with no lines."},"vendor_id":{"type":["string","null"],"description":"link_vendor: a QuickBooks vendor id (null unlinks)."},"q":{"type":"string","description":"list_vendors: name contains."},"settings":{"type":"object","description":"update_settings: { rules?, timezone?, start_period?, qbo_account_id?, qbo_account_name?, qbo_line_mode?: summary | detail, qbo_due_days?, provider_closer?: { stripe?, square?, whop?, fanbasis?: boolean }, closing_calls?: { on?, window_days? (1-365, default 60), closing_calendars?: calendar ids | null (null = every calendar), setting_calendars?: calendar ids | null (their host is the setter) } }"},"status":{"type":"string","enum":["pending","received","part_refunded","refunded","cancelled"],"description":"list_lines: only lines with this payment mark."},"provider":{"type":"string","enum":["stripe","square","whop","fanbasis"],"description":"link_seller / create_payee_for_seller: the provider of the seller."},"ref":{"type":"string","description":"link_seller / create_payee_for_seller: the seller's id, username or email at the provider (from list_needs_payee)."},"role":{"type":"string","enum":["closer","setter"],"description":"create_payee_for_seller: their role (default closer)."},"provider_refs":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","enum":["stripe","square","whop","fanbasis"]},"value":{"type":"string"}}},"description":"create_payee / update_payee: the payee's ids at each provider (replaces the list on update)."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/complete_task": {"post":{"operationId":"tool_complete_task","summary":"Complete a task","description":"Mark a task done. Pass a short result note (20+ characters) when the task was created by an AI agent; it is required there and saved to the task thread.","tags":["tools"],"x-ctrl-tool":"complete_task","x-ctrl-scopes":{"*":["tasks:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"task_id":{"type":["number","string"]},"result":{"type":"string","description":"What was done."}},"required":["task_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/conversation_turn": {"post":{"operationId":"tool_conversation_turn","summary":"Conversation turn","description":"Act in the conversation you are answering right now (only inside a Conversation AI turn). Save what they told you, move along the pathway, reply, send a photo from the media library, offer real times and book one, update the CRM, hand off to a person, end, and score the thread. Nothing is sent from here: your reply goes out after the guard rails, with human pacing (or as a draft for a person to approve).\nActions:\n- set_vars: Save answers: vars = { name: value } (the variables the current step asks for; numbers as numbers, yes / no as yes or no).\n- go_to: Move to a way forward listed in the context (target = its node id, or \"return\" to go back after a global step). Call it BEFORE reply when their message fits a way forward; the result tells you what to do at the new step.\n- reply: The message to send them (text). One reply per turn; keep it short on texts.\n- offer_times: Real open times on the playbook calendar (or the step's). Present them plainly.\n- book: Book one offered time: slot_id (s1, s2...) and confirmed = true only after they said yes to that exact time. Books once.\n- send_media: Send one photo / short video from the media library listed under PHOTOS (media_id). It goes out right after your reply text; at most the playbook's limit per conversation, never the same one twice. Say something natural about it in your reply.\n- tag: Add a tag (tag), or remove it (remove = true).\n- update_field: Set a person field: field (name | email | phone | title | lead_source | notes | f:<custom field id>), value.\n- move_stage: Move their open deal (or make one): pipeline_id, stage_id.\n- hand_off: A person takes over (reason): they asked for a person, you cannot answer, anything sensitive. You may reply one short line that someone will follow up.\n- end: End the conversation: outcome = qualified | disqualified | booked | not_now | other (message optional, your last line).\n- score: Every turn: level = hot | warm | cold, reason = one short line.\n- note: A note on the timeline for the team (text).\n- call_tool: Call a tool the current step lists (name = the tool, input = { field: value } per its input). Its answer comes back here and fills variables.","tags":["tools"],"x-ctrl-tool":"conversation_turn","x-ctrl-scopes":{"set_vars":["automations:write"],"go_to":["automations:write"],"reply":["automations:write"],"offer_times":["automations:write"],"book":["automations:write"],"send_media":["automations:write"],"tag":["automations:write"],"update_field":["automations:write"],"move_stage":["automations:write"],"hand_off":["automations:write"],"end":["automations:write"],"score":["automations:write"],"note":["automations:write"],"call_tool":["automations:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["set_vars","go_to","reply","offer_times","book","send_media","tag","update_field","move_stage","hand_off","end","score","note","call_tool"],"description":"What to do."},"vars":{"type":"object","description":"set_vars: { variable: value }."},"target":{"type":"string"},"text":{"type":"string"},"slot_id":{"type":"string"},"confirmed":{"type":"boolean"},"tag":{"type":"string"},"remove":{"type":"boolean"},"field":{"type":"string"},"value":{"type":"string"},"pipeline_id":{"type":"number"},"stage_id":{"type":"string"},"reason":{"type":"string"},"outcome":{"type":"string"},"message":{"type":"string"},"level":{"type":"string"},"name":{"type":"string"},"input":{"type":"object","description":"call_tool: the tool input."},"media_id":{"type":"number","description":"send_media: the photo id from PHOTOS."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/create_calendar_event": {"post":{"operationId":"tool_create_calendar_event","summary":"Create a calendar event","description":"Add an event to your Google calendar. An event with guests sends them invitations, so it waits for your approval in Plodder first. An event with no guests is created right away.","tags":["tools"],"x-ctrl-tool":"create_calendar_event","x-ctrl-scopes":{"*":["bookings:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"start_time":{"type":"string","description":"ISO 8601 with offset, e.g. 2026-10-08T16:00:00-07:00. For all-day: YYYY-MM-DD."},"end_time":{"type":"string","description":"ISO 8601. For all-day: the next day YYYY-MM-DD."},"all_day":{"type":"boolean"},"description":{"type":"string"},"location":{"type":"string"},"guests":{"type":"array","items":{"type":"string"},"description":"Email addresses to invite. Needs approval."},"conferencing":{"type":"string","enum":["none","meet","zoom"],"description":"Add a video call."},"busy_status":{"type":"string","enum":["busy","free"]},"timezone":{"type":"string","description":"IANA zone, e.g. America/Vancouver."}},"required":["title","start_time","end_time"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/create_doc": {"post":{"operationId":"tool_create_doc","summary":"Create a doc","description":"Create a doc from markdown (headings, lists, checklists, code, bold/italic/links; $$ LaTeX $$ or a ```math fence for a math block; ![caption](file.mp4) or ![video](link) for a video block; a ```poll fence with the question line and \"- option\" lines, flags multi / anonymous / closed, for a poll people vote on).","tags":["tools"],"x-ctrl-tool":"create_doc","x-ctrl-scopes":{"*":["docs:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"content":{"type":"string","description":"Markdown."},"workspace_id":{"type":"number","description":"Workspace id (from list_workspaces). Defaults to your primary workspace."},"project_id":{"type":"number","description":"Attach to a project."}},"required":["title"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/create_project": {"post":{"operationId":"tool_create_project","summary":"Create a project","description":"Create a project with the standard Todo, In Progress and Done stages.","tags":["tools"],"x-ctrl-tool":"create_project","x-ctrl-scopes":{"*":["projects:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"workspace_id":{"type":"number","description":"Workspace id (from list_workspaces). Defaults to your primary workspace."},"color":{"type":"string","description":"Hex colour, optional."}},"required":["name"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/create_routine": {"post":{"operationId":"tool_create_routine","summary":"Schedule a routine","description":"Schedule work for later: a routine runs the instructions as an agent (or a bare model) on a cron schedule, or once at run_at (or say it in words with schedule), and reports to the user's Inbox or a chat channel. To follow up yourself, leave agent_id out (your own default agent). Use list_agents to delegate to another agent. Do not create a routine from inside a routine run unless the user asked for it, and never one per run.","tags":["tools"],"x-ctrl-tool":"create_routine","x-ctrl-scopes":{"*":["automations:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"instructions":{"type":"string","description":"What the agent should do each time it runs."},"cron":{"type":"string","description":"Repeat on this 5-field cron schedule, for example \"0 9 * * 1-5\" (9am on weekdays)."},"run_at":{"type":"string","description":"Run once at this ISO date-time (with offset), then turn off. Use instead of cron."},"timezone":{"type":"string","description":"IANA zone for cron, default America/Vancouver."},"schedule":{"type":"string","description":"The schedule in words instead of cron / run_at, read in the routine's time zone: \"every weekday at 9\", \"mondays and thursdays 4pm\", \"every 2 hours\", \"monthly on the 15th\" (repeat), or \"tomorrow 3pm\", \"next fri 9am\", \"in 2h\" (once)."},"agent_id":{"type":"number","description":"Run as this agent (list_agents). Default: your default agent."},"model":{"type":"string","description":"Run on this model with no agent setup instead."},"channel_id":{"type":"number","description":"Post results in this chat channel instead of the Inbox."}},"required":["name","instructions"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/create_task": {"post":{"operationId":"tool_create_task","summary":"Create a task","description":"Create a task inside a project. Every task needs a project: call list_projects first if you do not know the id. Assignee defaults to the project default, else you.","tags":["tools"],"x-ctrl-tool":"create_task","x-ctrl-scopes":{"*":["tasks:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Task title."},"project_id":{"type":["number","string"],"description":"Project id or public id (required)."},"description":{"type":"string","description":"Plain text or simple HTML."},"status":{"type":"string","enum":["backlog","todo","in_progress","blocked","review","done","cancelled","archived"],"description":"Default todo."},"priority":{"type":"string","enum":["urgent","high","medium","low"]},"assignee":{"type":"string","description":"Team member id, user_id, email or name (list_team_members), \"ai-<agent id>\" to give it to an agent (it starts working on it), or \"none\"."},"due_date":{"type":"string","description":"YYYY-MM-DD."},"start_date":{"type":"string","description":"YYYY-MM-DD."},"duration_minutes":{"type":"number"},"parent_task_id":{"type":"number","description":"Make this a subtask."}},"required":["title","project_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_activity": {"post":{"operationId":"tool_crm_activity","summary":"CRM activity","description":"A CRM record's timeline, logging a call made outside the app, and the discussion comments on a company or person, as you (/api/crm/calls, /api/crm/comments).\nActions:\n- timeline: Forms, bookings, meetings, signatures, deal moves, logged calls and sends for one company_id, contact_id or deal_id.\n- log_call: Log a call on a contact_id, company_id or deal_id: outcome connected|no-answer|voicemail|busy|wrong-number, duration_min?, notes?, at? (epoch s), direction? in|out.\n- comments: Discussion on a company_id or contact_id.\n- comment: Post a comment (company_id or contact_id, body, parent_id? to reply). @agent mentions get a reply.\n- edit_comment: Edit your own comment (comment_id, body).\n- delete_comment: Delete your own comment. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"crm_activity","x-ctrl-scopes":{"timeline":["crm:read"],"log_call":["calls:write"],"comments":["crm:read"],"comment":["crm:write"],"edit_comment":["crm:write"],"delete_comment":["crm:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["timeline","log_call","comments","comment","edit_comment","delete_comment"],"description":"What to do."},"company_id":{"type":"number"},"contact_id":{"type":"number"},"deal_id":{"type":"number"},"outcome":{"type":"string","enum":["connected","no-answer","voicemail","busy","wrong-number"]},"duration_min":{"type":"number"},"notes":{"type":"string"},"at":{"type":"number"},"direction":{"type":"string","enum":["in","out"]},"comment_id":{"type":"number"},"parent_id":{"type":"number"},"body":{"type":"string"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_assign": {"post":{"operationId":"tool_crm_assign","summary":"CRM round robin","description":"Round-robin rules that hand new deals and leads to teammates in turn, as you (/api/crm/assign). Members are team keys (deal_owner values).\nActions:\n- list: Every rule.\n- save: Create, or change with rule_id: fields = {scope: pipeline|source, pipeline_id?, source_kind? form|booking|any, source_ref?, members: [..], away?: [..], enabled?}.\n- delete: Delete a rule. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"crm_assign","x-ctrl-scopes":{"list":["crm:read"],"save":["crm:write"],"delete":["crm:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","save","delete"],"description":"What to do."},"rule_id":{"type":"number"},"fields":{"type":"object"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_automations": {"post":{"operationId":"tool_crm_automations","summary":"Automations","description":"Workflows (CTRL Automations: triggers, emails, texts, waits, branches, CRM steps), as you (/api/automations). A graph is { nodes: [{ id, type, x, y, config }], edges: [{ from, port, to }] }; call `catalog` for node types, ports and config fields, and `options` for the forms, pipelines, mailboxes and templates to pick from. Edits change the draft; `publish` makes it live. Custom values in text: {{contact.first_name}}, {{company.name}}, {{deal.value}}, {{form.answer.<question>}}, {{booking.start}}, {{workspace.<key>}}, {{values.<saved>}}, fallback {{contact.first_name|there}}.\nActions:\n- list: Your workflows with status, counts and folder_id, plus your folders.\n- get: One workflow: draft graph, settings, problems blocking publish, per-step counts, webhook URL (workflow_id).\n- catalog: Node types with their ports and config fields, trigger events, condition operators, starter templates.\n- options: Lists for pickers: forms, booking links, pipelines + stages, mailboxes, project templates, workspaces, projects, team, workflows, segments, fields, tags, custom values (workspace_id?, form_id?).\n- create: A new draft: name, template? (booking_reminders | client_onboarding | lead_follow_up | cold_outreach), graph?, settings?, workspace_id?, folder_id?.\n- update: Change the draft (Custom code steps are read-only for agents and API keys: only the account owner edits them in the app): workflow_id, name?, description?, graph?, settings? (allow_reentry, stop_on_reply, stop_on_booking, stop_on_bounce, send_window {days, start_hour, end_hour}, timezone, mailbox_daily_cap, mailbox_spacing_sec, allow_multiple_deals (once per deal instead of once per person), default_mailbox_id, default_from_email, default_from_name, default_reply_to, default_sms_number (the default sender for steps that leave it empty)), workspace_id?.\n- publish: Publish the draft as a new version and turn the workflow on (workflow_id, note?). (may wait for approval)\n- pause: Pause: no new people, everyone held where they are (workflow_id).\n- resume: Resume a paused workflow (workflow_id). (may wait for approval)\n- delete: Delete a workflow; people still in it are removed (workflow_id). (waits for approval: delete)\n- versions: Published versions (workflow_id).\n- restore_version: Load an old version into the draft (workflow_id, version).\n- enroll: Add people by hand: workflow_id, contact_ids and / or segment_id (max 2,000). (may wait for approval)\n- remove: Take people out: workflow_id, contact_ids or enrollment_ids.\n- contact_workflows: The workflows one person is in or was in (contact_id): name, state (active | waiting_until | waiting_reply | waiting | finished | removed | exited | failed), until, current step. Add with enroll, take out with remove.\n- enrollments: Who is in it: workflow_id, status? (active | completed | exited | removed | failed), limit?, offset?.\n- runs: Run history with each step's input and output: workflow_id, enrollment_id?, node_id?, contact_id?, limit?; plus send / open / reply counts per step and A/B subject.\n- test_step: Run one step on sample data, nothing sent or written: workflow_id, node_id, contact_id? (a real person) or sample? ({ contact: {...}, form: { answer: {...} } }).\n- values: Workspace custom values ({{workspace.key}}): workspace_id.\n- set_value: Add or change a workspace custom value: workspace_id, label, key?, value.\n- delete_value: Remove a workspace custom value: workspace_id, key.\n- bookings: Your recent and upcoming bookings with their showed / no-show mark.\n- booking_outcome: Mark a booking showed or no_show (or null to clear): booking_id, outcome. Starts the showed / no-show triggers. (may wait for approval)\n- folders: Your workflow folders: id, name, parent_id (null = top level), workflows in each.\n- create_folder: A new folder: name, parent_id? (a folder to nest it in).\n- update_folder: Rename a folder or move it under another one: folder_id, name?, parent_id? (null = top level).\n- delete_folder: Delete a folder (folder_id). Its workflows move to No folder and its subfolders move up; no workflow is deleted. (waits for approval: delete)\n- move: Put a workflow in a folder: workflow_id, folder_id (null = No folder).\n- connections: Slack / Google Sheets connections the Send Slack message and Add Google Sheets row steps post to (workspace_id?): id, kind, name, a hint of the URL (never the URL). Also the Apps Script to paste into a sheet.\n- save_connection: Add a connection or change one: kind (slack | sheets), name, url (Slack incoming webhook or Apps Script /exec URL), workspace_id; id? to rename or replace the URL.\n- delete_connection: Remove a connection (connection_id). Steps using it skip until another is picked. (waits for approval: delete)\n- scoring: Contact scoring: every named score (profiles: id, name, first, rules [{ event, value, points }], decay { points, days }), the events a rule can use and the presets (forms, emails, booked, lead, blank). The first score is the default (People Score column, {{contact.score}}).\n- create_score: A new named score: name?, preset? (forms | emails | booked | lead | blank: starting rules), rules? [{ event, value?, points }], decay? { points, days }.\n- set_scoring: Replace one score's rules and decay, or rename it: profile_id? (default the first score), name?, rules [{ event, value?, points }] (e.g. email_opened +1, booking_created +10, booking_showed +20, booking_no_show -10), decay? { points, days } (points 0 = off).\n- delete_score: Delete a named score (profile_id) with its rules, every person's value and its history. The next score becomes the default. (waits for approval: delete)\n- contact_scores: One person's value in every score (contact_id).\n- adjust_score: Move one person's score: contact_id, profile_id? (default the first score), points (+ or -), or set (the new score), reason?. Fires Contact score changed.\n- score_history: One person's score changes, newest first (contact_id, profile_id? for one score).\n- suppressions: The unsubscribe / bounce list automations never send to.\n- unsuppress: Take an address off the suppression list: channel (email | sms), address. (waits for approval: send email)","tags":["tools"],"x-ctrl-tool":"crm_automations","x-ctrl-scopes":{"list":["automations:read"],"get":["automations:read"],"catalog":["automations:read"],"options":["automations:read"],"create":["automations:write"],"update":["automations:write"],"publish":["automations:write"],"pause":["automations:write"],"resume":["automations:write"],"delete":["automations:write"],"versions":["automations:read"],"restore_version":["automations:write"],"enroll":["automations:write"],"remove":["automations:write"],"contact_workflows":["automations:read"],"enrollments":["automations:read"],"runs":["automations:read"],"test_step":["automations:read"],"values":["automations:read"],"set_value":["automations:write"],"delete_value":["automations:write"],"bookings":["automations:read"],"booking_outcome":["automations:write"],"folders":["automations:read"],"create_folder":["automations:write"],"update_folder":["automations:write"],"delete_folder":["automations:write"],"move":["automations:write"],"connections":["automations:read"],"save_connection":["automations:write"],"delete_connection":["automations:write"],"scoring":["automations:read"],"create_score":["automations:write"],"set_scoring":["automations:write"],"delete_score":["automations:write"],"contact_scores":["automations:read"],"adjust_score":["automations:write"],"score_history":["automations:read"],"suppressions":["automations:read"],"unsuppress":["automations:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get","catalog","options","create","update","publish","pause","resume","delete","versions","restore_version","enroll","remove","contact_workflows","enrollments","runs","test_step","values","set_value","delete_value","bookings","booking_outcome","folders","create_folder","update_folder","delete_folder","move","connections","save_connection","delete_connection","scoring","create_score","set_scoring","delete_score","contact_scores","adjust_score","score_history","suppressions","unsuppress"],"description":"What to do."},"workflow_id":{"type":"number"},"folder_id":{"type":["number","null"]},"parent_id":{"type":["number","null"]},"name":{"type":"string"},"description":{"type":"string"},"template":{"type":"string"},"graph":{"type":"object","description":"{ nodes, edges }"},"settings":{"type":"object"},"workspace_id":{"type":"number"},"form_id":{"type":"number"},"note":{"type":"string"},"version":{"type":"number"},"contact_ids":{"type":"array","items":{"type":"number"}},"enrollment_ids":{"type":"array","items":{"type":"number"}},"segment_id":{"type":"number"},"status":{"type":"string"},"enrollment_id":{"type":"number"},"node_id":{"type":"string"},"contact_id":{"type":"number"},"sample":{"type":"object"},"limit":{"type":"number"},"offset":{"type":"number"},"label":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"},"booking_id":{"type":"number"},"outcome":{"type":["string","null"],"enum":["showed","no_show",null]},"channel":{"type":"string","enum":["email","sms"]},"address":{"type":"string"},"connection_id":{"type":"number"},"rules":{"type":"array","items":{"type":"object"}},"profile_id":{"type":"number"},"preset":{"type":"string","enum":["forms","emails","booked","lead","blank"]},"decay":{"type":"object"},"points":{"type":"number"},"set":{"type":"number"},"reason":{"type":"string"},"id":{"type":"number"},"kind":{"type":"string","enum":["slack","sheets"]},"url":{"type":"string"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_bulk_actions": {"post":{"operationId":"tool_crm_bulk_actions","summary":"CRM bulk actions","description":"Bulk actions on people (CRM > Bulk actions), as you: add / remove a tag, add to / remove from a workflow, email, text or delete a selection, a People filter or a smart list, now, at a set time, or in batches (N people every M minutes), with the log (counts, progress, errors) and pause / resume / cancel. Each person goes through the same rules as the app's own buttons (consent, Do not contact, Mailjet opted-in only, workflow rules, Recently deleted for 30 days). The people are fixed when it is made.\nActions:\n- list: Bulk actions, newest first: kind, label, status (scheduled, running, paused, done, cancelled, failed), total, done, ok, skipped, failed, next batch, errors.\n- get: One bulk action (bulk_id).\n- create: kind tag_add | tag_remove | workflow_add | workflow_remove | email | sms | delete | instantly_add; who: contact_ids, filter (People filter) or segment_id; params: { tag } | { workflow_id } | instantly_add { campaign_id, custom? [{ field, as }] } | email { subject, body, via gmail | mailjet, account_id (Gmail), format rich | html | text, from_email?, from_name?, reply_to?, track? } | sms { body }; when now | at | batches, start_at, batch_size, batch_every_min. (may wait for approval)\n- pause: Stop a running or scheduled bulk action (bulk_id) until resume.\n- resume: Carry on (bulk_id): the batch that fell due goes now, the spacing after it stays. (may wait for approval)\n- cancel: Cancel (bulk_id): the people not reached yet are left as they are.","tags":["tools"],"x-ctrl-tool":"crm_bulk_actions","x-ctrl-scopes":{"list":["crm:read"],"get":["crm:read"],"create":["messages:send"],"pause":["crm:write"],"resume":["messages:send"],"cancel":["crm:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get","create","pause","resume","cancel"],"description":"What to do."},"bulk_id":{"type":"number"},"kind":{"type":"string","enum":["tag_add","tag_remove","workflow_add","workflow_remove","email","sms","delete","instantly_add"]},"contact_ids":{"type":"array","items":{"type":"number"}},"filter":{"type":"object","description":"A People filter (crm_contacts filter_fields)."},"segment_id":{"type":"number"},"params":{"type":"object"},"when":{"type":"string","enum":["now","at","batches"]},"start_at":{"type":["string","number"]},"batch_size":{"type":"number"},"batch_every_min":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_companies": {"post":{"operationId":"tool_crm_companies","summary":"CRM companies","description":"Create, change, merge and delete CRM companies (businesses), as you (POST/PATCH/DELETE /api/crm/companies), and find them with the relational filter engine (query, filter_fields: deals, people, payments, tags, custom fields). List and read them with crm_list_companies and crm_get_company.\nActions:\n- create: A new company from a domain (domain, optional name, stage_id). You own it. An existing domain returns that company (existed=true).\n- update: Change fields: fields = {name, stage_id, deal_owner, revenue, hidden, email_sync, notes, client_id, business_id}.\n- add_domain: Add a domain (value).\n- remove_domain: Remove a domain (value).\n- add_email: List a full email address on it (value).\n- remove_email: Remove a listed address (value).\n- add_address: An address lists it, a domain adds the domain (value).\n- enrich: Look its domain up again (name, icon).\n- refresh: Re-read its recent mail from your connected mailboxes.\n- merge: Merge company from_id into company_id (from_id is removed). (waits for approval: delete)\n- delete: Delete the company. (waits for approval: delete)\n- link_business: Link (or make) the CRM company for a business (business_id).\n- link_client: Link company_id (or null to unlink) to a client profile (client_id).\n- convert_to_client: Make company_id a client (optional workspace_id).\n- mark_churned: Mark company_id churned (PATCH /api/crm/companies/:id { churn }): churned_on (YYYY-MM-DD, default today), optional reason. Its won recurring deals churn from the same day (they stop counting in MRR), unless a deal also covers a business that has not churned. Fires the Client churned workflow trigger.\n- reactivate: Reactivate a churned company_id: clears the churn and reactivates the deals it churned (back in MRR). Fires Client reactivated.\n- query: Find businesses with the same engine as the CRM list: filter (the crm_contacts filter schema; field keys from action filter_fields), q (search), owner (\"\" | none | an owner key), stage (\"\" | none | open | closed | a stage id), activity (followup | recent), hidden (only | include), columns (field keys to return per row), sort (a field key) + dir, limit (<= 500), offset. Returns total and rows {id, name, values}.\n- filter_fields: Every business filter field and column (key, label, group, type, operators, options with their values), incl. custom fields f:<id> and the people fields p:<key> (a business matches when any of its people does).","tags":["tools"],"x-ctrl-tool":"crm_companies","x-ctrl-scopes":{"create":["crm:write"],"update":["crm:write"],"add_domain":["crm:write"],"remove_domain":["crm:write"],"add_email":["crm:write"],"remove_email":["crm:write"],"add_address":["crm:write"],"enrich":["crm:write"],"refresh":["crm:write"],"merge":["crm:write"],"delete":["crm:write"],"link_business":["crm:write"],"link_client":["crm:write"],"convert_to_client":["crm:write"],"mark_churned":["crm:write"],"reactivate":["crm:write"],"query":["crm:read"],"filter_fields":["crm:read"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["create","update","add_domain","remove_domain","add_email","remove_email","add_address","enrich","refresh","merge","delete","link_business","link_client","convert_to_client","mark_churned","reactivate","query","filter_fields"],"description":"What to do."},"filter":{"type":"object","description":"query: { mode: and|or, items: [{ field, op, value? }] }."},"q":{"type":"string","description":"search: businesses by name, domains, industry, location, their people (name, emails, phones), tag names, text custom fields; deals by title, source, person (name, emails, phones), businesses, tag names, text custom fields"},"owner":{"type":"string"},"stage":{"type":"string"},"activity":{"type":"string"},"hidden":{"type":"string","enum":["only","include"]},"pipeline":{"type":"string"},"columns":{"type":"array","items":{"type":"string"}},"sort":{"type":"string"},"dir":{"type":"string","enum":["asc","desc"]},"offset":{"type":"number"},"limit":{"type":"number"},"company_id":{"type":"number"},"domain":{"type":"string","description":"create: the company domain, like acme.com."},"name":{"type":"string"},"stage_id":{"type":"string"},"value":{"type":"string","description":"The domain, email or address for the add_/remove_ actions."},"fields":{"type":"object","description":"update: the fields to change."},"from_id":{"type":"number","description":"merge: the company that goes away."},"business_id":{"type":"number"},"client_id":{"type":"number"},"workspace_id":{"type":"number"},"churned_on":{"type":"string","description":"mark_churned: the churn day, YYYY-MM-DD (default today)."},"reason":{"type":"string","description":"mark_churned: why (optional)."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_contacts": {"post":{"operationId":"tool_crm_contacts","summary":"CRM people","description":"Create, change, merge and delete people in the CRM, set do-not-contact flags and lead source, find duplicates, and SEARCH people with the smart-list filter engine (the People list, segments, smart lists and workflow filters run the same SQL), as you (/api/crm/contacts, /api/crm/contacts/query, /api/crm/duplicates). Filter schema: filter = { mode: \"and\"|\"or\", items: [ { field, op, value? } | a nested group (one level) ] }. field = a key from action filter_fields (contact info, tags, custom fields f:<id>, source / UTM, consent, SMS, email, calls, conversations, pipelines, calendars, forms, workflows, speed to lead, payments, business, activity). op by type: text contains|not_contains|is|is_not|empty|not_empty; number / money eq|ne|gt|gte|lt|lte|empty|not_empty; date last_days|older_days|next_days (value = days)|before|after|on (value = YYYY-MM-DD)|empty|not_empty; select / multi / person any|none|all|empty|not_empty (value = array of option values from filter_fields); bool true|false. Related-record fields (deal stage, workflow, form, calendar...) mean \"has one that matches\" for any and \"has none that matches\" for none.\nActions:\n- search: People matching a filter (filter, q, owner, columns = extra field keys to return, sort = a field key, dir asc|desc, limit <= 500, offset). Returns total + the page.\n- filter_fields: Every people filter field (key, label, group, type, operators, options with their values) and the GHL filters that are not available.\n- get: One person with company, consent and lead source.\n- create: A new person (email, optional name, title, company_id). You own them; lead source \"manual\".\n- update: fields = {name, title, phone, notes, hidden, deal_owner, lead_source, lead_medium, lead_campaign, dnc_email, dnc_sms, dnc_call, unsubscribed, consent_reason, address_street, address_city, address_region, address_postal, address_country, timezone (IANA, like America/Vancouver: texting hours, pathways and the dialer use it), birthday (YYYY-MM-DD, or MM-DD with no year: the Birthday trigger reads it), website, email_label, phone_label}.\n- channels: Every email and phone of a person (contact_id), primary first, with labels. Sends use the primary; matching (forms, texts, payments, leads, replies), search, filters and duplicates use them all.\n- add_channel: Add an email or phone: contact_id, channel (email|phone), value, label (work|home|mobile|personal|other or your own), primary (true = make it the primary now). A person with no real primary gets it as the primary. A value another person has is refused (merge them instead).\n- remove_channel: Remove an email or phone (contact_id, channel, value). Removing the primary promotes the next one; their only email stays (change it instead).\n- make_primary: Make one of their emails or phones the primary (contact_id, channel, value); the old primary becomes an extra.\n- label_channel: Label an email or phone (contact_id, channel, value, label; empty clears).\n- matches: What is tied to a person (contact_id) through any of their emails: bookings (invitee email), payments (with the buyer email the provider gave) and payment customer records. Payments and closing calls (commissions) match by these emails.\n- changes: The change log of a person (contact_id): who changed which field, old -> new, when (90 days, newest first). field (e.g. email, timezone, f:<custom field id>, tags) and limit (<= 500) optional.\n- set_consent: Do-not-contact flags for one or many people: contact_ids + fields {dnc_email?, dnc_sms?, dnc_call?, unsubscribed?, consent_reason?}.\n- delete: Delete the person. They go to Recently deleted for 30 days (restore brings them back with their deals, tags, fields and timeline); their workflow runs end. Returns batch. (waits for approval: delete)\n- delete_many: Delete several people (contact_ids, at most 500) to Recently deleted. Returns batch: restore with that batch undoes it. (waits for approval: delete)\n- deleted: Recently deleted (last 30 days): people (id, name, email, deleted_at, deleted_by, days_left) and deals. q filters by name / email / title.\n- restore: Restore deleted people: contact_ids, or batch (from a delete). Someone added again with the same email at the same business since is not restored (the reason says so).\n- duplicates: Groups of companies or people that look like duplicates.\n- merge: Merge duplicates: kind company|person, into (kept), from (ids that go away, max 50). A person merged from another company keeps that company as one of their businesses. (waits for approval: delete)\n- businesses: Every business the person is part of (primary first, with their role there).\n- add_business: Add the person to a business (company_id, optional role). Their primary stays.\n- remove_business: Take the person out of a business (company_id). Not their primary: set another primary first. Their deals stay theirs (the business stays on them as recorded).\n- set_primary_business: Make one business (company_id) their primary; the old primary stays one of their businesses.\n- set_business_role: Their role at a business (company_id, role; empty clears).","tags":["tools"],"x-ctrl-tool":"crm_contacts","x-ctrl-scopes":{"search":["crm:read"],"filter_fields":["crm:read"],"get":["crm:read"],"create":["crm:write"],"update":["crm:write"],"channels":["crm:read"],"add_channel":["crm:write"],"remove_channel":["crm:write"],"make_primary":["crm:write"],"label_channel":["crm:write"],"matches":["crm:read"],"changes":["crm:read"],"set_consent":["crm:write"],"delete":["crm:write"],"delete_many":["crm:write"],"deleted":["crm:read"],"restore":["crm:write"],"duplicates":["crm:read"],"merge":["crm:write"],"businesses":["crm:read"],"add_business":["crm:write"],"remove_business":["crm:write"],"set_primary_business":["crm:write"],"set_business_role":["crm:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["search","filter_fields","get","create","update","channels","add_channel","remove_channel","make_primary","label_channel","matches","changes","set_consent","delete","delete_many","deleted","restore","duplicates","merge","businesses","add_business","remove_business","set_primary_business","set_business_role"],"description":"What to do."},"contact_id":{"type":"number"},"role":{"type":"string","description":"add_business / set_business_role: their role there, like Owner."},"contact_ids":{"type":"array","items":{"type":"number"}},"batch":{"type":"string","description":"restore: the batch a delete returned (undo)."},"channel":{"type":"string","enum":["email","phone"],"description":"add_channel / remove_channel / make_primary / label_channel"},"value":{"type":"string","description":"The email address or phone number."},"label":{"type":"string"},"primary":{"type":"boolean"},"field":{"type":"string","description":"changes: only this field."},"email":{"type":"string"},"name":{"type":"string"},"title":{"type":"string"},"company_id":{"type":"number"},"fields":{"type":"object"},"kind":{"type":"string","enum":["company","person"]},"into":{"type":"number"},"from":{"type":"array","items":{"type":"number"}},"filter":{"type":"object","description":"search: { mode: and|or, items: [{ field, op, value }] }"},"q":{"type":"string","description":"search: text in name, any email or phone (digits match too), business, job title, tag names, text custom fields, deal names"},"owner":{"type":"string","description":"search: an owner key, or none"},"columns":{"type":"array","items":{"type":"string"},"description":"search: field keys to return per person"},"sort":{"type":"string"},"dir":{"type":"string","enum":["asc","desc"]},"limit":{"type":"number"},"offset":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_conversations": {"post":{"operationId":"tool_crm_conversations","summary":"CRM Conversations","description":"CRM Conversations (the team inbox of texts, email and Facebook / Instagram DMs), as you: saved views, the inbox with every filter, messages scheduled to send later, and response times. Scheduled texts keep texting hours (in the person's time zone): a text scheduled outside them goes at the start of the next window and note says so. A DM must go inside Meta's 24-hour window. Response times: with the target on, a message from a person that waits longer than N minutes of business time alerts the assignee (else the person's owner) and / or you, once. To reply now use the phone tool (reply, with cc / bcc for email).\nActions:\n- threads: Threads in a box with filters ({ box?: team | mine | unassigned | unread | done | scheduled, q?, tag? (tag option id), owner? (owner key | none), pipeline? (id), stage?, channel? sms | email | messenger | instagram, assignee? (team member key | none), unread? true, last? them | us }) and the count in every box.\n- views: Saved inbox views (id, name, config).\n- save_view: Save a view: name, config = { box?: team | mine | unassigned | unread | done | scheduled, q?, tag? (tag option id), owner? (owner key | none), pipeline? (id), stage?, channel? sms | email | messenger | instagram, assignee? (team member key | none), unread? true, last? them | us }.\n- update_view: Rename a view or change its filters: view_id, name?, config?.\n- delete_view: Delete a saved view (view_id). The conversations stay.\n- scheduled: Scheduled messages, soonest first: contact_id? (one person), status? scheduled (default) | sent | failed | cancelled | all.\n- schedule: Schedule a message: contact_id, channel sms | email | messenger | instagram, body, send_at (ISO date-time or epoch seconds), email: subject? (else Re: their newest thread), account_id? (mailbox), cc?, bcc? (lists or comma separated). (may wait for approval)\n- update_scheduled: Change a waiting message (scheduled_id): body?, subject?, send_at?, cc?, bcc?, channel?. (may wait for approval)\n- send_scheduled_now: Send a waiting message now (scheduled_id); a text outside texting hours still waits for the window. (may wait for approval)\n- cancel_scheduled: Cancel a waiting message (scheduled_id).\n- settings: Response-time target, business hours and texting hours, with first-response stats over the last 30 days.\n- set_settings: Change them: settings = { sla_on?, sla_minutes?, notify? assignee | owner | both, business? { tz, days [0 Sun..6 Sat], start \"09:00\", end \"17:00\" }, texting_start?, texting_end?, texting_tz? }.\n- response_stats: First response over the last N days (days, default 30): answered, waiting, overdue, avg / median seconds, avg business seconds, % within target.","tags":["tools"],"x-ctrl-tool":"crm_conversations","x-ctrl-scopes":{"threads":["messages:read"],"views":["messages:read"],"save_view":["messages:write"],"update_view":["messages:write"],"delete_view":["messages:write"],"scheduled":["messages:read"],"schedule":["messages:send"],"update_scheduled":["messages:send"],"send_scheduled_now":["messages:send"],"cancel_scheduled":["messages:write"],"settings":["messages:read"],"set_settings":["messages:write"],"response_stats":["messages:read"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["threads","views","save_view","update_view","delete_view","scheduled","schedule","update_scheduled","send_scheduled_now","cancel_scheduled","settings","set_settings","response_stats"],"description":"What to do."},"box":{"type":"string","enum":["team","mine","unassigned","unread","done","scheduled"]},"q":{"type":"string"},"tag":{"type":"number"},"owner":{"type":"string"},"pipeline":{"type":"number"},"stage":{"type":"string"},"channel":{"type":"string","enum":["sms","email","messenger","instagram"]},"assignee":{"type":"string"},"unread":{"type":"boolean"},"last":{"type":"string","enum":["them","us"]},"view_id":{"type":"number"},"name":{"type":"string"},"config":{"type":"object"},"contact_id":{"type":"number"},"status":{"type":"string"},"scheduled_id":{"type":"number"},"body":{"type":"string"},"subject":{"type":"string"},"account_id":{"type":"number"},"cc":{"type":["array","string"],"items":{"type":"string"}},"bcc":{"type":["array","string"],"items":{"type":"string"}},"send_at":{"type":["string","number"],"description":"ISO date-time (with offset) or epoch seconds"},"settings":{"type":"object"},"days":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_data": {"post":{"operationId":"tool_crm_data","summary":"CRM data and settings","description":"Bulk edits, CSV import, sync from email, the company stages and the team activity report, as you (/api/crm/bulk, import, sync, settings, report).\nActions:\n- bulk_edit: Set the same fields on many records: object people|deals|companies, ids, set = {field: value}. People: lead_source, lead_medium, lead_campaign, title, deal_owner, dnc_email, dnc_sms, dnc_call, unsubscribed, consent_reason. Deals: deal_owner, stage_id, status, lost_reason, close_date, source, probability, pipeline_id, recurring, currency. Companies: deal_owner, stage_id. Tags and custom fields: set_property.\n- import_preview: Plan a CSV import without saving: object companies|people|deals, csv, mapping? (one field key per column, \"\" skips), dedupe? skip|update|create. Shows the auto mapping and the first 50 rows.\n- import_run: Run the import (same inputs as import_preview). Max 5,000 rows, 5 MB.\n- sync_status: Email sync state per mailbox.\n- sync: Sync from email now (runs about 25 s, call again while a backfill is going).\n- settings: Company stages, closed and won stages, the workspace currency (settings.currency), the unknown-number lead setting and the speed to lead steps.\n- save_settings: Save stages: stages = [{id?, label, color}], closed_stage_ids?, won_stage_id? (max 50 stages); and / or unknown_number_lead = {on, pipeline_id, stage_id?} | null (texts / calls from unknown numbers make a lead); and / or speed_to_lead = {amber_min, red_min} | null (minutes before a waiting lead turns amber / red and alerts its owner; null = 5 / 15); and / or currency = USD | CAD | EUR | GBP | AUD | NZD | MXN | null (workspace currency: new deals and payments without one start in it, Reports show totals in it; null = USD).\n- report: Activity per teammate (calls, texts, emails, meetings, deals) for from..to (epoch s, default last 30 days, max 400). The workspace owner sees everyone.","tags":["tools"],"x-ctrl-tool":"crm_data","x-ctrl-scopes":{"bulk_edit":["crm:write"],"import_preview":["crm:read"],"import_run":["crm:write"],"sync_status":["crm:read"],"sync":["crm:write"],"settings":["crm:read"],"save_settings":["crm:write"],"report":["crm:read"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["bulk_edit","import_preview","import_run","sync_status","sync","settings","save_settings","report"],"description":"What to do."},"object":{"type":"string","enum":["people","deals","companies"]},"ids":{"type":"array","items":{"type":"number"}},"set":{"type":"object"},"csv":{"type":"string"},"mapping":{"type":"array","items":{"type":"string"}},"dedupe":{"type":"string","enum":["skip","update","create"]},"stages":{"type":"array","items":{"type":"object"}},"closed_stage_ids":{"type":"array","items":{"type":"string"}},"won_stage_id":{"type":["string","null"]},"unknown_number_lead":{"type":["object","null"]},"speed_to_lead":{"type":["object","null"]},"currency":{"type":["string","null"],"description":"save_settings: workspace currency (ISO code) or null."},"from":{"type":"number"},"to":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_deals": {"post":{"operationId":"tool_crm_deals","summary":"CRM deals","description":"Deals in the CRM pipelines, as you (/api/crm/deals). Deals belong to people: every deal has a contact_id. One deal can cover several of their businesses: company_ids (primary first; company_id = the primary). Values are integer cents. Status follows the stage (the won stage = won). Pipelines and stages come from crm_pipelines.\nActions:\n- list: Deals, optionally by pipeline_id, pipeline_kind (leads|sales: every pipeline of that kind), company_id (deals that cover that business), contact_id, person_id (their deals, plus older deals with no person at their businesses), status (open|won|lost|abandoned), waiting (true = open leads no person has texted, called or emailed yet), with the open forecast. Each deal has speed to lead: lead_started_at, first_response_at, first_response_channel (sms|call|email|logged_call), speed_to_lead_seconds, waiting.\n- speed_to_lead: Speed to lead numbers: leads that started in from..to (epoch s, default the last 24 h), scope me|team (default team), optional pipeline_id. Returns summary {leads, responded, waiting, median_seconds, avg_seconds, under_amber, under_amber_pct, oldest_waiting_seconds}, the same for every waiting lead, and thresholds {amber_min, red_min}. Only a person's own text, call, email or logged call counts as a response; workflows, notifications, agents (you), API sends and bulk sends never do.\n- get: One deal with its line items and stage history.\n- create: A new deal: fields = {title, value_cents, currency, recurring, close_date, probability, pipeline_id, stage_id, status, lost_reason, deal_owner, company_id, company_ids, contact_id, source, value_from_lines, notes}. You own it unless deal_owner is set. A deal belongs to a person: contact_id is required. company_ids = every business the deal covers, primary first ([] = none); company_id = one business; neither = all of the person's businesses. A business they are not part of adds them to it.\n- update: Change a deal: fields as in create (moving stage_id moves status; status won|lost moves the stage; status abandoned sets it aside in its stage, out of the open forecast, until it moves or is reopened with status open). contact_id cannot be cleared; company_ids replaces the business list (company_id = just that one; null or [] = no business).\n- set_lines: Replace every line item: lines = [{product_id?, name?, qty?, price_cents?, discount_pct?, recurring?}].\n- delete: Delete the deal. It stays in Recently deleted for 30 days (restore puts it back). (waits for approval: delete)\n- deleted: Deals deleted in the last 30 days (merged ones are not listed). q filters by title.\n- restore: Restore deleted deals: deal_ids. Merged deals and deals whose pipeline was deleted are refused (the reason says so).\n- merge: Fold deal_id into into_id (kept): notes, line items, history, tasks, links, tags and fields move; deal_id is removed. (waits for approval: delete)\n- mark_churned: Mark a won recurring deal_id churned (PATCH /api/crm/deals/:id { churn }): churned_on (YYYY-MM-DD, default today), optional reason. It stops counting in MRR from that day. One-time deals are refused. Its business is not churned.\n- reactivate: Reactivate a churned deal_id (back in MRR).\n- query: Find deals with the same engine as the CRM list: filter (the crm_contacts filter schema; field keys from action filter_fields), q (search), owner (\"\" | none | an owner key), pipeline (id, blank = every pipeline), status (open | won | lost; leave it out for all), stage (a stage id), columns (field keys to return per row), sort (a field key) + dir, limit (<= 500), offset. Returns total and rows {id, name, values}.\n- filter_fields: Every deal filter field and column (key, label, group, type, operators, options with their values), incl. custom fields f:<id> and the person fields p:<key> (the deal's person: tags, texts, calls, bookings, forms, workflows, payments...).","tags":["tools"],"x-ctrl-tool":"crm_deals","x-ctrl-scopes":{"list":["crm:read"],"speed_to_lead":["crm:read"],"get":["crm:read"],"create":["crm:write"],"update":["crm:write"],"set_lines":["crm:write"],"delete":["crm:write"],"deleted":["crm:read"],"restore":["crm:write"],"merge":["crm:write"],"mark_churned":["crm:write"],"reactivate":["crm:write"],"query":["crm:read"],"filter_fields":["crm:read"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","speed_to_lead","get","create","update","set_lines","delete","deleted","restore","merge","mark_churned","reactivate","query","filter_fields"],"description":"What to do."},"filter":{"type":"object","description":"query: { mode: and|or, items: [{ field, op, value? }] }."},"q":{"type":"string","description":"search: businesses by name, domains, industry, location, their people (name, emails, phones), tag names, text custom fields; deals by title, source, person (name, emails, phones), businesses, tag names, text custom fields"},"owner":{"type":"string"},"stage":{"type":"string"},"activity":{"type":"string"},"hidden":{"type":"string","enum":["only","include"]},"pipeline":{"type":"string"},"columns":{"type":"array","items":{"type":"string"}},"sort":{"type":"string"},"dir":{"type":"string","enum":["asc","desc"]},"offset":{"type":"number"},"limit":{"type":"number"},"deal_id":{"type":"number"},"deal_ids":{"type":"array","items":{"type":"number"},"description":"restore: the deleted deals to put back."},"into_id":{"type":"number","description":"merge: the deal that is kept."},"person_id":{"type":"number","description":"list: every deal of this person (plus older deals with no person at their businesses)."},"pipeline_id":{"type":"number"},"pipeline_kind":{"type":"string","enum":["leads","sales"],"description":"list: only deals in leads pipelines or in sales pipelines."},"company_id":{"type":"number"},"contact_id":{"type":"number"},"status":{"type":"string","enum":["open","won","lost","abandoned"]},"waiting":{"type":"boolean","description":"list: only open leads with no response by a person yet."},"scope":{"type":"string","enum":["me","team"],"description":"speed_to_lead: my deals or the whole CRM."},"from":{"type":"number","description":"speed_to_lead: epoch seconds."},"to":{"type":"number","description":"speed_to_lead: epoch seconds."},"fields":{"type":"object"},"lines":{"type":"array","items":{"type":"object"}},"churned_on":{"type":"string","description":"mark_churned: the churn day, YYYY-MM-DD (default today)."},"reason":{"type":"string","description":"mark_churned: why (optional)."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_field_categories": {"post":{"operationId":"tool_crm_field_categories","summary":"CRM field categories","description":"Custom field categories on CRM records (the field cards on person, business and deal pages, like a Forms card), as you: list (entity_type crm_contact | crm_company | crm_deal) gives the categories in order and which field (property definition id) sits in which; create, rename, reorder, delete (its fields go back to General), and place_field to move a field into a category (category_id null = General).\nActions:\n- list: Categories and field placements (entity_type, default crm_contact).\n- create: New category (name, entity_type?).\n- rename: Rename (category_id, name).\n- reorder: Order the categories (order: ids first to last).\n- place_field: Put a field in a category (definition_id, category_id or null for General, sort_order?).\n- delete: Delete a category (category_id); its fields go back to General. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"crm_field_categories","x-ctrl-scopes":{"list":["crm:read"],"create":["crm:write"],"rename":["crm:write"],"reorder":["crm:write"],"place_field":["crm:write"],"delete":["crm:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","create","rename","reorder","place_field","delete"],"description":"What to do."},"entity_type":{"type":"string","enum":["crm_contact","crm_company","crm_deal"]},"category_id":{"type":["number","null"]},"name":{"type":"string"},"order":{"type":"array","items":{"type":"number"},"description":"reorder: category ids, first to last."},"definition_id":{"type":"number","description":"place_field: the field (property definition id)."},"sort_order":{"type":"number","description":"place_field: position inside the category."},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_forecast": {"post":{"operationId":"tool_crm_forecast","summary":"Deal forecast","description":"The deal forecast (CRM > Deals > Forecast): open deals by expected close month or quarter as commit (win chance >= commit_min), best case (>= best_min, commit included) and pipeline (every open deal), weighted by win chance (the deal's own, else its stage's), what was won per period, by pipeline or owner, and the deals that need attention: past due (close date passed, still open), stale (no change for stale_days), at risk (stale or past due; high = both), missing close date or value. One currency at a time (the most used, or currency). Leads pipelines are left out unless pipeline = all or a leads pipeline id.\nActions:\n- get: The forecast: period (month | quarter), periods (1-12), group (none | pipeline | owner), pipeline (id, sales = every sales pipeline, all), owner (teammate key | none), currency, tz.\n- settings: The forecast rules: commit_min, best_min (win chance %), stale_days.\n- set_settings: Change the rules: commit_min?, best_min? (<= commit_min), stale_days?.","tags":["tools"],"x-ctrl-tool":"crm_forecast","x-ctrl-scopes":{"get":["crm:read"],"settings":["crm:read"],"set_settings":["crm:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["get","settings","set_settings"],"description":"What to do."},"period":{"type":"string","enum":["month","quarter"]},"periods":{"type":"number"},"group":{"type":"string","enum":["none","pipeline","owner"]},"pipeline":{"type":"string"},"owner":{"type":"string"},"currency":{"type":"string"},"tz":{"type":"string"},"commit_min":{"type":"number"},"best_min":{"type":"number"},"stale_days":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_get_company": {"post":{"operationId":"tool_crm_get_company","summary":"CRM: get a company","description":"One CRM company with its contacts.","tags":["tools"],"x-ctrl-tool":"crm_get_company","x-ctrl-scopes":{"*":["crm:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"company_id":{"type":"number"}},"required":["company_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_list_companies": {"post":{"operationId":"tool_crm_list_companies","summary":"CRM: list companies","description":"Companies in your CRM (built from your email), with stage and deal owner.","tags":["tools"],"x-ctrl-tool":"crm_list_companies","x-ctrl-scopes":{"*":["crm:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string"},"stage_id":{"type":"string"},"limit":{"type":"number"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_list_contacts": {"post":{"operationId":"tool_crm_list_contacts","summary":"CRM: list contacts","description":"People in your CRM, optionally for one company or matching text.","tags":["tools"],"x-ctrl-tool":"crm_list_contacts","x-ctrl-scopes":{"*":["crm:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"company_id":{"type":"number"},"query":{"type":"string"},"limit":{"type":"number"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_objects": {"post":{"operationId":"tool_crm_objects","summary":"CRM custom objects","description":"Custom objects (CRM > Objects), GHL style: your own record types next to people, businesses and deals (Properties, Vehicles, Policies...). A type has a name, plural, icon, fields (data_type: string, long_text, number, currency, percent, date, boolean, select, multi_select, email, phone, url, person) and link labels. Records have a name, owner (teammate key), notes and values ({ field key | field id | field name: value }; selects take option labels or ids; null clears). Records link many-to-many (with a label) to people (contact), businesses (company), deals (deal) and other records (object). query takes the CRM smart-list filter { mode: \"and\" | \"or\", items: [{ field, op, value }] } where field = name, owner, created_at, updated_at, people, businesses, deals, people_count, businesses_count, deals_count, records_count, assoc_label or f:<field id>. Creating / changing a record fires the Object record created / changed workflow triggers and the object.* webhooks.\nActions:\n- list_types: Every object type with its fields (id, key, data_type, options) and record count.\n- get_type: One type (type_id) with its smart lists.\n- create_type: New type: name, plural?, icon?, description?, labels? (link labels), fields? [{ name, data_type, options?, required? }].\n- update_type: Change a type (type_id): name, plural, icon, description, labels.\n- delete_type: Delete a type and all its records (type_id). (waits for approval: delete)\n- add_field: Add a field (type_id, name, data_type, options?, required?).\n- update_field: Rename a field, change its options or required (field_id, name?, options?, required?, sort_order?). A field keeps its type.\n- delete_field: Delete a field and its values (field_id). (waits for approval: delete)\n- query: Records of a type (type_id): filter?, q?, owner?, sort? (name, created_at, updated_at, f:<id>, people_count...), dir?, limit? (<= 1000), offset?. Returns records with values by field id and linked names.\n- get: One record (record_id) with its type and every link.\n- create: New record: type_id, name, values?, owner?, notes?, associations? [{ target_type, target_id, label? }].\n- update: Change a record (record_id): name?, values? (merged), owner?, notes?.\n- delete: Delete a record (record_id). (waits for approval: delete)\n- associate: Link a record (record_id) to target_type contact | company | deal | object, target_id, label?. Again = changes the label.\n- unassociate: Remove a link: association_id, or record_id + target_type + target_id.\n- related: Records linked to a person / business / deal / record (target_type, target_id), grouped by type.\n- import: Import CSV text into a type (type_id, csv, mapping? { header: name | owner | notes | f:<id> | person_email | business_domain | \"\" }, update_existing? default true, label?).\n- views: Smart lists of a type (type_id) with live counts.\n- save_view: Save a smart list (type_id, name, config { filter, q?, owner?, sort?, dir? }); with view_id it updates that one.\n- delete_view: Delete a smart list (view_id). Records stay.","tags":["tools"],"x-ctrl-tool":"crm_objects","x-ctrl-scopes":{"list_types":["crm:read"],"get_type":["crm:read"],"create_type":["crm:write"],"update_type":["crm:write"],"delete_type":["crm:write"],"add_field":["crm:write"],"update_field":["crm:write"],"delete_field":["crm:write"],"query":["crm:read"],"get":["crm:read"],"create":["crm:write"],"update":["crm:write"],"delete":["crm:write"],"associate":["crm:write"],"unassociate":["crm:write"],"related":["crm:read"],"import":["crm:write"],"views":["crm:read"],"save_view":["crm:write"],"delete_view":["crm:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list_types","get_type","create_type","update_type","delete_type","add_field","update_field","delete_field","query","get","create","update","delete","associate","unassociate","related","import","views","save_view","delete_view"],"description":"What to do."},"type_id":{"type":"number"},"field_id":{"type":"number"},"record_id":{"type":"number"},"view_id":{"type":"number"},"association_id":{"type":"number"},"name":{"type":"string"},"plural":{"type":"string"},"icon":{"type":"string"},"description":{"type":"string"},"labels":{"type":"array","items":{"type":"string"}},"fields":{"type":"array","items":{"type":"object"},"description":"[{ name, data_type, options?, required? }]"},"data_type":{"type":"string"},"options":{"type":"array","items":{},"description":"Option labels, or { id?, label, color? }"},"required":{"type":"boolean"},"sort_order":{"type":"number"},"values":{"type":"object","description":"{ field key | id | name: value }"},"owner":{"type":"string"},"notes":{"type":"string"},"associations":{"type":"array","items":{"type":"object"}},"target_type":{"type":"string","enum":["contact","company","deal","object"]},"target_id":{"type":"number"},"label":{"type":"string"},"filter":{"type":"object"},"q":{"type":"string"},"sort":{"type":"string"},"dir":{"type":"string","enum":["asc","desc"]},"limit":{"type":"number"},"offset":{"type":"number"},"csv":{"type":"string"},"mapping":{"type":"object"},"update_existing":{"type":"boolean"},"config":{"type":"object"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_pipelines": {"post":{"operationId":"tool_crm_pipelines","summary":"CRM pipelines","description":"Deal pipelines and their stages, as you (/api/crm/pipelines). Pipeline 0 is the default one (its stages are the company stages, always kind sales). A stage is {id?, label, color?, probability?}. LEADS ARE DEALS (GHL style): a pipeline is kind leads or sales; a leads pipeline's won_handoff {mode: move|create, pipeline_id, stage_id?} runs when a deal enters its won stage (move = the same deal goes to that pipeline + stage with its history; create = a new linked deal there). One open deal per person per pipeline for leads made by forms, calendars and unknown numbers (forms tool op lead_pipeline, calendars tool set_lead_pipeline, set_unknown_number_lead here). A workflow with Create / update deal can do the same.\nActions:\n- list: Every pipeline with its stages, kind and won_handoff.\n- create: A new pipeline (name, kind leads|sales default sales, optional stages: leads start New, Contacted, Qualified, Booked, Won (handed off), Lost; optional won_handoff).\n- update: Change a pipeline: pipeline_id + any of name, stages, closed_stage_ids, won_stage_id, kind, won_handoff (null clears), access, card_fields.\n- set_access: Who can see a pipeline and its deals (pipeline_id, access = {mode: everyone | users | owner, users: team member keys, agent ids ai-<id>, or \"api\" for API keys}). owner = only the CRM owner signed in; agents and API keys do not see it. Lists, boards, filters, reports, the API and agents follow it.\n- set_card_fields: The fields its board cards show (pipeline_id, card_fields = up to 4 of value, win_chance, close_date, days_in_stage, source, person, business, owner, created, recurring, phone, f:<deal custom field id>; null = the standard card).\n- copy: Copy a pipeline (pipeline_id, name optional): the same stages, closed / won stages, kind, access and card fields; no deals and no automations.\n- reorder: New order: ids = every pipeline id (0 included).\n- delete: Delete a pipeline; its deals move to move_to (default 0). (waits for approval: delete)\n- unknown_number_lead: The setting \"New texts / calls from unknown numbers create a lead\": {on, pipeline_id, stage_id} or null.\n- set_unknown_number_lead: Set it: setting = {on, pipeline_id, stage_id?} or null to clear.","tags":["tools"],"x-ctrl-tool":"crm_pipelines","x-ctrl-scopes":{"list":["crm:read"],"create":["crm:write"],"update":["crm:write"],"set_access":["crm:write"],"set_card_fields":["crm:write"],"copy":["crm:write"],"reorder":["crm:write"],"delete":["crm:write"],"unknown_number_lead":["crm:read"],"set_unknown_number_lead":["crm:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","create","update","set_access","set_card_fields","copy","reorder","delete","unknown_number_lead","set_unknown_number_lead"],"description":"What to do."},"pipeline_id":{"type":"number"},"name":{"type":"string"},"stages":{"type":"array","items":{"type":"object"}},"closed_stage_ids":{"type":"array","items":{"type":"string"}},"won_stage_id":{"type":["string","null"]},"ids":{"type":"array","items":{"type":"number"}},"move_to":{"type":"number"},"kind":{"type":"string","enum":["leads","sales"]},"won_handoff":{"type":["object","null"],"description":"{mode: move|create, pipeline_id, stage_id?}; null clears."},"setting":{"type":["object","null"],"description":"set_unknown_number_lead: {on, pipeline_id, stage_id?} or null."},"access":{"type":"object","description":"{ mode: everyone | users | owner, users?: string[] }"},"card_fields":{"type":["array","null"],"items":{"type":"string"}}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_products": {"post":{"operationId":"tool_crm_products","summary":"CRM products","description":"The product list deal line items pick from, as you (/api/crm/products). Prices are integer cents.\nActions:\n- list: Products (include_archived to see archived ones).\n- save: Create a product, or change one with product_id: fields = {name, description?, price_cents, currency?, recurring? none|monthly|yearly, archived?}. Archive instead of deleting.","tags":["tools"],"x-ctrl-tool":"crm_products","x-ctrl-scopes":{"list":["crm:read"],"save":["crm:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","save"],"description":"What to do."},"product_id":{"type":"number"},"fields":{"type":"object"},"include_archived":{"type":"boolean"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_send": {"post":{"operationId":"tool_crm_send","summary":"CRM send","description":"Email or text CRM people one by one (merge fields {{first_name}}, {{name}}, {{company}}, {{first_name|there}}), as you (/api/crm/send). Do-not-contact flags and missing addresses are skipped. At most 200 people per send. Email goes via gmail (your connected mailbox, account_id: personal, one to one) or via mailjet (bulk / marketing: a verified sender from_email or your default, Reply-to your Gmail, unsubscribe link + mailing address footer added, OPTED-IN PEOPLE ONLY: lists flagged cold are refused, people tagged cold / no-consent and the suppression list are skipped). Texts from your Twilio number. format: text (default), rich or html (sanitized). The audience can be a people filter (the People list filter schema) as well as ids or a segment / smart list.\nActions:\n- preview: Dry run: who gets it and who is skipped and why. Nothing is sent.\n- send: Send it: channel email|sms, the audience (contact_ids, company_ids, deal_ids, segment_id or filter), via (gmail | mailjet), account_id (gmail), from_email / from_name / reply_to / track (mailjet), subject (email), body, format. (waits for approval: send email)","tags":["tools"],"x-ctrl-tool":"crm_send","x-ctrl-scopes":{"preview":["messages:read"],"send":["messages:send"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["preview","send"],"description":"What to do."},"channel":{"type":"string","enum":["email","sms"]},"contact_ids":{"type":"array","items":{"type":"number"}},"company_ids":{"type":"array","items":{"type":"number"}},"deal_ids":{"type":"array","items":{"type":"number"}},"segment_id":{"type":"number"},"filter":{"type":"object","description":"People filter: { mode: and|or, items: [{ field, op, value }] } (crm_views smart list schema)."},"via":{"type":"string","enum":["gmail","mailjet"]},"account_id":{"type":"number","description":"Mailbox id to send email from (list_email_threads shows accounts)."},"from_email":{"type":"string","description":"Mailjet: a verified sender (mailjet status lists them)."},"from_name":{"type":"string"},"reply_to":{"type":"string"},"track":{"type":"boolean","description":"Mailjet: count opens and clicks."},"format":{"type":"string","enum":["text","rich","html"]},"subject":{"type":"string"},"body":{"type":"string"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_tasks": {"post":{"operationId":"tool_crm_tasks","summary":"CRM tasks","description":"CRM tasks are normal tasks linked to a deal, person or company. Make the task with create_task, then link it here (/api/crm/tasks).\nActions:\n- mine: Open tasks assigned to you that are linked to a CRM record, by due date.\n- link: Link task_id to records: links = [{type: crm_deal|crm_contact|crm_company, id}], remind_at? (epoch s, null clears).","tags":["tools"],"x-ctrl-tool":"crm_tasks","x-ctrl-scopes":{"mine":["crm:read"],"link":["crm:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["mine","link"],"description":"What to do."},"task_id":{"type":"number"},"links":{"type":"array","items":{"type":"object"}},"remind_at":{"type":["number","null"]}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_update_company": {"post":{"operationId":"tool_crm_update_company","summary":"CRM: update a company","description":"Change a CRM company: name, stage, revenue, notes, or hide it.","tags":["tools"],"x-ctrl-tool":"crm_update_company","x-ctrl-scopes":{"*":["crm:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"company_id":{"type":"number"},"name":{"type":"string"},"stage_id":{"type":"string","description":"Stage id from crm_list_companies stages."},"revenue":{"type":"number"},"notes":{"type":"string"},"hidden":{"type":"boolean"}},"required":["company_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_update_contact": {"post":{"operationId":"tool_crm_update_contact","summary":"CRM: update a contact","description":"Change a CRM contact: name, title, phone or notes.","tags":["tools"],"x-ctrl-tool":"crm_update_contact","x-ctrl-scopes":{"*":["crm:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contact_id":{"type":"number"},"name":{"type":"string"},"title":{"type":"string"},"phone":{"type":"string"},"notes":{"type":"string"}},"required":["contact_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/crm_views": {"post":{"operationId":"tool_crm_views","summary":"CRM views and segments","description":"Saved CRM views (companies, people or pipeline, with filters in config) and segments (views with config.segment = true, the audiences for crm_send), and SMART LISTS (views with config.segment = true and config.smart = true: people lists use config.filter = the crm_contacts filter schema, config.psort / pdir = sort field key and direction; businesses (kind companies) and deals (kind pipeline) lists use config.filter with the crm_companies / crm_deals filter_fields keys, config.xsort / xdir, and for deals config.pipeline + config.status; list columns are each person's own setting, not part of the list), as you (/api/crm/views, /api/crm/segments, /api/crm/smart-lists).\nActions:\n- list: Saved views.\n- segments: Segments with live counts.\n- smart_lists: Smart lists (people, businesses, deals) with live counts and their kind, and the starter lists you can add.\n- add_starters: Make starter smart lists (keys, default all): no_open_30, no_phone, textable, no_showed, upcoming_week, waiting_reply, unworked_7, clients, dnd. Already made ones are returned, not duplicated.\n- tag_list: Add (or with remove: true, take off) a CRM tag on everyone in a smart list (view_id, tag).\n- segment_people: Who is in a segment right now (view_id).\n- create: A new view: name, kind companies|people|pipeline, config (filters, columns, sort; segment: true makes it a segment).\n- update: Change a view (view_id, name?, config?).\n- set_consent: A smart list / segment's consent basis (view_id, consent: opt_in | customers | cold | not_set). Cold lists are refused by Mailjet bulk sends, and people added to a workflow from one skip its Mailjet marketing steps (Gmail one to one only).\n- delete: Delete a view. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"crm_views","x-ctrl-scopes":{"list":["crm:read"],"segments":["crm:read"],"smart_lists":["crm:read"],"add_starters":["crm:write"],"tag_list":["crm:write"],"segment_people":["crm:read"],"create":["crm:write"],"update":["crm:write"],"set_consent":["crm:write"],"delete":["crm:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","segments","smart_lists","add_starters","tag_list","segment_people","create","update","set_consent","delete"],"description":"What to do."},"view_id":{"type":"number"},"name":{"type":"string"},"kind":{"type":"string","enum":["companies","people","pipeline"]},"config":{"type":"object"},"limit":{"type":"number"},"keys":{"type":"array","items":{"type":"string"}},"tag":{"type":"string"},"remove":{"type":"boolean"},"consent":{"type":"string","enum":["opt_in","customers","cold","not_set"]}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/data_cleanup": {"post":{"operationId":"tool_data_cleanup","summary":"Data cleanup (Trash and retention)","description":"A workspace's Drive housekeeping (Settings > Workspace > Data cleanup): Empty Trash after 30 days (items 30+ days in Trash deleted for good; folders and forms stay) and Move untouched items to Trash after N days (plain uploads and plain docs nobody changed or opened; never favorites, linked records, generated or signed files, task/project docs, CRM, payments, commissions, e-sign). Both off by default; the first run after turning one on only lists what it would do, deleting starts 24 h later. Only the workspace owner changes them; turning one on or lowering the days waits for approval.\nActions:\n- get: Settings, the last run of each job (dry or real, with counts and a sample), whether you can change them, whether the background service runs.\n- preview: What a job would do right now (job, retention_days?). Nothing is written.\n- set: Change the settings (trash_purge?, retention_days?). Owner only. Turning a job on or lowering the days waits for approval; turning off does not. (may wait for approval)","tags":["tools"],"x-ctrl-tool":"data_cleanup","x-ctrl-scopes":{"get":["admin:read"],"preview":["admin:read"],"set":["admin:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["get","preview","set"],"description":"What to do."},"workspace_id":{"type":"number"},"trash_purge":{"type":"boolean","description":"set: Empty Trash after 30 days on/off."},"retention_days":{"type":["number","null"],"description":"set: 30-3650 days, or null / 0 for off. preview: days to check (default the saved setting)."},"job":{"type":"string","enum":["trash_purge","retention"],"description":"preview: which job."},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/database_admin": {"post":{"operationId":"tool_database_admin","summary":"Database structure and sharing","description":"Change a database's structure, as you: the same operations as the database grid (op = create_sheet {name, workspace_id}, rename_sheet {name}, add_column {name, type}, rename_column {column_name, new_name}, delete_column {column_name}, and the grid's other ops; pass their fields in fields), plus sharing with an email. Rows: add_database_row / update_database_row / delete_database_row.\nActions:\n- change: Run one grid operation (op, database_id, fields): create_sheet, rename_sheet, add_column, rename_column, delete_column, reorder_columns, update_sheet, update_sheet_labels, update_settings, set_column_required {column_name, required} (a required column must hold a value in every row), import_csv, set_link_access, delete_sheet. Deletes and opening link access wait for approval. (may wait for approval)\n- shares: Who the database is shared with by email.\n- share: Share with an email (role, notify). (waits for approval: external)\n- unshare: Remove an email's access.","tags":["tools"],"x-ctrl-tool":"database_admin","x-ctrl-scopes":{"change":["docs:write"],"shares":["docs:read"],"share":["docs:write"],"unshare":["docs:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["change","shares","share","unshare"],"description":"What to do."},"database_id":{"type":"string"},"op":{"type":"string","description":"The grid operation (the database API's action)."},"email":{"type":"string"},"role":{"type":"string","enum":["viewer","editor"]},"notify":{"type":"boolean"},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/database_questions": {"post":{"operationId":"tool_database_questions","summary":"Database SQL and saved questions","description":"Read a database with SQL and keep questions, as you. sql runs ONE read-only SELECT over one database (database_id) and only its rows you can see: FROM the database's name (or FROM this); no joins, subqueries, writes or other tables; up to 500 rows, 3 seconds. Supported: WHERE with = <> < > LIKE ILIKE IN BETWEEN IS NULL, 'label' = ANY(\"Multi select\"), GROUP BY (a multi-select groups once per label; date_trunc('month', \"Date\") for day/week/month/quarter/year), HAVING, count/sum/avg/min/max (DISTINCT), ORDER BY, LIMIT, OFFSET, lower/upper/length/trim/coalesce/round/abs. Quote names with spaces: \"Deal value\". Text compares ignore case. A saved question is a named SELECT that re-runs live each time it is opened (in the database's Ask panel and as a doc question block). The query_database tool answers without SQL.\nActions:\n- sql: Run a read-only SELECT (database_id, sql) and get its columns and rows.\n- list: Saved questions you can see (database_id to limit to one database, query to filter by name).\n- run: Run a saved question live (question_id): its rows, chart and any error.\n- save: Save a question (database_id, name, sql, chart). The SQL must run.\n- update: Rename a question or change its SQL or chart (question_id, name / sql / chart).\n- delete: Delete a saved question (question_id). (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"database_questions","x-ctrl-scopes":{"sql":["admin:read"],"list":["admin:read"],"run":["admin:read"],"save":["admin:write"],"update":["admin:write"],"delete":["admin:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["sql","list","run","save","update","delete"],"description":"What to do."},"database_id":{"type":"string","description":"Database id, public id or sheet:<id>."},"question_id":{"type":"number","description":"A saved question (question:<id>)."},"sql":{"type":"string"},"name":{"type":"string","description":"save / update: the question's name."},"chart":{"type":"string","enum":["table","bar","line","pie"],"description":"How the question is drawn."},"query":{"type":"string","description":"list: filter by name."},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/delete_calendar_event": {"post":{"operationId":"tool_delete_calendar_event","summary":"Delete a calendar event","description":"Delete an event from your Google calendar (a repeating event: this occurrence, or every one with all_occurrences). Waits for your approval unless you allowed deletes; an event with guests emails them a cancellation, so it also follows the external-actions setting.","tags":["tools"],"x-ctrl-tool":"delete_calendar_event","x-ctrl-scopes":{"*":["bookings:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"event_id":{"type":"string"},"calendar_id":{"type":"string"},"all_occurrences":{"type":"boolean","description":"Delete the whole series."}},"required":["event_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/delete_database_row": {"post":{"operationId":"tool_delete_database_row","summary":"Delete a database row","description":"Delete one row of a database. Needs the user's approval unless they allowed deletes.","tags":["tools"],"x-ctrl-tool":"delete_database_row","x-ctrl-scopes":{"*":["docs:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"delete","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"database":{"type":"string","description":"Database id or sheet:<id>."},"row_id":{"type":"number"}},"required":["database","row_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/delete_task": {"post":{"operationId":"tool_delete_task","summary":"Delete a task","description":"Delete a task. Needs your approval in Plodder before it happens (unless you changed that in Settings > AI agents).","tags":["tools"],"x-ctrl-tool":"delete_task","x-ctrl-scopes":{"*":["tasks:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"delete","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"task_id":{"type":["number","string"]}},"required":["task_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/design": {"post":{"operationId":"tool_design","summary":"Design files","description":"Read and edit design files, as you. Items: pages {t:\"page\", name, bg, modes: {<collection id>: <mode id>}}, color/text styles {t:\"style\", kind}, variable collections {t:\"vars\", name, modes: [{id, name}]}, variables {t:\"var\", coll, name, type: color|number|string|boolean, values: {<mode id>: value}}, and nodes {t: frame|group|rect|ellipse|polygon|star|path|text|instance|boolean, parent: page or container id, name, x, y, w, h, fills: [{type:\"solid\", color:\"#hex\"}], strokes, strokeWidth, effects: [], text nodes {text, style}}. Bind a node property to a variable with bind: {fill|stroke|opacity|radius|strokeWidth|w|h|gap|padding|text|visible|fontSize: <variable id>} (the value is written on save). Frames take layout grids: grids: [{type: columns|rows|grid, count, size, gutter, margin, align: stretch|start|center|end, color, opacity}] and modes like pages. Get a design first to see its pages and node shapes, then add nodes with the same fields.\nActions:\n- list: Designs you can open, newest first.\n- get: The design: title, your access, and every item as JSON.\n- create: A new design (title, workspace_id or folder_id), filed in Drive.\n- rename: Change the title.\n- add: Add items: a list of objects (an id is made when missing).\n- update: Change items by id: a list of {id, ...fields to change}.\n- remove: Remove items by id (ids). Undo is in the editor's history.\n- replace: Replace the whole design content (content = {items: [...]}).\n- trash: Move the design to Trash. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"design","x-ctrl-scopes":{"list":["docs:read"],"get":["docs:read"],"create":["docs:write"],"rename":["docs:write"],"add":["docs:write"],"update":["docs:write"],"remove":["docs:write"],"replace":["docs:write"],"trash":["docs:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get","create","rename","add","update","remove","replace","trash"],"description":"What to do."},"id":{"type":"string","description":"design id or public id."},"title":{"type":"string"},"workspace_id":{"type":"number"},"folder_id":{"type":"number"},"items":{"type":"array","items":{"type":"object"},"description":"add / update: the items."},"ids":{"type":"array","items":{"type":"string"},"description":"remove: ids."},"content":{"type":"object","description":"replace: the whole document."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/design_library": {"post":{"operationId":"tool_design_library","summary":"Design libraries","description":"Team libraries for design files, as you: publish a design file's components, colour and text styles and variables; use another file's library in a design file (its items are copied in on a hidden page); take newer versions; stop using one (anything still used stays as the file's own).\nActions:\n- list: Libraries you can use: src (the publishing file), name, version, counts, mine.\n- get: One library's current version (src) with its items.\n- publish: Publish (or publish the next version of) the design file id; description optional.\n- unpublish: Unpublish the design file id's library (files using it keep their copies).\n- use: Make the design file id use the library src (copies its items in).\n- update: Take the newest version of every library the design file id uses (or only lib, a lib item id).\n- stop_using: Stop using a library in the design file id (lib = the lib item id from get on the design).","tags":["tools"],"x-ctrl-tool":"design_library","x-ctrl-scopes":{"list":["docs:read"],"get":["docs:read"],"publish":["docs:write"],"unpublish":["docs:write"],"use":["docs:write"],"update":["docs:write"],"stop_using":["docs:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get","publish","unpublish","use","update","stop_using"],"description":"What to do."},"id":{"type":"string","description":"Design file id or public id."},"src":{"type":"string","description":"The library: the publishing design file's public id."},"lib":{"type":"string","description":"A lib item id inside the design file."},"description":{"type":"string"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/dialer": {"post":{"operationId":"tool_dialer","summary":"Power dialer","description":"The power dialer, read and set up (agents never place calls): sessions with live stats (dials, connects, talk time, sets, per hour) and the current / next people; call scripts with custom values ({{contact.first_name}}, {{company.name}}, {{user.first_name}}); the disposition list every call uses (No answer, Left voicemail, Interested, Booked, DNC request...) and what each does (move the lead stage, tags, workflow, task, lead status, Do not call, call back, book); voicemail drops (list, rename, delete; recording or uploading audio is done in the app); dialer settings (calling hours by the person's time zone, auto-advance, answering machine detection, caller ID local presence and daily cap, recording notice); set_disposition logs a disposition on any call (its side effects run once per call). Ringless voicemail is not offered: it counts as a call and needs consent.\nActions:\n- sessions: Dialer sessions, newest first, with stats.\n- session: One session (session_id): stats, counts, skipped by reason, the current person card and call, the next three.\n- end_session: End a session (session_id): frees everyone it still holds.\n- set_disposition: Log a disposition on a call (call_id, key, note?, callback_at?). Changing it later is fine; each disposition's side effects run once per call.\n- dispositions: The disposition list (archived too with fields.all = true).\n- save_disposition: Create or update a disposition (id to update; label, connect, set, kind, actions, archived).\n- reorder_dispositions: Order the dispositions (order: ids; the first nine are the 1-9 keys).\n- archive_disposition: Archive a disposition (id). Calls keep their label. (waits for approval: delete)\n- scripts: Call scripts and the custom values they can use.\n- save_script: Create or update a script (id to update; name, body, pipeline_id = the default for that pipeline).\n- delete_script: Delete a script (id). (waits for approval: delete)\n- drops: Voicemail drops (recorded messages) with their length.\n- rename_drop: Rename a voicemail drop (id, name).\n- delete_drop: Delete a voicemail drop (id). (waits for approval: delete)\n- settings: Dialer settings.\n- update_settings: Change dialer settings (settings: any of the keys).","tags":["tools"],"x-ctrl-tool":"dialer","x-ctrl-scopes":{"sessions":["calls:read"],"session":["calls:read"],"end_session":["calls:write"],"set_disposition":["calls:write"],"dispositions":["calls:read"],"save_disposition":["calls:write"],"reorder_dispositions":["calls:write"],"archive_disposition":["calls:write"],"scripts":["calls:read"],"save_script":["calls:write"],"delete_script":["calls:write"],"drops":["calls:read"],"rename_drop":["calls:write"],"delete_drop":["calls:write"],"settings":["calls:read"],"update_settings":["calls:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["sessions","session","end_session","set_disposition","dispositions","save_disposition","reorder_dispositions","archive_disposition","scripts","save_script","delete_script","drops","rename_drop","delete_drop","settings","update_settings"],"description":"What to do."},"session_id":{"type":"number"},"call_id":{"type":"number"},"key":{"type":"string","description":"set_disposition: the disposition key from dispositions (e.g. interested)."},"note":{"type":"string"},"callback_at":{"type":"number","description":"set_disposition with a Call back disposition: epoch seconds."},"id":{"type":"number","description":"save_script / save_disposition / rename_drop / delete_*: the row id (omit to create)."},"name":{"type":"string"},"body":{"type":"string","description":"save_script: the script text with custom values."},"pipeline_id":{"type":["number","null"],"description":"save_script: the default script for this pipeline."},"label":{"type":"string","description":"save_disposition: the button name."},"connect":{"type":"boolean","description":"save_disposition: counts as reaching the person."},"set":{"type":"boolean","description":"save_disposition: counts as an appointment set."},"kind":{"type":"string","enum":["no_answer","voicemail","busy","bad_number","contact","dnc"]},"actions":{"type":"object","description":"save_disposition: { stage?: {pipeline_id, stage_id}, add_tags?, remove_tags?, workflow_id?, task?: {title, due_in_days}, lead_status?: won|lost, dnc?, callback?, book? }"},"archived":{"type":"boolean"},"order":{"type":"array","items":{"type":"number"},"description":"reorder_dispositions: ids, first = hotkey 1."},"settings":{"type":"object","description":"update_settings: { start, end (HH:MM local), saturday, sunday, default_tz, skip_called_today, auto_advance_sec, amd, amd_drop_id, record, record_notice, local_presence, number_daily_cap, sort, script_id }"},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/discussion_actions": {"post":{"operationId":"tool_discussion_actions","summary":"Discussion actions","description":"Change comments in an item's discussion, as you: edit your message, resolve or reopen a thread, react with an emoji, delete a message. item = <type>:<id>. Read and post with read_discussion / post_discussion.\nActions:\n- edit: Edit your message (message_id, content).\n- resolve: Resolve (resolved true) or reopen a thread (thread_root_id).\n- react: Toggle an emoji reaction (message_id, emoji).\n- delete: Delete a message (message_id). (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"discussion_actions","x-ctrl-scopes":{"edit":["docs:write"],"resolve":["docs:write"],"react":["docs:write"],"delete":["docs:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["edit","resolve","react","delete"],"description":"What to do."},"item":{"type":"string"},"message_id":{"type":"number"},"content":{"type":"string"},"thread_root_id":{"type":"number"},"resolved":{"type":"boolean"},"emoji":{"type":"string"},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/doc_actions": {"post":{"operationId":"tool_doc_actions","summary":"Doc actions","description":"More doc actions, as you: delete, who wrote each block (blame), polls (results, vote, close), block comments (add, resolve, delete), publish to the web, share with someone by email, and synced blocks. Read and write a doc's text with get_doc / update_doc.\nActions:\n- delete: Delete a doc (to Trash). (waits for approval: delete)\n- polls: The doc's polls with results: question, options, votes per option, who voted (unless anonymous), your vote, closed.\n- vote: Vote in a doc poll as you (poll_id from get_doc's ```poll id=..., vote: option numbers from 0; [] withdraws).\n- close_poll: Close (closed: true) or reopen (closed: false) a doc poll.\n- blame: Who last changed each block of the doc and when ({blocks: {block_id: {name, user_id, source, at}}}), from its version history.\n- comments: A doc's block comments, threaded.\n- comment: Comment on a doc block (doc_id, content, block_id, parent_comment_id to reply). @Claude or an agent handle runs that agent.\n- resolve_comment: Resolve a block comment (comment_id).\n- delete_comment: Delete a block comment (comment_id). (waits for approval: delete)\n- publish: Publish the doc on a public web page (returns its slug). (waits for approval: external)\n- unpublish: Take the public page down.\n- shares: Who the doc is shared with by email.\n- share: Share the doc with an email (role viewer|editor; notify emails them, default true). (waits for approval: external)\n- unshare: Remove an email's access.\n- create_synced_block: A synced block (content shown live in several docs): blocks, workspace_id. Insert it in a doc with the returned id.\n- update_synced_block: Change a synced block everywhere it is shown (block, blocks, rev from its last read).","tags":["tools"],"x-ctrl-tool":"doc_actions","x-ctrl-scopes":{"delete":["docs:write"],"polls":["docs:read"],"vote":["docs:write"],"close_poll":["docs:write"],"blame":["docs:read"],"comments":["docs:read"],"comment":["docs:write"],"resolve_comment":["docs:write"],"delete_comment":["docs:write"],"publish":["docs:write"],"unpublish":["docs:write"],"shares":["docs:read"],"share":["docs:write"],"unshare":["docs:write"],"create_synced_block":["docs:write"],"update_synced_block":["docs:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["delete","polls","vote","close_poll","blame","comments","comment","resolve_comment","delete_comment","publish","unpublish","shares","share","unshare","create_synced_block","update_synced_block"],"description":"What to do."},"doc_id":{"type":"string","description":"Doc id or public id."},"content":{"type":"string"},"block_id":{"type":"string","description":"comment: the block it is on."},"parent_comment_id":{"type":"number"},"comment_id":{"type":"number"},"email":{"type":"string"},"role":{"type":"string","enum":["viewer","editor"]},"notify":{"type":"boolean"},"block":{"type":"string","description":"synced block id"},"blocks":{"type":"string","description":"synced block content: a JSON string of blocks."},"rev":{"type":"number"},"workspace_id":{"type":"number"},"poll_id":{"type":"string","description":"polls / vote / close_poll: the id in get_doc's ```poll id=... line."},"vote":{"type":"array","items":{"type":"number"},"description":"vote: the option numbers you pick (0 = first)."},"closed":{"type":"boolean"},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/documents": {"post":{"operationId":"tool_documents","summary":"Documents and contracts","description":"Document templates (contracts, proposals, NDAs) filled with custom values ({{contact.full_name}}, {{business.name}}, {{deal.value}}, {{date.today}}, {{custom_values.key}}, any CRM custom field key, {{input.key}} fill-ins), turned into a PDF on a person or deal, and sent for signature by email. Template kinds: pdf (fields placed on an uploaded PDF: kind merge {text}, input {key,label,default}, signature/initials/date/name/text/checkbox {role}; rect 0..1 of the page, origin top-left) or written (a Word document: edit it with word_docs; signer blocks in the text: {{sign:client}}, {{initials:client}}, {{date:client}}, {{text:client:Label}}, {{check:client}}). Roles: [{key, label, type: contact|teammate|email}].\nActions:\n- templates: List templates (type contract|proposal|agreement|other, query) and the starters.\n- get_template: One template with its fields, roles, fill-ins and settings.\n- create_template: New template: kind pdf with file (a Drive PDF), kind written (blank Word document) or file = a .docx, or starter (service_agreement | proposal | mutual_nda). name, type, workspace_id.\n- update_template: Change a template: set {name?, type?, fields?, roles?, settings? {order, doc_name, subject, message, expires_days, remind_days}, inputs? [{key,label,default}]}.\n- duplicate_template: Copy a template.\n- delete_template: Remove a template (documents made from it stay). (waits for approval: delete)\n- options: The values you can use in templates (incl. Custom values) and who can countersign.\n- preview: Check a template for a person (contact_id / deal_id, or the sample person): empty values, signer fields, pages, warnings. Nothing is stored.\n- check_values: Which values are empty for this person, and the fill-ins the template asks for.\n- generate: Make the PDF for a person / deal (template, contact_id, deal_id, inputs {key: value}). Saved in Drive (Documents folder) and on their record; not sent.\n- send: Send for signature: template (or document = a draft), contact_id, deal_id, inputs, countersign_user_id, order client_first|company_first|parallel, subject, message, expires_days, remind_days, role_emails {role: email}, pay_on_sign {amount?, currency?, description?} (Stripe: the client pays right after signing; amount empty = the deal's value). The contact gets an email link with a code check. (waits for approval: external)\n- documents: Documents for a person, deal or template (contact_id, deal_id, template, status), with status and signers.\n- status: One document: status, each signer, links to the PDF and the signed copy.\n- cancel: Cancel a sent document (its signing links stop working). (waits for approval: external)","tags":["tools"],"x-ctrl-tool":"documents","x-ctrl-scopes":{"templates":["esign:read"],"get_template":["esign:read"],"create_template":["esign:write"],"update_template":["esign:write"],"duplicate_template":["esign:write"],"delete_template":["esign:write"],"options":["esign:read"],"preview":["esign:read"],"check_values":["esign:read"],"generate":["esign:write"],"send":["esign:write"],"documents":["esign:read"],"status":["esign:read"],"cancel":["esign:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["templates","get_template","create_template","update_template","duplicate_template","delete_template","options","preview","check_values","generate","send","documents","status","cancel"],"description":"What to do."},"pay_on_sign":{"type":["object","boolean"],"description":"send: collect a payment when the client signs { amount?, currency?, description? }."},"template":{"type":"string","description":"Template id or public id."},"document":{"type":"string","description":"Document id or public id."},"kind":{"type":"string","enum":["pdf","written"]},"type":{"type":"string","enum":["contract","proposal","agreement","other"]},"name":{"type":"string"},"file":{"type":"string","description":"Drive file id or public id (a PDF or .docx)."},"starter":{"type":"string","enum":["service_agreement","proposal","mutual_nda"]},"workspace_id":{"type":"number"},"query":{"type":"string"},"set":{"type":"object","description":"update_template changes."},"contact_id":{"type":"number"},"deal_id":{"type":"number"},"inputs":{"type":"object","description":"Fill-in values {key: text}; text may use {{values}}."},"countersign_user_id":{"type":"number"},"order":{"type":"string","enum":["client_first","company_first","parallel"]},"subject":{"type":"string"},"message":{"type":"string"},"expires_days":{"type":"number"},"remind_days":{"type":"number"},"role_emails":{"type":"object"},"status":{"type":"string"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/draft_email": {"post":{"operationId":"tool_draft_email","summary":"Draft an email or reply","description":"Save a Gmail draft (new email, or a reply when thread_id is set). Nothing is sent; you can review it in Gmail or Plodder Email.","tags":["tools"],"x-ctrl-tool":"draft_email","x-ctrl-scopes":{"*":["messages:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"account_id":{"type":"number","description":"Sending account. Default: your first connected account."},"thread_id":{"type":"string","description":"Reply in this thread. Recipients and subject come from the newest message unless you pass them."},"reply_all":{"type":"boolean","description":"With thread_id: cc everyone on the last message."},"to":{"type":"array","items":{"type":"string"}},"cc":{"type":"array","items":{"type":"string"}},"bcc":{"type":"array","items":{"type":"string"}},"subject":{"type":"string"},"body":{"type":"string","description":"Plain text body."},"draft_id":{"type":"string","description":"Update this existing draft instead of creating one."}},"required":["body"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/drive": {"post":{"operationId":"tool_drive","summary":"Drive and folders","description":"Organize Drive and the sidebar, as you: folders (create, rename, colour, delete), move items between folders and projects, rename, trash, restore and delete forever, save a chat/task/mail attachment into Drive, extract a .zip, and the project/stage/task Trash. kind: doc | sheet | folder | file (move also takes mail_thread | channel).\nActions:\n- folders: Folders in a workspace.\n- create_folder: A folder (workspace_id, name, color, parent_id).\n- update_folder: Change a folder (folder_id, set).\n- color_folder_tree: Give a folder and everything in it one colour (folder_id, color).\n- delete_folder: Delete a folder. (waits for approval: delete)\n- sidebar_move: Move a project, doc, folder or database into a folder or project in the sidebar (type, id, target_folder_id or target_project_id, sort_order).\n- move: Move Drive items (items, target).\n- rename: Rename one Drive item (items: [one], name).\n- trash: Move Drive items to Trash (items). (waits for approval: delete)\n- restore: Bring Drive items back from Trash (items).\n- extract_zip: Extract a .zip in Drive next to it (items: [{kind: \"file\", id}]): a new folder named after the zip with its folders and files. Links, system files and blocked types are skipped; oversized or zip-bomb archives are refused.\n- delete_forever: Delete Drive items that are already in Trash, for good (items). (waits for approval: delete)\n- save_attachment: Copy an attachment into Drive (source attachment with attachment, url with url, or mail with mail), to target.\n- work_trash: Deleted projects, stages and tasks.\n- restore_work: Restore deleted projects, stages or tasks (project_ids, stage_ids, task_ids).\n- purge_work: Delete deleted projects, stages or tasks for good (project_ids, stage_ids, task_ids). (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"drive","x-ctrl-scopes":{"folders":["files:read"],"create_folder":["files:write"],"update_folder":["files:write"],"color_folder_tree":["files:write"],"delete_folder":["files:write"],"sidebar_move":["files:write"],"move":["files:write"],"rename":["files:write"],"trash":["files:write"],"restore":["files:write"],"extract_zip":["files:write"],"delete_forever":["files:write"],"save_attachment":["files:write"],"work_trash":["files:read"],"restore_work":["files:write"],"purge_work":["files:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["folders","create_folder","update_folder","color_folder_tree","delete_folder","sidebar_move","move","rename","trash","restore","extract_zip","delete_forever","save_attachment","work_trash","restore_work","purge_work"],"description":"What to do."},"folder_id":{"type":"number"},"name":{"type":"string"},"color":{"type":"string"},"parent_id":{"type":"number"},"workspace_id":{"type":"number"},"set":{"type":"object","description":"update_folder: fields (name, color, parent_id, sort_order, ...)."},"items":{"type":"array","items":{"type":"object"},"description":"[{kind, id}]"},"target":{"type":"object","description":"{folderId?, workspaceId?}"},"type":{"type":"string","description":"sidebar_move: project | doc | folder | database; trash: project | stage | task."},"id":{"type":"number"},"target_folder_id":{"type":"number"},"target_project_id":{"type":"number"},"sort_order":{"type":"number"},"source":{"type":"string","enum":["attachment","url","mail"]},"attachment":{"type":"string","description":"save_attachment: msg:<id> | task:<id>"},"url":{"type":"string"},"mail":{"type":"object","description":"save_attachment from mail: {account, message, id, name, type}"},"project_ids":{"type":"array","items":{"type":"number"}},"stage_ids":{"type":"array","items":{"type":"number"}},"task_ids":{"type":"array","items":{"type":"number"}},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/email_campaigns": {"post":{"operationId":"tool_email_campaigns","summary":"Email campaigns","description":"Email campaigns through Mailjet (Email > Campaigns), as you: one email to an audience of OPTED-IN people (smart lists / segments, tags, people, a People filter; minus excluded smart lists and tags). People without an address, with do not email / unsubscribed, tagged cold / no-consent, or on the suppression list (bounced, complained, unsubscribed) are skipped with the reason; a smart list flagged cold is refused. Send now, schedule at a wall clock time in a time zone, or batches (batch.size people every batch.every_min minutes). Every email carries the unsubscribe link and your mailing address; links are click tracked per link; opens come from Mailjet. Stats: sent, delivered, opened, clicked (per link), bounced, unsubscribed, complained; recipients filterable by event; add those people to a tag or a workflow; resend to the ones who did not open after N hours (optionally a new subject). audience = { segment_ids?: number[], tag_ids?: number[] (tag option ids, crm_contacts filter_fields \"tags\"), contact_ids?: number[], filter?: People filter }; exclusions = { segment_ids?, tag_ids? }. A/B test: ab = { on, variants: [{ subject, body? }] (versions B and C; A is the campaign itself), test_pct 10-50 (share of people in the test, split evenly), metric open | click, wait_hours 1-72 }; the winner goes to everyone else after the wait (get shows per version results and the winner). Invalid, throwaway and no-mail-server addresses are skipped (the address check, email_domains validate). A client's own From address works once their sending domain is verified (email_domains).\nActions:\n- list: Every campaign, newest first, with status, counts (total, sent, skipped, failed), delivered, opened and clicked.\n- get: One campaign (campaign_id) with stats, clicks per link and its resends.\n- create: A draft: name, subject, body (HTML for rich / html, merge fields {{first_name|there}}, {{name}}, {{company}}, {{unsubscribe_link}}, {{workspace.mailing_address}}), format (rich | html | text), from_email (a verified Mailjet sender, else your default), from_name, reply_to, track (opens and clicks, default true), audience, exclusions, batch, send_local + timezone.\n- update: Change a draft or scheduled campaign (same fields as create). After it starts only the name changes.\n- duplicate: Copy a campaign into a new draft (campaign_id).\n- delete: Delete a draft, sent or cancelled campaign (campaign_id). (waits for approval: delete)\n- preview: The email exactly as a send builds it, nothing sent: subject, body, format (rich | html | text), contact_id? (one person on the list; else sample values). Returns subject, html, text, person and a warning when the mailing address is missing.\n- preview_audience: Who it would reach: audience + exclusions (or campaign_id of a resend). Returns total, sendable, skipped reasons with counts, a sample.\n- test: Send the campaign (campaign_id; subject / body override optional) to yourself only, subject prefixed [Test]. (waits for approval: send email)\n- send: Send now (campaign_id). Batches go out on their schedule. (waits for approval: send email)\n- schedule: Schedule (campaign_id, send_local \"YYYY-MM-DDTHH:mm\", timezone e.g. America/Vancouver). (waits for approval: send email)\n- unschedule: A scheduled campaign back to draft (campaign_id).\n- pause: Stop sending (campaign_id). Nothing more goes out until resume.\n- resume: Carry on (campaign_id). Batches that fell due while paused go now, the rest keep their spacing. (waits for approval: send email)\n- cancel: Cancel (campaign_id): everyone not sent yet is skipped.\n- resend_unopened: Resend to people who did not open it: campaign_id, after_hours (1 to 720, from when it started), subject (optional new subject), name. Creates a scheduled campaign. (waits for approval: send email)\n- recipients: Recipients (campaign_id), filter event = sent | delivered | opened | clicked | bounced | unsubscribed | complained | not_opened | skipped | failed | queued, q, limit (<= 1000), offset.\n- add_to: Give the people behind an event (campaign_id, event) a tag (tag = the tag name) or add them to a workflow (workflow_id; it must be published and on). (may wait for approval)","tags":["tools"],"x-ctrl-tool":"email_campaigns","x-ctrl-scopes":{"list":["messages:read"],"get":["messages:read"],"create":["messages:write"],"update":["messages:write"],"duplicate":["messages:write"],"delete":["messages:write"],"preview":["messages:read"],"preview_audience":["messages:read"],"test":["messages:send"],"send":["messages:send"],"schedule":["messages:send"],"unschedule":["messages:write"],"pause":["messages:write"],"resume":["messages:send"],"cancel":["messages:write"],"resend_unopened":["messages:send"],"recipients":["messages:read"],"add_to":["messages:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get","create","update","duplicate","delete","preview","preview_audience","test","send","schedule","unschedule","pause","resume","cancel","resend_unopened","recipients","add_to"],"description":"What to do."},"campaign_id":{"type":"number"},"name":{"type":"string"},"subject":{"type":"string"},"body":{"type":"string"},"format":{"type":"string","enum":["rich","html","text"]},"from_email":{"type":"string"},"from_name":{"type":"string"},"reply_to":{"type":"string"},"track":{"type":"boolean"},"audience":{"type":"object","description":"{ segment_ids?, tag_ids?, contact_ids?, filter? }"},"exclusions":{"type":"object","description":"{ segment_ids?, tag_ids? }"},"batch":{"type":"object","description":"{ size: people per batch (0 = all at once), every_min: minutes between batches }"},"send_local":{"type":"string","description":"YYYY-MM-DDTHH:mm, wall clock time in timezone"},"timezone":{"type":"string"},"ab":{"type":["object","null"],"description":"A/B test: { on, variants: [{ subject, body? }], test_pct, metric open | click, wait_hours }"},"after_hours":{"type":"number"},"event":{"type":"string","enum":["sent","delivered","opened","clicked","bounced","unsubscribed","complained","not_opened","skipped","failed","queued"]},"q":{"type":"string"},"limit":{"type":"number"},"offset":{"type":"number"},"tag":{"type":"string"},"workflow_id":{"type":"number"},"contact_id":{"type":"number","description":"preview: one person on the list"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/email_domains": {"post":{"operationId":"tool_email_domains","summary":"Sending domains and address checks","description":"Each client's own sending domain for Mailjet email (Settings > Email > Sending domains), as you: add a domain (it becomes a Mailjet domain sender; the DNS records to add come back: SPF include, DKIM key, ownership token), check DNS (SPF / DKIM status and errors; verified when both are OK), set who it belongs to (business_id = a client, null = your own company) and label. A domain has several senders (hello@, news@, support@ ... each with its own name and Reply-to, one the default; the Mailjet domain sender already covers every address, so a sender needs no Mailjet step): senders / add_sender / update_sender / remove_sender. Every sender of a verified domain shows in campaign, workflow and CRM From pickers, grouped by domain. validate: check an address (email) or people (contact_ids): syntax, a mail server for the domain (MX, cached 30 days), throwaway domains; the result (valid | invalid | no_mx | disposable | unknown) is flagged on each person and campaigns / bulk sends skip invalid, no_mx and disposable.\nActions:\n- list: Every sending domain with its records, SPF / DKIM status, client and default From.\n- add: Add a domain: domain (client.com), business_id? (the client), label?, default_from? (address or local part on the domain), default_name?.\n- update: Change a domain (domain_id): label, business_id; default_from / default_name set the default sender (made when new).\n- senders: The domain's senders (domain_id): address, display_name, reply_to, is_default.\n- add_sender: Add a sender (domain_id, local_part = the part before the @, like news; display_name?, reply_to?, is_default?). Unique per domain; the first one is the default.\n- update_sender: Change a sender (domain_id, sender_id): display_name, reply_to, is_default: true makes it the default.\n- remove_sender: Remove a sender (domain_id, sender_id); when it was the default, the oldest one left becomes the default. (waits for approval: delete)\n- check: Ask Mailjet to check its DNS now (domain_id).\n- remove: Take a domain off the list (domain_id); the Mailjet sender stays on the account. (waits for approval: delete)\n- validate: Check an address (email) or people's primary addresses (contact_ids, <= 2000); fresh = skip the 30-day cache.","tags":["tools"],"x-ctrl-tool":"email_domains","x-ctrl-scopes":{"list":["messages:read"],"add":["messages:write"],"update":["messages:write"],"senders":["messages:read"],"add_sender":["messages:write"],"update_sender":["messages:write"],"remove_sender":["messages:write"],"check":["messages:write"],"remove":["messages:write"],"validate":["messages:read"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","add","update","senders","add_sender","update_sender","remove_sender","check","remove","validate"],"description":"What to do."},"domain_id":{"type":"number"},"domain":{"type":"string"},"business_id":{"type":["number","null"]},"label":{"type":"string"},"default_from":{"type":"string"},"default_name":{"type":"string"},"sender_id":{"type":"number"},"local_part":{"type":"string"},"display_name":{"type":"string"},"reply_to":{"type":"string"},"is_default":{"type":"boolean"},"email":{"type":"string"},"contact_ids":{"type":"array","items":{"type":"number"}},"fresh":{"type":"boolean"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/favorites": {"post":{"operationId":"tool_favorites","summary":"Favorites","description":"Your favorites (the starred items in the palette and lists). Items are \"type:id\" refs, e.g. \"task:12\", \"doc:abc\".\nActions:\n- list: Your favorites with titles and links.\n- add: Favorite items (refs).\n- remove: Unfavorite items (refs).","tags":["tools"],"x-ctrl-tool":"favorites","x-ctrl-scopes":{"list":["admin:read"],"add":["admin:write"],"remove":["admin:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","add","remove"],"description":"What to do."},"refs":{"type":"array","items":{"type":"string"}}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/fetch": {"post":{"operationId":"tool_fetch","summary":"Fetch an item","description":"Get the full content of an item by the id search returned: task:<id>, project:<id>, doc:<id>, meeting:<id>, client:<id>, or email:<account_id>:<thread_id>.","tags":["tools"],"x-ctrl-tool":"fetch","x-ctrl-scopes":{"*":["ctrl-flow:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"For example task:123 or doc:45."}},"required":["id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/forms": {"post":{"operationId":"tool_forms","summary":"Forms","description":"Build forms, read their responses and answer them, as you. A form writes its answers into a database (one row per response). Find forms with list_items types [\"form\"]. Question types: short, paragraph, email, phone, url, choice, checkboxes, dropdown, checkbox, number, date, datetime, person, relation.\nActions:\n- get: The form: questions (layout sections), settings, columns, counts, status.\n- create: A new form (name, optional workspace_id); with database_id it asks that database's columns, or returns the form that database already has (the database page's Form button).\n- edit: One builder change: op = meta {name, description} | add_question {type, label, sectionId?, afterQuestionId?, relationSheetId?} | add_existing {columnId} | rename_question {questionId, name} | set_type {questionId, type, convert?} | set_options {questionId, options:[..]} | remove_question {questionId, deleteColumn?} | add_section {kind: questions|gate|booking, bookingLinkId?} | layout {layout} | settings {settings} | public_link {enabled} | lead_pipeline {lead_pipeline: {on, pipeline_id, stage_id?, title? (custom values, default \"{{form.name}}: {{contact.name}}\"), value_question? (a question id whose number answer is the deal value)} or null}: on = each submission finds or makes the person from the email / phone answer and adds a deal in that CRM pipeline unless they already have an open one there (then the submission is logged on it); lead source from the page UTM. Put the op's fields in `fields`. (may wait for approval)\n- trash: Move the form to Trash. (waits for approval: delete)\n- responses: Every response (the linked database rows) with column names.\n- stats: Public link traffic (same numbers as the Responses tab): views (each visitor once a day), starts (first touch), submits (public link responses), start_rate and conversion (submits / views, 0..1), per-day series. days = only the last N days.\n- questions: What a respondent sees: sections and questions with their ids, for answer.\n- answer: Submit a response as you: answers = {questionId: value}. preview=true checks without saving.","tags":["tools"],"x-ctrl-tool":"forms","x-ctrl-scopes":{"get":["forms:read"],"create":["forms:write"],"edit":["forms:write"],"trash":["forms:write"],"responses":["forms:read"],"stats":["forms:read"],"questions":["forms:read"],"answer":["forms:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["get","create","edit","trash","responses","stats","questions","answer"],"description":"What to do."},"form_id":{"type":"string","description":"Form id or public id."},"name":{"type":"string"},"workspace_id":{"type":"number"},"database_id":{"type":"string","description":"create: a database to build the form on."},"op":{"type":"string","enum":["meta","layout","settings","public_link","lead_pipeline","add_question","add_existing","rename_question","set_type","set_options","remove_question","add_section"],"description":"edit: which builder change."},"fields":{"type":"object","description":"edit: the op's fields."},"answers":{"type":"object","description":"answer: {questionId: value}."},"preview":{"type":"boolean"},"days":{"type":"number","description":"stats: only the last N days (omit for all time)."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/get_business": {"post":{"operationId":"tool_get_business","summary":"Get a business","description":"One business profile: context, offer, audience, location, budgets, channels.","tags":["tools"],"x-ctrl-tool":"get_business","x-ctrl-scopes":{"*":["crm:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"business_id":{"type":["number","string"],"description":"Id or slug."}},"required":["business_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/get_call": {"post":{"operationId":"tool_get_call","summary":"Get a video call","description":"One Plodder video call: summary, action items (with their task ids), who was there, the related item, the in-call chat, and the transcript when include_transcript is true.","tags":["tools"],"x-ctrl-tool":"get_call","x-ctrl-scopes":{"*":["calls:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"call_id":{"type":"string","description":"The 12-character call id from list_calls or a /calls/<id> link."},"include_transcript":{"type":"boolean"}},"required":["call_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/get_client": {"post":{"operationId":"tool_get_client","summary":"Get a client","description":"One client profile (context, goals, offer, audience, channels) and its businesses.","tags":["tools"],"x-ctrl-tool":"get_client","x-ctrl-scopes":{"*":["crm:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":["number","string"],"description":"Id or slug."}},"required":["client_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/get_database_row": {"post":{"operationId":"tool_get_database_row","summary":"Read a database row","description":"One database row with every column (formulas computed, relations as titles), its notes, and its database. Rows are items too: sheet_row:<id> works with get_properties, set_tags, read_discussion, post_discussion and read_activity.","tags":["tools"],"x-ctrl-tool":"get_database_row","x-ctrl-scopes":{"*":["docs:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"row_id":{"type":"string","description":"Row id or sheet_row:<id>."}},"required":["row_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/get_doc": {"post":{"operationId":"tool_get_doc","summary":"Read a doc","description":"Read a doc as markdown-style text, with its word_count, characters and reading_minutes. checklists lists each checklist (2+ items in a row) with its done/total progress and the filter that shows items (all, open, done).","tags":["tools"],"x-ctrl-tool":"get_doc","x-ctrl-scopes":{"*":["docs:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"doc_id":{"type":["number","string"],"description":"Doc id or public id."}},"required":["doc_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/get_email_thread": {"post":{"operationId":"tool_get_email_thread","summary":"Read an email thread","description":"Every message in a Gmail thread as plain text.","tags":["tools"],"x-ctrl-tool":"get_email_thread","x-ctrl-scopes":{"*":["messages:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"thread_id":{"type":"string"},"account_id":{"type":"number","description":"From list_email_threads."}},"required":["thread_id","account_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/get_meeting": {"post":{"operationId":"tool_get_meeting","summary":"Get a meeting","description":"One meeting: summary, action items, attendees, and the transcript if include_transcript is true.","tags":["tools"],"x-ctrl-tool":"get_meeting","x-ctrl-scopes":{"*":["meetings:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"meeting_id":{"type":"number"},"include_transcript":{"type":"boolean"}},"required":["meeting_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/get_project": {"post":{"operationId":"tool_get_project","summary":"Get a project","description":"One project with its stages, task counts and open tasks.","tags":["tools"],"x-ctrl-tool":"get_project","x-ctrl-scopes":{"*":["projects:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"project_id":{"type":["number","string"]}},"required":["project_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/get_properties": {"post":{"operationId":"tool_get_properties","summary":"Read properties","description":"Every property of an item with its current value (status, priority, assignee, dates, tags, deal stage, custom fields), and for select properties the allowed options.","tags":["tools"],"x-ctrl-tool":"get_properties","x-ctrl-scopes":{"*":["ctrl-flow:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"string","description":"An item as type:id, for example task:123, doc:45, project:7, crm_company:3, sheet_row:88, meeting:12 or mail_thread:<account>-<thread>. list_items and search return these."},"with_options":{"type":"boolean","description":"Include the options of select properties (default true)."}},"required":["item"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/get_routine": {"post":{"operationId":"tool_get_routine","summary":"Read a routine","description":"One routine with its full instructions, triggers and its recent runs.","tags":["tools"],"x-ctrl-tool":"get_routine","x-ctrl-scopes":{"*":["automations:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"routine_id":{"type":"number"}},"required":["routine_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/get_task": {"post":{"operationId":"tool_get_task","summary":"Get a task","description":"Full detail for one task: description, subtasks and recent comments/activity.","tags":["tools"],"x-ctrl-tool":"get_task","x-ctrl-scopes":{"*":["tasks:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"task_id":{"type":["number","string"],"description":"Task id or public id."}},"required":["task_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/get_user": {"post":{"operationId":"tool_get_user","summary":"Get a user","description":"One person who works in the app, by their id (team id), user_id (numeric login id), email (any case) or, when only one person has it, their exact name. Returns id, user_id, name, email, role, type, active. Fails naming the ref when nobody or more than one person matches.","tags":["tools"],"x-ctrl-tool":"get_user","x-ctrl-scopes":{"*":["users:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ref":{"type":"string","description":"id, user_id, email or exact name."}},"required":["ref"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/github_pulls": {"post":{"operationId":"tool_github_pulls","summary":"GitHub pull requests","description":"Reviews: your GitHub pull requests, read live through your own GitHub link (/api/github/pulls). Comments, reviews (approve / request changes) and merges are posted as you on GitHub, so they wait for your approval; a merge always does.\nActions:\n- list: Pull requests: scope all|authored|assigned|involving|review_requests, state open|closed|all, repository?, author?, label?, q?, priority? (urgent|high|medium|low|none), linked? (agent|ticket|customer), sort? (recently_updated|priority). Each carries links: coding sessions, tasks, customers, priority and origin.\n- get: One pull request (repo owner/name, number) with changed files and the conversation.\n- repositories: Repositories you can hand a coding agent.\n- comment: Comment on the pull request (body). (waits for approval: external)\n- review: Submit a review: event APPROVE|REQUEST_CHANGES|COMMENT, body?. (waits for approval: external)\n- merge: Merge it (method merge|squash|rebase, default squash). Always waits for approval. (waits for approval: external)","tags":["tools"],"x-ctrl-tool":"github_pulls","x-ctrl-scopes":{"list":["automations:read"],"get":["automations:read"],"repositories":["automations:read"],"comment":["automations:write"],"review":["automations:write"],"merge":["automations:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get","repositories","comment","review","merge"],"description":"What to do."},"repo":{"type":"string","description":"owner/name."},"number":{"type":"number"},"body":{"type":"string"},"event":{"type":"string","enum":["APPROVE","REQUEST_CHANGES","COMMENT"]},"method":{"type":"string","enum":["merge","squash","rebase"]},"scope":{"type":"string","enum":["all","authored","assigned","involving","review_requests"]},"state":{"type":"string","enum":["open","closed","all"]},"repository":{"type":"array","items":{"type":"string"}},"author":{"type":"array","items":{"type":"string"}},"label":{"type":"array","items":{"type":"string"}},"q":{"type":"string"},"limit":{"type":"number"},"priority":{"type":"array","items":{"type":"string","enum":["urgent","high","medium","low","none"]}},"linked":{"type":"array","items":{"type":"string","enum":["agent","ticket","customer"]}},"sort":{"type":"string","enum":["recently_updated","least_recently_updated","newest","oldest","priority"]}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/google_calendars": {"post":{"operationId":"tool_google_calendars","summary":"Calendar settings","description":"Settings for each connected Google calendar in the app (see list_calendars for ids): shown or hidden, default busy or free, whether it blocks auto-scheduling, color, and the primary calendar new events go to. App-side only; nothing changes in Google.\nActions:\n- set: visible, default_busy_status (busy | free), use_for_conflicts. Changes reshuffle auto-scheduled tasks.\n- set_color: color (hex).\n- set_primary: Make this the calendar new events go to.","tags":["tools"],"x-ctrl-tool":"google_calendars","x-ctrl-scopes":{"set":["bookings:write"],"set_color":["bookings:write"],"set_primary":["bookings:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["set","set_color","set_primary"],"description":"What to do."},"calendar_id":{"type":"string"},"visible":{"type":"boolean"},"default_busy_status":{"type":"string","enum":["busy","free"]},"use_for_conflicts":{"type":"boolean"},"color":{"type":"string"}},"required":["action","calendar_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/home_feed": {"post":{"operationId":"tool_home_feed","summary":"Home feed","description":"Your Home sidebar as one list, built on the server: task activity, Important email, chat mentions, replies and DMs, notifications, things you opened lately and agent conversations. Events about one thing fold into one row (unread while any is), newest first, with date groups. signal marks rows someone is waiting on you for.","tags":["tools"],"x-ctrl-tool":"home_feed","x-ctrl-scopes":{"*":["admin:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["all","unread","read"]},"types":{"type":"array","items":{"type":"string","enum":["tasks","email","chat","notifications","files","projects","agents"]}},"tz_offset_min":{"type":"number","description":"Minutes east of UTC for the date groups (default 0)."},"limit":{"type":"number","description":"Rows, default 50, max 200."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/illustration": {"post":{"operationId":"tool_illustration","summary":"Illustrations","description":"Read and edit illustrations (vector artwork on artboards), as you. Same document as design files: artboards are top-level frames on a page; draw with path nodes {t:\"path\", paths: [{pts: [{x, y, i?: [dx, dy], o?: [dx, dy]}], closed}], fills, strokes, strokeWidth}, shapes and text. Items: pages {t:\"page\", name, bg, modes: {<collection id>: <mode id>}}, color/text styles {t:\"style\", kind}, variable collections {t:\"vars\", name, modes: [{id, name}]}, variables {t:\"var\", coll, name, type: color|number|string|boolean, values: {<mode id>: value}}, and nodes {t: frame|group|rect|ellipse|polygon|star|path|text|instance|boolean, parent: page or container id, name, x, y, w, h, fills: [{type:\"solid\", color:\"#hex\"}], strokes, strokeWidth, effects: [], text nodes {text, style}}. Bind a node property to a variable with bind: {fill|stroke|opacity|radius|strokeWidth|w|h|gap|padding|text|visible|fontSize: <variable id>} (the value is written on save). Frames take layout grids: grids: [{type: columns|rows|grid, count, size, gutter, margin, align: stretch|start|center|end, color, opacity}] and modes like pages.\nActions:\n- list: Illustrations you can open, newest first.\n- get: The illustration: title, your access, and every item as JSON.\n- create: A new illustration (title, workspace_id or folder_id), filed in Drive.\n- rename: Change the title.\n- add: Add items: a list of objects (an id is made when missing).\n- update: Change items by id: a list of {id, ...fields to change}.\n- remove: Remove items by id (ids). Undo is in the editor's history.\n- replace: Replace the whole illustration content (content = {items: [...]}).\n- trash: Move the illustration to Trash. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"illustration","x-ctrl-scopes":{"list":["docs:read"],"get":["docs:read"],"create":["docs:write"],"rename":["docs:write"],"add":["docs:write"],"update":["docs:write"],"remove":["docs:write"],"replace":["docs:write"],"trash":["docs:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get","create","rename","add","update","remove","replace","trash"],"description":"What to do."},"id":{"type":"string","description":"illustration id or public id."},"title":{"type":"string"},"workspace_id":{"type":"number"},"folder_id":{"type":"number"},"items":{"type":"array","items":{"type":"object"},"description":"add / update: the items."},"ids":{"type":"array","items":{"type":"string"},"description":"remove: ids."},"content":{"type":"object","description":"replace: the whole document."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/images": {"post":{"operationId":"tool_images","summary":"Image editor","description":"Layered image projects (the image editor), as you: list, create, open a Drive image for editing, read and change layers (name, visibility, opacity, position, order, text of text layers, remove), versions, and Remove background. Painting, filters and selections are in the editor in the browser.\nActions:\n- list: Image projects you can open, newest first.\n- get: Size, access, where it sits, the Drive image it came from, and its layers.\n- create: A blank image: title, width, height, background (default white), workspace_id or folder_id.\n- open_drive_image: Open a Drive image (drive_file) as a layered project (reuses the one made before).\n- update_layers: Change layers: layers [{id, name?, visible?, opacity?, x?, y?, w?, h?, blend?, text?, locked?}].\n- move_layer: Move a layer (layer_id) to index (0 = bottom) within its group.\n- remove_layer: Remove a layer (layer_id). (waits for approval: delete)\n- remove_background: Remove the background of a pixel layer (layer_id): with the user's OpenAI key the result is added as a new layer above (the original is hidden, not lost); without one, the answer explains the in-browser path. (may wait for approval)\n- rename: Change the title.\n- move: Move in Drive: workspace_id and/or folder_id.\n- versions: Earlier saves of the project, and of the Drive image it came from.\n- restore_version: Make an earlier project save current (version_id), or of the Drive original (original=true).\n- trash: Move to Drive Trash. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"images","x-ctrl-scopes":{"list":["docs:read"],"get":["docs:read"],"create":["docs:write"],"open_drive_image":["docs:write"],"update_layers":["docs:write"],"move_layer":["docs:write"],"remove_layer":["docs:write"],"remove_background":["docs:write"],"rename":["docs:write"],"move":["docs:write"],"versions":["docs:read"],"restore_version":["docs:write"],"trash":["docs:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get","create","open_drive_image","update_layers","move_layer","remove_layer","remove_background","rename","move","versions","restore_version","trash"],"description":"What to do."},"id":{"type":"string","description":"Image project id or public id."},"drive_file":{"type":"string"},"title":{"type":"string"},"width":{"type":"number"},"height":{"type":"number"},"background":{"type":"boolean"},"layers":{"type":"array","items":{"type":"object"}},"layer_id":{"type":"string"},"index":{"type":"number"},"version_id":{"type":"number"},"original":{"type":"boolean"},"workspace_id":{"type":"number"},"folder_id":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/imports": {"post":{"operationId":"tool_imports","summary":"Imports","description":"Import jobs from other tools (/api/import/jobs). File imports (Notion export, Slack, CSV) are uploaded by the person in the app; here: a GitHub import (options.repo) through your GitHub link, and every job's preview, options, start and cancel. API tokens (Notion, Linear) are pasted by the person.\nActions:\n- list: Your imports, newest first.\n- get: One import with its preview.\n- create: A new import that previews first (source, workspace_id, options). Nothing is written until start.\n- set_options: Change an import's options; the preview runs again (job_id, options).\n- start: Run the import the preview showed (again = update, never duplicate).\n- cancel: Stop a running import after its current item; what was written stays.","tags":["tools"],"x-ctrl-tool":"imports","x-ctrl-scopes":{"list":["files:read"],"get":["files:read"],"create":["files:write"],"set_options":["files:write"],"start":["files:write"],"cancel":["files:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get","create","set_options","start","cancel"],"description":"What to do."},"job_id":{"type":"number"},"source":{"type":"string","description":"create: github."},"workspace_id":{"type":"number"},"options":{"type":"object","description":"create / set_options: source options (repo, picked items, people mapping, filters)."},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/installed_skills": {"post":{"operationId":"tool_installed_skills","summary":"Installed skill library","description":"The installed skill library (/api/skills): list, turn on or off, give to a runtime agent, delete, install from GitHub. Owner only. Agent skills in the agent editor are agent_skills.\nActions:\n- list: Every installed skill.\n- get: One skill (skill_id) and the agents that have it.\n- set_enabled: Turn a skill on or off (skill_id, enabled).\n- assign: Give a skill to a runtime agent or take it away (skill_id, agent_id, enabled).\n- delete: Delete an installed skill (skill_id). (waits for approval: delete)\n- install_from_github: Install a skill from a GitHub URL (github_url). Downloads outside code. (waits for approval: external)","tags":["tools"],"x-ctrl-tool":"installed_skills","x-ctrl-scopes":{"list":["automations:read"],"get":["automations:read"],"set_enabled":["automations:write"],"assign":["automations:write"],"delete":["automations:write"],"install_from_github":["automations:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get","set_enabled","assign","delete","install_from_github"],"description":"What to do."},"skill_id":{"type":"number"},"agent_id":{"type":"string"},"enabled":{"type":"boolean"},"github_url":{"type":"string"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/instantly": {"post":{"operationId":"tool_instantly","summary":"Instantly (cold email)","description":"Cold email runs through Instantly. status: connected workspace, webhook, settings (lead pipeline for Interested, skips, stop triggers), 30 day campaign totals. campaigns: the workspace's campaigns (id, name, status). push: add CRM people (contact_ids, segment_id = a smart list or segment, filter = a People filter, company_ids, deal_ids) to an Instantly campaign (campaign_id) with their name, business, website, phone and custom variables (custom: [{ field: \"title\" | \"address_city\" | \"f:<custom field id>\", as: \"variable_name\" }]); people with no email, unsubscribed, Do not email, a bounced address or already in another active Instantly campaign are left out and reported. dry_run: true shows the report without adding anyone. stop: take a person (contact_id) out of their Instantly campaigns. sync: read the daily campaign numbers now (sent, replies, interested, booked).\nActions:\n- status: Connection, webhook, settings and 30 day campaign totals. Never the key.\n- campaigns: The Instantly campaigns (id, name, status), active first.\n- push: Add people to an Instantly campaign (campaign_id + who). Instantly then emails them on its schedule. (may wait for approval)\n- stop: Take a person (contact_id) out of every Instantly campaign Plodder knows them in. (waits for approval: external)\n- sync: Read the daily campaign numbers from Instantly now.","tags":["tools"],"x-ctrl-tool":"instantly","x-ctrl-scopes":{"status":["instantly:read"],"campaigns":["instantly:read"],"push":["messages:send"],"stop":["instantly:write"],"sync":["instantly:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["status","campaigns","push","stop","sync"],"description":"What to do."},"campaign_id":{"type":"string","description":"Instantly campaign id (from campaigns)."},"contact_ids":{"type":"array","items":{"type":"number"}},"company_ids":{"type":"array","items":{"type":"number"}},"deal_ids":{"type":"array","items":{"type":"number"}},"segment_id":{"type":"number","description":"A smart list or segment (CRM saved view) id."},"filter":{"type":"object","description":"A People filter (as the CRM sends it)."},"custom":{"type":"array","items":{"type":"object"},"description":"[{ field, as }] CRM fields sent as Instantly custom variables."},"dry_run":{"type":"boolean"},"contact_id":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/invitations": {"post":{"operationId":"tool_invitations","summary":"Invitations","description":"Email invitations to join a workspace (people who are not users yet). Sending one emails an outside person, so it waits for approval.\nActions:\n- list: Invitations for a workspace.\n- send: Invite someone by email (email, name?, role admin|member|client). Owners and admins. (waits for approval: external)\n- revoke: Revoke a pending invitation (invitation_id). (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"invitations","x-ctrl-scopes":{"list":["admin:read"],"send":["admin:write"],"revoke":["admin:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","send","revoke"],"description":"What to do."},"workspace_id":{"type":"number"},"email":{"type":"string"},"name":{"type":"string"},"role":{"type":"string","enum":["admin","member","client"]},"invitation_id":{"type":"number"},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/knowledge": {"post":{"operationId":"tool_knowledge","summary":"Knowledge base","description":"Workspace knowledge entries (text notes and links) agents can read (/api/knowledge).\nActions:\n- list: Entries in a workspace (workspace_id?) or every workspace you are in.\n- create: A new entry: title, type? text|url, content?, url?, workspace_id?, private?.\n- update: Change an entry (entry_id + fields).\n- delete: Delete an entry. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"knowledge","x-ctrl-scopes":{"list":["automations:read"],"create":["automations:write"],"update":["automations:write"],"delete":["automations:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","create","update","delete"],"description":"What to do."},"entry_id":{"type":"number"},"workspace_id":{"type":"number"},"title":{"type":"string"},"type":{"type":"string"},"content":{"type":"string"},"url":{"type":"string"},"private":{"type":"boolean"},"fields":{"type":"object"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/link_items": {"post":{"operationId":"tool_link_items","summary":"Link two items","description":"Relate one item to another (for example an email thread to a task, or a doc to a CRM company). Shows up in both items' References.","tags":["tools"],"x-ctrl-tool":"link_items","x-ctrl-scopes":{"*":["*"]},"x-ctrl-read-only":false,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"source":{"type":"string","description":"An item as type:id, for example task:123, doc:45, project:7, crm_company:3, sheet_row:88, meeting:12 or mail_thread:<account>-<thread>. list_items and search return these."},"target":{"type":"string","description":"An item as type:id, for example task:123, doc:45, project:7, crm_company:3, sheet_row:88, meeting:12 or mail_thread:<account>-<thread>. list_items and search return these."}},"required":["source","target"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_agents": {"post":{"operationId":"tool_list_agents","summary":"List agents","description":"The agents you can use or delegate to (yours and the team's shared ones): id, @handle, what each is for, its model.","tags":["tools"],"x-ctrl-tool":"list_agents","x-ctrl-scopes":{"*":["automations:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_booking_links": {"post":{"operationId":"tool_list_booking_links","summary":"List booking links","description":"Your scheduling (booking) pages: name, link, lengths, whether they take bookings. Share a link with someone so they can book time.","tags":["tools"],"x-ctrl-tool":"list_booking_links","x-ctrl-scopes":{"*":["bookings:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"include_inactive":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_businesses": {"post":{"operationId":"tool_list_businesses","summary":"List businesses","description":"Businesses (a client can have several) in your workspaces.","tags":["tools"],"x-ctrl-tool":"list_businesses","x-ctrl-scopes":{"*":["crm:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":["number","string"]},"query":{"type":"string"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_calendar_events": {"post":{"operationId":"tool_list_calendar_events","summary":"List calendar events","description":"Events on your connected Google calendars between two dates (default: today through the next 7 days). Times are ISO 8601.","tags":["tools"],"x-ctrl-tool":"list_calendar_events","x-ctrl-scopes":{"*":["bookings:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"string","description":"ISO date or datetime. Default now."},"end":{"type":"string","description":"ISO date or datetime. Default start + 7 days."},"query":{"type":"string","description":"Match in title, location or description."},"include_hidden":{"type":"boolean","description":"Include calendars hidden in the app."},"limit":{"type":"number","description":"Default 100."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_calendars": {"post":{"operationId":"tool_list_calendars","summary":"List calendars","description":"Your connected Google calendars: id, name, primary, shown in the app, used for conflicts. Pass a calendar_id to create_calendar_event-style tools when an event is not on the primary calendar.","tags":["tools"],"x-ctrl-tool":"list_calendars","x-ctrl-scopes":{"*":["bookings:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_calls": {"post":{"operationId":"tool_list_calls","summary":"List video calls","description":"Plodder video calls you can see, newest first: title, when, how long, who was there, and whether a summary exists. Filter by tab (all, missed, unattended, live), by a chat channel, or by the item a call was about (entity_type + entity_id, e.g. task 812).","tags":["tools"],"x-ctrl-tool":"list_calls","x-ctrl-scopes":{"*":["calls:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tab":{"type":"string","enum":["all","missed","unattended","live"]},"channel_id":{"type":"number"},"entity_type":{"type":"string","description":"task, project, crm_company, crm_contact, client ..."},"entity_id":{"type":"string"},"query":{"type":"string","description":"Match in the title."},"limit":{"type":"number","description":"Default 25."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_chat_channels": {"post":{"operationId":"tool_list_chat_channels","summary":"List chat channels","description":"Plodder chat channels and DMs you can read.","tags":["tools"],"x-ctrl-tool":"list_chat_channels","x-ctrl-scopes":{"*":["messages:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"include_dms":{"type":"boolean","description":"Default true."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_clients": {"post":{"operationId":"tool_list_clients","summary":"List clients","description":"Clients in your workspaces with their status and industry: the Clients roster with its search, filters and sort (D 10-09).","tags":["tools"],"x-ctrl-tool":"list_clients","x-ctrl-scopes":{"*":["crm:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string","description":"Search: name, slug, industry, contacts, notes and linked business names."},"where":{"type":["object","string"],"description":"The roster filter panel: {mode: and|or, items: [{field, op, value}]}. Fields: name, contact, industry, businesses (text: is, contains, ...), status (select: active|onboarding|paused|churned, ops any|none), crm_status (current|past|not_in_crm), business_count (number), created, updated (date: last_days, before, after, ...)."},"sort":{"type":"string","enum":["name","contact","status","crm_status","industry","businesses","business_count","created","updated"]},"dir":{"type":"string","enum":["asc","desc"]}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_database_rows": {"post":{"operationId":"tool_list_database_rows","summary":"Read a database","description":"The columns and rows of a database (sheet). Find databases with list_items types [\"sheet\"]. Optionally filter rows by text.","tags":["tools"],"x-ctrl-tool":"list_database_rows","x-ctrl-scopes":{"*":["docs:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"database":{"type":"string","description":"Database id or sheet:<id> ref."},"text":{"type":"string","description":"Only rows containing this text."},"limit":{"type":"number","description":"Max rows (default 100, max 500)."}},"required":["database"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_docs": {"post":{"operationId":"tool_list_docs","summary":"List docs","description":"List docs in your workspaces, optionally by project or a text search on title and content.","tags":["tools"],"x-ctrl-tool":"list_docs","x-ctrl-scopes":{"*":["docs:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"workspace_id":{"type":"number"},"project_id":{"type":"number"},"query":{"type":"string"},"limit":{"type":"number","description":"Default 50."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_email_labels": {"post":{"operationId":"tool_list_email_labels","summary":"List email labels","description":"The Gmail labels of your connected accounts (system labels like INBOX and your own), with unread counts. Use the names with update_email_thread.","tags":["tools"],"x-ctrl-tool":"list_email_labels","x-ctrl-scopes":{"*":["messages:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"account_id":{"type":"number","description":"One account. Default: all of yours."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_email_threads": {"post":{"operationId":"tool_list_email_threads","summary":"List email threads","description":"Gmail threads from your connected accounts. Filter by folder (inbox, starred, sent, drafts, all, spam, trash), unread only, or a Gmail search query (from:, to:, subject:, label:, has:attachment, newer_than:7d).","tags":["tools"],"x-ctrl-tool":"list_email_threads","x-ctrl-scopes":{"*":["messages:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"folder":{"type":"string","enum":["inbox","starred","sent","calendar","drafts","archived","all","spam","trash"],"description":"Default inbox. calendar = threads with invites, archived = out of the inbox."},"unread_only":{"type":"boolean"},"query":{"type":"string","description":"Gmail search syntax."},"account_id":{"type":"number","description":"One account. Default: all of yours merged."},"limit":{"type":"number","description":"Default 20, max 50."},"page_token":{"type":"string","description":"next_page_token from a previous call."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_inbox": {"post":{"operationId":"tool_list_inbox","summary":"List inbox","description":"Your Plodder inbox: notifications (assignments, mentions, leads...), recent task activity in your workspaces, and AI actions waiting for your approval.","tags":["tools"],"x-ctrl-tool":"list_inbox","x-ctrl-scopes":{"*":["messages:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"unread_only":{"type":"boolean"},"limit":{"type":"number","description":"Per section. Default 25."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_items": {"post":{"operationId":"tool_list_items","summary":"List items","description":"List anything the user can see across Plodder (tasks, projects, docs, databases, channels, meetings, clients, businesses, CRM companies and contacts, calendar events, email threads, agent conversations, recordings, bookings), newest first. Filter by type, title text, project, tag names or a date window. Returns item refs (type:id) for get_properties, read_discussion, read_activity and link_items.","tags":["tools"],"x-ctrl-tool":"list_items","x-ctrl-scopes":{"*":["ctrl-flow:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"types":{"type":"array","items":{"type":"string","enum":["task","project","doc","sheet","channel","meeting","client","business","crm_company","crm_contact","calendar_event","mail_thread","agent_session","recording","booking","file","form","canvas","code_file","word_doc","image_doc","spreadsheet","presentation","design","illustration"]},"description":"Only these kinds. Default: everything except CRM contacts."},"text":{"type":"string","description":"Title contains these words."},"project_ids":{"type":"array","items":{"type":"number"},"description":"Only items in these projects (tasks, docs)."},"tags":{"type":"array","items":{"type":"string"},"description":"Tag names (from list_tags). Items with any of them."},"updated_after":{"type":"string","description":"ISO date or datetime: changed on or after this."},"sort":{"type":"string","enum":["updated","created","viewed","frecency"],"description":"updated (default), created, viewed (recently opened by the user) or frecency."},"favorites":{"type":"boolean","description":"Only the user's favourites."},"limit":{"type":"number","description":"Max results (default 25, max 100)."},"cursor":{"type":"string","description":"The cursor from a previous page."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_links": {"post":{"operationId":"tool_list_links","summary":"List linked items","description":"What an item links to (mentions, attachments, hand-made relations) and what links to it (backlinks).","tags":["tools"],"x-ctrl-tool":"list_links","x-ctrl-scopes":{"*":["ctrl-flow:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"string","description":"An item as type:id, for example task:123, doc:45, project:7, crm_company:3, sheet_row:88, meeting:12 or mail_thread:<account>-<thread>. list_items and search return these."}},"required":["item"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_meetings": {"post":{"operationId":"tool_list_meetings","summary":"List meetings","description":"Recorded meetings with their AI summaries, newest first.","tags":["tools"],"x-ctrl-tool":"list_meetings","x-ctrl-scopes":{"*":["meetings:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"workspace_id":{"type":"number"},"query":{"type":"string","description":"Match in title or summary."},"limit":{"type":"number","description":"Default 25."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_pending_approvals": {"post":{"operationId":"tool_list_pending_approvals","summary":"List approval requests","description":"Actions that needed your approval (send email, post to chat, delete, invites) and what happened to them: pending, executed, rejected or failed. Use it to check whether something you queued went out.","tags":["tools"],"x-ctrl-tool":"list_pending_approvals","x-ctrl-scopes":{"*":["admin:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["pending","executed","rejected","failed","all"],"description":"Default all."},"limit":{"type":"number"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_projects": {"post":{"operationId":"tool_list_projects","summary":"List projects","description":"List projects in your workspaces (or one workspace). Archived ones are left out unless include_archived is true.","tags":["tools"],"x-ctrl-tool":"list_projects","x-ctrl-scopes":{"*":["projects:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"workspace_id":{"type":"number","description":"Only this workspace. Default: all of yours."},"query":{"type":"string","description":"Match in the project name."},"include_archived":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_routines": {"post":{"operationId":"tool_list_routines","summary":"List routines","description":"Your routines (scheduled or event-triggered agent work): name, schedule, the agent it runs as, next and last run. Filter by text or enabled state.","tags":["tools"],"x-ctrl-tool":"list_routines","x-ctrl-scopes":{"*":["automations:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","description":"Name or instructions contain this."},"enabled":{"type":"boolean","description":"Only enabled (true) or paused (false) routines."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_skills": {"post":{"operationId":"tool_list_skills","summary":"List skills","description":"Saved skills (reusable agent instructions): yours, the team's shared ones and the built-in ones. Read one with read_skill and follow it when the user's request matches.","tags":["tools"],"x-ctrl-tool":"list_skills","x-ctrl-scopes":{"*":["automations:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","description":"Name or description contains this."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_tags": {"post":{"operationId":"tool_list_tags","summary":"List tags","description":"Tags, in two separate sets: scope \"crm\" (people, businesses, deals, conversations, bookings, forms, workflow tag steps) and scope \"work\" (tasks, projects, docs, files, everything else). The same name can exist in both. No scope = both, each with its scope.","tags":["tools"],"x-ctrl-tool":"list_tags","x-ctrl-scopes":{"*":["docs:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"scope":{"type":"string","enum":["crm","work"],"description":"Which set. Omit for both."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_tasks": {"post":{"operationId":"tool_list_tasks","summary":"List tasks","description":"List tasks you can see, newest first. Filter by workspace, project, status, assignee, due dates or text. Open tasks only unless include_done is true.","tags":["tools"],"x-ctrl-tool":"list_tasks","x-ctrl-scopes":{"*":["tasks:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"workspace_id":{"type":"number","description":"Only this workspace. Default: every workspace you belong to."},"project_id":{"type":["number","string"],"description":"Only this project (id or public id)."},"status":{"type":"string","enum":["backlog","todo","in_progress","blocked","review","done","cancelled","archived"]},"assignee":{"type":"string","description":"Team member name (partial match), id, user_id or email. Use \"me\" for yourself."},"due_before":{"type":"string","description":"YYYY-MM-DD, inclusive."},"due_after":{"type":"string","description":"YYYY-MM-DD, inclusive."},"query":{"type":"string","description":"Text to match in title or description."},"include_done":{"type":"boolean","description":"Include done, cancelled and archived tasks."},"limit":{"type":"number","description":"Max results (default 50, max 200)."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_team_members": {"post":{"operationId":"tool_list_team_members","summary":"List team members","description":"The people who work in the app (owner, teammates, closers) and the team roster: id (their team id, the one to pass anywhere a person is taken), user_id (numeric login id, null when they do not log in), name, email, role (owner | team | client), type, active. Anywhere a person is taken (task assignee, deal owner, payee member_key, booking host, round robin members) you can pass the id, the user_id or the email.","tags":["tools"],"x-ctrl-tool":"list_team_members","x-ctrl-scopes":{"*":["users:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"include_agents":{"type":"boolean","description":"Include agent team members (default false)."},"include_inactive":{"type":"boolean","description":"Include inactive roster rows (default false)."},"q":{"type":"string","description":"Only people whose name, email or id contains this."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/list_workspaces": {"post":{"operationId":"tool_list_workspaces","summary":"List workspaces","description":"The workspaces you belong to. Other tools take workspace_id from here; your primary one is the default.","tags":["tools"],"x-ctrl-tool":"list_workspaces","x-ctrl-scopes":{"*":["admin:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/mail_organize": {"post":{"operationId":"tool_mail_organize","summary":"Email organizing","description":"Organize email threads in your own mailboxes, as you: file a thread to a client, snooze it or set a follow-up reminder, shared tags, create or delete a Gmail label, delete a draft, share a thread into a chat channel. Gmail writes are dry runs while sending is off on the server. Read, archive, star or label a thread with update_email_thread.\nActions:\n- client_links: Clients a thread is filed to (account_id + thread_id), or counts per client with neither.\n- file_to_client: File a thread to a client (account_id, thread_id, client_id).\n- unfile_from_client: Take a thread off a client.\n- reminders: Live reminders (snoozed and follow-ups), optionally for one thread.\n- remind: Snooze a thread or set a follow-up (account_id, thread_id, kind, due_at at least a minute out).\n- reminder_seen: Clear the \"back in your inbox\" marker on a thread.\n- cancel_reminder: Unsnooze (back to the inbox now) or cancel a follow-up (reminder_id).\n- tags: Tags used on your mail with counts.\n- set_tags: Set a thread's shared tags (account_id, thread_id, tag_ids; subject optional).\n- create_label: Create a Gmail label in a mailbox (account_id, name).\n- delete_label: Delete a Gmail label you made (account_id, label_id from list_email_labels). Gmail takes it off every thread; the email stays. (waits for approval: delete)\n- delete_draft: Delete a Gmail draft (account_id, draft_id). (waits for approval: delete)\n- share_to_chat: Post a thread as a link card in a chat channel or DM (account_id, thread_id, channel_id, note). (waits for approval: post chat)","tags":["tools"],"x-ctrl-tool":"mail_organize","x-ctrl-scopes":{"client_links":["messages:read"],"file_to_client":["messages:write"],"unfile_from_client":["messages:write"],"reminders":["messages:read"],"remind":["messages:write"],"reminder_seen":["messages:write"],"cancel_reminder":["messages:write"],"tags":["messages:read"],"set_tags":["messages:write"],"create_label":["messages:write"],"delete_label":["messages:write"],"delete_draft":["messages:write"],"share_to_chat":["messages:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["client_links","file_to_client","unfile_from_client","reminders","remind","reminder_seen","cancel_reminder","tags","set_tags","create_label","delete_label","delete_draft","share_to_chat"],"description":"What to do."},"account_id":{"type":"number","description":"Your mailbox id (list_email_threads / list_email_labels show it)."},"thread_id":{"type":"string","description":"Gmail thread id."},"client_id":{"type":"number"},"kind":{"type":"string","enum":["snooze","followup"],"description":"remind: snooze (back in the inbox at due_at) or followup (nudge if nobody replied)."},"due_at":{"type":["string","number"],"description":"remind: ISO time or epoch ms."},"reminder_id":{"type":"number"},"tag_ids":{"type":"array","items":{"type":"number"},"description":"set_tags: the thread's full tag list (option ids); [] clears."},"subject":{"type":"string"},"name":{"type":"string"},"draft_id":{"type":"string"},"label_id":{"type":"string","description":"delete_label: Gmail label id (Label_...)."},"channel_id":{"type":"number"},"note":{"type":"string"},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/mail_scheduled": {"post":{"operationId":"tool_mail_scheduled","summary":"Scheduled email","description":"Email waiting to send (scheduled, or in the undo window), as you: list, open, reschedule or edit, cancel, or send now. Schedule a new one with send_email.\nActions:\n- list: Everything waiting to send.\n- get: One scheduled email with its content.\n- reschedule: Move it (send_at) and/or replace its content (input).\n- cancel: Cancel it; nothing is sent (answers with its content).\n- send_now: Send it now. (waits for approval: send email)","tags":["tools"],"x-ctrl-tool":"mail_scheduled","x-ctrl-scopes":{"list":["messages:read"],"get":["messages:read"],"reschedule":["messages:write"],"cancel":["messages:write"],"send_now":["messages:send"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get","reschedule","cancel","send_now"],"description":"What to do."},"scheduled_id":{"type":"number"},"send_at":{"type":["string","number"],"description":"reschedule: ISO time or epoch ms."},"input":{"type":"object","description":"reschedule: the whole email (same shape get returns as input) to replace its content."},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/mail_settings": {"post":{"operationId":"tool_mail_settings","summary":"Email settings","description":"Your email settings, as you: undo-send seconds, signatures (and the one set in Gmail), split inbox, blocked senders, Important rules (always or never important by address or domain), and mailbox sync (status, force sync).\nActions:\n- get: Your email settings.\n- set: Change settings: undo_seconds, signature_html, signature_on, signature_replies, split_inbox, or account_id + mailbox_signature_html.\n- sync_status: How a mailbox's sync stands (account_id): phase backfill / resync / live, status, emails copied, and the Gmail total while copying.\n- force_sync: Force sync a mailbox (account_id): a live one re-reads everything from Gmail, a stuck one retries now.\n- gmail_signature: The signature set in Gmail for a mailbox (account_id), to copy in with set (mailbox_signature_html).\n- list_blocked: Senders you blocked.\n- block: Block senders (addresses); their inbox mail goes to Trash. (waits for approval: delete)\n- unblock: Unblock one sender (address).\n- list_rules: Your Important rules.\n- set_rule: Always (important true) or never (false) important for an address or domain (kind, pattern).\n- delete_rule: Delete an Important rule (rule_id, or kind + pattern). (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"mail_settings","x-ctrl-scopes":{"get":["messages:read"],"set":["messages:write"],"sync_status":["messages:read"],"force_sync":["messages:write"],"gmail_signature":["messages:read"],"list_blocked":["messages:read"],"block":["messages:write"],"unblock":["messages:write"],"list_rules":["messages:read"],"set_rule":["messages:write"],"delete_rule":["messages:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["get","set","sync_status","force_sync","gmail_signature","list_blocked","block","unblock","list_rules","set_rule","delete_rule"],"description":"What to do."},"undo_seconds":{"type":"number"},"signature_html":{"type":"string"},"signature_on":{"type":"boolean"},"signature_replies":{"type":"boolean","description":"set: the signature also goes on replies and forwards (false = new emails only)."},"split_inbox":{"type":"boolean"},"account_id":{"type":"number","description":"Your mailbox id (list_email_threads / list_email_labels show it)."},"mailbox_signature_html":{"type":"string","description":"set: one mailbox's own signature (with account_id); \"\" removes it."},"addresses":{"type":"array","items":{"type":"string"}},"address":{"type":"string"},"kind":{"type":"string","enum":["address","domain"]},"pattern":{"type":"string"},"important":{"type":"boolean"},"rule_id":{"type":"number"},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/mailjet": {"post":{"operationId":"tool_mailjet","summary":"Mailjet and email editor","description":"Mailjet (bulk / marketing email) and the email editor, as you. Verified senders, your default From name / address / Reply-to, the event webhook (opens, clicks, bounces, spam, unsubscribes feed the CRM timeline, workflow stats and the suppression list), plus preview and test of an email body exactly as a workflow step or CRM bulk send would build it (custom values, sanitized HTML, the marketing footer: unsubscribe link + {{workspace.mailing_address}}). Marketing sends are for opted-in contacts only.\nActions:\n- status: Connected?, verified senders, your defaults, your connected Gmail (default Reply-to), mailing address, last 30 days (sent, opened, clicked, bounced, spam, unsub); the webhook URL and what Mailjet has registered (app owner only). refresh?: true skips the 10 minute sender cache.\n- save_settings: Your default marketing sender: from_email (must be a verified Mailjet sender), from_name, reply_to (empty = your connected Gmail).\n- register_webhook: Point Mailjet's event webhooks (sent, open, click, bounce, spam, blocked, unsub) at this app (app owner only).\n- preview: Build an email without sending: context (workflow | crm), subject, body, format (rich | html | text), from (mailbox | system) for workflows or via (gmail | mailjet) for crm, mailjet_kind (marketing | transactional), from_email?, from_name?, reply_to?, account_id?, unsubscribe?, contact_id? (else sample data), workflow_id? / workspace_id? (custom values). Returns subject, html, text, from, warnings, error.\n- send_test: Send that email to yourself only (subject prefixed [Test]) through the path it would use. Same fields as preview. (waits for approval: send email)","tags":["tools"],"x-ctrl-tool":"mailjet","x-ctrl-scopes":{"status":["messages:read"],"save_settings":["messages:write"],"register_webhook":["messages:write"],"preview":["messages:read"],"send_test":["messages:send"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["status","save_settings","register_webhook","preview","send_test"],"description":"What to do."},"refresh":{"type":"boolean"},"context":{"type":"string","enum":["workflow","crm"]},"subject":{"type":"string"},"body":{"type":"string","description":"Plain text, or HTML for rich / html (sanitized: no scripts, styles blocks, forms or event handlers). Custom values like {{contact.first_name|there}}, {{unsubscribe_link}}, {{workspace.mailing_address}}."},"format":{"type":"string","enum":["rich","html","text"]},"from":{"type":"string","enum":["mailbox","system"]},"via":{"type":"string","enum":["gmail","mailjet"]},"mailjet_kind":{"type":"string","enum":["marketing","transactional"]},"from_email":{"type":"string"},"from_name":{"type":"string"},"reply_to":{"type":"string","description":"Empty = your connected Gmail; \"none\" = no Reply-to."},"account_id":{"type":"number"},"unsubscribe":{"type":"boolean"},"cold":{"type":"boolean"},"contact_id":{"type":"number"},"workflow_id":{"type":"number"},"workspace_id":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/meetings_admin": {"post":{"operationId":"tool_meetings_admin","summary":"Meeting links and notes","description":"Make a meeting link (Plodder call, Google Meet or Zoom) for an event, re-run meeting-notes processing on a transcript, and set a recording's client (owner) (/api/meetings/*). Read meetings with list_meetings and get_meeting.\nActions:\n- create_link: A meeting link (topic, start_time, duration, provider default zoom). Invites nobody by itself.\n- queue_status: Meeting-notes queue (owner).\n- process: Queue a transcript for meeting-notes processing (transcript_id, mode). Owner.\n- set_client: Set a recording's client by name (transcript_id, client, null clears). Owner.","tags":["tools"],"x-ctrl-tool":"meetings_admin","x-ctrl-scopes":{"create_link":["meetings:write"],"queue_status":["meetings:read"],"process":["meetings:write"],"set_client":["meetings:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["create_link","queue_status","process","set_client"],"description":"What to do."},"topic":{"type":"string"},"start_time":{"type":"string","description":"ISO date-time."},"duration":{"type":"number","description":"Minutes."},"provider":{"type":"string","enum":["ctrlflow","google_meet","zoom"]},"transcript_id":{"type":"number"},"mode":{"type":"string","enum":["process","reprocess"]},"client":{"type":["string","null"]},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/memories": {"post":{"operationId":"tool_memories","summary":"Memory browser","description":"The long-term memory store behind the memory browser (/api/memories): search and delete. Owner only. Agent notes live in agent_memory.\nActions:\n- browse: Search memories (q, sector semantic/episodic, source, client, page). 50 a page, with counts by source and sector.\n- delete: Delete a memory (memory_id). (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"memories","x-ctrl-scopes":{"browse":["automations:read"],"delete":["automations:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["browse","delete"],"description":"What to do."},"q":{"type":"string"},"sector":{"type":"string"},"source":{"type":"string"},"client":{"type":"string"},"page":{"type":"number"},"memory_id":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/meta": {"post":{"operationId":"tool_meta","summary":"Meta (Facebook and Instagram)","description":"Meta lead ads, Messenger / Instagram DMs and comments, Conversions API and Custom Audiences, as you. Pages the connected token reaches (with the linked Instagram account), which of leads / messages / comments each page sends here (turning any on subscribes the page to the Plodder app), lead forms with their field mapping (question key -> a target: email | phone | name | first_name | last_name | title | extra_email | extra_phone | website | timezone | birthday | notes | address_street | address_city | address_region | address_postal | address_country | business_name (find or make the business by name, link the person) | business_website | business_phone | deal_value | deal_title (lead pipeline on) | f:<person custom field id> | b:<business custom field id> | skip), answer rules per question (an answer is / contains a value -> add tags and / or move the lead deal to a stage), the workspace default mapping (question key / label patterns, applied to newly found forms), a Test lead dry run, and the lead pipeline ({ on, pipeline_id, stage_id?, title? }: every lead makes a deal there and starts the speed to lead clock), recent leads, the ad account for Custom Audiences, and the Conversions API pixel per workspace (workspace_id 0 = default). Workflows use the Facebook lead form submitted, Facebook / Instagram comment and DM received triggers and the Send Meta conversion, Meta audience and Send FB/IG DM steps (crm_automations).\nActions:\n- status: Connection, pages, lead forms (questions, mapping, pipeline, on), Conversions API settings, the last 10 leads, the webhook URL.\n- leads: Recent leads (limit?): form, campaign, ad set, ad, person, deal, status (done | failed | skipped), via (webhook | poll).\n- ad_accounts: Ad accounts the token reaches.\n- audiences: Customer list Custom Audiences of the picked ad account.\n- available_pages: Facebook Pages the connected account reaches: picked (in this CRM), elsewhere (another Plodder account has it). No tokens.\n- sync: Re-read the pages the token manages (and the forms of pages with leads on).\n- sync_forms: Re-read one page's lead forms (page_id).\n- poll: Check every live lead form for leads the webhook missed (each lead lands once).\n- pick_pages: Keep exactly these Pages (page_ids, from available_pages) after Continue with Facebook: picked ones are added with switches off, the rest are unsubscribed and removed.\n- check_health: Check the Meta token and every page now: health (ok | expiring | missing | broken), expiry, missing permissions, still subscribed. Status shows the last check; a daily job alerts the owner.\n- set_page: Turn a page's leads_on / messages_on / comments_on on or off (page_id). Any on subscribes the page to the Plodder Meta app; all off unsubscribes it.\n- set_form: A lead form (form_id): on?, mapping? ({ question key: target }), rules? ({ question key: [{ op: is | contains, value, tags: [], stage_id? }] }, null clears), lead_pipeline? (object or null). Status lists each form's questions (with multiple-choice options), its unmapped count (questions added after the last save) and the targets (custom fields as f:<id> / b:<id>).\n- default_mapping: The workspace default lead form mapping: rules [{ by: key | label, pattern (* wildcard; label = contains), target }], auto (apply to newly found forms).\n- set_default_mapping: Save the default mapping: rules?, auto?, apply_now? (also fill forms whose mapping was never saved).\n- copy_mapping: Copy the targets and answer rules of another form (from_form_id) onto form_id: same question key, else the same label.\n- new_field: Make a person custom field from a question (form_id, question_key, name?, type? text | number | date | select (select gets the question choices), category_id? = a person field card) and map the question to it.\n- test_lead: Dry run (nothing saved, Meta not called): a fake lead (sample answers, or answers { question key: value }, platform? facebook | instagram) through the form's mapping, rules and pipeline. Returns the person (found or new, built-ins, extra emails / phones, notes, custom fields), the business (link or create), tags, the deal (pipeline, stage, title, value), the attribution and warnings.\n- set_ad_account: The ad account for Custom Audiences (ad_account_id, act_123...; empty clears).\n- set_capi: Conversions API for a workspace (workspace_id?, 0 = default): pixel_id (dataset id), test_event_code (empty clears). The access token is set in Settings by a person.","tags":["tools"],"x-ctrl-tool":"meta","x-ctrl-scopes":{"status":["crm:read"],"leads":["crm:read"],"ad_accounts":["crm:read"],"audiences":["crm:read"],"available_pages":["crm:read"],"sync":["crm:write"],"sync_forms":["crm:write"],"poll":["crm:write"],"pick_pages":["crm:write"],"check_health":["crm:write"],"set_page":["crm:write"],"set_form":["crm:write"],"default_mapping":["crm:read"],"set_default_mapping":["crm:write"],"copy_mapping":["crm:write"],"new_field":["crm:write"],"test_lead":["crm:read"],"set_ad_account":["crm:write"],"set_capi":["crm:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["status","leads","ad_accounts","audiences","available_pages","sync","sync_forms","poll","pick_pages","check_health","set_page","set_form","default_mapping","set_default_mapping","copy_mapping","new_field","test_lead","set_ad_account","set_capi"],"description":"What to do."},"page_id":{"type":"string"},"page_ids":{"type":"array","items":{"type":"string"},"description":"pick_pages: the Facebook Page ids to keep."},"form_id":{"type":"string"},"leads_on":{"type":"boolean"},"messages_on":{"type":"boolean"},"comments_on":{"type":"boolean"},"on":{"type":"boolean","description":"set_form: the form feeds the CRM."},"mapping":{"type":"object","description":"set_form: { question key: target }."},"rules":{"description":"set_form: { question key: [{ op, value, tags, stage_id? }] } (null clears). set_default_mapping: [{ by, pattern, target }]."},"auto":{"type":"boolean","description":"set_default_mapping: apply to newly found forms."},"apply_now":{"type":"boolean","description":"set_default_mapping: also fill forms never mapped."},"from_form_id":{"type":"string","description":"copy_mapping: the form to copy from."},"question_key":{"type":"string","description":"new_field: the question."},"name":{"type":"string","description":"new_field: the field name (default: the question)."},"type":{"type":"string","description":"new_field: text | number | date | select."},"category_id":{"type":"number","description":"new_field: the person field card."},"answers":{"type":"object","description":"test_lead: { question key: answer } (others get sample answers)."},"platform":{"type":"string","description":"test_lead: facebook | instagram."},"lead_pipeline":{"description":"set_form: { on, pipeline_id, stage_id?, title? } or null to turn off."},"ad_account_id":{"type":"string"},"workspace_id":{"type":"number"},"pixel_id":{"type":"string"},"test_event_code":{"type":"string"},"limit":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/my_activity": {"post":{"operationId":"tool_my_activity","summary":"My activity","description":"What you did in the app: the My activity page (your actions over the last days, grouped by item), the year graph (actions per day), or your latest actions.\nActions:\n- summary: Your actions in the last `days` (1-31, default 7) with the items they touched.\n- graph: Actions per local day for the past year (tz_offset_min = your getTimezoneOffset).\n- recent: Your latest actions (limit).","tags":["tools"],"x-ctrl-tool":"my_activity","x-ctrl-scopes":{"summary":["reports:read"],"graph":["reports:read"],"recent":["reports:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["summary","graph","recent"],"description":"What to do."},"days":{"type":"number"},"tz_offset_min":{"type":"number"},"limit":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/my_settings": {"post":{"operationId":"tool_my_settings","summary":"My profile and settings","description":"Your profile (name, bio, pronouns, display role), your app settings (rail, ai_default_model, accent) and the interactive tutorial progress.\nActions:\n- profile: Your profile.\n- update_profile: Change your profile (name, bio, pronouns, display_role).\n- setting: One app setting (key).\n- set_setting: Save one app setting (key, value).\n- tutorial: Tutorial progress.\n- set_tutorial: Mark a tutorial lesson done or not (lesson, done?), or reset=true.","tags":["tools"],"x-ctrl-tool":"my_settings","x-ctrl-scopes":{"profile":["admin:read"],"update_profile":["admin:write"],"setting":["admin:read"],"set_setting":["admin:write"],"tutorial":["admin:read"],"set_tutorial":["admin:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["profile","update_profile","setting","set_setting","tutorial","set_tutorial"],"description":"What to do."},"name":{"type":"string"},"bio":{"type":"string"},"pronouns":{"type":"string"},"display_role":{"type":"string"},"key":{"type":"string","enum":["rail","ai_default_model","accent"]},"value":{},"lesson":{"type":"string","enum":["move","select","create","palette","mention"]},"done":{"type":"boolean"},"reset":{"type":"boolean"},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/notification_settings": {"post":{"operationId":"tool_notification_settings","summary":"Notification settings","description":"How you get notified: per-kind channels (in app, email, push), snooze everything, mute one item (any type, incl. a chat channel), and the daily or weekly email digest.\nActions:\n- get: Your per-kind preferences, snooze, and digest settings.\n- set_pref: One kind: in_app, email, push on or off.\n- snooze: Hold every notification until `until` (ISO or epoch seconds; max 30 days).\n- resume: End the snooze now.\n- mute_status: Is this item muted (type, id)?\n- mute: Mute an item (type, id), optional until; a chat channel muted with no end also goes to Nothing.\n- unmute: Unmute it (a chat channel goes back to All).\n- set_digest: Digest: frequency off | daily | weekly, send_hour 0-23, weekday 0-6, timezone.\n- test_digest: Email yourself your digest now (one every 5 minutes). (waits for approval: send email)","tags":["tools"],"x-ctrl-tool":"notification_settings","x-ctrl-scopes":{"get":["admin:read"],"set_pref":["admin:write"],"snooze":["admin:write"],"resume":["admin:write"],"mute_status":["admin:read"],"mute":["admin:write"],"unmute":["admin:write"],"set_digest":["admin:write"],"test_digest":["admin:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["get","set_pref","snooze","resume","mute_status","mute","unmute","set_digest","test_digest"],"description":"What to do."},"kind":{"type":"string","enum":["new_lead","inbound_reply","mention","assignment","appointment_booked","appointment_cancelled","appointment_rescheduled","appointment_conflict","ai_handoff","task_assigned","review_received","opportunity_won","opportunity_lost","opportunity_abandoned","affiliate_conversion","integration_alert"]},"in_app":{"type":"boolean"},"email":{"type":"boolean"},"push":{"type":"boolean"},"until":{"type":["string","number"]},"type":{"type":"string"},"id":{"type":["string","number"]},"frequency":{"type":"string","enum":["off","daily","weekly"]},"send_hour":{"type":"number"},"weekday":{"type":"number"},"timezone":{"type":"string"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/notifications": {"post":{"operationId":"tool_notifications","summary":"Notifications","description":"Your notification feed (bell and Inbox) and its state: unseen, seen, done. Keys look like \"u:12\" (yours) or \"w:3\" (workspace bell, owner).\nActions:\n- list: Your feed, newest first (filter all | active | unseen, limit), with the unseen count.\n- set_state: op = seen | done | reopen on keys, or all=true.\n- mark_read: Mark your notifications read: ids, or none for all.\n- mark_workspace_read: Owner: mark the workspace bell read (ids, or all=true).\n- mark_task_activity_read: Mark task activity in the Inbox read (activity ids, or all=true).","tags":["tools"],"x-ctrl-tool":"notifications","x-ctrl-scopes":{"list":["admin:read"],"set_state":["admin:write"],"mark_read":["admin:write"],"mark_workspace_read":["admin:write"],"mark_task_activity_read":["admin:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","set_state","mark_read","mark_workspace_read","mark_task_activity_read"],"description":"What to do."},"filter":{"type":"string","enum":["all","active","unseen"]},"limit":{"type":"number"},"op":{"type":"string","enum":["seen","done","reopen"]},"keys":{"type":"array","items":{"type":"string"}},"ids":{"type":"array","items":{"type":"number"}},"all":{"type":"boolean"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/pathways": {"post":{"operationId":"tool_pathways","summary":"Pathways (Conversation AI)","description":"Conversation AI as you, agent-centric (D 10-10): each AI agent runs jobs (a job = a playbook row with the agent_id: one pathway plus its trigger, audience and channels), owns its pathways, and keeps one set of conversation settings for all of its jobs (texting style, voice, knowledge, guard rails, hand-off, photos per conversation: agent_settings / update_agent_settings). Templates (built-in for leads, appointments, payments, clients, support; plus saved ones and Bland imports): templates, template, use_template (an independent copy on an agent plus an off job), save_template. Underneath: playbooks (which agent answers leads on SMS, email, Messenger / Instagram DMs and comments, and how: engagement rules, knowledge, personality, audience lock, channels, segments in priority order, guard rails, hand-off rules, qualifying, booking care, outreach, A/B, outcomes), pathways (node graphs the agent follows: draft, staging and live versions, Bland JSON import, starters), the kill switch and auto-pause limits, channel shortcuts (prebuilt Hand to agent workflows), conversations and the agent side of a CRM thread (suggested replies, take over / hand back / end), learned voice examples. Workflows use the Hand to agent and Agent: start conversation steps and the Agent booked a call / handed off / Pathway ended / Lead qualified triggers (crm_automations).\nActions:\n- overview: Playbooks (with what blocks each from going on), pathways, settings, shortcuts, counts, starters.\n- pickers: Agents, smart lists, calendars, pipelines, team, workflows, person fields for the editors.\n- playbook: One playbook (playbook_id) with its problems and voice examples.\n- create_playbook: New playbook (set: { name, agent_id, ...config }); made off.\n- update_playbook: Change a playbook (playbook_id, set: { name?, agent_id?, on?, is_default?, scope?, scope_confirmed?, audience_list_ids?, channels?, mode?, outreach?, engagement? (incl. texting style: always_split, split_chance 0-100, max_parts, typo_rate off | rare | sometimes, typo_fix, lowercase, casual, reply delays), knowledge?, personality? (incl. voice_source none | me | teammate | custom, voice_member = team key, voice_samples = pasted lines), guards?, handoff?, qualify?, booking?, pathway_ids?, ab_playbook_id?, ab_split?, outcome_fields?, instructions?, media_max? (photos per conversation), preset? = \"super_human\" (casual, lowercase-ish, 2-3 texts back to back, typos sometimes, your voice, short, replies after 30-150 s) }). Nested objects merge. (may wait for approval)\n- delete_playbook: Delete a playbook (playbook_id) with no open conversations. (waits for approval: delete)\n- reorder_playbooks: Priority order (ids): the first whose rules fit answers.\n- pathway: One pathway (pathway_id): draft graph, problems, versions, playbooks using it, saved tests.\n- create_pathway: New pathway: name, agent_id (the agent it belongs to; without one it is a template, and a Bland import always is), and starter (index from overview) or import_json (a Bland pathway export) or graph.\n- agent_home: One agent's Conversation AI home (agent_id): its jobs (with problems), its pathways, shared settings, counts, 30 day results, recent conversations.\n- create_job: A new job on an agent (agent_id, set: { name, pathway_id? }): made off with the agent's shared settings. Change it with update_playbook (playbook_id = the job id).\n- agent_settings: The agent's shared conversation settings (agent_id).\n- update_agent_settings: Change the agent's shared settings (agent_id, set: { engagement?, personality?, knowledge?, guards?, handoff?, media_max?, learn_from_edits? }, preset? = \"super_human\", job_ids? to copy to only those jobs). Saved on the agent and copied to its jobs. Nested objects merge.\n- rebuild_agent_voice: Rebuild the agent's voice profile from real outbound messages of its voice source (agent_id); goes on the agent and every job.\n- agent_media: An agent's media library (agent_id): its photos, its jobs' and the shared ones.\n- templates: Every template: saved ones (id) and built-in ones (key \"builtin:<key>\"), with category (leads, appointments, payments, clients, support), suggested trigger and texting style, outcomes, node count.\n- template: One template with its graph and problems (key: \"builtin:<key>\" or a saved template id).\n- use_template: Copy a template into an agent (key, agent_id, name?, job? default true): an independent pathway on the agent plus an off job with the suggested trigger (channels, outreach). Later template edits never reach the copy.\n- save_template: Save as template (from_pathway_id = an agent's pathway, or import_json = a Bland export, or graph; name?, set: { description?, category?, trigger?, style? }). An independent copy.\n- update_template: Change a saved template's name, description, category, trigger or style (template_id, set). Edit its graph with save_pathway (pathway_id = template_id).\n- delete_template: Delete a saved template (template_id). Copies already on agents stay. (waits for approval: delete)\n- save_pathway: Save the draft (pathway_id, set: { name?, description?, graph?, require_tests? }).\n- publish_pathway: Publish the draft as a new version: stage = staging (test first) or live (conversations use it). (may wait for approval)\n- set_live_version: Make a version live (pathway_id, version): promote staging or roll back. (waits for approval: send email)\n- duplicate_pathway: Copy a pathway (pathway_id) as a new draft.\n- delete_pathway: Delete a pathway (pathway_id) no playbook uses. (waits for approval: delete)\n- settings: Kill switch, auto-pause limits (opt-outs / complaints a day), channel shortcuts.\n- set_settings: paused (true stops every agent at once; false lets them go again), reason, optout_limit, complaint_limit. (may wait for approval)\n- set_shortcut: Channel shortcut (channel: sms | email | messenger | instagram | comments | missed_call): on, playbook_id?, agent_id?, segment_id? (only people in that smart list), pipeline_id + stage_id?. (may wait for approval)\n- conversations: Conversations (state: open | active | human | handed_off | ended, playbook_id?, agent_id?, q?).\n- thread: The agent side of one person's thread (contact_id): conversation, log, pending suggested replies.\n- take_over: A person takes the conversation (conversation_id): the agent stops.\n- hand_back: Give the conversation back to the agent (conversation_id). (waits for approval: send email)\n- end_conversation: End a conversation (conversation_id, outcome: qualified | disqualified | booked | not_now | other).\n- send_draft: Send the agent's suggested reply (draft_id, text? edited). Goes out paced, as the agent. (waits for approval: send email)\n- discard_draft: Drop a suggested reply (draft_id).\n- voice_examples: Learned voice examples of a playbook (playbook_id): drafts a person edited.\n- voice_profile: The texting-voice profile of a playbook (playbook_id): source, style facts and the guidance the agent writes to.\n- rebuild_voice: Rebuild the voice profile from real outbound messages (playbook_id): the voice source's sent texts and DMs (me / a teammate) or the pasted examples (custom). Keeps style facts only.\n- media_library: Photos / short videos the agent may send (playbook_id: that playbook's + the shared ones; none = all): id, title, when to send, tags, type, size.\n- add_media: Add to the media library: agent_id (every job of that agent) or playbook_id (one job); omit both or set.shared = true for every agent, set: { title, when (when the agent should send it), tags? }, and the file: drive_file (a Drive file id / public id of an image or MP4) or data_base64 + filename + mime (image/jpeg|png|gif|webp, video/mp4|quicktime; 25 MB).\n- update_media: Change a media item (media_id, set: { title?, when?, tags?, playbook_id? (null = every playbook) }).\n- delete_media: Take an item out of the media library (media_id); the Drive file stays. (waits for approval: delete)\n- delete_voice_example: Forget one voice example (example_id). (waits for approval: delete)\n- test_chat: One test turn (nothing sent, nothing written): playbook_id? and / or pathway_id, test_version? (draft | staging | live), message, test_state? (the state from the last turn), lead? ({ name, email, phone, tags, fields }). Returns the reply as it would go (parts and delays), the node, variables, trace and the state for the next turn.\n- tests: Saved tests of a pathway (pathway_id) with their last runs.\n- save_test: Save a node test (pathway_id, test_id? to change one, set: { name?, node_id?, message, vars?, expect: { node?, var?, value?, goal? }, variations? (0-5 paraphrases) }).\n- delete_test: Delete a saved test (test_id). (waits for approval: delete)\n- run_test: Run one saved test (test_id).\n- run_suite: Run every saved test of a pathway (pathway_id).\n- results: Results per playbook, pathway and A/B side over days? (default 30): conversations, replied, booked, showed, hand-offs, opt-outs.\n- campaigns: Agent text campaigns with their stats.\n- campaign: One campaign (campaign_id): settings, stats, people.\n- preview_audience: Who an audience is now (set: { segment_ids?, tags?, contact_ids?, exclude_segment_ids? }).\n- create_campaign: New draft campaign (set: { name, playbook_id, audience, opener: { mode: agent | fixed, text }, pacing: { per_hour, daily_cap } }).\n- update_campaign: Change a campaign (campaign_id, set); audience / playbook / opener only while a draft.\n- start_campaign: Start or resume (campaign_id): texts go out by the drip (consent, texting hours, caps). (waits for approval: send email)\n- pause_campaign: Pause (campaign_id).\n- stop_campaign: Stop for good (campaign_id); people not reached yet are skipped.\n- delete_campaign: Delete a draft, stopped or finished campaign (campaign_id). (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"pathways","x-ctrl-scopes":{"overview":["automations:read"],"pickers":["automations:read"],"playbook":["automations:read"],"create_playbook":["automations:write"],"update_playbook":["automations:write"],"delete_playbook":["automations:write"],"reorder_playbooks":["automations:write"],"pathway":["automations:read"],"create_pathway":["automations:write"],"agent_home":["automations:read"],"create_job":["automations:write"],"agent_settings":["automations:read"],"update_agent_settings":["automations:write"],"rebuild_agent_voice":["automations:write"],"agent_media":["automations:read"],"templates":["automations:read"],"template":["automations:read"],"use_template":["automations:write"],"save_template":["automations:write"],"update_template":["automations:write"],"delete_template":["automations:write"],"save_pathway":["automations:write"],"publish_pathway":["automations:write"],"set_live_version":["automations:write"],"duplicate_pathway":["automations:write"],"delete_pathway":["automations:write"],"settings":["automations:read"],"set_settings":["automations:write"],"set_shortcut":["automations:write"],"conversations":["automations:read"],"thread":["automations:read"],"take_over":["automations:write"],"hand_back":["automations:write"],"end_conversation":["automations:write"],"send_draft":["messages:send"],"discard_draft":["automations:write"],"voice_examples":["automations:read"],"voice_profile":["automations:read"],"rebuild_voice":["automations:write"],"media_library":["automations:read"],"add_media":["automations:write"],"update_media":["automations:write"],"delete_media":["automations:write"],"delete_voice_example":["automations:write"],"test_chat":["automations:write"],"tests":["automations:read"],"save_test":["automations:write"],"delete_test":["automations:write"],"run_test":["automations:write"],"run_suite":["automations:write"],"results":["automations:read"],"campaigns":["automations:read"],"campaign":["automations:read"],"preview_audience":["automations:read"],"create_campaign":["automations:write"],"update_campaign":["automations:write"],"start_campaign":["messages:send"],"pause_campaign":["automations:write"],"stop_campaign":["automations:write"],"delete_campaign":["automations:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["overview","pickers","playbook","create_playbook","update_playbook","delete_playbook","reorder_playbooks","pathway","create_pathway","agent_home","create_job","agent_settings","update_agent_settings","rebuild_agent_voice","agent_media","templates","template","use_template","save_template","update_template","delete_template","save_pathway","publish_pathway","set_live_version","duplicate_pathway","delete_pathway","settings","set_settings","set_shortcut","conversations","thread","take_over","hand_back","end_conversation","send_draft","discard_draft","voice_examples","voice_profile","rebuild_voice","media_library","add_media","update_media","delete_media","delete_voice_example","test_chat","tests","save_test","delete_test","run_test","run_suite","results","campaigns","campaign","preview_audience","create_campaign","update_campaign","start_campaign","pause_campaign","stop_campaign","delete_campaign"],"description":"What to do."},"playbook_id":{"type":"number"},"pathway_id":{"type":"number"},"test_id":{"type":"number"},"campaign_id":{"type":"number"},"days":{"type":"number"},"message":{"type":"string"},"test_state":{"type":"object"},"lead":{"type":"object"},"test_version":{"type":"string"},"conversation_id":{"type":"number"},"draft_id":{"type":"number"},"example_id":{"type":"number"},"contact_id":{"type":"number"},"set":{"type":"object","description":"Fields to set."},"ids":{"type":"array","items":{"type":"number"}},"name":{"type":"string"},"starter":{"type":"number"},"import_json":{"description":"A Bland pathway export (object or JSON text)."},"graph":{"type":"object"},"stage":{"type":"string"},"note":{"type":"string"},"version":{"type":"number"},"paused":{"type":"boolean"},"reason":{"type":"string"},"optout_limit":{"type":"number"},"complaint_limit":{"type":"number"},"channel":{"type":"string"},"on":{"type":"boolean"},"agent_id":{"type":"number"},"segment_id":{"type":"number"},"pipeline_id":{"type":"number"},"stage_id":{"type":"string"},"state":{"type":"string"},"q":{"type":"string"},"outcome":{"type":"string"},"text":{"type":"string"},"template_id":{"type":"number"},"key":{"type":"string","description":"Template key: \"builtin:<key>\" or a saved template id"},"from_pathway_id":{"type":"number"},"job":{"type":"boolean"},"preset":{"type":"string"},"job_ids":{"type":"array","items":{"type":"number"}},"media_id":{"type":"number"},"drive_file":{"type":"string"},"data_base64":{"type":"string"},"filename":{"type":"string"},"mime":{"type":"string"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/payments": {"post":{"operationId":"tool_payments","summary":"Payments (Stripe, Square, Whop, FanBasis)","description":"Get paid through the connected providers: Stripe (payment links, hosted invoices, subscriptions), Square (payment links, invoices), Whop (checkout links, monthly / yearly links), FanBasis (read only: collect in FanBasis, then record). provider picks one (default: the workspace default, else Stripe). Payments read from Square / Whop / FanBasis show up on their own (kind sale). The payments list with status (Paid, Open, Overdue, Failed, Refunded, Active...), refunds (Stripe, Square, Whop), cancel, resend, and record (a payment made outside: cash, e-transfer, the provider's own page). Amounts: amount in the currency (1500 or \"1500.00\"), or amount_cents, or lines [{name, qty, unit_cents}], or a deal_id (its line items, else its value). Currency: given, else the deal's, else the account's. Clients pay on the provider's pages; nothing here charges a card.\nActions:\n- status: Which providers are connected (Stripe mode, account names, Square location, last sync, webhook set up) and the default. Never a key.\n- test_connection: Test a connected Square, Whop or FanBasis account (provider): its name and how many recent payments the key reads; FanBasis also returns the field names of 1 payment (keys only) and which ones the mapping found. Owner only. Never a key.\n- list: Payments, newest first (contact_id?, deal_id?, status?, kind?, provider?, limit?).\n- get: One payment (payment_id).\n- create_link: A Stripe payment link (single use) for an amount, lines or a deal. Returns the url to share.\n- create_invoice: A Stripe hosted invoice for a person (contact_id or a deal with a person), due_days (default 14) or due_date, send stripe | email | none. (may wait for approval)\n- create_subscription: A retainer: monthly / yearly (interval) for a person or deal, collect link (card on Stripe) or invoice. (waits for approval: money)\n- refund: Refund a paid payment (payment_id, amount? = all that is left, reason?). (waits for approval: money)\n- cancel: Stop a payment link, void an open invoice, or end a subscription (at_period_end?). (waits for approval: external)\n- resend: Send an open invoice again (via stripe | email). (waits for approval: external)\n- record: Record a payment made outside (cash, e-transfer, the provider's own page): contact_id or deal_id, amount, provider? (manual default), paid_at?. Marks it paid and fires Payment received once. Moves no money. (waits for approval: external)","tags":["tools"],"x-ctrl-tool":"payments","x-ctrl-scopes":{"status":["payments:read"],"test_connection":["payments:read"],"list":["payments:read"],"get":["payments:read"],"create_link":["payments:write"],"create_invoice":["payments:write"],"create_subscription":["payments:write"],"refund":["payments:write"],"cancel":["payments:write"],"resend":["payments:write"],"record":["payments:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["status","test_connection","list","get","create_link","create_invoice","create_subscription","refund","cancel","resend","record"],"description":"What to do."},"contact_id":{"type":"number"},"deal_id":{"type":"number"},"payment_id":{"type":"number"},"amount":{"type":["number","string"],"description":"In the currency, e.g. 1500 = $1,500.00."},"amount_cents":{"type":"number","description":"Minor units (cents)."},"lines":{"type":"array","items":{"type":"object"},"description":"[{ name, qty, unit_cents }]"},"use_deal_lines":{"type":"boolean","description":"With deal_id: use its line items (default) or false for its value."},"currency":{"type":"string","description":"ISO code. Default: the deal's, else the workspace currency (crm_data settings), else the provider account's (Square, Whop, FanBasis: the account's first)."},"description":{"type":"string"},"due_days":{"type":"number"},"due_date":{"type":"string","description":"YYYY-MM-DD"},"send":{"type":"string","enum":["stripe","email","none"],"description":"Invoices: Stripe emails it, our billing email, or none (just the link)."},"interval":{"type":"string","enum":["month","year"]},"collect":{"type":"string","enum":["link","invoice"],"description":"Subscriptions: link = the client enters a card on Stripe; invoice = Stripe emails an invoice each period."},"status":{"type":"string"},"kind":{"type":"string","enum":["link","invoice","subscription","checkout"]},"limit":{"type":"number"},"reason":{"type":"string","enum":["requested_by_customer","duplicate","fraudulent"]},"at_period_end":{"type":"boolean"},"via":{"type":"string","enum":["stripe","email"]},"idem_key":{"type":"string","description":"Optional: the same key returns the same payment."},"provider":{"type":"string","enum":["stripe","square","whop","fanbasis","manual"],"description":"Which provider makes it (create_*), filters list, or names where a recorded payment was paid (manual = cash / bank / other)."},"paid_at":{"type":"string","description":"record: when it was paid (YYYY-MM-DD), default now."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/pdf": {"post":{"operationId":"tool_pdf","summary":"PDF annotate and sign","description":"Marks and signature requests on a PDF in Drive, as you. Coordinates are 0..1 of the displayed page, origin top-left. Mark data: {kind:\"highlight\", rects:[{x,y,w,h}], color: yellow|green|blue|pink|purple, quote} | {kind:\"ink\", strokes:[[[x,y],...]], color: black|blue|red|green, width} | {kind:\"text\", rect, text, color, size} | {kind:\"signature\", rect, image: PNG data URL}. Read the text of a PDF with read_file; a finished request has signedFileId (read it with read_file).\nActions:\n- marks: Every mark on the file and whether you can edit.\n- add_mark: Add a mark: page (1-based) and data.\n- update_mark: Change a mark's data (mark_id).\n- delete_mark: Remove a mark (mark_id). (waits for approval: delete)\n- export: Save a copy with every mark flattened into a new Drive file next to the original.\n- outline: The PDF's outline (bookmarks / table of contents) as a tree: [{title, page, items}], like the viewer's Outline panel; [] when it has none.\n- requests: Signature requests on the file; options=true also lists who you can ask (teammates, portals, CRM people).\n- request_signatures: Ask people to sign: signers [{signerUserId | portalSlug | email (+name) | crmContactId, fields?: [{id, page, kind: signature|initials|date|name, rect}]}], message, order sequential|parallel, expiresDays, autoRemindDays. Teammates only runs at once; portals and emails reach outside people. (may wait for approval)\n- remind: Remind a teammate signer (request_id; once an hour).\n- cancel_request: Cancel the whole envelope (request_id). (waits for approval: external)\n- my_requests: Requests waiting on you to sign.\n- view_request: A request made to you: the file, marks and fields to fill.\n- sign: Sign a request made to you (request_id). Fields are filled with your saved signature (signature_id, default the newest) plus your name and today, or pass values [{id, image|text}]. (waits for approval: external)\n- decline: Decline a request made to you (request_id, reason). (waits for approval: external)\n- signatures: Your saved signatures (ids and kinds).\n- save_signature: Save a signature picture: kind draw|type|upload, image (PNG data URL).\n- delete_signature: Delete a saved signature (signature_id). (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"pdf","x-ctrl-scopes":{"marks":["files:read"],"add_mark":["files:write"],"update_mark":["files:write"],"delete_mark":["files:write"],"export":["files:write"],"outline":["files:read"],"requests":["esign:read"],"request_signatures":["esign:write"],"remind":["esign:write"],"cancel_request":["esign:write"],"my_requests":["esign:read"],"view_request":["esign:read"],"sign":["esign:write"],"decline":["esign:write"],"signatures":["esign:read"],"save_signature":["esign:write"],"delete_signature":["esign:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["marks","add_mark","update_mark","delete_mark","export","outline","requests","request_signatures","remind","cancel_request","my_requests","view_request","sign","decline","signatures","save_signature","delete_signature"],"description":"What to do."},"file":{"type":"string","description":"Drive file id, public id or file:<id> (a PDF)."},"page":{"type":"number"},"data":{"type":"object","description":"Mark data (see description)."},"mark_id":{"type":"number"},"options":{"type":"boolean"},"signers":{"type":"array","items":{"type":"object"}},"message":{"type":"string"},"order":{"type":"string","enum":["sequential","parallel"]},"expiresDays":{"type":"number"},"autoRemindDays":{"type":"number"},"request_id":{"type":"number"},"signature_id":{"type":"number"},"values":{"type":"array","items":{"type":"object"}},"placements":{"type":"array","items":{"type":"object"},"description":"sign without sender fields: [{page, data: signature or text mark}]."},"reason":{"type":"string"},"kind":{"type":"string","enum":["draw","type","upload"]},"image":{"type":"string"},"query":{"type":"string","description":"requests with options: filter who you can ask."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/phone": {"post":{"operationId":"tool_phone","summary":"Phone","description":"Your business phone (Twilio), as you: text people, call them (rings your cell first), read and assign threads, triage people known only by number, AI reply drafts, templates and the phone inbox. CRM Conversations: inbox_threads lists a box (team, mine, unassigned, unread, done) with filters, thread gives one thread with its person, company, owner, tags and open deal, mark_done / reopen / mark_unread. Texts and calls wait for approval. Read a thread with action messages, or the whole conversation (texts + linked email + activity) with conversation; reply on either channel with reply, or by text with send_text.\nActions:\n- conversations: Text threads (scope team or mine) and recent calls.\n- messages: Texts with a contact (contact_id) or a company (company_id), and whether they opted out.\n- send_text: Text a contact (contact_id) or a number (to): text, media. (waits for approval: external)\n- mark_thread_read: Mark a contact's text thread read.\n- assign: Assign a text thread (contact_id, assignee; null clears).\n- inbox_threads: CRM Conversations: threads in a box (team, mine = assigned to you or people you own, unassigned, unread, done) with search, tag_id, owner, pipeline_id, stage_id; and the count in every box.\n- thread: One CRM conversation (contact_id): the person, company, owner, assignee, tags, open deal, done, and whether texting is blocked. Read the texts with messages.\n- conversation: The whole CRM conversation with a person (contact_id), GHL style: texts, their email from your own and shared mailboxes (only mail with their address), and activity rows (bookings, forms, calls, deal moves, tags, notes, tasks) in time order; default_channel is where they last wrote in; reply gives the email thread a reply lands in.\n- reply: Reply in a CRM conversation (contact_id, channel sms | email | messenger | instagram, text; email: subject? to start a new thread, account_id? mailbox, cc? / bcc? addresses). To send later use crm_conversations schedule. Email lands in their newest thread with Re: and your signature; messenger / instagram go in their DM thread (24-hour window). Waits for approval like send_text / send_email. (may wait for approval)\n- mark_done: Mark a conversation done (contact_id): it leaves the open boxes until a newer text comes in.\n- reopen: Reopen a done conversation (contact_id).\n- mark_unread: Mark a conversation unread again (contact_id): its newest incoming text.\n- draft_reply: An AI draft of the next text to a contact (never sent).\n- calls: Calls with a contact or company, or one call (call_id).\n- call: Call a contact or number: rings your cell first, then connects them. (waits for approval: external)\n- hold: Hold or resume a live call (call_id, hold).\n- hang_up: Hang up a live call (call_id).\n- transfer: Transfer a connected call (call_id) to a teammate or a number (transfer_to), cold or warm (mode). The customer waits on hold music while it rings. Rings someone outside the app, so it waits for approval. (waits for approval: external)\n- transfer_state: The transfer going on a call (call_id): ringing, talking (warm, answered), completed, cancelled, no-answer.\n- complete_transfer: Warm transfer: you leave and the customer is connected to the person you transferred to (call_id).\n- cancel_transfer: Warm transfer called off: the person you rang leaves, the customer comes off hold (call_id).\n- mark_call_read: Mark a call (missed call, voicemail) read.\n- add_contact: Add a person known by phone (phone, name, company_id); returns contact_id.\n- move_contact: Move a person (and their texts and calls) to another company (contact_id, company_id).\n- inbox: Unread texts, missed calls and voicemails.\n- mark_inbox_read: Mark inbox items read (items from inbox, or all=true).\n- templates: Your text templates.\n- save_template: Create or update a template (template_id to update, name, body).\n- delete_template: Delete a template. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"phone","x-ctrl-scopes":{"conversations":["messages:read"],"messages":["messages:read"],"send_text":["messages:send"],"mark_thread_read":["messages:write"],"assign":["messages:write"],"inbox_threads":["messages:read"],"thread":["messages:read"],"conversation":["messages:read"],"reply":["messages:send"],"mark_done":["messages:write"],"reopen":["messages:write"],"mark_unread":["messages:write"],"draft_reply":["messages:write"],"calls":["calls:read"],"call":["calls:write"],"hold":["calls:write"],"hang_up":["calls:write"],"transfer":["calls:write"],"transfer_state":["calls:read"],"complete_transfer":["calls:write"],"cancel_transfer":["calls:write"],"mark_call_read":["calls:write"],"add_contact":["messages:write"],"move_contact":["messages:write"],"inbox":["messages:read"],"mark_inbox_read":["messages:write"],"templates":["messages:read"],"save_template":["messages:write"],"delete_template":["messages:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["conversations","messages","send_text","mark_thread_read","assign","inbox_threads","thread","conversation","reply","mark_done","reopen","mark_unread","draft_reply","calls","call","hold","hang_up","transfer","transfer_state","complete_transfer","cancel_transfer","mark_call_read","add_contact","move_contact","inbox","mark_inbox_read","templates","save_template","delete_template"],"description":"What to do."},"contact_id":{"type":"number"},"company_id":{"type":"number"},"to":{"type":"string","description":"A full number (+1 604 555 0123) when there is no contact."},"channel":{"type":"string","enum":["sms","email","messenger","instagram"],"description":"reply: which channel (messenger / instagram: their Facebook or Instagram DM thread, only inside the 24-hour window after their last message)."},"subject":{"type":"string","description":"reply (email): subject for a new thread."},"account_id":{"type":"number","description":"reply (email): the mailbox to send from."},"cc":{"type":["array","string"],"items":{"type":"string"},"description":"reply (email): CC addresses (list or comma separated)."},"bcc":{"type":["array","string"],"items":{"type":"string"},"description":"reply (email): BCC addresses."},"text":{"type":"string"},"media":{"type":"array","items":{"type":"string"},"description":"send_text: media tokens."},"call_id":{"type":"number"},"hold":{"type":"boolean"},"transfer_to":{"type":"object","description":"transfer: { kind: \"member\", id: team member id } or { kind: \"number\", phone }."},"mode":{"type":"string","enum":["cold","warm"],"description":"transfer: cold = you leave at once; warm = you talk to them first, then complete_transfer."},"assignee":{"type":["string","null"],"description":"assign: team member id or name; null clears."},"phone":{"type":"string"},"name":{"type":"string"},"hint":{"type":"string"},"template_id":{"type":"number"},"body":{"type":"string","description":"save_template: the text (merge fields {{first_name}}, {{company}}, {{my_name}})."},"all":{"type":"boolean"},"items":{"type":"array","items":{"type":"object"},"description":"mark_inbox_read: [{kind, id, contact_id}] from inbox."},"scope":{"type":"string","enum":["team","mine"]},"box":{"type":"string","enum":["team","mine","unassigned","unread","done"],"description":"inbox_threads: which box (default team)."},"search":{"type":"string","description":"inbox_threads: name, company, number or text."},"tag_id":{"type":"number","description":"inbox_threads: a tag option id."},"owner":{"type":"string","description":"inbox_threads: an owner (team member id) or none."},"pipeline_id":{"type":"number","description":"inbox_threads: open deal in this pipeline (0 = the default)."},"stage_id":{"type":"string","description":"inbox_threads: open deal in this stage (with pipeline_id)."},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/phone_setup": {"post":{"operationId":"tool_phone_setup","summary":"Phone setup","description":"Phone settings and numbers, as you: forwarding to your cell, ring time, recording, voicemail greeting, auto replies, browser calling, the numbers on your Twilio account (buy, use, repair webhooks, default, give a number to a teammate), call flows (flows, save_flow, number_flow, delete_flow: business hours, menus, ring groups, voicemail), call tracking numbers per ad source (tracking, save_tracking_source, tracking_number, tracking_swap_target for the website number swap, delete_tracking_source), and the US A2P 10DLC texting registration (a2p: stage, answers, what is blocked; a2p_save answers; a2p_submit; a2p_check; a2p_link one made in the Twilio Console; a2p_attach numbers; a2p_reset). Buying a number and submitting the registration charge your Twilio account, so they wait for approval. Connecting Twilio (credentials) stays in Settings > Phone.\nActions:\n- get: Your phone account settings and numbers in use.\n- set: Change settings (settings object).\n- setup_browser_calling: Create (or redo) the Twilio API key and app that browser calling uses. Waits for approval (makes a credential on the Twilio account). (waits for approval: external)\n- disconnect: Forget the Twilio credentials (texts and calls stay). (waits for approval: delete)\n- numbers: Every number on your Twilio account.\n- search_numbers: Numbers you could buy (country, area_code, contains, type), with the monthly price.\n- buy_number: Buy a number (phone_number from search_numbers). This COSTS MONEY: Twilio bills it every month. (waits for approval: external)\n- use_number: Point a number already on your Twilio account here (sid).\n- default_number: Texts and calls go out from this number (sid).\n- repair_number: Set a number's SMS and voice webhooks back to Plodder (sid; numbers with needs_repair).\n- assign_number: Give a number to a teammate (sid, assignee team member id; null = everyone). Texts in threads assigned to them go out from it.\n- a2p: The US texting registration (A2P 10DLC): stage (draft, profile_review, brand_review, campaign_review, approved, rejected), the answers, errors / warnings before submit, reasons Twilio gave, and what is blocked until it is approved.\n- a2p_save: Save registration answers (brand and / or campaign objects; partial is fine). Only while it is a draft or Twilio asked for changes.\n- a2p_submit: Send the registration to Twilio (or resubmit the step Twilio rejected). This COSTS MONEY: Twilio charges brand and campaign fees plus a monthly campaign fee. (waits for approval: external)\n- a2p_check: Read the registration status from Twilio now (and take the next step when one is due).\n- a2p_link: Track a registration already made in the Twilio Console (finds the brand and its campaign).\n- a2p_attach: Add every US number in use to the registered campaign.\n- a2p_reset: Start the registration over here (nothing is deleted at Twilio; the answers stay). (waits for approval: delete)\n- flows: Call flows: business hours, menus (press 1, 2, 3), ring groups (all at once or round robin), voicemail; each with its steps, problems and the numbers that follow it.\n- save_flow: Create or update a call flow (flow object). Numbers can only follow a flow without problems.\n- number_flow: A number (sid) follows a call flow (flow_id; null = ring the app, then the cell, then voicemail).\n- delete_flow: Delete a call flow (flow_id). Its numbers and tracking sources ring the business line again. (waits for approval: delete)\n- tracking: Call tracking: the ad sources with their numbers, route (business line, forward, call flow), calls per source over 30 days, and the website number swap (key, number replaced, script tag).\n- save_tracking_source: Create or update a tracking source (source object). Calls and texts to its numbers credit the person to it (lead source, first / last touch) and can tag them.\n- tracking_number: Put one number in use here on a tracking source (sid, source_id; null takes it off).\n- tracking_swap_target: Set the number written on the website that the swap script replaces with a tracking number (target; empty turns the swap off).\n- delete_tracking_source: Delete a tracking source (source_id). Its numbers become plain lines; calls already tracked keep the name. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"phone_setup","x-ctrl-scopes":{"get":["admin:read"],"set":["admin:write"],"setup_browser_calling":["admin:write"],"disconnect":["admin:write"],"numbers":["admin:read"],"search_numbers":["admin:read"],"buy_number":["admin:write"],"use_number":["admin:write"],"default_number":["admin:write"],"repair_number":["admin:write"],"assign_number":["admin:write"],"a2p":["admin:read"],"a2p_save":["admin:write"],"a2p_submit":["admin:write"],"a2p_check":["admin:write"],"a2p_link":["admin:write"],"a2p_attach":["admin:write"],"a2p_reset":["admin:write"],"flows":["admin:read"],"save_flow":["admin:write"],"number_flow":["admin:write"],"delete_flow":["admin:write"],"tracking":["admin:read"],"save_tracking_source":["admin:write"],"tracking_number":["admin:write"],"tracking_swap_target":["admin:write"],"delete_tracking_source":["admin:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["get","set","setup_browser_calling","disconnect","numbers","search_numbers","buy_number","use_number","default_number","repair_number","assign_number","a2p","a2p_save","a2p_submit","a2p_check","a2p_link","a2p_attach","a2p_reset","flows","save_flow","number_flow","delete_flow","tracking","save_tracking_source","tracking_number","tracking_swap_target","delete_tracking_source"],"description":"What to do."},"settings":{"type":"object","description":"set: cell_phone, call_cell_first, forward_unanswered, ring_seconds, record_calls, record_notice, voicemail_greeting, help_reply, a2p."},"country":{"type":"string","enum":["US","CA"]},"area_code":{"type":"string"},"contains":{"type":"string"},"type":{"type":"string","enum":["Local","TollFree"]},"phone_number":{"type":"string","description":"buy_number: a number from search_numbers."},"sid":{"type":"string","description":"A number's Twilio sid."},"assignee":{"type":["string","null"],"description":"assign_number: team member id (from the team list); null = everyone."},"brand":{"type":"object","description":"a2p_save: business answers: legal_name, business_type (Sole Proprietorship | Partnership | Limited Liability Corporation | Co-operative | Non-profit Corporation | Corporation), company_type (private | public | non-profit | government), stock_exchange, stock_ticker, industry (e.g. PROFESSIONAL_SERVICES, CONSTRUCTION, REAL_ESTATE, HEALTHCARE, RETAIL), tax_id (EIN or BN), website, social, street, city, region, postal_code, country (US | CA), email, rep_first_name, rep_last_name, rep_email, rep_phone, rep_title, rep_position (Director | GM | VP | CEO | CFO | General Counsel | Other), brand_type (LOW_VOLUME_STANDARD | STANDARD)."},"source":{"type":"object","description":"save_tracking_source: id (to update), name (e.g. \"Meta ads - Eco Spa\"), utm_source / utm_medium / utm_campaign (what the person is credited with), match_sources (utm_source values that pick it on the website swap), match_referrers (sites, e.g. facebook.com), is_default (visitors nothing else matches), route (business | forward | flow), forward_to (number), flow_id, tag (put on callers), numbers (sids in its pool)."},"source_id":{"type":["number","null"],"description":"A tracking source id (tracking_number: null takes the number off its source)."},"flow":{"type":"object","description":"save_flow: id (to update), name, steps (ordered; each { id, type: hours | greeting | menu | ring | voicemail | hangup, label, ... }: hours { source: calendar | custom, link_id, hours {\"1\":[{start,end}]}, timezone, holidays: none | us | ca, open, closed }, greeting { say | audio, next }, menu { say | audio, options [{ digit, label, goto }], timeout, retries, invalid, no_input }, ring { targets [{ kind: member, id } | { kind: number, phone } | { kind: me } | { kind: my_cell }], mode: simultaneous | round_robin, seconds, no_answer }, voicemail { say | audio }, hangup { say }; a goto is a step id, \"\" for the next step, \"end\" to hang up), numbers (sids that follow it)."},"flow_id":{"type":["number","null"],"description":"A call flow id (number_flow: null = default ringing)."},"target":{"type":"string","description":"tracking_swap_target: the number written on the website that the swap script replaces (empty turns it off)."},"campaign":{"type":"object","description":"a2p_save: campaign answers: use_case (LOW_VOLUME | MIXED | CUSTOMER_CARE | ACCOUNT_NOTIFICATION | MARKETING), description, message_flow (how people opt in), samples (2 to 5 texts), has_links, has_phone, opt_in_message, opt_out_message, help_message, opt_in_keywords."},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/post_chat_message": {"post":{"operationId":"tool_post_chat_message","summary":"Post a chat message","description":"Post a message to a Plodder channel or DM as you. By default it waits for your approval in the Plodder Inbox and posts when you approve.","tags":["tools"],"x-ctrl-tool":"post_chat_message","x-ctrl-scopes":{"*":["messages:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"post_chat","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"channel_id":{"type":"number"},"text":{"type":"string"},"thread_parent_id":{"type":"number","description":"Reply in this thread."}},"required":["channel_id","text"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/post_discussion": {"post":{"operationId":"tool_post_discussion","summary":"Comment on an item","description":"Post a comment on an item, or reply in one of its threads (thread_id from read_discussion). Comments stay inside Plodder; people @mentioned are notified there. On a chat channel or chat message this posts in the channel (a message: in its thread), which waits for approval like every chat post.","tags":["tools"],"x-ctrl-tool":"post_discussion","x-ctrl-scopes":{"*":["*"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"string","description":"An item as type:id, for example task:123, doc:45, project:7, crm_company:3, sheet_row:88, meeting:12 or mail_thread:<account>-<thread>. list_items and search return these."},"text":{"type":"string","description":"The comment (markdown)."},"thread_id":{"type":"number","description":"Reply in this thread instead of starting a new one."}},"required":["item","text"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/presentations": {"post":{"operationId":"tool_presentations","summary":"Presentations","description":"Read and build slide decks, as you. Layouts: title, title-content, section, two-content, title-only, blank. A slide from fields: {layout, title, subtitle, body (one line per paragraph, \"- \" for bullets, indent two spaces per level), notes}. get_slide returns the raw elements (text, image, table) for fine edits with set_elements.\nActions:\n- list: Presentations you can open, newest first.\n- get: Title, access, theme name and every slide's title, text and notes.\n- get_slide: One slide (slide_id or slide number) as raw JSON.\n- create: A new deck: title, slides [{layout, title, subtitle, body, notes}], theme (name), workspace_id or folder_id.\n- add_slide: Add a slide from fields (fields) at position (1-based; default the end).\n- update_slide: Change a slide's layout, title, subtitle, body or notes (fields), or hide it (hidden).\n- set_elements: Replace a slide's elements with raw JSON (elements), for layouts the fields cannot express.\n- move_slide: Move a slide to position (1-based).\n- delete_slide: Remove a slide. (waits for approval: delete)\n- rename: Change the title.\n- move: Move in Drive: workspace_id and/or folder_id.\n- versions: Saved .pptx versions (exports, imports).\n- trash: Move to Drive Trash. (waits for approval: delete)\n- list_items: A slide's items like the Selection pane: id, kind, name, hidden, locked, group, box (front first).\n- update_items: Selection pane changes on a slide: items [{id, name, hidden, locked, x, y, w, h}], order (ids front first) to restack.\n- group: Group items (ids) so they select and move together.\n- ungroup: Ungroup: every group touching ids comes apart.\n- set_animations: Replace a slide's animations, in play order: animations [{element (item id), class entrance|emphasis|exit, effect, trigger click|with|after, duration_ms, delay_ms, direction left|right|up|down (fly, float, wipe), repeat}]. Effects: entrance:appear, entrance:fade, entrance:fly, entrance:float, entrance:split, entrance:wipe, entrance:zoom, entrance:growTurn, entrance:bounce, entrance:swivel, emphasis:pulse, emphasis:teeter, emphasis:spin, emphasis:growShrink, emphasis:transparency, exit:disappear, exit:fade, exit:fly, exit:float, exit:split, exit:wipe, exit:zoom, exit:shrinkTurn. Empty list removes them.\n- set_transition: Set a slide's transition (or every slide's with all_slides): transition {type none|fade|push|wipe|cover|uncover|split|zoom|cut|dissolve, duration_ms, direction (push, wipe, cover, uncover), after_ms (move on by itself), no_click}; null removes it.\n- add_chart: Add a native chart: chart {type bar|stacked|line|area|stacked-area|pie|doughnut|scatter|radar, title, legend, categories [labels], series [{name, values}]}, optional x/y/w/h (points, slide 960x540). update with element_id to change an existing chart.\n- add_equation: Add an equation from LaTeX (latex, size), optional x/y; element_id changes an existing one.\n- add_diagram: Add a SmartArt-style diagram: diagram {type list|process|cycle|hierarchy, text (one line per shape; indent two spaces for a child in a hierarchy)}, optional x/y/w/h; element_id (any shape of it) redraws an existing diagram.\n- get_master: The slide master: graphics, background, title/body text styles, and per layout its graphics, background, placeholder boxes and hide flag.\n- set_master: Replace the slide master (same JSON as get_master: {els, bg, title {font,size,color,bold,italic}, body, layouts {<layout>: {els, bg, boxes, hideMaster}}}); null removes it. Slides whose placeholders follow the master move and restyle with it.\n- set_slide_options: Slide flags: hide_background_graphics (hide the master graphics on this slide).\n- find: Find text like the editor's find bar: query, match_case, whole_word, include_notes (default true), optional slide / slide_id to look on one slide. Returns each match (slide, item id or notes, table cell, context).\n- replace_text: Find and replace across the deck (text boxes, shapes, table cells and, unless include_notes is false, speaker notes): query, replacement, match_case, whole_word, optional slide / slide_id. The new text keeps the formatting where each match starts. Returns how many changed.","tags":["tools"],"x-ctrl-tool":"presentations","x-ctrl-scopes":{"list":["docs:read"],"get":["docs:read"],"get_slide":["docs:read"],"create":["docs:write"],"add_slide":["docs:write"],"update_slide":["docs:write"],"set_elements":["docs:write"],"move_slide":["docs:write"],"delete_slide":["docs:write"],"rename":["docs:write"],"move":["docs:write"],"versions":["docs:read"],"trash":["docs:write"],"list_items":["docs:read"],"update_items":["docs:write"],"group":["docs:write"],"ungroup":["docs:write"],"set_animations":["docs:write"],"set_transition":["docs:write"],"add_chart":["docs:write"],"add_equation":["docs:write"],"add_diagram":["docs:write"],"get_master":["docs:read"],"set_master":["docs:write"],"set_slide_options":["docs:write"],"find":["docs:read"],"replace_text":["docs:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get","get_slide","create","add_slide","update_slide","set_elements","move_slide","delete_slide","rename","move","versions","trash","list_items","update_items","group","ungroup","set_animations","set_transition","add_chart","add_equation","add_diagram","get_master","set_master","set_slide_options","find","replace_text"],"description":"What to do."},"id":{"type":"string","description":"Presentation id or public id."},"title":{"type":"string"},"theme":{"type":"string","description":"create: Office, Paper, Night, Slate, Forest, Ocean, Sunset, Mono."},"slides":{"type":"array","items":{"type":"object"}},"fields":{"type":"object","description":"{layout, title, subtitle, body, notes}"},"elements":{"type":"array","items":{"type":"object"}},"slide_id":{"type":"string"},"slide":{"type":"number","description":"1-based slide number."},"position":{"type":"number"},"hidden":{"type":"boolean"},"workspace_id":{"type":"number"},"folder_id":{"type":"number"},"items":{"type":"array","items":{"type":"object"},"description":"update_items: [{id, name, hidden, locked, x, y, w, h}]"},"order":{"type":"array","items":{"type":"string"},"description":"update_items: item ids front first."},"ids":{"type":"array","items":{"type":"string"}},"animations":{"type":"array","items":{"type":"object"}},"transition":{"type":["object","null"]},"all_slides":{"type":"boolean"},"chart":{"type":"object"},"diagram":{"type":"object"},"latex":{"type":"string"},"size":{"type":"number"},"element_id":{"type":"string"},"master":{"type":["object","null"]},"hide_background_graphics":{"type":"boolean"},"query":{"type":"string","description":"find / replace_text: the text to look for."},"replacement":{"type":"string","description":"replace_text: the new text (may be empty)."},"match_case":{"type":"boolean"},"whole_word":{"type":"boolean"},"include_notes":{"type":"boolean","description":"find / replace_text: look in speaker notes too (default true)."},"x":{"type":"number"},"y":{"type":"number"},"w":{"type":"number"},"h":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/project_actions": {"post":{"operationId":"tool_project_actions","summary":"Project actions","description":"Project settings, copies, stages and the project's comment box, as you. For tasks in a project use create_task / list_tasks.\nActions:\n- update: Change a project (project_id, set). archived 1 archives it, 0 brings it back.\n- delete: Delete a project (it goes to Trash with its stages and tasks). (waits for approval: delete)\n- copy: Copy a project with its stages, tasks and docs (project_id, name).\n- comments: The project's comment box.\n- comment: Post in the project's comment box (project_id, body).\n- delete_comment: Delete your own project comment (comment_id). (waits for approval: delete)\n- stages: The project's stages in order.\n- add_stage: Add a stage (project_id, name, color).\n- update_stage: Change a stage (stage_id, set: {name, color, is_active, ...}).\n- reorder_stages: Put the stages in this order (project_id, stage_ids).\n- delete_stage: Delete a stage (stage_id); its tasks move to reassign_to, or lose their stage. (waits for approval: delete)\n- apply_template: Add a project template's stages and tasks to a project (project_id, template_id).","tags":["tools"],"x-ctrl-tool":"project_actions","x-ctrl-scopes":{"update":["projects:write"],"delete":["projects:write"],"copy":["projects:write"],"comments":["projects:read"],"comment":["projects:write"],"delete_comment":["projects:write"],"stages":["projects:read"],"add_stage":["projects:write"],"update_stage":["projects:write"],"reorder_stages":["projects:write"],"delete_stage":["projects:write"],"apply_template":["projects:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["update","delete","copy","comments","comment","delete_comment","stages","add_stage","update_stage","reorder_stages","delete_stage","apply_template"],"description":"What to do."},"project_id":{"type":"number"},"name":{"type":"string"},"set":{"type":"object","description":"update: project fields (name, description, color, status, priority, start_date, due_date, folder_id, archived 1|0, ...)."},"body":{"type":"string","description":"comment: the text (@Claude or an agent handle runs that agent)."},"comment_id":{"type":"number"},"stage_id":{"type":"number"},"color":{"type":"string"},"stage_ids":{"type":"array","items":{"type":"number"}},"reassign_to":{"type":"number","description":"delete_stage: move its tasks to this stage."},"template_id":{"type":"number"},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/property_admin": {"post":{"operationId":"tool_property_admin","summary":"Properties and tags admin","description":"Manage properties (custom fields) and tags, as you: create, rename and archive property definitions, their options, the tag lists (CRM tags and Work tags, separate sets: create with scope, rename, recolour, merge within a set, delete), restore an archived property, and clear one value on an item. Set values with set_property / set_tags.\nActions:\n- definitions: Definitions that apply to a type (target type in property, workspace_id).\n- create_definition: A property (display_name, data_type, target_types, ref_entity_type, workspace_id or personal).\n- update_definition: Rename or retarget a property (id, display_name, target_types, sort_order).\n- archive_definition: Archive a property (id); restore brings it back. (waits for approval: delete)\n- restore_definition: Bring an archived property back (id).\n- options: A property's options (property; stage needs project_id; tags take scope crm|work).\n- add_option: Add an option (property, label, color; tags take scope crm|work).\n- update_option: Change an option (property, id, label, color, sort_order).\n- delete_option: Remove an option (property, id). (waits for approval: delete)\n- tags: Tags with usage counts. scope crm|work picks one set (CRM tags: people, businesses, deals, conversations, bookings, forms; Work tags: everything else); omit for both.\n- create_tag: A tag (label, color, scope crm|work; default work). The same name may exist once per scope.\n- update_tag: Rename or recolour a tag everywhere it is used (id, label, color).\n- merge_tags: Fold one tag into another (from, into). (waits for approval: delete)\n- delete_tag: Remove a tag from every item and list (id). (waits for approval: delete)\n- clear_value: Clear one property on an item (item, property).","tags":["tools"],"x-ctrl-tool":"property_admin","x-ctrl-scopes":{"definitions":["admin:read"],"create_definition":["admin:write"],"update_definition":["admin:write"],"archive_definition":["admin:write"],"restore_definition":["admin:write"],"options":["admin:read"],"add_option":["admin:write"],"update_option":["admin:write"],"delete_option":["admin:write"],"tags":["admin:read"],"create_tag":["admin:write"],"update_tag":["admin:write"],"merge_tags":["admin:write"],"delete_tag":["admin:write"],"clear_value":["admin:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["definitions","create_definition","update_definition","archive_definition","restore_definition","options","add_option","update_option","delete_option","tags","create_tag","update_tag","merge_tags","delete_tag","clear_value"],"description":"What to do."},"id":{"type":"string","description":"Definition id, option id or tag id."},"display_name":{"type":"string"},"data_type":{"type":"string","description":"text, number, select, multi_select, date, checkbox, url, email, person, relation, ..."},"target_types":{"type":"array","items":{"type":"string"}},"ref_entity_type":{"type":"string"},"workspace_id":{"type":"number"},"personal":{"type":"boolean"},"sort_order":{"type":"number"},"property":{"type":"string","description":"options: the property id or system key (status, stage, tags, ...)."},"project_id":{"type":"number"},"label":{"type":"string"},"color":{"type":"string"},"from":{"type":"number"},"into":{"type":"number"},"scope":{"type":"string","enum":["crm","work"],"description":"Tags: CRM tags (people, businesses, deals, conversations, bookings, forms) or Work tags (everything else)."},"item":{"type":"string","description":"clear_value: <type>:<id>"},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/query_database": {"post":{"operationId":"tool_query_database","summary":"Query a database","description":"Answer a question from a database's rows: filter, group, aggregate, sort and limit, computed by the app over the rows the user can see (formula and rollup columns are filled in, relation cells are titles). Returns the result table and the equivalent SQL. Leave out group_by and metrics to list rows; give metrics without group_by for one number; give group_by (plus bucket for a date column) for a breakdown. chart is a display hint for the app (bar for categories, line for time, pie for parts of a whole, table for lists); it does not change the result. Column names must match the database (see list_database_rows).","tags":["tools"],"x-ctrl-tool":"query_database","x-ctrl-scopes":{"*":["docs:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"database":{"type":"string","description":"Database id or sheet:<id>."},"filters":{"type":"array","items":{"type":"object","properties":{"column":{"type":"string"},"op":{"type":"string","enum":["eq","neq","contains","not_contains","gt","gte","lt","lte","empty","not_empty"]},"value":{"type":["string","number","boolean"]}},"required":["column","op"]}},"group_by":{"type":"string","description":"Column to group by. Multi-select rows count once per label."},"bucket":{"type":"string","enum":["day","week","month","quarter","year"],"description":"Date bucket when group_by is a date column."},"metrics":{"type":"array","items":{"type":"object","properties":{"op":{"type":"string","enum":["count","sum","avg","min","max"]},"column":{"type":"string"},"as":{"type":"string","description":"Result column name."}},"required":["op"]}},"columns":{"type":"array","items":{"type":"string"},"description":"Columns to show when listing rows."},"sort":{"type":"object","properties":{"by":{"type":"string","description":"A column, or a metric name."},"dir":{"type":"string","enum":["asc","desc"]}}},"limit":{"type":"number","description":"Max result rows (default 50 for groups, 100 for rows, max 500)."},"chart":{"type":"string","enum":["bar","line","pie","table"],"description":"How the app should draw this answer."},"title":{"type":"string","description":"Short chart title."}},"required":["database"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/read_activity": {"post":{"operationId":"tool_read_activity","summary":"Read activity","description":"The history of an item (created, edited fields with before and after, comments, sends, status changes), newest first. Without an item: the user's own recent actions across the app.","tags":["tools"],"x-ctrl-tool":"read_activity","x-ctrl-scopes":{"*":["ctrl-flow:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"string","description":"An item as type:id, for example task:123, doc:45, project:7, crm_company:3, sheet_row:88, meeting:12 or mail_thread:<account>-<thread>. list_items and search return these."},"limit":{"type":"number","description":"Max events (default 30)."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/read_chat_messages": {"post":{"operationId":"tool_read_chat_messages","summary":"Read chat messages","description":"Recent messages in a channel, oldest first.","tags":["tools"],"x-ctrl-tool":"read_chat_messages","x-ctrl-scopes":{"*":["messages:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"channel_id":{"type":"number"},"limit":{"type":"number","description":"Default 30, max 100."},"before_id":{"type":"number","description":"Page back from this message id."},"thread_id":{"type":"number","description":"Read one thread instead: the first message (thread_parent_id) and its replies."}},"required":["channel_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/read_discussion": {"post":{"operationId":"tool_read_discussion","summary":"Read comments","description":"The comment threads on an item (tasks, docs, projects, database rows, CRM companies and contacts, meetings, clients, email threads, files and more). For a chat channel it returns the latest messages, for a chat message its thread.","tags":["tools"],"x-ctrl-tool":"read_discussion","x-ctrl-scopes":{"*":["ctrl-flow:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"string","description":"An item as type:id, for example task:123, doc:45, project:7, crm_company:3, sheet_row:88, meeting:12 or mail_thread:<account>-<thread>. list_items and search return these."},"limit":{"type":"number","description":"Newest threads to return (default 30)."}},"required":["item"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/read_file": {"post":{"operationId":"tool_read_file","summary":"Read a file","description":"The text of a file in Drive (text, markdown, code, csv, json, pdf, docx) plus its name, type and size. Find files with list_items types [\"file\"] or search. Images, audio and video have no text.","tags":["tools"],"x-ctrl-tool":"read_file","x-ctrl-scopes":{"*":["files:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"file":{"type":"string","description":"File id, public id, or file:<id>."},"max_chars":{"type":"number","description":"Return at most this many characters (default 20000, max 100000)."}},"required":["file"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/read_skill": {"post":{"operationId":"tool_read_skill","summary":"Read a skill","description":"The full instructions of one skill, by id or /command.","tags":["tools"],"x-ctrl-tool":"read_skill","x-ctrl-scopes":{"*":["automations:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"skill":{"type":"string","description":"Skill id or its /command."}},"required":["skill"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/recent_items": {"post":{"operationId":"tool_recent_items","summary":"Recently opened","description":"Tell the app you opened an item, so it rises in your \"recently viewed\" and \"frequent\" sorts (list_items sort viewed / frecency).\nActions:\n- record_open: Record that you opened item type + id (only items you can see).","tags":["tools"],"x-ctrl-tool":"recent_items","x-ctrl-scopes":{"record_open":["admin:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["record_open"],"description":"What to do."},"type":{"type":"string","description":"Item type: task, doc, project, form, sheet, file, ..."},"id":{"type":["string","number"]}},"required":["action","type","id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/referrals": {"post":{"operationId":"tool_referrals","summary":"Referral invite links","description":"Invite links people outside the app use to sign up into a workspace (Settings > Referrals). Owners and admins make them; anyone can turn off a link they made.\nActions:\n- list: Invite links and who joined through them (workspace_id?).\n- create: A new invite link (workspace_id?, max_uses?, ttl_hours?, recipient_email? = only that address can use it, note?, free_months? = free months of a paid plan at their first Checkout, owners/admins, up to the owner's cap). (waits for approval: external)\n- turn_off: Turn a link off (link_id).","tags":["tools"],"x-ctrl-tool":"referrals","x-ctrl-scopes":{"list":["admin:read"],"create":["admin:write"],"turn_off":["admin:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","create","turn_off"],"description":"What to do."},"workspace_id":{"type":"number"},"max_uses":{"type":"number"},"ttl_hours":{"type":"number"},"link_id":{"type":"number"},"recipient_email":{"type":"string"},"note":{"type":"string"},"free_months":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/reports": {"post":{"operationId":"tool_reports","summary":"Reports (CRM > Reports)","description":"Custom report dashboards like GHL / HubSpot custom reports: a grid of widgets over CRM and activity data, with a dashboard date range, compare-to-previous and filters (owner, pipeline, pipeline_kind leads|sales, business, tag, lead_source). Answer a numbers question by calling action \"run\" with a widget config, e.g. revenue this month: config {\"title\":\"Revenue\",\"visual\":\"number\",\"source\":\"revenue\",\"metric\":\"sum\",\"field\":\"value\"} with filters {\"range\":{\"preset\":\"this_month\"},\"compare\":true}. Config: source (deals | revenue | people | businesses | bookings = calendar calls | phone_calls | texts | emails | logged_calls | forms | tasks | workflow_runs), metric (count | sum | avg | median | min | max | unique (field) | daily_avg | rate | mrr | velocity (deals) | formula), field (number field for sum/avg/min/max, e.g. value in cents, talk_time in seconds), rate (preset: win_rate, close_rate, show_rate, no_show_rate, cancel_rate, answer_rate, connect_rate, done_rate, completion_rate) or rate_num/rate_den filter lists, group_by (\"time\" or a field such as owner, stage, pipeline, pipeline_kind, business, tag, lead_source, calendar, form, result, direction, status), interval (day | week | month | quarter), breakdown (second field: one series per value), filters [{field, op: eq|neq|in|nin|gt|gte|lt|lte|contains|empty|not_empty|is_true|is_false, value}], range override {preset: today|yesterday|this_week|last_week|this_month|last_month|this_quarter|last_quarter|ytd|last_year|last_7|last_30|last_90|last_12_months|all|custom, from, to}, visual (number | line | bar | area | donut | table | funnel | leaderboard | goal), funnel {mode: cohort (default: follows the same people; breakdown?: a step-1 field to split by; orientation?: horizontal) | steps (counts each step on its own), steps:[{label, source, metric?, filters?, time_field?}]} or {mode: pipeline, pipeline_id, pipeline_kind?: leads} (stage conversion, days in stage, lost reasons), formula = an equation {vars: {A: {source, metric?, field?, rate?, filters?, time_field?, label?} | {source: \"ad_spend\"} (the dashboard's ad spend, cents), B: ..., up to H}, expr: \"(A - B) / C * 100\" (numbers, A-H, + - * /, parentheses, unary minus; dividing by zero gives null), format?: count|number|money|duration|percent} with metric \"formula\"; works as a number, over time (per bucket) and grouped by a field every variable has (group_by); the first kind {op, a, b} still works as \"A op B\". custom_metric (a saved metric id, see metric_list) with metric \"formula\" runs that saved equation (edit it once, every widget follows; a deleted one shows a removed note), goal (target in the metric's units: cents, seconds, 0..1), currency (default: the workspace currency, crm_data save_settings currency; other currencies are listed apart, never added in), color, limit, sort, retention (churn_rate on businesses | revenue_churn | expansion | contraction | nrr on revenue: churn and NRR over the range, number or line / bar; MRR leaves out deals from their churn day). Money is in cents. Revenue = won deals' value in the period of their close date; add filter {field: \"pipeline_kind\", op: \"eq\", value: \"sales\"} for true revenue (a leads pipeline's won stage is a handoff to sales, not money). MRR = monthly value of won recurring deals won before the period ends (yearly / 12). Show rate = showed / booked calls whose time has passed. Action \"catalog\" lists every field. Results only count records the caller can see.\nActions:\n- list: Your dashboards and those shared with you, plus the templates.\n- get: One dashboard with its widgets, filters and grid positions (dashboard_id).\n- library: The metrics library: ~70 ready metrics by area (revenue, leads, calls, messaging, forms, workflows, tasks), each a widget config. Run one with run (config) or add it with add_metric.\n- add_metric: Add a library metric to a dashboard as a widget (dashboard_id, metric_key, position?).\n- catalog: Sources with their date fields, fields (type, groupable), rate presets and which dashboard filters apply. Includes what the workspace made: custom fields (cf_<id>) on people / businesses / deals, contact scores (score_<id>, score_<id>_band) on people, and one source per custom object (obj_<id>, fields of_<id>). A deleted custom field or object shows a removed note instead of numbers.\n- options: Values for filters you can use: owners, pipelines, stages, businesses, tags, lead sources, calendars, forms, workflows.\n- metric_list: Saved custom metrics: named equations (\"Cost per showed call\", \"Profit\") every dashboard can use (widget config {metric: \"formula\", custom_metric: id}).\n- metric_create: Save an equation as a named custom metric (name, formula {vars, expr, format?}). Variables can use custom fields, contact scores, custom objects and ad spend ({source: \"ad_spend\"}).\n- metric_update: Change a saved custom metric (metric_id, name?, formula?); every widget using it follows.\n- metric_delete: Delete a saved custom metric (metric_id); widgets using it show \"This metric was removed.\" (waits for approval: delete)\n- run: Run a widget config and return its numbers plus a one-line answer (config, filters?: dashboard filters {range, compare, owner, pipeline, business, tag, lead_source}, tz?).\n- run_widget: Run a saved widget under its dashboard filters (dashboard_id, widget_id, filters? to override, tz?).\n- run_dashboard: Run every widget on a dashboard (dashboard_id, filters?, tz?): one answer line per widget.\n- create: New dashboard, blank or from a template (name?, template?: sales_overview | calls | funnels | funnel | monthly_revenue, description?, shared?, workspace_id?, filters?).\n- update: Rename, describe, set dashboard filters, or share (dashboard_id, name?, description?, filters?, shared?: private | view | edit, workspace_id?). Sharing is owner only.\n- duplicate: Your own private copy of a dashboard (dashboard_id, name?).\n- delete: Delete a dashboard you own (dashboard_id). (waits for approval: delete)\n- add_widget: Add a widget (dashboard_id, config, position? {x, y, w, h} on the 12-column grid; default: the next free spot).\n- update_widget: Change a widget's config or position (dashboard_id, widget_id, config?, position?). config replaces the whole config.\n- delete_widget: Remove a widget (dashboard_id, widget_id). (waits for approval: delete)\n- set_layout: Move / resize several widgets at once (dashboard_id, positions: [{id, x, y, w, h}]).\n- list_report: One page of a list report, the Appointments / Calls / Texts / Emails / Attribution tabs of CRM > Reports (kind, range?: preset or custom with from/to (default last_30), filters?: appointments {date_field: start|booked, calendar, status: booked|confirmed|showed|no_show|cancelled|invalid|needs, host (team member id or none), source, medium, campaign, pipeline, contact (person id: their meetings), company (business id: the meetings of its people), when: upcoming|past (not cancelled) or any (with range all, bookings ahead count too: the Calendars > Bookings table)}; calls {direction: in|out, user (user id), outcome: answered|missed|voicemail|no_answer|not_returned, number, contact}; texts {direction, user, origin: manual|automated, status: delivered|sent|failed|pending|received|replied, contact}; emails (every email the app sent a person since 10-09: compose, CRM send, workflows, calendar, documents) {origin: user|automation|agent|api, user (user id), source: compose|crm|workflow|calendar|document, provider: gmail|mailjet, status: sent|delivered|opened|clicked|replied|bounced|failed|spam, contact}; attribution {source, medium, campaign ((none) = blank), booked|showed|won: yes|no}, where? the filter panel conditions: {mode: and|or, items: [{field, op, value}]} (one nested group allowed) on the fields of the kind from list_report_options (where_fields: key, type, options; ops by type: select any|none|empty|not_empty with value a list, multi also all, text is|is_not|contains|not_contains|starts_with|ends_with|empty|not_empty, number / money gt|gte|lt|lte|eq|ne|between, date last_days|next_days|older_days|before|after|on|between|today|this_week|this_month|..., bool true|false; e.g. appointments person tag, business stage, booked on, lead source), q? search, sort?, dir?, page?, page_size? (max 200), tz?). Returns rows, total, the totals tiles and, for attribution, the summary by source and by campaign (leads, booked, showed, won, won value in sales pipelines, cash collected from payments, ad spend). action list_report_options gives the filter values.\n- list_report_options: Values for the list report filters (calendars, hosts, users, pipelines, lead sources, mediums, campaigns, tags, business stages, dispositions) and where_fields: per kind, the fields the where conditions take.\n- export_list_report: The whole filtered list as CSV text (same arguments as list_report, plus columns?: list of column keys; at most 50,000 rows). Formula-like cells are prefixed with an apostrophe.","tags":["tools"],"x-ctrl-tool":"reports","x-ctrl-scopes":{"list":["reports:read"],"get":["reports:read"],"library":["reports:read"],"add_metric":["reports:write"],"catalog":["reports:read"],"options":["reports:read"],"metric_list":["reports:read"],"metric_create":["reports:write"],"metric_update":["reports:write"],"metric_delete":["reports:write"],"run":["reports:read"],"run_widget":["reports:read"],"run_dashboard":["reports:read"],"create":["reports:write"],"update":["reports:write"],"duplicate":["reports:write"],"delete":["reports:write"],"add_widget":["reports:write"],"update_widget":["reports:write"],"delete_widget":["reports:write"],"set_layout":["reports:write"],"list_report":["reports:read"],"list_report_options":["reports:read"],"export_list_report":["reports:read"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get","library","add_metric","catalog","options","metric_list","metric_create","metric_update","metric_delete","run","run_widget","run_dashboard","create","update","duplicate","delete","add_widget","update_widget","delete_widget","set_layout","list_report","list_report_options","export_list_report"],"description":"What to do."},"dashboard_id":{"type":"number"},"metric_id":{"type":"number","description":"metric_update / metric_delete: a saved custom metric id (metric_list)."},"formula":{"type":"object","description":"metric_create / metric_update: {vars: {A: {...}, B: {...}}, expr: \"A / B\", format?}."},"widget_id":{"type":"number"},"name":{"type":"string"},"description":{"type":"string"},"template":{"type":"string","enum":["sales_overview","calls","funnels","funnel","monthly_revenue"]},"shared":{"type":"string","enum":["private","view","edit"]},"workspace_id":{"type":["number","null"]},"config":{"type":"object","description":"A widget config (see the tool description)."},"filters":{"type":"object","description":"Dashboard filters: {range: {preset, from?, to?}, compare?, owner?, pipeline?, pipeline_kind?: leads|sales, business?, tag?, lead_source?}."},"position":{"type":"object","description":"{x, y, w, h} in grid steps (12 columns, rows of 40px)."},"positions":{"type":"array","items":{"type":"object"}},"metric_key":{"type":"string","description":"A key from action library (e.g. revenue, speed_to_lead, velocity, show_rate, cpl)."},"tz":{"type":"string","description":"IANA time zone for ranges and buckets (default the dashboard's, else UTC)."},"kind":{"type":"string","enum":["appointments","calls","texts","emails","attribution"],"description":"list_report / export_list_report: which list."},"range":{"type":"string","description":"list_report: date range preset (today, this_week, this_month, last_month, last_7, last_30, last_90, ytd, all, custom with from / to)."},"from":{"type":"string","description":"list_report custom range start, YYYY-MM-DD."},"to":{"type":"string","description":"list_report custom range end (inclusive), YYYY-MM-DD."},"q":{"type":"string","description":"list_report search (name, email, number, message)."},"sort":{"type":"string","description":"list_report: a column key to sort by."},"dir":{"type":"string","enum":["asc","desc"]},"page":{"type":"number"},"page_size":{"type":"number"},"columns":{"type":"array","items":{"type":"string"},"description":"export_list_report: column keys to include (default all)."},"where":{"type":["object","string"],"description":"list_report / export_list_report: the filter panel conditions {mode, items: [{field, op, value}]} (fields: list_report_options where_fields)."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/runtime_agent_skills": {"post":{"operationId":"tool_runtime_agent_skills","summary":"Runtime agent skills","description":"Skills on the runtime agents (/api/agents/skills). Owner only. Running one is not offered here: it starts a CLI on the server; use agent_runs for agent runs.\nActions:\n- list: Skills of one runtime agent (agent_id), or all.\n- create: A new skill: agent_id, name, and any of description, type, trigger_type, trigger_config, prompt, enabled, mode, visibility.\n- update: Change a skill (skill_id + fields).\n- delete: Delete a skill (skill_id). (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"runtime_agent_skills","x-ctrl-scopes":{"list":["automations:read"],"create":["automations:write"],"update":["automations:write"],"delete":["automations:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","create","update","delete"],"description":"What to do."},"skill_id":{"type":"number"},"agent_id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"type":{"type":"string"},"trigger_type":{"type":"string"},"trigger_config":{"type":"string"},"prompt":{"type":"string"},"enabled":{"type":"boolean"},"mode":{"type":"string"},"visibility":{"type":"string"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/save_file": {"post":{"operationId":"tool_save_file","summary":"Save a file to Drive","description":"Save text you wrote (a report, a CSV export, notes, code) as a new file in Drive, in a folder or a workspace root, or in the user's own space. The file name's extension sets the type (.md, .txt, .csv, .json). For a rich document the team edits, use create_doc instead.","tags":["tools"],"x-ctrl-tool":"save_file","x-ctrl-scopes":{"*":["files:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"File name with extension, for example \"October report.md\"."},"content":{"type":"string","description":"The file's text."},"folder_id":{"type":"number","description":"Drive folder id (list_items types [\"folder\"])."},"workspace_id":{"type":"number","description":"Workspace root, when no folder is given. Default: your own space."}},"required":["name","content"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/saved_views": {"post":{"operationId":"tool_saved_views","summary":"Saved views","description":"Your saved list views on Home, Drive and Search (filters, sort, grouping saved under a name). Only you see them. config is the list settings object the app saves. Home: { status: all|unread|read, types: [tasks|email|chat|notifications|files|projects|agents], group?: date|type|none, sort?: latest|oldest|unread|name }. Search: { q, scope, group?: type|date|none, sort?: relevance|updated|oldest|name }.\nActions:\n- list: Your saved views on a surface.\n- create: Save a view (surface, name, config).\n- update: Rename it, replace its config, or make it the default (is_default).\n- delete: Delete one of your saved views. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"saved_views","x-ctrl-scopes":{"list":["tasks:read"],"create":["tasks:write"],"update":["tasks:write"],"delete":["tasks:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","create","update","delete"],"description":"What to do."},"surface":{"type":"string","enum":["home","drive","search"]},"view_id":{"type":"number"},"name":{"type":"string"},"config":{"type":"object"},"is_default":{"type":"boolean"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/search": {"post":{"operationId":"tool_search","summary":"Search Plodder","description":"Search everything you can access in Plodder: tasks, projects, docs, meetings and clients. Returns ids you can pass to fetch for the full item.","tags":["tools"],"x-ctrl-tool":"search","x-ctrl-scopes":{"*":["ctrl-flow:read"]},"x-ctrl-read-only":true,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string","description":"Words to look for."},"types":{"type":"array","items":{"type":"string","enum":["task","project","doc","meeting","client"]},"description":"Limit to these kinds."},"limit":{"type":"number","description":"Max results (default 20)."}},"required":["query"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/send_email": {"post":{"operationId":"tool_send_email","summary":"Send an email","description":"Send an email or reply from your Gmail. By default this does NOT send right away: it creates an approval request in your Plodder Inbox, and the email goes out when you approve it there. Pass draft_id to send an existing draft.","tags":["tools"],"x-ctrl-tool":"send_email","x-ctrl-scopes":{"*":["messages:send"]},"x-ctrl-read-only":false,"x-ctrl-approval":"send_email","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"account_id":{"type":"number","description":"Sending account. Default: your first connected account."},"thread_id":{"type":"string","description":"Reply in this thread. Recipients and subject come from the newest message unless you pass them."},"reply_all":{"type":"boolean","description":"With thread_id: cc everyone on the last message."},"to":{"type":"array","items":{"type":"string"}},"cc":{"type":"array","items":{"type":"string"}},"bcc":{"type":"array","items":{"type":"string"}},"subject":{"type":"string"},"body":{"type":"string","description":"Plain text body."},"draft_id":{"type":"string","description":"Send this saved draft."}},"required":["body"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/set_property": {"post":{"operationId":"tool_set_property","summary":"Set a property","description":"Set (or clear with null) one property of an item, or of up to 50 items at once with items: a task's status, priority, assignee, due date or stage, a company's deal stage, a custom field. Name the property by name or key (get_properties lists them). Select values take the option label; dates take YYYY-MM-DD; people take the team member name; references take type:id.","tags":["tools"],"x-ctrl-tool":"set_property","x-ctrl-scopes":{"*":["*"]},"x-ctrl-read-only":false,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"string","description":"An item as type:id, for example task:123, doc:45, project:7, crm_company:3, sheet_row:88, meeting:12 or mail_thread:<account>-<thread>. list_items and search return these."},"items":{"type":"array","items":{"type":"string"},"description":"Several items (type:id) to set the same value on, instead of item. Max 50."},"property":{"type":"string","description":"Property name or key, for example status, Priority, Deal stage, due_date."},"value":{"description":"The new value: a string, number, boolean, a list for multi-value properties, or null to clear."}},"required":["property"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/set_tags": {"post":{"operationId":"tool_set_tags","summary":"Tag an item","description":"Add or remove tags on an item. Tags come from the item's own set (CRM tags for people, businesses, deals, conversations, bookings and forms; Work tags for everything else); new names are created in that set. Returns the item's tags after the change.","tags":["tools"],"x-ctrl-tool":"set_tags","x-ctrl-scopes":{"*":["*"]},"x-ctrl-read-only":false,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"string","description":"An item as type:id, for example task:123, doc:45, project:7, crm_company:3, sheet_row:88, meeting:12 or mail_thread:<account>-<thread>. list_items and search return these."},"add":{"type":"array","items":{"type":"string"},"description":"Tag names to add."},"remove":{"type":"array","items":{"type":"string"},"description":"Tag names to remove."}},"required":["item"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/sharing": {"post":{"operationId":"tool_sharing","summary":"Sharing","description":"The Share dialog for any item, as you: who has access, give or remove access for a teammate, an email or a whole workspace, anyone-with-the-link access (docs, databases), and share links with an expiry. item = <type>:<id> (task:12, doc:abc, project:4, ...). Levels: none, view, comment, edit, manage.\nActions:\n- get: Who has access and how.\n- set: Give or change access (principal, level; level none removes). An email principal can get an email (notify). (may wait for approval)\n- set_general: Anyone with the link (docs, databases): none, view or edit. (may wait for approval)\n- create_link: A share link (level, expires); people who open it get that level after signing in. (waits for approval: external)\n- update_link: Change a share link (link_id, level, expires, enabled). (may wait for approval)\n- disable_link: Turn a share link off (link_id).","tags":["tools"],"x-ctrl-tool":"sharing","x-ctrl-scopes":{"get":["admin:read"],"set":["admin:write"],"set_general":["admin:write"],"create_link":["admin:write"],"update_link":["admin:write"],"disable_link":["admin:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["get","set","set_general","create_link","update_link","disable_link"],"description":"What to do."},"item":{"type":"string"},"principal":{"type":"object","description":"{type: user|email|workspace, id | email}"},"level":{"type":"string"},"notify":{"type":"boolean"},"general":{"type":"string","enum":["none","view","edit"]},"link_id":{"type":"number"},"expires":{"type":"string","description":"\"7d\", an ISO date, or null for never."},"enabled":{"type":"boolean"},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/spreadsheets": {"post":{"operationId":"tool_spreadsheets","summary":"Spreadsheets","description":"Read and write spreadsheets (the grid editor, not databases), as you. Ranges are A1 notation (\"B2\", \"A1:D20\"); sheet is the tab name (default the first). Values are written like typing: numbers, \"12%\", \"$1,200\", dates, TRUE/FALSE, and \"=SUM(A1:A5)\" formulas. read returns computed values, the formulas behind them and the formatted text.\nActions:\n- list: Spreadsheets you can open, newest first.\n- get: Title, access, sheets with their used ranges, named ranges.\n- read: Cells in range (default the used range) on sheet: values, formulas, text.\n- write: Write values (rows: a 2D list) starting at cell (default A1) on sheet. Empty string clears a cell; null leaves it.\n- format: Style a range: style {b, i, u, fc, bg, fs, ha: left|center|right, nf: number format like \"#,##0.00\" or \"0%\", wrap}.\n- evaluate: Compute a formula against the workbook without saving it (formula, sheet).\n- create: A new spreadsheet (title, workspace_id or folder_id); rows fills the first sheet.\n- add_sheet: Add a sheet tab (name).\n- rename_sheet: Rename a sheet tab (sheet, name).\n- delete_sheet: Remove a sheet tab and its cells (sheet). (waits for approval: delete)\n- rename: Change the spreadsheet title.\n- trash: Move the spreadsheet to Trash. (waits for approval: delete)\n- conditional_formats: The sheet's conditional formatting rules in priority order (earlier wins), each {id, range, kind, v1, v2, style | scale}.\n- add_conditional_format: Add a conditional formatting rule on sheet: rule {range (A1), kind: gt|gte|lt|lte|eq|ne|between|notbetween|contains|notcontains|begins|ends|blank|notblank|error|top|bottom|above_avg|below_avg|dup|unique|scale, v1, v2 (between), style {bg, fc (#RRGGBB), b, i, st} or for kind scale: scale {min, mid?, max} colours}. top / bottom take v1 = a count or \"10%\".\n- charts: The sheet's charts, each {id, kind, range, title, one_axis, cell (top-left anchor), w, h}.\n- add_chart: Add a chart on sheet from range (first column = categories, first row = series names): chart {range, kind: bar|stacked|percent-stacked|percent-bar|line|area|stacked-area|percent-area|combo|pie|doughnut|scatter|radar|stock, title?, cell? (anchor, default right of the data), w?, h?, one_axis? (combo: the line shares the left axis)}. combo = columns for every series but the last, which is a line on its own right axis; percent-* = 100% stacks.\n- set_charts: Replace the sheet's charts with charts (same shape as add_chart plus id; keep ids to keep charts; [] removes them). Use it to change a chart type, range or title, or delete one.\n- dropdowns: The sheet's in-cell dropdowns (data validation lists): each {id, range, list | source, allow_other, choices}, plus invalid: cells whose entry is not a choice.\n- set_dropdown: Put an in-cell dropdown on range: list (choices, a list or \"a, b, c\") or source (a range of cells like F2:F9 or 'Lists'!A2:A20); allow_other true keeps other entries and flags them (default: refused in the editor). remove true takes dropdowns off range. A cell has one dropdown: a new one replaces what was there.\n- goal_seek: Goal seek on sheet: find the number for changing_cell (a value cell) that makes set_cell (a formula) equal to_value, then write it (one save). dry_run true only reports it.\n- set_conditional_formats: Replace the sheet's rules with rules (same shape as add; keep ids to keep rules; [] removes them all). Use it to edit, reorder or delete rules.","tags":["tools"],"x-ctrl-tool":"spreadsheets","x-ctrl-scopes":{"list":["docs:read"],"get":["docs:read"],"read":["docs:read"],"write":["docs:write"],"format":["docs:write"],"evaluate":["docs:read"],"create":["docs:write"],"add_sheet":["docs:write"],"rename_sheet":["docs:write"],"delete_sheet":["docs:write"],"rename":["docs:write"],"trash":["docs:write"],"conditional_formats":["docs:read"],"add_conditional_format":["docs:write"],"charts":["docs:read"],"add_chart":["docs:write"],"set_charts":["docs:write"],"dropdowns":["docs:read"],"set_dropdown":["docs:write"],"goal_seek":["docs:write"],"set_conditional_formats":["docs:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get","read","write","format","evaluate","create","add_sheet","rename_sheet","delete_sheet","rename","trash","conditional_formats","add_conditional_format","charts","add_chart","set_charts","dropdowns","set_dropdown","goal_seek","set_conditional_formats"],"description":"What to do."},"id":{"type":"string","description":"Spreadsheet id or public id."},"sheet":{"type":"string","description":"Sheet tab name."},"range":{"type":"string","description":"A1 range."},"cell":{"type":"string","description":"write: the top-left cell (default A1)."},"rows":{"type":"array","items":{"type":"array"},"description":"write / create: rows of cell values."},"style":{"type":"object"},"formula":{"type":"string"},"name":{"type":"string"},"title":{"type":"string"},"workspace_id":{"type":"number"},"folder_id":{"type":"number"},"rule":{"type":"object","description":"add_conditional_format: one rule."},"chart":{"type":"object","description":"add_chart: one chart."},"charts":{"type":"array","items":{"type":"object"},"description":"set_charts: every chart (images are kept)."},"list":{"description":"set_dropdown: the choices (array of strings, or one comma-separated string)."},"source":{"type":"string","description":"set_dropdown: a range holding the choices instead of list."},"allow_other":{"type":"boolean","description":"set_dropdown: keep entries that are not a choice (flagged)."},"remove":{"type":"boolean","description":"set_dropdown: remove dropdowns from range."},"set_cell":{"type":"string","description":"goal_seek: the formula cell (A1)."},"to_value":{"type":"number","description":"goal_seek: the target value for set_cell."},"changing_cell":{"type":"string","description":"goal_seek: the input cell to change (A1)."},"dry_run":{"type":"boolean","description":"goal_seek: report the answer without writing it."},"rules":{"type":"array","items":{"type":"object"},"description":"set_conditional_formats: every rule, in priority order."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/status_updates": {"post":{"operationId":"tool_status_updates","summary":"Status updates","description":"Status updates posted on a task or a project (/api/status-updates). Give task_id or project_id.\nActions:\n- list: Updates on a task or project, newest first.\n- create: Post an update (content) on a task or project.\n- delete: Delete an update (update_id). (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"status_updates","x-ctrl-scopes":{"list":["tasks:read"],"create":["tasks:write"],"delete":["tasks:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","create","delete"],"description":"What to do."},"task_id":{"type":"number"},"project_id":{"type":"number"},"content":{"type":"string"},"update_id":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/task_actions": {"post":{"operationId":"tool_task_actions","summary":"Task actions","description":"More task actions, as you: bulk changes over many tasks, copy a task, recurring series (edit or delete one occurrence or the whole series), scheduled work blocks (chunks), possible duplicates, and GitHub pull requests on a task. For one task's fields use update_task.\nActions:\n- bulk: Change many tasks at once (task_ids, bulk, value). (may wait for approval)\n- copy: Copy a task (task_id): same project, stage, assignee, priority and description, status todo.\n- recurrence: A recurring task's series (task_id, series, changes): detach and change one occurrence, edit_all, delete_one, delete_all. (may wait for approval)\n- complete_chunk: Mark one scheduled work block done or not done (task_id, chunk_start, completed).\n- lock_chunk: Move and pin a scheduled work block (task_id, old_chunk_start, new_chunk_start, new_chunk_end); the rest reschedules around it.\n- duplicates: Possible duplicates of a task (task_id).\n- dismiss_duplicate: Say two tasks are not duplicates (task_id, other_task_id).\n- pulls: GitHub pull requests linked to a task, ones that look related, and your open ones to pick from (task_id).\n- link_pull: Link a GitHub pull request to a task (task_id, url, title).\n- unlink_pull: Unlink a pull request from a task (task_id, url).","tags":["tools"],"x-ctrl-tool":"task_actions","x-ctrl-scopes":{"bulk":["tasks:write"],"copy":["tasks:write"],"recurrence":["tasks:write"],"complete_chunk":["tasks:write"],"lock_chunk":["tasks:write"],"duplicates":["tasks:read"],"dismiss_duplicate":["tasks:write"],"pulls":["tasks:read"],"link_pull":["tasks:write"],"unlink_pull":["tasks:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["bulk","copy","recurrence","complete_chunk","lock_chunk","duplicates","dismiss_duplicate","pulls","link_pull","unlink_pull"],"description":"What to do."},"task_ids":{"type":"array","items":{"type":"number"}},"bulk":{"type":"string","enum":["set_priority","set_status","set_assignee","set_project","complete","delete"],"description":"bulk: what to do to every task."},"value":{"description":"bulk: the new priority / status / assignee (team member public id or null) / project id."},"cancel_reason":{"type":"string"},"task_id":{"type":"number"},"series":{"type":"string","enum":["detach","edit_all","delete_one","delete_all"],"description":"recurrence: detach = change only this occurrence."},"changes":{"type":"object","description":"recurrence: task fields to change."},"chunk_start":{"type":"number","description":"epoch seconds of the work block"},"completed":{"type":"boolean"},"old_chunk_start":{"type":"number"},"new_chunk_start":{"type":"number"},"new_chunk_end":{"type":"number"},"other_task_id":{"type":"number"},"url":{"type":"string","description":"a GitHub pull request link"},"title":{"type":"string"},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/task_views": {"post":{"operationId":"tool_task_views","summary":"Task board views","description":"The named views on the task board (list, kanban, gantt, dashboard, workload) with their saved settings. Everyone on the board shares them, the same as in the app.\nActions:\n- list: Every task view.\n- create: A new view (name, view_type, config).\n- update: Change name, view_type, config or sort_order.\n- delete: Delete a view. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"task_views","x-ctrl-scopes":{"list":["tasks:read"],"create":["tasks:write"],"update":["tasks:write"],"delete":["tasks:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","create","update","delete"],"description":"What to do."},"view_id":{"type":"number"},"name":{"type":"string"},"view_type":{"type":"string","enum":["list","kanban","gantt","dashboard","workload"]},"config":{"type":"object"},"sort_order":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/team_directory": {"post":{"operationId":"tool_team_directory","summary":"Team directory","description":"The team roster used for assignees (/api/team). Owner only for changes. Setting a teammate's role or permissions waits for approval.\nActions:\n- list: Teammates with their workspaces.\n- create: A new teammate (name, email?, role?, workspace_ids?). With role or permissions it waits for approval. (may wait for approval)\n- update: Change a teammate (member_id, set). Changing role or permissions waits for approval. (may wait for approval)\n- delete: Remove a teammate. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"team_directory","x-ctrl-scopes":{"list":["admin:read"],"create":["admin:write"],"update":["admin:write"],"delete":["admin:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","create","update","delete"],"description":"What to do."},"member_id":{"type":"number"},"name":{"type":"string"},"email":{"type":"string"},"role":{"type":"string"},"permissions":{"description":"Permission list."},"workspace_ids":{"type":"array","items":{"type":"number"}},"set":{"type":"object"},"include_inactive":{"type":"boolean"},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/templates": {"post":{"operationId":"tool_templates","summary":"Templates","description":"Project templates, task templates and doc templates, as you: list, create, change, delete, and use them.\nActions:\n- list_project: Project templates.\n- create_project: A project template (name, description, set {stages, default_tasks, roles, text_variables} as JSON strings), or from_project_id.\n- update_project: Change a project template (template_id, set).\n- delete_project: Delete a project template (template_id). (waits for approval: delete)\n- list_task: Task templates (workspace_id optional).\n- create_task: A task template (name, set {default_title, default_priority, default_duration_minutes, default_status, description, subtasks}).\n- use_task: Create the task(s) from a task template (template_id, project_id, stage_id, workspace_id).\n- delete_task: Delete a task template (template_id). (waits for approval: delete)\n- list_doc: Doc templates you can use (built-in and your workspaces').\n- get_doc: One doc template with its blocks.\n- save_doc: Save a doc as a doc template (doc_id, name, description, set {category}).\n- update_doc: Change a doc template (template_id, set {name, description, category, icon}, or set {duplicate: true}).\n- delete_doc: Delete a doc template (template_id). (waits for approval: delete)\n- use_doc: A new doc from a doc template (template_id, title, workspace_id or project_id).","tags":["tools"],"x-ctrl-tool":"templates","x-ctrl-scopes":{"list_project":["projects:read"],"create_project":["projects:write"],"update_project":["projects:write"],"delete_project":["projects:write"],"list_task":["projects:read"],"create_task":["projects:write"],"use_task":["projects:write"],"delete_task":["projects:write"],"list_doc":["projects:read"],"get_doc":["projects:read"],"save_doc":["projects:write"],"update_doc":["projects:write"],"delete_doc":["projects:write"],"use_doc":["projects:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list_project","create_project","update_project","delete_project","list_task","create_task","use_task","delete_task","list_doc","get_doc","save_doc","update_doc","delete_doc","use_doc"],"description":"What to do."},"kind":{"type":"string","enum":["project","task","doc"]},"template_id":{"type":"number"},"name":{"type":"string"},"description":{"type":"string"},"from_project_id":{"type":"number","description":"create a project template from this project."},"doc_id":{"type":"string","description":"save this doc as a doc template."},"workspace_id":{"type":"number"},"project_id":{"type":"number"},"stage_id":{"type":"number"},"title":{"type":"string"},"set":{"type":"object","description":"update: fields (project: name, description, stages, default_tasks, roles, text_variables; doc: name, description, category, icon)."},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/time_tracking": {"post":{"operationId":"tool_time_tracking","summary":"Time tracking","description":"Plodder time tracking from the Mac collector, as you (/api/ctrl-flow/*): classify sessions and apps to businesses and projects (the review queue), log a session's time to a task, privacy rules, retention, and maintenance backfills. Pairing a new Mac happens in the app.\nActions:\n- classify: One review / attribution change (op + fields). merge_sessions and merge_entities wait for approval. (may wait for approval)\n- log_time: Log a session's business time to a task (session_id, business_id, task_id or new_task_title, project_id?, note?). reverse=true takes it back off.\n- backfill: Re-derive tracked data (backfill: default | localAiSessionIds | fleetFlags | fleetDispatch | reviewSweep | businessSeconds | reconcile; limit?, max_seconds?, days?).\n- delete_range: Delete tracked activity in a range (start + end ISO, or the last `days`). (waits for approval: delete)\n- retention: Keep `days` of tracked activity and delete what is older now. (waits for approval: delete)\n- privacy_rules: Privacy rules (what the collector never records).\n- add_privacy_rule: A privacy rule (kind, value, rule_action block|allow, default block).\n- remove_privacy_rule: Remove a privacy rule (rule_id). (waits for approval: delete)\n- devices: Paired Mac collectors.\n- revoke_device: Revoke a Mac collector (device_id); its token stops working. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"time_tracking","x-ctrl-scopes":{"classify":["tasks:write"],"log_time":["tasks:write"],"backfill":["tasks:write"],"delete_range":["tasks:write"],"retention":["tasks:write"],"privacy_rules":["tasks:read"],"add_privacy_rule":["tasks:write"],"remove_privacy_rule":["tasks:write"],"devices":["tasks:read"],"revoke_device":["tasks:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["classify","log_time","backfill","delete_range","retention","privacy_rules","add_privacy_rule","remove_privacy_rule","devices","revoke_device"],"description":"What to do."},"op":{"type":"string","enum":["label_entity","attach_entity_business","attach_entity_project","attach_session_business","set_session_business","set_session_attribution","confirm_session_topic","add_business_keywords","add_business_app_rule","remove_business_app_rule","set_business_keywords","attach_session_project","reject_entity_business","always_private_entity_business","mark_private","ignore_review","merge_sessions","merge_entities","correct_task_label","unlearn_business_keyword"],"description":"classify: which change. Its fields (sessionId, entityId, businessId, projectId, label, keywords, kind, value, reviewId, sourceSessionId, targetSessionId, sourceEntityId, targetEntityId, keyword) go in `fields`."},"session_id":{"type":"number"},"business_id":{"type":"number"},"project_id":{"type":"number"},"task_id":{"type":"number"},"new_task_title":{"type":"string"},"note":{"type":"string"},"reverse":{"type":"boolean","description":"log_time: take a logged entry back off the task."},"backfill":{"type":"string","enum":["default","localAiSessionIds","fleetFlags","fleetDispatch","reviewSweep","businessSeconds","reconcile"]},"limit":{"type":"number"},"max_seconds":{"type":"number"},"days":{"type":"number"},"start":{"type":"string","description":"ISO date-time."},"end":{"type":"string","description":"ISO date-time."},"rule_id":{"type":"number"},"kind":{"type":"string","description":"privacy rule kind (app, domain, title, ...)."},"value":{"type":"string"},"rule_action":{"type":"string","enum":["block","allow"]},"device_id":{"type":"number"},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/unlink_items": {"post":{"operationId":"tool_unlink_items","summary":"Unlink two items","description":"Remove a hand-made relation between two items (mentions in content stay until the content changes).","tags":["tools"],"x-ctrl-tool":"unlink_items","x-ctrl-scopes":{"*":["*"]},"x-ctrl-read-only":false,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"source":{"type":"string","description":"An item as type:id, for example task:123, doc:45, project:7, crm_company:3, sheet_row:88, meeting:12 or mail_thread:<account>-<thread>. list_items and search return these."},"target":{"type":"string","description":"An item as type:id, for example task:123, doc:45, project:7, crm_company:3, sheet_row:88, meeting:12 or mail_thread:<account>-<thread>. list_items and search return these."}},"required":["source","target"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/update_business": {"post":{"operationId":"tool_update_business","summary":"Update a business","description":"Update fields on a business profile. Only the fields you pass change.","tags":["tools"],"x-ctrl-tool":"update_business","x-ctrl-scopes":{"*":["crm:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"business_id":{"type":["number","string"]},"name":{"type":"string"},"industry":{"type":"string"},"status":{"type":"string"},"context":{"type":"string"},"brand_voice":{"type":"string"},"goals":{"type":"string"},"target_audience":{"type":"string"},"services":{"type":"string"},"offer":{"type":"string"},"offer_details":{"type":"string"},"location":{"type":"string"},"targeting_area":{"type":"string"},"website":{"type":"string"},"instagram_handle":{"type":"string"},"tiktok_handle":{"type":"string"},"facebook_page":{"type":"string"},"monthly_budget":{"type":"number"}},"required":["business_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/update_calendar_event": {"post":{"operationId":"tool_update_calendar_event","summary":"Update a calendar event","description":"Change an event on your Google calendar: title, time, description, location, busy or free, guests. Only the fields you pass change; a repeating event changes this one occurrence. When the event has guests (or you add some) Google emails them the change, so it waits for your approval first.","tags":["tools"],"x-ctrl-tool":"update_calendar_event","x-ctrl-scopes":{"*":["bookings:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"event_id":{"type":"string","description":"From list_calendar_events."},"calendar_id":{"type":"string","description":"From list_calendar_events."},"title":{"type":"string"},"start_time":{"type":"string","description":"ISO 8601 with offset. Pass end_time too (or the old end is kept)."},"end_time":{"type":"string"},"all_day":{"type":"boolean"},"description":{"type":"string"},"location":{"type":"string"},"busy_status":{"type":"string","enum":["busy","free"]},"guests":{"type":"array","items":{"type":"string"},"description":"The full guest list after the change (email addresses)."}},"required":["event_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/update_client": {"post":{"operationId":"tool_update_client","summary":"Update a client","description":"Update fields on a client profile. Only the fields you pass change.","tags":["tools"],"x-ctrl-tool":"update_client","x-ctrl-scopes":{"*":["crm:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":["number","string"]},"name":{"type":"string"},"industry":{"type":"string"},"status":{"type":"string"},"context":{"type":"string"},"notes":{"type":"string"},"brand_voice":{"type":"string"},"goals":{"type":"string"},"target_audience":{"type":"string"},"services":{"type":"string"},"location":{"type":"string"},"offer":{"type":"string"},"offer_details":{"type":"string"},"website":{"type":"string"},"instagram_handle":{"type":"string"},"tiktok_handle":{"type":"string"},"facebook_page":{"type":"string"},"monthly_budget":{"type":"number"}},"required":["client_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/update_database_row": {"post":{"operationId":"tool_update_database_row","summary":"Update a database row","description":"Change cells of one row. Only the columns you pass change.","tags":["tools"],"x-ctrl-tool":"update_database_row","x-ctrl-scopes":{"*":["docs:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"database":{"type":"string","description":"Database id or sheet:<id>."},"row_id":{"type":"number"},"values":{"type":"object","description":"Column name -> value."}},"required":["database","row_id","values"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/update_doc": {"post":{"operationId":"tool_update_doc","summary":"Update a doc","description":"Rename a doc, replace its whole content with new markdown, move it into a project (project_id, or null to take it out), and/or turn checklist items into tasks (checklist_to_tasks). The old version is kept in version history.","tags":["tools"],"x-ctrl-tool":"update_doc","x-ctrl-scopes":{"*":["docs:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"doc_id":{"type":["number","string"]},"title":{"type":"string"},"content":{"type":"string","description":"Markdown that replaces the body."},"project_id":{"type":["number","string","null"],"description":"Move the doc into this project (same workspace), or null to take it out of its project."},"checklist_filter":{"type":"object","description":"Set which items a checklist shows: {index (from get_doc checklists), filter: all | open | done}.","properties":{"index":{"type":"number"},"filter":{"type":"string","enum":["all","open","done"]}}},"checklist_to_tasks":{"type":"object","description":"Turn open checklist items into tasks in the doc's project (or project_id); each item becomes that task's live card in the doc. items: item texts (exact or how they start), or all: true for every open item.","properties":{"items":{"type":"array","items":{"type":"string"}},"all":{"type":"boolean"},"project_id":{"type":["number","string"]}}}},"required":["doc_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/update_email_thread": {"post":{"operationId":"tool_update_email_thread","summary":"Update an email thread","description":"Mark a Gmail thread read or unread, archive it, move it back to the inbox, star it, trash it, or add and remove labels by name (list_email_labels). Trashing waits for approval like every delete.","tags":["tools"],"x-ctrl-tool":"update_email_thread","x-ctrl-scopes":{"read":["messages:write"],"unread":["messages:write"],"archive":["messages:write"],"inbox":["messages:write"],"star":["messages:write"],"unstar":["messages:write"],"trash":["messages:write"],"untrash":["messages:write"],"spam":["messages:write"],"notspam":["messages:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"thread_id":{"type":"string"},"account_id":{"type":"number","description":"From list_email_threads."},"action":{"type":"string","enum":["read","unread","archive","inbox","star","unstar","trash","untrash","spam","notspam"]},"add_labels":{"type":"array","items":{"type":"string"},"description":"Label names (or ids) to add."},"remove_labels":{"type":"array","items":{"type":"string"},"description":"Label names (or ids) to remove."}},"required":["thread_id","account_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/update_routine": {"post":{"operationId":"tool_update_routine","summary":"Change a routine","description":"Pause or resume a routine, or change its name, instructions, schedule (cron, run_at, or schedule in words) or agent. Only the fields you pass change. Read it with get_routine first.","tags":["tools"],"x-ctrl-tool":"update_routine","x-ctrl-scopes":{"*":["automations:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"routine_id":{"type":"number"},"enabled":{"type":"boolean","description":"false pauses it, true resumes it."},"name":{"type":"string"},"instructions":{"type":"string"},"cron":{"type":"string","description":"Repeat on this 5-field cron schedule, for example \"0 9 * * 1-5\" (9am on weekdays)."},"run_at":{"type":"string","description":"Run once at this ISO date-time (with offset), then turn off. Use instead of cron."},"timezone":{"type":"string","description":"IANA zone for cron, default America/Vancouver."},"schedule":{"type":"string","description":"The schedule in words instead of cron / run_at, read in the routine's time zone: \"every weekday at 9\", \"mondays and thursdays 4pm\", \"every 2 hours\", \"monthly on the 15th\" (repeat), or \"tomorrow 3pm\", \"next fri 9am\", \"in 2h\" (once)."},"agent_id":{"type":"number"}},"required":["routine_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/update_task": {"post":{"operationId":"tool_update_task","summary":"Update a task","description":"Change a task: title, description, status, priority, assignee, dates, duration, or move it to another project. Only the fields you pass change.","tags":["tools"],"x-ctrl-tool":"update_task","x-ctrl-scopes":{"*":["tasks:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":null,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"task_id":{"type":["number","string"]},"title":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["backlog","todo","in_progress","blocked","review","done","cancelled","archived"]},"priority":{"type":"string","enum":["urgent","high","medium","low"]},"assignee":{"type":"string","description":"Team member id, user_id, email or name (list_team_members), \"ai-<agent id>\" to give it to an agent (it starts working on it), or \"none\" to unassign."},"due_date":{"type":["string","null"],"description":"YYYY-MM-DD, or null to clear."},"start_date":{"type":["string","null"]},"duration_minutes":{"type":"number"},"project_id":{"type":["number","string"],"description":"Move to this project."},"result":{"type":"string","description":"What was done. Required when marking a task created by an AI agent done (20+ characters)."}},"required":["task_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/video_calls": {"post":{"operationId":"tool_video_calls","summary":"Video calls","description":"Plodder video calls and scheduled call meetings (/api/calls*). Joining with camera and microphone happens in the app; here: start a call room, ring teammates, chat, guest links, admit guests, mute or remove people, recording, meetings, and the calls switch. Read calls with list_calls and get_call.\nActions:\n- start: Start (or join) the live call of a channel, an item, or a quick call (channel_id | entity | title), ringing user_ids. People are notified. (waits for approval: post chat)\n- ring: Ring teammates into a live call (call_id, user_ids). (waits for approval: post chat)\n- chat: A call's chat.\n- post_chat: Post in a call's chat (call_id, body). (waits for approval: post chat)\n- guest_link: A join link for people without an account (call_id). (waits for approval: external)\n- admit_guest: Let a waiting guest in, or turn them away (call_id, guest_id, admit). Hosts. (waits for approval: external)\n- participant: Mute or remove someone (call_id, identity, participant_action mute|remove). Hosts. Remove waits for approval. (may wait for approval)\n- recording: Start or stop recording (call_id, recording start|stop). Starting waits for approval. (may wait for approval)\n- meetings: Call meetings you organize or are invited to.\n- create_meeting: A scheduled call meeting (title, description?, scheduled_start/end epoch s, lobby_enabled?, invite_user_ids?, invitee_emails?, channel_id?, entity?). Inviting teammates notifies them; inviting emails waits for approval. (may wait for approval)\n- update_meeting: Change a meeting you organize (meeting_id + title, scheduled_start/end, lobby_enabled, invite_user_ids, invitee_emails, cancelled). Cancelling waits for approval. (may wait for approval)\n- join_meeting: Open a meeting from its link token (token): the call it runs in. The media join happens in the app.\n- settings: The calls switch and server (owner).\n- set_settings: Turn calls on or off (enabled), point at a server (server_url, waits for approval) or test the connection (test). LiveKit keys are entered by the owner in the app. (may wait for approval)","tags":["tools"],"x-ctrl-tool":"video_calls","x-ctrl-scopes":{"start":["calls:write"],"ring":["calls:write"],"chat":["calls:read"],"post_chat":["calls:write"],"guest_link":["calls:write"],"admit_guest":["calls:write"],"participant":["calls:write"],"recording":["calls:write"],"meetings":["calls:read"],"create_meeting":["calls:write"],"update_meeting":["calls:write"],"join_meeting":["calls:write"],"settings":["calls:read"],"set_settings":["calls:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["start","ring","chat","post_chat","guest_link","admit_guest","participant","recording","meetings","create_meeting","update_meeting","join_meeting","settings","set_settings"],"description":"What to do."},"call_id":{"type":"string","description":"The call's public id."},"channel_id":{"type":"number"},"entity":{"type":"object","description":"{type, id} of the item the call is about."},"title":{"type":"string"},"user_ids":{"type":"array","items":{"type":"number"}},"body":{"type":"string"},"guest_id":{"type":"number"},"admit":{"type":"boolean"},"identity":{"type":"string"},"participant_action":{"type":"string","enum":["mute","remove"]},"recording":{"type":"string","enum":["start","stop"]},"meeting_id":{"type":"number"},"description":{"type":"string"},"scheduled_start":{"type":"number"},"scheduled_end":{"type":"number"},"lobby_enabled":{"type":"boolean"},"invite_user_ids":{"type":"array","items":{"type":"number"}},"invitee_emails":{"type":"array","items":{"type":"string"}},"cancelled":{"type":"boolean"},"token":{"type":"string","description":"join_meeting: the meeting link token."},"enabled":{"type":"boolean"},"server_url":{"type":"string"},"test":{"type":"boolean"},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/webhooks": {"post":{"operationId":"tool_webhooks","summary":"Webhooks and API keys","description":"Outgoing webhooks that POST app events (CRM, bookings, forms, texts, email, calls, workflows, e-sign, speed to lead, tasks) to an outside URL, signed with HMAC-SHA256, retried for 6 hours, with a delivery log (Settings > Integrations, owner only). Making one, or changing its URL, events or turning it on, waits for approval. Signing secrets are shown to the person in Settings, never here. API keys: list them and revoke one (with approval); new keys are made by the person in Settings.\nActions:\n- list: Webhooks (status, failures, pending retries) and the events you can send.\n- events: Every event with its area and when it fires.\n- log: A webhook's delivery log, newest first (webhook_id, limit?): event, attempt, status code, time taken, response snippet.\n- create: A webhook (name, url, events? default all). Only public URLs. (waits for approval: external)\n- update: Change a webhook (webhook_id, set). (may wait for approval)\n- test: Post a sample event to the webhook now (webhook_id, event?). Returns the status code and response. (waits for approval: external)\n- resend: Post one delivery again now (delivery_id from the log). (waits for approval: external)\n- delete: Delete a webhook. (waits for approval: delete)\n- list_keys: API keys: name, scopes, last used (never the key).\n- revoke_key: Revoke an API key (key_id). Anything using it stops working. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"webhooks","x-ctrl-scopes":{"list":["webhooks:read"],"events":["webhooks:read"],"log":["webhooks:read"],"create":["webhooks:write"],"update":["webhooks:write"],"test":["webhooks:write"],"resend":["webhooks:write"],"delete":["webhooks:write"],"list_keys":["admin:read"],"revoke_key":["admin:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","events","log","create","update","test","resend","delete","list_keys","revoke_key"],"description":"What to do."},"webhook_id":{"type":"number"},"name":{"type":"string"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"},"description":"Event names (see events), \"family.*\" or \"*\"."},"set":{"type":"object","description":"update: name, url, events, active."},"event":{"type":"string","description":"test: the sample event to send (default webhook.test)."},"delivery_id":{"type":"number"},"key_id":{"type":"number"},"limit":{"type":"number"},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/whiteboard": {"post":{"operationId":"tool_whiteboard","summary":"Whiteboards","description":"Read and draw on whiteboards, as you. Element kinds: rect, ellipse, diamond {stroke, fill, strokeWidth, text}, sticky {fill: yellow|orange|pink|purple|blue|teal|green|gray, text}, text {text, fontSize, color}, pen {points: [[x,y,pressure]]}, arrow / line {start: {x,y,bind?: {id, anchor: center|n|e|s|w}}, end, text}, image {src: a Drive raw path}, card {ref: {type, id}} (a live CTRL item). Every element has x, y, w, h. Colors: ink, gray, red, orange, yellow, green, teal, blue, purple, pink, none.\nActions:\n- list: Whiteboards you can open, newest first.\n- get: The whiteboard: title, your access, and every element as JSON.\n- create: A new whiteboard (title, workspace_id or folder_id), filed in Drive.\n- rename: Change the title.\n- add: Add elements: a list of objects (an id is made when missing).\n- update: Change elements by id: a list of {id, ...fields to change}.\n- remove: Remove elements by id (ids). Undo is in the editor's history.\n- replace: Replace the whole whiteboard content (content = {elements: [...]}).\n- trash: Move the whiteboard to Trash. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"whiteboard","x-ctrl-scopes":{"list":["docs:read"],"get":["docs:read"],"create":["docs:write"],"rename":["docs:write"],"add":["docs:write"],"update":["docs:write"],"remove":["docs:write"],"replace":["docs:write"],"trash":["docs:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get","create","rename","add","update","remove","replace","trash"],"description":"What to do."},"id":{"type":"string","description":"whiteboard id or public id."},"title":{"type":"string"},"workspace_id":{"type":"number"},"folder_id":{"type":"number"},"elements":{"type":"array","items":{"type":"object"},"description":"add / update: the elements."},"ids":{"type":"array","items":{"type":"string"},"description":"remove: ids."},"content":{"type":"object","description":"replace: the whole document."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/word_docs": {"post":{"operationId":"tool_word_docs","summary":"Word documents","description":"Read and write Word documents (.docx), as you: docs made in Plodder (id) or .docx files in Drive (drive_file). Content is simple markdown: # / ## / ### headings, - bullets, 1. numbered lines, > quotes, **bold**, *italic*, [link text](https://...) links. Every save keeps the replaced copy as a version.\nActions:\n- list: Word docs you can open, newest first.\n- get: Title, access, where it sits, and the document text.\n- create: A new Word doc: title, content (markdown), workspace_id or folder_id.\n- append: Add paragraphs (content) at the end.\n- write: Replace the whole body with content (headers, footers and styles are kept).\n- replace_text: Replace old_text with new_text where it sits inside one run of text (all=true for every match).\n- rename: Change the title (Plodder docs).\n- move: Move in Drive: workspace_id and/or folder_id (Plodder docs).\n- versions: Earlier saved versions.\n- restore_version: Make an earlier version current (version_id); what it replaces is kept as a version.\n- trash: Move to Drive Trash (Plodder docs). (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"word_docs","x-ctrl-scopes":{"list":["docs:read"],"get":["docs:read"],"create":["docs:write"],"append":["docs:write"],"write":["docs:write"],"replace_text":["docs:write"],"rename":["docs:write"],"move":["docs:write"],"versions":["docs:read"],"restore_version":["docs:write"],"trash":["docs:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get","create","append","write","replace_text","rename","move","versions","restore_version","trash"],"description":"What to do."},"id":{"type":"string","description":"Word doc id or public id."},"drive_file":{"type":"string","description":"A .docx file id in Drive."},"title":{"type":"string"},"content":{"type":"string","description":"Markdown-style text."},"old_text":{"type":"string"},"new_text":{"type":"string"},"all":{"type":"boolean"},"version_id":{"type":"number"},"workspace_id":{"type":"number"},"folder_id":{"type":"number"},"max_chars":{"type":"number"}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/workspace_settings": {"post":{"operationId":"tool_workspace_settings","summary":"Workspace labels, statuses and fields","description":"A workspace's task labels, statuses and custom fields, team settings (members can invite, share link defaults, task codes), and the global labels (/api/workspaces/labels, statuses, custom-fields, team-settings, /api/labels).\nActions:\n- labels: Workspace labels.\n- add_label: A workspace label (name, color?).\n- update_label: Change a label (id, set {name, color, sort_order}).\n- delete_label: Delete a label (id). (waits for approval: delete)\n- statuses: Workspace statuses.\n- add_status: A workspace status (name, color?).\n- update_status: Change a status (id, set {name, color, sort_order}).\n- delete_status: Delete a status (id). (waits for approval: delete)\n- fields: Workspace custom fields.\n- add_field: A custom field (name, field_type?, options?).\n- update_field: Change a custom field (id, set).\n- delete_field: Delete a custom field (id). (waits for approval: delete)\n- set_task_field: Set a custom field on a task (task_id, field_id, value).\n- team_settings: Team settings: the team name, members can invite, the level and expiry new share links start at, the task code prefix (ENG in ENG-42), project prefixes, the GitHub autolink template.\n- set_team_settings: Change team settings (team_name?, members_can_invite?, share_link_level? view|comment|edit, share_link_expiry? 1d|7d|30d|never, task_prefix? letters, blank = default). Owners and admins. (may wait for approval)\n- set_project_prefix: A project's own task code prefix (project_id, project_prefix; blank = use the workspace prefix). Owners and admins.\n- task_code: A task's short code (ENG-42) and git branch name (task_id).\n- global_labels: Global labels.\n- add_global_label: A global label (name, color?).\n- delete_global_label: Delete a global label (id). (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"workspace_settings","x-ctrl-scopes":{"labels":["admin:read"],"add_label":["admin:write"],"update_label":["admin:write"],"delete_label":["admin:write"],"statuses":["admin:read"],"add_status":["admin:write"],"update_status":["admin:write"],"delete_status":["admin:write"],"fields":["admin:read"],"add_field":["admin:write"],"update_field":["admin:write"],"delete_field":["admin:write"],"set_task_field":["admin:write"],"team_settings":["admin:read"],"set_team_settings":["admin:write"],"set_project_prefix":["admin:write"],"task_code":["admin:read"],"global_labels":["admin:read"],"add_global_label":["admin:write"],"delete_global_label":["admin:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["labels","add_label","update_label","delete_label","statuses","add_status","update_status","delete_status","fields","add_field","update_field","delete_field","set_task_field","team_settings","set_team_settings","set_project_prefix","task_code","global_labels","add_global_label","delete_global_label"],"description":"What to do."},"workspace_id":{"type":"number"},"id":{"type":"number","description":"The label, status or field id."},"name":{"type":"string"},"color":{"type":"string"},"field_type":{"type":"string","description":"text, number, date, select, ..."},"options":{"description":"select options."},"set":{"type":"object"},"task_id":{"type":"number"},"field_id":{"type":"number"},"value":{},"team_name":{"type":"string","description":"set_team_settings: the team (workspace) name."},"members_can_invite":{"type":"boolean"},"share_link_level":{"type":"string","enum":["view","comment","edit"]},"share_link_expiry":{"type":"string","enum":["1d","7d","30d","never"]},"task_prefix":{"type":"string"},"project_id":{"type":"number"},"project_prefix":{"type":"string"},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}},
    "/api/external/tools/workspaces": {"post":{"operationId":"tool_workspaces","summary":"Workspaces and team","description":"Workspaces and who is in them, as you (/api/workspaces, /api/workspaces/[id]/roles, /invite, /primary). Team roles: owner, admin, member, guest. Changing anyone's role waits for approval.\nActions:\n- list: Your workspaces.\n- create: A new workspace (name, color?, copy_from_id?). You own it.\n- update: Change a workspace you own (workspace_id, set).\n- delete: Delete a workspace you own. (waits for approval: delete)\n- set_primary: Make a workspace you own your primary one.\n- team: The team with roles, your role and whether you can manage it.\n- add_to_team: Add an existing Plodder user to the team (email, role). Waits for approval. (waits for approval: external)\n- set_role: Change someone's role (user_id, role). Any role change (raising or lowering, yours included) waits for approval. (may wait for approval)\n- remove_from_team: Take someone off the team (user_id). (waits for approval: delete)\n- members: Workspace members (people).\n- add_member: Add an existing user as a member by email (email, role?). Owners and admins. Waits for approval. (waits for approval: external)\n- remove_member: Remove a member (user_id). Owners and admins. (waits for approval: delete)","tags":["tools"],"x-ctrl-tool":"workspaces","x-ctrl-scopes":{"list":["admin:read"],"create":["admin:write"],"update":["admin:write"],"delete":["admin:write"],"set_primary":["admin:write"],"team":["admin:read"],"add_to_team":["admin:write"],"set_role":["admin:write"],"remove_from_team":["admin:write"],"members":["admin:read"],"add_member":["admin:write"],"remove_member":["admin:write"]},"x-ctrl-read-only":false,"x-ctrl-approval":"some actions","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["list","create","update","delete","set_primary","team","add_to_team","set_role","remove_from_team","members","add_member","remove_member"],"description":"What to do."},"workspace_id":{"type":"number"},"name":{"type":"string"},"color":{"type":"string"},"copy_from_id":{"type":"number","description":"create: copy settings from this workspace."},"set":{"type":"object","description":"update: workspace fields (name, color, ...)."},"email":{"type":"string"},"role":{"type":"string","enum":["owner","admin","member","guest"]},"user_id":{"type":"number"},"fields":{"type":"object","description":"Any other body fields the action documents."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"result":{}},"required":["ok","result"],"additionalProperties":true}}}},"202":{"description":"Waiting for approval in the Inbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}}},"400":{"description":"Refused (bad input, no access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The key lacks a scope (missing_scopes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown tool"}}}}
  },
  "webhooks": {
    "contact.created": {"post":{"summary":"Person added","description":"A person is added to the CRM (app, form, import, agent, API).","tags":["CRM"],"operationId":"webhook_contact_created","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"contact.created"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "contact.updated": {"post":{"summary":"Person changed","description":"A person's fields change. data.changes has { field: { from, to } }.","tags":["CRM"],"operationId":"webhook_contact_updated","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"contact.updated"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "contact.deleted": {"post":{"summary":"Person deleted","description":"A person is deleted (they stay in Recently deleted for 30 days).","tags":["CRM"],"operationId":"webhook_contact_deleted","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"contact.deleted"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "contact.restored": {"post":{"summary":"Person restored","description":"A deleted person is restored from Recently deleted.","tags":["CRM"],"operationId":"webhook_contact_restored","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"contact.restored"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "contact.tag_added": {"post":{"summary":"Tag added to a person","description":"A tag is put on a person. data.tag names it.","tags":["CRM"],"operationId":"webhook_contact_tag_added","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"contact.tag_added"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "contact.tag_removed": {"post":{"summary":"Tag removed from a person","description":"A tag comes off a person.","tags":["CRM"],"operationId":"webhook_contact_tag_removed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"contact.tag_removed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "business.created": {"post":{"summary":"Business added","description":"A business is added to the CRM.","tags":["CRM"],"operationId":"webhook_business_created","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"business.created"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "business.updated": {"post":{"summary":"Business changed","description":"A business's fields change.","tags":["CRM"],"operationId":"webhook_business_updated","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"business.updated"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "business.stage_changed": {"post":{"summary":"Business stage changed","description":"A business moves to another stage.","tags":["CRM"],"operationId":"webhook_business_stage_changed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"business.stage_changed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "deal.created": {"post":{"summary":"Deal created","description":"A deal is created.","tags":["CRM"],"operationId":"webhook_deal_created","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"deal.created"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "deal.updated": {"post":{"summary":"Deal changed","description":"A deal's fields change.","tags":["CRM"],"operationId":"webhook_deal_updated","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"deal.updated"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "deal.stage_changed": {"post":{"summary":"Deal stage changed","description":"A deal moves to another stage or pipeline.","tags":["CRM"],"operationId":"webhook_deal_stage_changed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"deal.stage_changed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "deal.won": {"post":{"summary":"Deal won","description":"A deal is marked won.","tags":["CRM"],"operationId":"webhook_deal_won","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"deal.won"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "deal.lost": {"post":{"summary":"Deal lost","description":"A deal is marked lost.","tags":["CRM"],"operationId":"webhook_deal_lost","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"deal.lost"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "deal.status_changed": {"post":{"summary":"Deal status changed","description":"A deal turns open, won, lost or abandoned. data.status.","tags":["CRM"],"operationId":"webhook_deal_status_changed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"deal.status_changed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "conversation.response_overdue": {"post":{"summary":"Reply overdue","description":"A message from a person waited longer than the response-time target (business hours). data.contact_id, data.channel, data.waiting_since, data.target_minutes.","tags":["Conversations"],"operationId":"webhook_conversation_response_overdue","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"conversation.response_overdue"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "conversation.scheduled_sent": {"post":{"summary":"Scheduled message sent","description":"A text, email or DM scheduled from a conversation went out. data.scheduled.","tags":["Conversations"],"operationId":"webhook_conversation_scheduled_sent","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"conversation.scheduled_sent"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "email.domain_verified": {"post":{"summary":"Sending domain verified","description":"A client sending domain passed Mailjet's SPF and DKIM check. data.domain.","tags":["Email"],"operationId":"webhook_email_domain_verified","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"email.domain_verified"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "email.campaign_winner": {"post":{"summary":"A/B test winner picked","description":"An email campaign's A/B test picked its winner and sends it to everyone else. data.winner, data.results per version.","tags":["Email"],"operationId":"webhook_email_campaign_winner","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"email.campaign_winner"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "bulk_action.finished": {"post":{"summary":"Bulk action finished","description":"A bulk action on people (tag, workflow, email, text, delete) finished, stopped or was cancelled. data.bulk_action: kind, status, total, ok, skipped, failed.","tags":["CRM"],"operationId":"webhook_bulk_action_finished","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"bulk_action.finished"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "conversation.scheduled_failed": {"post":{"summary":"Scheduled message failed","description":"A scheduled message could not go (consent, STOP, Meta's 24-hour window, mailbox). data.scheduled.error.","tags":["Conversations"],"operationId":"webhook_conversation_scheduled_failed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"conversation.scheduled_failed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "deal.stale": {"post":{"summary":"Deal stale","description":"An open deal had no stage or field change for 14 days (once per stale stretch).","tags":["CRM"],"operationId":"webhook_deal_stale","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"deal.stale"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "contact.dnd_changed": {"post":{"summary":"Do not contact changed","description":"Do not email / text / call or unsubscribed turns on or off (also STOP / START). data.dnd.","tags":["CRM"],"operationId":"webhook_contact_dnd_changed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"contact.dnd_changed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "contact.score_changed": {"post":{"summary":"Contact score changed","description":"A person's score moves (a scoring rule, a workflow step, decay). data.score.from / to / delta, profile_id and profile (the score's name).","tags":["CRM"],"operationId":"webhook_contact_score_changed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"contact.score_changed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "contact.replied": {"post":{"summary":"Customer replied","description":"A person texts or emails in (any channel). data.channel.","tags":["CRM"],"operationId":"webhook_contact_replied","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"contact.replied"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "deal.deleted": {"post":{"summary":"Deal deleted","description":"A deal is deleted.","tags":["CRM"],"operationId":"webhook_deal_deleted","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"deal.deleted"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "deal.restored": {"post":{"summary":"Deal restored","description":"A deleted deal is restored from Recently deleted.","tags":["CRM"],"operationId":"webhook_deal_restored","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"deal.restored"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "deal.merged": {"post":{"summary":"Deals merged","description":"A deal is merged into another. data.merged_into is the one kept.","tags":["CRM"],"operationId":"webhook_deal_merged","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"deal.merged"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "note.added": {"post":{"summary":"Note added","description":"A note or comment is added on a person, business or deal.","tags":["CRM"],"operationId":"webhook_note_added","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"note.added"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "client.created": {"post":{"summary":"Client made","description":"A client is made (Make client on a business, or a new client).","tags":["CRM"],"operationId":"webhook_client_created","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"client.created"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "client.churned": {"post":{"summary":"Client churned","description":"A business is marked churned. data.churned_on and data.reason; its recurring deals churn with it.","tags":["CRM"],"operationId":"webhook_client_churned","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"client.churned"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "client.reactivated": {"post":{"summary":"Client reactivated","description":"A churned business is reactivated.","tags":["CRM"],"operationId":"webhook_client_reactivated","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"client.reactivated"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "deal.churned": {"post":{"summary":"Deal churned","description":"A recurring deal is marked churned (it stops counting in MRR from data.churned_on).","tags":["CRM"],"operationId":"webhook_deal_churned","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"deal.churned"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "deal.reactivated": {"post":{"summary":"Deal reactivated","description":"A churned deal is reactivated (back in MRR).","tags":["CRM"],"operationId":"webhook_deal_reactivated","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"deal.reactivated"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "lead.waiting_amber": {"post":{"summary":"Lead waiting (amber)","description":"A new lead has had no text, call or email for the amber time.","tags":["Speed to lead"],"operationId":"webhook_lead_waiting_amber","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"lead.waiting_amber"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "lead.waiting_red": {"post":{"summary":"Lead waiting (red)","description":"A new lead has had no reply for the red time.","tags":["Speed to lead"],"operationId":"webhook_lead_waiting_red","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"lead.waiting_red"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "lead.responded": {"post":{"summary":"Lead answered","description":"Someone answered a lead for the first time.","tags":["Speed to lead"],"operationId":"webhook_lead_responded","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"lead.responded"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "booking.created": {"post":{"summary":"Booking made","description":"Someone books a time on a booking calendar.","tags":["Bookings"],"operationId":"webhook_booking_created","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"booking.created"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "booking.rescheduled": {"post":{"summary":"Booking moved","description":"A booking moves to a new time.","tags":["Bookings"],"operationId":"webhook_booking_rescheduled","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"booking.rescheduled"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "booking.cancelled": {"post":{"summary":"Booking cancelled","description":"A booking is cancelled.","tags":["Bookings"],"operationId":"webhook_booking_cancelled","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"booking.cancelled"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "booking.confirmed": {"post":{"summary":"Booking confirmed","description":"A booking is confirmed (the guest's Confirm link, or by hand).","tags":["Bookings"],"operationId":"webhook_booking_confirmed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"booking.confirmed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "booking.showed": {"post":{"summary":"Showed","description":"A booking is marked showed.","tags":["Bookings"],"operationId":"webhook_booking_showed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"booking.showed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "booking.no_show": {"post":{"summary":"No-show","description":"A booking is marked no-show.","tags":["Bookings"],"operationId":"webhook_booking_no_show","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"booking.no_show"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "booking.invalid": {"post":{"summary":"Booking invalid","description":"A booking is marked invalid.","tags":["Bookings"],"operationId":"webhook_booking_invalid","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"booking.invalid"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "booking.status_changed": {"post":{"summary":"Booking status changed","description":"Any booking status change. data.from and data.status.","tags":["Bookings"],"operationId":"webhook_booking_status_changed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"booking.status_changed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "booking.series_created": {"post":{"summary":"Repeating booking made","description":"Someone books a repeating series (weekly, every 2 weeks, monthly). data.series, data.bookings (each is also a booking.created), data.skipped dates.","tags":["Bookings"],"operationId":"webhook_booking_series_created","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"booking.series_created"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "booking.calendar_sync_broken": {"post":{"summary":"Outlook / iCloud calendar stopped syncing","description":"A connected Outlook or iCloud calendar stops answering (signed out, password changed). data.connection, data.error, data.reconnect.","tags":["Bookings"],"operationId":"webhook_booking_calendar_sync_broken","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"booking.calendar_sync_broken"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "booking.series_cancelled": {"post":{"summary":"Repeating booking cancelled","description":"This and the following, or every upcoming booking of a series is cancelled at once. data.scope, data.cancelled booking ids.","tags":["Bookings"],"operationId":"webhook_booking_series_cancelled","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"booking.series_cancelled"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "form.submitted": {"post":{"summary":"Form submitted","description":"A form is submitted. data.answers holds the answers by question.","tags":["Forms"],"operationId":"webhook_form_submitted","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"form.submitted"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "form.abandoned": {"post":{"summary":"Form abandoned","description":"Someone typed an email on a public form and left it unsent for 30 minutes.","tags":["Forms"],"operationId":"webhook_form_abandoned","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"form.abandoned"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "sms.received": {"post":{"summary":"Text received","description":"A text comes in on one of your numbers.","tags":["Messages"],"operationId":"webhook_sms_received","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"sms.received"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "sms.failed": {"post":{"summary":"Text failed","description":"A text we sent failed or was not delivered. data.sms.error.","tags":["Messages"],"operationId":"webhook_sms_failed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"sms.failed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "sms.sent": {"post":{"summary":"Text sent","description":"A text goes out (you, a workflow, an agent).","tags":["Messages"],"operationId":"webhook_sms_sent","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"sms.sent"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "object.record_created": {"post":{"summary":"Object record created","description":"A record of a custom object (CRM > Objects) is made: app, import, workflow, agent or API. data.record has its fields by key.","tags":["Custom objects"],"operationId":"webhook_object_record_created","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"object.record_created"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "object.record_updated": {"post":{"summary":"Object record changed","description":"A custom object record changes. data.changes has { field key: { from, to } }.","tags":["Custom objects"],"operationId":"webhook_object_record_updated","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"object.record_updated"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "object.record_deleted": {"post":{"summary":"Object record deleted","description":"A custom object record is deleted.","tags":["Custom objects"],"operationId":"webhook_object_record_deleted","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"object.record_deleted"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "object.associated": {"post":{"summary":"Object record linked","description":"A custom object record is linked to a person, business, deal or another record. data.association.","tags":["Custom objects"],"operationId":"webhook_object_associated","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"object.associated"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "phone.number_added": {"post":{"summary":"Phone number added","description":"A Twilio number is bought or put to use in Plodder (webhooks set on it). data.number.","tags":["Messages"],"operationId":"webhook_phone_number_added","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"phone.number_added"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "phone.a2p_changed": {"post":{"summary":"US texting registration changed","description":"The A2P 10DLC registration moves stage (in review, approved, changes asked). data.registration.stage, reasons.","tags":["Messages"],"operationId":"webhook_phone_a2p_changed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"phone.a2p_changed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "phone.call_transferred": {"post":{"summary":"Call transferred","description":"A call is transferred from the call bar (cold or warm). data.transfer.mode, label.","tags":["Messages"],"operationId":"webhook_phone_call_transferred","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"phone.call_transferred"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "phone.tracking_call": {"post":{"summary":"Call on a tracking number","description":"Someone calls a tracking number (Settings > Phone > Tracking numbers). data.source names the ad source; data.website_visit is the visit when the number came from the website swap.","tags":["Messages"],"operationId":"webhook_phone_tracking_call","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"phone.tracking_call"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "email.received": {"post":{"summary":"Email received","description":"Email from a CRM person arrives in a connected mailbox.","tags":["Messages"],"operationId":"webhook_email_received","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"email.received"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "email.sent": {"post":{"summary":"Email sent","description":"The app sends an email (you, scheduled, a workflow, an agent).","tags":["Messages"],"operationId":"webhook_email_sent","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"email.sent"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "email.opened": {"post":{"summary":"Workflow email opened","description":"A workflow email is opened (first open).","tags":["Messages"],"operationId":"webhook_email_opened","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"email.opened"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "email.replied": {"post":{"summary":"Workflow email replied","description":"Someone replies to a workflow email.","tags":["Messages"],"operationId":"webhook_email_replied","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"email.replied"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "email.delivered": {"post":{"summary":"Email delivered","description":"Mailjet confirms an email reached the person's mail server.","tags":["Messages"],"operationId":"webhook_email_delivered","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"email.delivered"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "email.clicked": {"post":{"summary":"Email link clicked","description":"A person clicks a link in an email we sent (first click per link). data.email.url.","tags":["Messages"],"operationId":"webhook_email_clicked","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"email.clicked"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "email.bounced": {"post":{"summary":"Email bounced","description":"An email to a person bounces. data.email.hard is true for a hard bounce.","tags":["Messages"],"operationId":"webhook_email_bounced","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"email.bounced"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "email.unsubscribed": {"post":{"summary":"Unsubscribed from email","description":"A person unsubscribes (our link, the one-click header, or Mailjet).","tags":["Messages"],"operationId":"webhook_email_unsubscribed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"email.unsubscribed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "email.campaign_sent": {"post":{"summary":"Email campaign sent","description":"An email campaign finishes sending. data.campaign has the counts (sent, skipped, failed).","tags":["Messages"],"operationId":"webhook_email_campaign_sent","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"email.campaign_sent"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "meta.lead_received": {"post":{"summary":"Facebook lead form submitted","description":"A Meta lead ad (instant form) lead lands in the CRM. data.meta has the page, form, campaign, ad set and ad; data.form.answer the answers.","tags":["Facebook and Instagram"],"operationId":"webhook_meta_lead_received","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"meta.lead_received"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "meta.dm_received": {"post":{"summary":"Facebook / Instagram DM received","description":"A person messages your page on Messenger or your Instagram account. data.channel, data.message.","tags":["Facebook and Instagram"],"operationId":"webhook_meta_dm_received","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"meta.dm_received"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "meta.comment": {"post":{"summary":"Facebook / Instagram comment","description":"A person comments on your page's posts or your Instagram media. data.comment.","tags":["Facebook and Instagram"],"operationId":"webhook_meta_comment","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"meta.comment"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "meta.connection_broken": {"post":{"summary":"Meta connection needs attention","description":"The daily Meta check finds the token stopped working, expires within 7 days, or a page lost its token or app subscription (once per new problem). data.problems.","tags":["Facebook and Instagram"],"operationId":"webhook_meta_connection_broken","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"meta.connection_broken"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "conversation.booked": {"post":{"summary":"Agent booked a call","description":"An agent booked a time on a calendar in a conversation. data.booking_id, data.when, data.playbook.","tags":["Conversation AI"],"operationId":"webhook_conversation_booked","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"conversation.booked"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "conversation.handed_off": {"post":{"summary":"Agent handed off","description":"An agent handed a conversation to a person. data.reason, data.summary (two lines).","tags":["Conversation AI"],"operationId":"webhook_conversation_handed_off","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"conversation.handed_off"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "conversation.ended": {"post":{"summary":"Pathway ended","description":"A conversation ended. data.outcome, data.summary, data.outcomes (post-conversation fields), data.tags (pathway tags), data.variables.","tags":["Conversation AI"],"operationId":"webhook_conversation_ended","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"conversation.ended"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "conversation.qualified": {"post":{"summary":"Lead qualified","description":"The qualifying answers passed. data.answers.","tags":["Conversation AI"],"operationId":"webhook_conversation_qualified","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"conversation.qualified"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "conversation.disqualified": {"post":{"summary":"Lead disqualified","description":"The qualifying answers did not pass. data.answers.","tags":["Conversation AI"],"operationId":"webhook_conversation_disqualified","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"conversation.disqualified"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "call.started": {"post":{"summary":"Call started","description":"A phone call starts (in or out).","tags":["Calls"],"operationId":"webhook_call_started","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"call.started"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "call.completed": {"post":{"summary":"Call ended","description":"A phone call ends after it was answered. duration, direction, recording_url.","tags":["Calls"],"operationId":"webhook_call_completed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"call.completed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "call.missed": {"post":{"summary":"Call missed","description":"A call ends unanswered (missed, busy, no answer, voicemail).","tags":["Calls"],"operationId":"webhook_call_missed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"call.missed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "call.logged": {"post":{"summary":"Call logged","description":"A call is logged by hand on a CRM record.","tags":["Calls"],"operationId":"webhook_call_logged","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"call.logged"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "workflow.enrolled": {"post":{"summary":"Entered a workflow","description":"A person enters a workflow.","tags":["Automations"],"operationId":"webhook_workflow_enrolled","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"workflow.enrolled"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "workflow.completed": {"post":{"summary":"Finished a workflow","description":"A person reaches the end of a workflow.","tags":["Automations"],"operationId":"webhook_workflow_completed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"workflow.completed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "workflow.failed": {"post":{"summary":"Workflow step failed","description":"A workflow stops on an error.","tags":["Automations"],"operationId":"webhook_workflow_failed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"workflow.failed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "workflow.goal_reached": {"post":{"summary":"Workflow goal reached","description":"A person meets a workflow goal.","tags":["Automations"],"operationId":"webhook_workflow_goal_reached","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"workflow.goal_reached"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "signature.requested": {"post":{"summary":"Signature requested","description":"A document is sent for signature (one event per signer).","tags":["E-sign"],"operationId":"webhook_signature_requested","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"signature.requested"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "signature.signed": {"post":{"summary":"Signed","description":"A signer signs.","tags":["E-sign"],"operationId":"webhook_signature_signed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"signature.signed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "signature.declined": {"post":{"summary":"Declined","description":"A signer declines (the envelope closes).","tags":["E-sign"],"operationId":"webhook_signature_declined","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"signature.declined"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "signature.completed": {"post":{"summary":"All signed","description":"Every signer has signed; the signed copy is ready.","tags":["E-sign"],"operationId":"webhook_signature_completed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"signature.completed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "payment.received": {"post":{"summary":"Payment received","description":"A client pays (payment link, invoice, subscription invoice, booking, document). data.payment.","tags":["Payments"],"operationId":"webhook_payment_received","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"payment.received"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "payment.failed": {"post":{"summary":"Payment failed","description":"A payment attempt fails (card declined, bank debit failed, renewal failed).","tags":["Payments"],"operationId":"webhook_payment_failed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"payment.failed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "invoice.paid": {"post":{"summary":"Invoice paid","description":"A Stripe invoice is paid (one-off or a subscription's).","tags":["Payments"],"operationId":"webhook_invoice_paid","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"invoice.paid"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "invoice.overdue": {"post":{"summary":"Invoice overdue","description":"An open invoice passes its due date (once per invoice).","tags":["Payments"],"operationId":"webhook_invoice_overdue","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"invoice.overdue"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "subscription.started": {"post":{"summary":"Subscription started","description":"A client starts a retainer / subscription.","tags":["Payments"],"operationId":"webhook_subscription_started","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"subscription.started"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "subscription.cancelled": {"post":{"summary":"Subscription cancelled","description":"A subscription ends.","tags":["Payments"],"operationId":"webhook_subscription_cancelled","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"subscription.cancelled"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "subscription.renewed": {"post":{"summary":"Subscription renewed","description":"A subscription renewal invoice is paid.","tags":["Payments"],"operationId":"webhook_subscription_renewed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"subscription.renewed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "refund.issued": {"post":{"summary":"Refund issued","description":"A payment is refunded, fully or in part. data.payment.refund_cents.","tags":["Payments"],"operationId":"webhook_refund_issued","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"refund.issued"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "commission.pending": {"post":{"summary":"Commission pending payment","description":"A deal is won or a payment is made but not paid yet: the matched closer / setter get a pending line (expected, not payable). data.commission.status = pending.","tags":["Commissions"],"operationId":"webhook_commission_pending","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"commission.pending"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "commission.earned": {"post":{"summary":"Commission earned","description":"A closer or setter earns a commission line (a payment received or a deal won), or a refund claws one back (negative). data.commission.status, .provider, .payment_ref, .attribution.","tags":["Commissions"],"operationId":"webhook_commission_earned","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"commission.earned"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "commission.refunded": {"post":{"summary":"Commission refunded","description":"A refund claws a commission back (negative line in the refund's month; the payment's line is marked Refunded / Part refunded). data.commission.","tags":["Commissions"],"operationId":"webhook_commission_refunded","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"commission.refunded"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "commission.statement_finalized": {"post":{"summary":"Commission month finalized","description":"A payee's month is finalized (its lines are locked). data.statement.","tags":["Commissions"],"operationId":"webhook_commission_statement_finalized","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"commission.statement_finalized"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "commission.statement_sent": {"post":{"summary":"Commission bill sent to QuickBooks","description":"A finalized month becomes a bill in QuickBooks Online. data.statement.qbo_bill_id.","tags":["Commissions"],"operationId":"webhook_commission_statement_sent","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"commission.statement_sent"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "commission.statement_paid": {"post":{"summary":"Commission paid","description":"QuickBooks shows the payee's bill paid (once per statement). data.statement.","tags":["Commissions"],"operationId":"webhook_commission_statement_paid","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"commission.statement_paid"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "instantly.reply_received": {"post":{"summary":"Instantly reply received","description":"A lead replied to an Instantly campaign email. data.instantly.reply_text when Instantly sends it.","tags":["Instantly"],"operationId":"webhook_instantly_reply_received","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"instantly.reply_received"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "instantly.lead_interested": {"post":{"summary":"Instantly lead interested","description":"A lead is marked interested in Instantly (once per person per campaign). data.instantly.deal_id = the lead deal made.","tags":["Instantly"],"operationId":"webhook_instantly_lead_interested","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"instantly.lead_interested"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "instantly.lead_not_interested": {"post":{"summary":"Instantly lead not interested","description":"A lead is marked not interested in Instantly.","tags":["Instantly"],"operationId":"webhook_instantly_lead_not_interested","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"instantly.lead_not_interested"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "instantly.meeting_booked": {"post":{"summary":"Instantly meeting booked","description":"A lead is marked meeting booked in Instantly.","tags":["Instantly"],"operationId":"webhook_instantly_meeting_booked","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"instantly.meeting_booked"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "instantly.out_of_office": {"post":{"summary":"Instantly out of office","description":"An auto reply or out of office from a lead.","tags":["Instantly"],"operationId":"webhook_instantly_out_of_office","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"instantly.out_of_office"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "instantly.unsubscribed": {"post":{"summary":"Instantly unsubscribed","description":"A lead unsubscribed in Instantly (the address is suppressed here too).","tags":["Instantly"],"operationId":"webhook_instantly_unsubscribed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"instantly.unsubscribed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "instantly.bounced": {"post":{"summary":"Instantly bounced","description":"An Instantly email bounced (the person is marked Do not email).","tags":["Instantly"],"operationId":"webhook_instantly_bounced","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"instantly.bounced"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "instantly.email_sent": {"post":{"summary":"Instantly email sent","description":"Instantly sent a campaign email to a person in the CRM.","tags":["Instantly"],"operationId":"webhook_instantly_email_sent","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"instantly.email_sent"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "instantly.email_opened": {"post":{"summary":"Instantly email opened","description":"A person opened an Instantly campaign email.","tags":["Instantly"],"operationId":"webhook_instantly_email_opened","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"instantly.email_opened"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "instantly.link_clicked": {"post":{"summary":"Instantly link clicked","description":"A person clicked a tracked link in an Instantly campaign email.","tags":["Instantly"],"operationId":"webhook_instantly_link_clicked","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"instantly.link_clicked"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "instantly.lead_status": {"post":{"summary":"Instantly lead status","description":"Any other Instantly lead label (neutral, closed, wrong person, a custom label) or campaign event. data.instantly.event_type.","tags":["Instantly"],"operationId":"webhook_instantly_lead_status","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"instantly.lead_status"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "task.created": {"post":{"summary":"Task created","description":"A task is created.","tags":["Work"],"operationId":"webhook_task_created","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"task.created"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "task.updated": {"post":{"summary":"Task changed","description":"A task changes.","tags":["Work"],"operationId":"webhook_task_updated","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"task.updated"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "task.completed": {"post":{"summary":"Task done","description":"A task is marked done.","tags":["Work"],"operationId":"webhook_task_completed","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"task.completed"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "task.reminder": {"post":{"summary":"Task reminder","description":"A task on a CRM record reaches its reminder time.","tags":["Work"],"operationId":"webhook_task_reminder","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"task.reminder"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "task.deleted": {"post":{"summary":"Task deleted","description":"A task is deleted.","tags":["Work"],"operationId":"webhook_task_deleted","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"task.deleted"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "task.agent_started": {"post":{"summary":"Agent started work","description":"An agent starts work it was assigned (a task, project or doc given to it). data.work, data.agent, data.assigned_by.","tags":["Work"],"operationId":"webhook_task_agent_started","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"task.agent_started"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "task.agent_finished": {"post":{"summary":"Agent finished work","description":"An agent finishes assigned work and hands it back. data.work, data.agent, data.outcome (review | done), data.summary.","tags":["Work"],"operationId":"webhook_task_agent_finished","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"task.agent_finished"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "project.created": {"post":{"summary":"Project created","description":"A project is created.","tags":["Work"],"operationId":"webhook_project_created","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"project.created"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "doc.created": {"post":{"summary":"Doc created","description":"A doc is created.","tags":["Work"],"operationId":"webhook_doc_created","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"doc.created"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "doc.updated": {"post":{"summary":"Doc changed","description":"A doc is edited.","tags":["Work"],"operationId":"webhook_doc_updated","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"doc.updated"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}},
    "webhook.test": {"post":{"summary":"Test event","description":"Send test event in Settings, or the webhooks tool action test.","tags":["Webhooks"],"operationId":"webhook_webhook_test","parameters":[{"name":"X-CtrlFlow-Event","in":"header","schema":{"const":"webhook.test"}},{"name":"X-CtrlFlow-Delivery","in":"header","schema":{"type":"string"}},{"name":"X-CtrlFlow-Signature","in":"header","schema":{"type":"string","description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"2XX":{"description":"Received. Anything else is retried after 1 m, 5 m, 30 m, 2 h, 6 h."}}}}
  },
  "components": { "securitySchemes": {"bearer":{"type":"http","scheme":"bearer","description":"ctrl_... API key"}}, "schemas": {
    "ApiError": {"type":"object","properties":{"error":{"type":"string"},"detail":{"type":"string"},"missing_scopes":{"type":"array","items":{"type":"string"}}},"required":["error"],"additionalProperties":true},
    "Task": {"type":"object","properties":{"id":{"type":"integer"},"public_id":{"type":"string"},"title":{"type":"string"},"description":{"type":["string","null"]},"status":{"type":"string","description":"backlog, todo, in_progress, review, done, blocked, cancelled, archived"},"priority":{"type":"string","description":"urgent, high, medium, low"},"assignee":{"type":["string","null"]},"client":{"type":["string","null"]},"due_date":{"type":["string","null"]},"start_date":{"type":["string","null"]},"duration_minutes":{"type":"integer"},"workspace_id":{"type":["integer","null"]},"project_id":{"type":["integer","null"]},"stage_id":{"type":["integer","null"]},"labels":{"type":["string","null"]},"blocked_by":{"type":["string","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"completed_at":{"type":["integer","null"]}},"required":["id","title","status"],"additionalProperties":true},
    "TaskList": {"type":"object","properties":{"tasks":{"type":"array","items":{"$ref":"#/components/schemas/Task"}},"total":{"type":"integer"},"returned":{"type":"integer"},"offset":{"type":"integer"},"limit":{"type":["integer","null"]},"has_more":{"type":"boolean"}},"required":["tasks","total","offset","has_more"],"additionalProperties":false},
    "TaskInput": {"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"status":{"type":"string"},"priority":{"type":"string"},"assignee":{"type":"string","description":"Team id, login id (u_12 or 12), email or name."},"due_date":{"type":"string","description":"YYYY-MM-DD"},"start_date":{"type":"string"},"duration_minutes":{"type":"integer"},"workspace_id":{"type":["integer","string"],"description":"Numeric id or public id."},"project_id":{"type":["integer","string"],"description":"Numeric id or public id."},"stage_id":{"type":["integer","string"],"description":"Numeric id or public id."},"labels":{"type":"string"},"blocked_by":{"type":"string"},"agent":{"type":"string","description":"The agent creating it (marks the task as agent work)."},"needs_approval":{"type":"boolean"},"auto_schedule":{"type":"boolean"},"force_new":{"type":"boolean","description":"Create even when an open task with the same title exists."}},"additionalProperties":true},
    "TaskActivity": {"type":"object","properties":{"id":{"type":"integer"},"task_id":{"type":"integer"},"agent_id":{"type":["string","null"]},"activity_type":{"type":"string"},"message":{"type":"string"},"created_at":{"type":"integer"}},"required":["id","task_id","message"],"additionalProperties":true},
    "Project": {"type":"object","properties":{"id":{"type":"integer"},"public_id":{"type":"string"},"workspace_id":{"type":"integer"},"folder_id":{"type":["integer","null"]},"name":{"type":"string"},"description":{"type":["string","null"]},"color":{"type":"string"},"status":{"type":"string"},"archived":{"type":"integer"}},"required":["id","name"],"additionalProperties":true},
    "Stage": {"type":"object","properties":{"id":{"type":"integer"},"project_id":{"type":"integer"},"name":{"type":"string"},"color":{"type":"string"},"sort_order":{"type":"integer"}},"required":["id","project_id","name"],"additionalProperties":true},
    "Folder": {"type":"object","properties":{"id":{"type":"integer"},"workspace_id":{"type":"integer"},"parent_id":{"type":["integer","null"]},"name":{"type":"string"},"color":{"type":"string"},"sort_order":{"type":"integer"}},"required":["id","name"],"additionalProperties":true},
    "Doc": {"type":"object","properties":{"id":{"type":"integer"},"public_id":{"type":"string"},"workspace_id":{"type":["integer","null"]},"folder_id":{"type":["integer","null"]},"project_id":{"type":["integer","null"]},"title":{"type":"string"},"content":{"type":"string","description":"Markdown or block JSON."},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","title"],"additionalProperties":true},
    "MeetingNote": {"type":"object","properties":{"id":{"type":"integer"},"title":{"type":"string"},"source":{"type":"string"},"transcript":{"type":["string","null"]},"summary":{"type":["string","null"]},"action_items":{"type":"string","description":"JSON list."},"attendees":{"type":"string","description":"JSON list."},"meeting_date":{"type":["string","null"]},"created_at":{"type":"integer"}},"required":["id","title"],"additionalProperties":true},
    "CalendarEvent": {"type":"object","properties":{"id":{},"title":{"type":"string"},"start":{"type":"string"},"end":{"type":"string"},"all_day":{"type":"boolean"},"calendar_id":{}},"additionalProperties":true},
    "FreeSlot": {"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"},"duration_minutes":{"type":"integer"}},"required":["start","end","duration_minutes"],"additionalProperties":true},
    "User": {"type":"object","properties":{"id":{"type":"string","description":"Team id (what the app stores)."},"user_id":{"type":["integer","null"],"description":"Numeric login id; null when they do not log in."},"name":{"type":"string"},"email":{"type":["string","null"]},"role":{"type":"string","description":"owner, team, client"},"type":{"type":"string","description":"human, agent"},"active":{"type":"boolean"}},"required":["id","name","role","type","active"],"additionalProperties":false},
    "Workspace": {"type":"object","properties":{"id":{"type":"integer"},"public_id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"is_primary":{"type":"integer"}},"required":["id","name"],"additionalProperties":true},
    "ToolInfo": {"type":"object","properties":{"name":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"input_schema":{"type":"object","properties":{},"additionalProperties":true},"read_only":{"type":"boolean"},"approval":{"type":["string","null"]}},"required":["name","input_schema","read_only"],"additionalProperties":false},
    "PendingApproval": {"type":"object","properties":{"ok":{"const":true},"status":{"const":"pending_approval"},"approval_id":{},"approve_url":{"type":"string"},"message":{"type":"string"}},"required":["status","approval_id"],"additionalProperties":true},
    "WebhookEnvelope": {"type":"object","properties":{"id":{"type":"string","description":"Same id on retries and resends: dedupe on it."},"event":{"type":"string"},"created_at":{"type":"string","description":"ISO time."},"test":{"type":"boolean"},"workspace":{"type":"object","properties":{"id":{"type":["integer","null"]}},"additionalProperties":true},"owner":{"type":"object","properties":{"id":{"type":["integer","null"]}},"additionalProperties":true},"data":{"type":"object","properties":{},"additionalProperties":true}},"required":["id","event","created_at","data"],"additionalProperties":true}
  } }
}
