Build on Plodder
Everything you can do in Plodder, you can do over HTTP: create tasks, update deals, write docs, book meetings, and run any of the 190 tools Plodder's own agents use. Plodder can also tell your server when things change, with 127 signed webhook events.
https://app.useplodder.comAuthentication
Make a key in Plodder under Settings, Integrations and API, API keys. Pick the scopes it needs. Keys start with ctrl_ and are shown once, so store them somewhere safe.
Send the key in the Authorization header on every request:
curl https://app.useplodder.com/api/external/tasks?limit=5 \
-H "Authorization: Bearer ctrl_your_key"
curl -X POST https://app.useplodder.com/api/external/tasks \
-H "Authorization: Bearer ctrl_your_key" \
-H "Content-Type: application/json" \
-d '{"title": "Call Jordan back", "due_date": "2026-11-12"}'
A key acts as the person who made it and only reaches what that person can see.
Scopes
Every route and tool checks the key's scopes. Write includes read.
tasks:read tasks:write | Tasks, views, schedules, comments |
projects:read projects:write | Projects, stages, folders, templates |
docs:read docs:write | Docs, sheets, slides, designs, discussions, properties |
meetings:read meetings:write | Meeting notes and transcripts |
crm:read crm:write | People, businesses, deals, notes, clients |
bookings:read bookings:write | Calendar events, booking calendars, bookings |
forms:read forms:write | Forms and responses |
messages:read messages:write | Email, texts, chat (reading and organizing) |
calls:read calls:write | Phone calls, recordings, video calls |
automations:read automations:write | Workflows, routines, agents |
reports:read reports:write | Dashboards and activity |
files:read files:write | Drive, uploads, imports |
esign:read esign:write | PDF marks, signature requests, documents and contract templates |
payments:read payments:write | Payment links, invoices, subscriptions, refunds, recorded payments |
commissions:read commissions:write | Sales team payees, commission lines, monthly statements |
instantly:read instantly:write | Cold email campaigns |
webhooks:read webhooks:write | Outgoing webhooks and their delivery log |
users:read users:write | The people who log in and the team roster (never passwords or sessions) |
admin:read admin:write | Workspaces, team, settings, everything else |
messages:send | Send texts and email |
* | Full access |
Errors and approvals
| Status | Meaning |
|---|---|
401 | No key, or the key is wrong or revoked. |
403 | The key is missing a scope. The body lists them in missing_scopes. |
400 | Bad input, or no access to that record. A name that matches more than one person is refused here too: pass an id or email instead. |
202 | The action waits for a person to approve it in the Plodder Inbox (deleting, sending and money). The body has approval_id and approve_url. |
{ "error": "Missing scope", "missing_scopes": ["crm:write"] }REST API
The core records have plain routes. Anything else is reachable through the agent tools below.
Tasks
/api/external/taskstasks:readList tasks (filters, offset/limit paging), or one task with ?id.
Query parameters
| Field | Type | Notes |
|---|---|---|
id | integer or string | Numeric id or public id. |
workspace_id | integer or string | Numeric id or public id. |
project_id | integer or string | Numeric id or public id. |
assignee | string | Team id, login id, email or a name fragment. |
status | string | |
priority | string | |
include_completed | string | "false" hides done, cancelled and archived. |
limit | integer | |
offset | integer |
/api/external/taskstasks:writeCreate a task.
Body (JSON)
| Field | Type | Notes |
|---|---|---|
title required | string | |
description | string | |
status | string | |
priority | string | |
assignee | string | Team id, login id (u_12 or 12), email or name. |
due_date | string | YYYY-MM-DD |
start_date | string | |
duration_minutes | integer | |
workspace_id | integer or string | Numeric id or public id. |
project_id | integer or string | Numeric id or public id. |
stage_id | integer or string | Numeric id or public id. |
labels | string | |
blocked_by | string | |
agent | string | The agent creating it (marks the task as agent work). |
needs_approval | boolean | |
auto_schedule | boolean | |
force_new | boolean | Create even when an open task with the same title exists. |
/api/external/taskstasks:writeUpdate a task (id in the body).
Body (JSON)
| Field | Type | Notes |
|---|---|---|
title | string | |
description | string | |
status | string | |
priority | string | |
assignee | string | Team id, login id (u_12 or 12), email or name. |
due_date | string | YYYY-MM-DD |
start_date | string | |
duration_minutes | integer | |
workspace_id | integer or string | Numeric id or public id. |
project_id | integer or string | Numeric id or public id. |
stage_id | integer or string | Numeric id or public id. |
labels | string | |
blocked_by | string | |
agent | string | The agent creating it (marks the task as agent work). |
needs_approval | boolean | |
auto_schedule | boolean | |
force_new | boolean | Create even when an open task with the same title exists. |
id required | integer or string | Numeric id or public id. |
/api/external/taskstasks:writeDelete a task (soft).
Query parameters
| Field | Type | Notes |
|---|---|---|
id required | integer or string | Numeric id or public id. |
Activities
/api/external/activitiestasks:readA task's comment thread (task_id), or many (task_ids=1,2,3).
Query parameters
| Field | Type | Notes |
|---|---|---|
task_id | integer or string | Numeric id or public id. |
task_ids | string | Comma separated ids. |
/api/external/activitiestasks:writeComment on a task.
Body (JSON)
| Field | Type | Notes |
|---|---|---|
task_id required | integer or string | Numeric id or public id. |
message required | string | |
agent | string | |
activity_type | string |
Projects
/api/external/projectsprojects:readList projects, or one with ?id (detail=true adds stages, tasks, progress).
Query parameters
| Field | Type | Notes |
|---|---|---|
id | integer or string | Numeric id or public id. |
workspace_id | integer or string | Numeric id or public id. |
detail | string | "true" |
/api/external/projectsprojects:writeCreate a project.
Body (JSON)
| Field | Type | Notes |
|---|---|---|
name required | string | |
workspace_id | integer or string | Numeric id or public id. |
folder_id | integer or string | Numeric id or public id. |
color | string | |
business_id | integer |
/api/external/projectsprojects:writeUpdate a project.
Body (JSON)
| Field | Type | Notes |
|---|---|---|
id required | integer or string | Numeric id or public id. |
name | string | |
color | string | |
status | string | |
description | string |
/api/external/projectsprojects:writeSoft delete a project and its tasks and stages.
Query parameters
| Field | Type | Notes |
|---|---|---|
id required | integer or string | Numeric id or public id. |
Stages
/api/external/stagesprojects:readA project's stages.
Query parameters
| Field | Type | Notes |
|---|---|---|
project_id required | integer or string | Numeric id or public id. |
/api/external/stagesprojects:writeCreate a stage.
Body (JSON)
| Field | Type | Notes |
|---|---|---|
project_id required | integer or string | Numeric id or public id. |
name required | string | |
color | string |
/api/external/stagesprojects:writeUpdate a stage.
Body (JSON)
| Field | Type | Notes |
|---|---|---|
id required | integer or string | Numeric id or public id. |
name | string | |
color | string | |
sort_order | integer |
/api/external/stagesprojects:writeDelete a stage.
Query parameters
| Field | Type | Notes |
|---|---|---|
id required | integer or string | Numeric id or public id. |
Folders
/api/external/foldersprojects:readList folders.
Query parameters
| Field | Type | Notes |
|---|---|---|
workspace_id | integer or string | Numeric id or public id. |
name | string |
/api/external/foldersprojects:writeCreate a folder (returns the existing one with the same name).
Body (JSON)
| Field | Type | Notes |
|---|---|---|
name required | string | |
workspace_id | integer or string | Numeric id or public id. |
color | string | |
parent_id | integer or string | Numeric id or public id. |
/api/external/foldersprojects:writeUpdate a folder.
Body (JSON)
| Field | Type | Notes |
|---|---|---|
id required | integer or string | Numeric id or public id. |
name | string | |
color | string | |
parent_id | string or integer or null | |
sort_order | integer |
/api/external/foldersprojects:writeDelete a folder.
Query parameters
| Field | Type | Notes |
|---|---|---|
id required | integer or string | Numeric id or public id. |
Docs
/api/external/docsdocs:readList docs, or one with ?id.
Query parameters
| Field | Type | Notes |
|---|---|---|
id | integer or string | Numeric id or public id. |
workspace_id | integer or string | Numeric id or public id. |
project_id | integer or string | Numeric id or public id. |
search | string |
/api/external/docsdocs:writeCreate 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.
Body (JSON)
| Field | Type | Notes |
|---|---|---|
title required | string | |
content | string | |
workspace_id | integer | |
project_id | integer | |
folder_id | integer |
/api/external/docsdocs:writeUpdate a doc. Delete is not offered on the API (docs delete for good).
Body (JSON)
| Field | Type | Notes |
|---|---|---|
id required | integer or string | Numeric id or public id. |
title | string | |
content | string |
Meetings
/api/external/meetingsmeetings:readList meeting notes, or one with ?id.
Query parameters
| Field | Type | Notes |
|---|---|---|
id | string | |
limit | integer |
/api/external/meetingsmeetings:writeImport a meeting (Plaud, Otter, Zapier, anything).
Body (JSON)
| Field | Type | Notes |
|---|---|---|
title | string | |
transcript | string | |
summary | string | |
action_items | any | |
attendees | any | |
duration_seconds | integer | |
meeting_date | string | |
source | string | |
client_id | integer | |
workspace_id | integer |
/api/external/meetingsmeetings:writeUpdate a meeting note (id plus any fields).
Body (JSON)
| Field | Type | Notes |
|---|---|---|
id required | integer | |
title | string | |
transcript | string | |
summary | string | |
action_items | any | |
attendees | any | |
meeting_date | string |
/api/external/meetingsmeetings:writeDelete a meeting note for good (no undo).
Query parameters
| Field | Type | Notes |
|---|---|---|
id required | string |
Calendar
/api/external/calendarcalendar:readEvents in a range (start, end), free slots on a day (mode=free-slots) or the agenda (mode=agenda).
Query parameters
| Field | Type | Notes |
|---|---|---|
mode | events | free-slots | agenda | |
start | string | |
end | string | |
calendarId | string | |
date | string | YYYY-MM-DD |
duration_minutes | integer | |
work_start | string | |
work_end | string |
Users
/api/external/usersusers:readThe people who log in and the team roster.
Query parameters
| Field | Type | Notes |
|---|---|---|
email | string | Exact, any case. |
q | string | |
include | string | clients, agents, inactive (comma separated; clients needs crm:read). |
/api/external/users/{id}users:readOne person by id, u_<login id>, login id or email.
Query parameters
| Field | Type | Notes |
|---|---|---|
id required | string | |
include | string |
Workspaces
/api/external/workspacesworkspaces:readWorkspaces with counts.
Tools
/api/external/toolsper toolThe agent tools this key's scopes reach.
Agent tools
The same 190 tools Plodder's agents use, one route each. Send the arguments as a JSON body; the answer is {"ok": true, "result": ...}. GET /api/external/tools lists the ones your key's scopes reach.
curl -X POST https://app.useplodder.com/api/external/tools/add_task_comment \
-H "Authorization: Bearer ctrl_your_key" \
-H "Content-Type: application/json" \
-d '{"task_id": 4412, "comment": "Sent the proposal."}'
Tasks 14
add_task_commentComment on a task
POST/api/external/tools/add_task_comment
Scopes: tasks:write
Add a comment to a task thread. It shows as written through your AI client, not as you typing it.
| Field | Type | Notes |
|---|---|---|
task_id required | number or string | |
comment required | string |
agenda_snapshotsAgenda snapshotsasks for approval
POST/api/external/tools/agenda_snapshots
Scopes: tasks:read, tasks:write
Daily snapshots of every task with its status (/api/agendas). Owner only: snapshots are one table by date. Actions: - list: Dates that have a snapshot. - get: The snapshot for a date (YYYY-MM-DD). - save: Save today's snapshot from your workspaces' tasks (replaces today's).
| Field | Type | Notes |
|---|---|---|
action required | list | get | save | What to do. |
date | string | YYYY-MM-DD |
auto_scheduleAuto-schedulingasks for approval
POST/api/external/tools/auto_schedule
Scopes: tasks:read, tasks:write
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). Actions: - list_schedules: Every work-hours schedule. - create_schedule: A new schedule (name, blocks, color, is_default). - update_schedule: Change a schedule (schedule_id, name, blocks, color, is_default). - delete_schedule: Delete a schedule. (waits for approval: delete) - get_flexible_hours: The flexible hours set for one date (YYYY-MM-DD). - set_flexible_hours: Different hours for one date (date, blocks). - clear_flexible_hours: Back to the normal schedule for that date. (waits for approval: delete) - reschedule: Run the auto-scheduler now (repacks auto-scheduled tasks around the calendar). - toggle_task_timer: Start the timer on a task (status in progress), or stop it when it is running.
| Field | Type | Notes |
|---|---|---|
action required | list_schedules | create_schedule | update_schedule | delete_schedule | get_flexi | What to do. |
schedule_id | number | |
name | string | |
blocks | array of object | |
color | string | |
is_default | boolean | |
date | string | YYYY-MM-DD. |
task_id | number or string |
complete_taskComplete a task
POST/api/external/tools/complete_task
Scopes: tasks:write
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.
| Field | Type | Notes |
|---|---|---|
task_id required | number or string | |
result | string | What was done. |
create_taskCreate a task
POST/api/external/tools/create_task
Scopes: tasks:write
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.
| Field | Type | Notes |
|---|---|---|
title required | string | Task title. |
project_id required | number or string | Project id or public id (required). |
description | string | Plain text or simple HTML. |
status | backlog | todo | in_progress | blocked | review | done | cancelled | archived | Default todo. |
priority | urgent | high | medium | low | |
assignee | string | 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 | string | YYYY-MM-DD. |
start_date | string | YYYY-MM-DD. |
duration_minutes | number | |
parent_task_id | number | Make this a subtask. |
delete_taskDelete a taskasks for approval
POST/api/external/tools/delete_task
Scopes: tasks:write
Delete a task. Needs your approval in Plodder before it happens (unless you changed that in Settings > AI agents).
| Field | Type | Notes |
|---|---|---|
task_id required | number or string |
get_taskGet a taskread only
POST/api/external/tools/get_task
Scopes: tasks:read
Full detail for one task: description, subtasks and recent comments/activity.
| Field | Type | Notes |
|---|---|---|
task_id required | number or string | Task id or public id. |
list_tasksList tasksread only
POST/api/external/tools/list_tasks
Scopes: tasks:read
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.
| Field | Type | Notes |
|---|---|---|
workspace_id | number | Only this workspace. Default: every workspace you belong to. |
project_id | number or string | Only this project (id or public id). |
status | backlog | todo | in_progress | blocked | review | done | cancelled | archived | |
assignee | string | Team member name (partial match), id, user_id or email. Use "me" for yourself. |
due_before | string | YYYY-MM-DD, inclusive. |
due_after | string | YYYY-MM-DD, inclusive. |
query | string | Text to match in title or description. |
include_done | boolean | Include done, cancelled and archived tasks. |
limit | number | Max results (default 50, max 200). |
saved_viewsSaved viewsasks for approval
POST/api/external/tools/saved_views
Scopes: tasks:read, tasks:write
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 }. Actions: - list: Your saved views on a surface. - create: Save a view (surface, name, config). - update: Rename it, replace its config, or make it the default (is_default). - delete: Delete one of your saved views. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | create | update | delete | What to do. |
surface | home | drive | search | |
view_id | number | |
name | string | |
config | object | |
is_default | boolean |
status_updatesStatus updatesasks for approval
POST/api/external/tools/status_updates
Scopes: tasks:read, tasks:write
Status updates posted on a task or a project (/api/status-updates). Give task_id or project_id. Actions: - list: Updates on a task or project, newest first. - create: Post an update (content) on a task or project. - delete: Delete an update (update_id). (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | create | delete | What to do. |
task_id | number | |
project_id | number | |
content | string | |
update_id | number |
task_actionsTask actionsasks for approval
POST/api/external/tools/task_actions
Scopes: tasks:read, tasks:write
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. Actions: - bulk: Change many tasks at once (task_ids, bulk, value). (may wait for approval) - copy: Copy a task (task_id): same project, stage, assignee, priority and description, status todo. - 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) - complete_chunk: Mark one scheduled work block done or not done (task_id, chunk_start, completed). - 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. - duplicates: Possible duplicates of a task (task_id). - dismiss_duplicate: Say two tasks are not duplicates (task_id, other_task_id). - pulls: GitHub pull requests linked to a task, ones that look related, and your open ones to pick from (task_id). - link_pull: Link a GitHub pull request to a task (task_id, url, title). - unlink_pull: Unlink a pull request from a task (task_id, url).
| Field | Type | Notes |
|---|---|---|
action required | bulk | copy | recurrence | complete_chunk | lock_chunk | duplicates | dismiss_du | What to do. |
task_ids | array of number | |
bulk | set_priority | set_status | set_assignee | set_project | complete | delete | bulk: what to do to every task. |
value | any | bulk: the new priority / status / assignee (team member public id or null) / project id. |
cancel_reason | string | |
task_id | number | |
series | detach | edit_all | delete_one | delete_all | recurrence: detach = change only this occurrence. |
changes | object | recurrence: task fields to change. |
chunk_start | number | epoch seconds of the work block |
completed | boolean | |
old_chunk_start | number | |
new_chunk_start | number | |
new_chunk_end | number | |
other_task_id | number | |
url | string | a GitHub pull request link |
title | string | |
fields | object | Any other body fields the action documents. |
task_viewsTask board viewsasks for approval
POST/api/external/tools/task_views
Scopes: tasks:read, tasks:write
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. Actions: - list: Every task view. - create: A new view (name, view_type, config). - update: Change name, view_type, config or sort_order. - delete: Delete a view. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | create | update | delete | What to do. |
view_id | number | |
name | string | |
view_type | list | kanban | gantt | dashboard | workload | |
config | object | |
sort_order | number |
time_trackingTime trackingasks for approval
POST/api/external/tools/time_tracking
Scopes: tasks:read, tasks:write
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. Actions: - classify: One review / attribution change (op + fields). merge_sessions and merge_entities wait for approval. (may wait for approval) - 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. - backfill: Re-derive tracked data (backfill: default | localAiSessionIds | fleetFlags | fleetDispatch | reviewSweep | businessSeconds | reconcile; limit?, max_seconds?, days?). - delete_range: Delete tracked activity in a range (start + end ISO, or the last `days`). (waits for approval: delete) - retention: Keep `days` of tracked activity and delete what is older now. (waits for approval: delete) - privacy_rules: Privacy rules (what the collector never records). - add_privacy_rule: A privacy rule (kind, value, rule_action block|allow, default block). - remove_privacy_rule: Remove a privacy rule (rule_id). (waits for approval: delete) - devices: Paired Mac collectors. - revoke_device: Revoke a Mac collector (device_id); its token stops working. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | classify | log_time | backfill | delete_range | retention | privacy_rules | add_ | What to do. |
op | label_entity | attach_entity_business | attach_entity_project | attach_session_b | classify: which change. Its fields (sessionId, entityId, businessId, projectId, label, keywords, kind, value, reviewId, sourceSessionId, targetSessionId, sourceEntityId, targetEntityId, keyword) go in `fields`. |
session_id | number | |
business_id | number | |
project_id | number | |
task_id | number | |
new_task_title | string | |
note | string | |
reverse | boolean | log_time: take a logged entry back off the task. |
backfill | default | localAiSessionIds | fleetFlags | fleetDispatch | reviewSweep | busines | |
limit | number | |
max_seconds | number | |
days | number | |
start | string | ISO date-time. |
end | string | ISO date-time. |
rule_id | number | |
kind | string | privacy rule kind (app, domain, title, ...). |
value | string | |
rule_action | block | allow | |
device_id | number | |
fields | object | Any other body fields the action documents. |
update_taskUpdate a task
POST/api/external/tools/update_task
Scopes: tasks:write
Change a task: title, description, status, priority, assignee, dates, duration, or move it to another project. Only the fields you pass change.
| Field | Type | Notes |
|---|---|---|
task_id required | number or string | |
title | string | |
description | string | |
status | backlog | todo | in_progress | blocked | review | done | cancelled | archived | |
priority | urgent | high | medium | low | |
assignee | string | 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 | string or null | YYYY-MM-DD, or null to clear. |
start_date | string or null | |
duration_minutes | number | |
project_id | number or string | Move to this project. |
result | string | What was done. Required when marking a task created by an AI agent done (20+ characters). |
Projects 5
create_projectCreate a project
POST/api/external/tools/create_project
Scopes: projects:write
Create a project with the standard Todo, In Progress and Done stages.
| Field | Type | Notes |
|---|---|---|
name required | string | |
workspace_id | number | Workspace id (from list_workspaces). Defaults to your primary workspace. |
color | string | Hex colour, optional. |
get_projectGet a projectread only
POST/api/external/tools/get_project
Scopes: projects:read
One project with its stages, task counts and open tasks.
| Field | Type | Notes |
|---|---|---|
project_id required | number or string |
list_projectsList projectsread only
POST/api/external/tools/list_projects
Scopes: projects:read
List projects in your workspaces (or one workspace). Archived ones are left out unless include_archived is true.
| Field | Type | Notes |
|---|---|---|
workspace_id | number | Only this workspace. Default: all of yours. |
query | string | Match in the project name. |
include_archived | boolean |
project_actionsProject actionsasks for approval
POST/api/external/tools/project_actions
Scopes: projects:read, projects:write
Project settings, copies, stages and the project's comment box, as you. For tasks in a project use create_task / list_tasks. Actions: - update: Change a project (project_id, set). archived 1 archives it, 0 brings it back. - delete: Delete a project (it goes to Trash with its stages and tasks). (waits for approval: delete) - copy: Copy a project with its stages, tasks and docs (project_id, name). - comments: The project's comment box. - comment: Post in the project's comment box (project_id, body). - delete_comment: Delete your own project comment (comment_id). (waits for approval: delete) - stages: The project's stages in order. - add_stage: Add a stage (project_id, name, color). - update_stage: Change a stage (stage_id, set: {name, color, is_active, ...}). - reorder_stages: Put the stages in this order (project_id, stage_ids). - delete_stage: Delete a stage (stage_id); its tasks move to reassign_to, or lose their stage. (waits for approval: delete) - apply_template: Add a project template's stages and tasks to a project (project_id, template_id).
| Field | Type | Notes |
|---|---|---|
action required | update | delete | copy | comments | comment | delete_comment | stages | add_stag | What to do. |
project_id | number | |
name | string | |
set | object | update: project fields (name, description, color, status, priority, start_date, due_date, folder_id, archived 1|0, ...). |
body | string | comment: the text (@Claude or an agent handle runs that agent). |
comment_id | number | |
stage_id | number | |
color | string | |
stage_ids | array of number | |
reassign_to | number | delete_stage: move its tasks to this stage. |
template_id | number | |
fields | object | Any other body fields the action documents. |
templatesTemplatesasks for approval
POST/api/external/tools/templates
Scopes: projects:read, projects:write
Project templates, task templates and doc templates, as you: list, create, change, delete, and use them. Actions: - list_project: Project templates. - create_project: A project template (name, description, set {stages, default_tasks, roles, text_variables} as JSON strings), or from_project_id. - update_project: Change a project template (template_id, set). - delete_project: Delete a project template (template_id). (waits for approval: delete) - list_task: Task templates (workspace_id optional). - create_task: A task template (name, set {default_title, default_priority, default_duration_minutes, default_status, description, subtasks}). - use_task: Create the task(s) from a task template (template_id, project_id, stage_id, workspace_id). - delete_task: Delete a task template (template_id). (waits for approval: delete) - list_doc: Doc templates you can use (built-in and your workspaces'). - get_doc: One doc template with its blocks. - save_doc: Save a doc as a doc template (doc_id, name, description, set {category}). - update_doc: Change a doc template (template_id, set {name, description, category, icon}, or set {duplicate: true}). - delete_doc: Delete a doc template (template_id). (waits for approval: delete) - use_doc: A new doc from a doc template (template_id, title, workspace_id or project_id).
| Field | Type | Notes |
|---|---|---|
action required | list_project | create_project | update_project | delete_project | list_task | cr | What to do. |
kind | project | task | doc | |
template_id | number | |
name | string | |
description | string | |
from_project_id | number | create a project template from this project. |
doc_id | string | save this doc as a doc template. |
workspace_id | number | |
project_id | number | |
stage_id | number | |
title | string | |
set | object | update: fields (project: name, description, stages, default_tasks, roles, text_variables; doc: name, description, category, icon). |
fields | object | Any other body fields the action documents. |
Docs and databases 24
add_database_rowAdd a database row
POST/api/external/tools/add_database_row
Scopes: docs:write
Add a row to a database. values maps column names (from list_database_rows) to values.
| Field | Type | Notes |
|---|---|---|
database required | string | Database id or sheet:<id>. |
values required | object | Column name -> value. |
append_to_docAppend to a doc
POST/api/external/tools/append_to_doc
Scopes: docs:write
Add markdown to the end of a doc without touching what is there.
| Field | Type | Notes |
|---|---|---|
doc_id required | number or string | |
content required | string | Markdown to append. |
code_filesCode filesasks for approval
POST/api/external/tools/code_files
Scopes: docs:read, docs:write
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). Actions: - list: Code files you can open, newest first. - get: A code file: title, type, revision, content, where it sits in Drive. - create: A new code file: title (with extension sets the language), file_type, content, workspace_id or folder_id. - write: Replace the whole content (base_rev from get; force=true overwrites a newer change). - edit: Replace old_text with new_text (exactly once unless all=true). - rename: Change the title (an extension also sets the language). - set_type: Change the language (file_type, an extension like "py"). - move: Move in Drive: workspace_id and/or folder_id. - trash: Move to Drive Trash. (waits for approval: delete) - drive_get: A code-type Drive upload (drive_file): content and its sha256. - drive_write: Save a Drive upload's text: content (base_sha from drive_get; force=true overwrites), or old_text/new_text.
| Field | Type | Notes |
|---|---|---|
action required | list | get | create | write | edit | rename | set_type | move | trash | drive_ge | What to do. |
id | string | Code file id or public id. |
drive_file | string | Drive file id (an uploaded code file). |
title | string | |
file_type | string | |
content | string | |
old_text | string | |
new_text | string | |
all | boolean | |
base_rev | number | |
base_sha | string | |
force | boolean | |
workspace_id | number | |
folder_id | number |
create_docCreate a doc
POST/api/external/tools/create_doc
Scopes: docs:write
Create a doc from markdown (headings, lists, checklists, code, bold/italic/links; $$ LaTeX $$ or a ```math fence for a math block;  or  for a video block; a ```poll fence with the question line and "- option" lines, flags multi / anonymous / closed, for a poll people vote on).
| Field | Type | Notes |
|---|---|---|
title required | string | |
content | string | Markdown. |
workspace_id | number | Workspace id (from list_workspaces). Defaults to your primary workspace. |
project_id | number | Attach to a project. |
database_adminDatabase structure and sharingasks for approval
POST/api/external/tools/database_admin
Scopes: docs:read, docs:write
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. Actions: - 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) - shares: Who the database is shared with by email. - share: Share with an email (role, notify). (waits for approval: external) - unshare: Remove an email's access.
| Field | Type | Notes |
|---|---|---|
action required | change | shares | share | unshare | What to do. |
database_id | string | |
op | string | The grid operation (the database API's action). |
email | string | |
role | viewer | editor | |
notify | boolean | |
fields | object | Any other body fields the action documents. |
delete_database_rowDelete a database rowasks for approval
POST/api/external/tools/delete_database_row
Scopes: docs:write
Delete one row of a database. Needs the user's approval unless they allowed deletes.
| Field | Type | Notes |
|---|---|---|
database required | string | Database id or sheet:<id>. |
row_id required | number |
designDesign filesasks for approval
POST/api/external/tools/design
Scopes: docs:read, docs:write
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. Actions: - list: Designs you can open, newest first. - get: The design: title, your access, and every item as JSON. - create: A new design (title, workspace_id or folder_id), filed in Drive. - rename: Change the title. - add: Add items: a list of objects (an id is made when missing). - update: Change items by id: a list of {id, ...fields to change}. - remove: Remove items by id (ids). Undo is in the editor's history. - replace: Replace the whole design content (content = {items: [...]}). - trash: Move the design to Trash. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | get | create | rename | add | update | remove | replace | trash | What to do. |
id | string | design id or public id. |
title | string | |
workspace_id | number | |
folder_id | number | |
items | array of object | add / update: the items. |
ids | array of string | remove: ids. |
content | object | replace: the whole document. |
design_libraryDesign librariesasks for approval
POST/api/external/tools/design_library
Scopes: docs:read, docs:write
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). Actions: - list: Libraries you can use: src (the publishing file), name, version, counts, mine. - get: One library's current version (src) with its items. - publish: Publish (or publish the next version of) the design file id; description optional. - unpublish: Unpublish the design file id's library (files using it keep their copies). - use: Make the design file id use the library src (copies its items in). - update: Take the newest version of every library the design file id uses (or only lib, a lib item id). - stop_using: Stop using a library in the design file id (lib = the lib item id from get on the design).
| Field | Type | Notes |
|---|---|---|
action required | list | get | publish | unpublish | use | update | stop_using | What to do. |
id | string | Design file id or public id. |
src | string | The library: the publishing design file's public id. |
lib | string | A lib item id inside the design file. |
description | string |
discussion_actionsDiscussion actionsasks for approval
POST/api/external/tools/discussion_actions
Scopes: docs:write
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. Actions: - edit: Edit your message (message_id, content). - resolve: Resolve (resolved true) or reopen a thread (thread_root_id). - react: Toggle an emoji reaction (message_id, emoji). - delete: Delete a message (message_id). (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | edit | resolve | react | delete | What to do. |
item | string | |
message_id | number | |
content | string | |
thread_root_id | number | |
resolved | boolean | |
emoji | string | |
fields | object | Any other body fields the action documents. |
doc_actionsDoc actionsasks for approval
POST/api/external/tools/doc_actions
Scopes: docs:read, docs:write
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. Actions: - delete: Delete a doc (to Trash). (waits for approval: delete) - polls: The doc's polls with results: question, options, votes per option, who voted (unless anonymous), your vote, closed. - vote: Vote in a doc poll as you (poll_id from get_doc's ```poll id=..., vote: option numbers from 0; [] withdraws). - close_poll: Close (closed: true) or reopen (closed: false) a doc poll. - blame: Who last changed each block of the doc and when ({blocks: {block_id: {name, user_id, source, at}}}), from its version history. - comments: A doc's block comments, threaded. - comment: Comment on a doc block (doc_id, content, block_id, parent_comment_id to reply). @Claude or an agent handle runs that agent. - resolve_comment: Resolve a block comment (comment_id). - delete_comment: Delete a block comment (comment_id). (waits for approval: delete) - publish: Publish the doc on a public web page (returns its slug). (waits for approval: external) - unpublish: Take the public page down. - shares: Who the doc is shared with by email. - share: Share the doc with an email (role viewer|editor; notify emails them, default true). (waits for approval: external) - unshare: Remove an email's access. - create_synced_block: A synced block (content shown live in several docs): blocks, workspace_id. Insert it in a doc with the returned id. - update_synced_block: Change a synced block everywhere it is shown (block, blocks, rev from its last read).
| Field | Type | Notes |
|---|---|---|
action required | delete | polls | vote | close_poll | blame | comments | comment | resolve_commen | What to do. |
doc_id | string | Doc id or public id. |
content | string | |
block_id | string | comment: the block it is on. |
parent_comment_id | number | |
comment_id | number | |
email | string | |
role | viewer | editor | |
notify | boolean | |
block | string | synced block id |
blocks | string | synced block content: a JSON string of blocks. |
rev | number | |
workspace_id | number | |
poll_id | string | polls / vote / close_poll: the id in get_doc's ```poll id=... line. |
vote | array of number | vote: the option numbers you pick (0 = first). |
closed | boolean | |
fields | object | Any other body fields the action documents. |
get_database_rowRead a database rowread only
POST/api/external/tools/get_database_row
Scopes: docs:read
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.
| Field | Type | Notes |
|---|---|---|
row_id required | string | Row id or sheet_row:<id>. |
get_docRead a docread only
POST/api/external/tools/get_doc
Scopes: docs:read
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).
| Field | Type | Notes |
|---|---|---|
doc_id required | number or string | Doc id or public id. |
illustrationIllustrationsasks for approval
POST/api/external/tools/illustration
Scopes: docs:read, docs:write
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. Actions: - list: Illustrations you can open, newest first. - get: The illustration: title, your access, and every item as JSON. - create: A new illustration (title, workspace_id or folder_id), filed in Drive. - rename: Change the title. - add: Add items: a list of objects (an id is made when missing). - update: Change items by id: a list of {id, ...fields to change}. - remove: Remove items by id (ids). Undo is in the editor's history. - replace: Replace the whole illustration content (content = {items: [...]}). - trash: Move the illustration to Trash. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | get | create | rename | add | update | remove | replace | trash | What to do. |
id | string | illustration id or public id. |
title | string | |
workspace_id | number | |
folder_id | number | |
items | array of object | add / update: the items. |
ids | array of string | remove: ids. |
content | object | replace: the whole document. |
imagesImage editorasks for approval
POST/api/external/tools/images
Scopes: docs:read, docs:write
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. Actions: - list: Image projects you can open, newest first. - get: Size, access, where it sits, the Drive image it came from, and its layers. - create: A blank image: title, width, height, background (default white), workspace_id or folder_id. - open_drive_image: Open a Drive image (drive_file) as a layered project (reuses the one made before). - update_layers: Change layers: layers [{id, name?, visible?, opacity?, x?, y?, w?, h?, blend?, text?, locked?}]. - move_layer: Move a layer (layer_id) to index (0 = bottom) within its group. - remove_layer: Remove a layer (layer_id). (waits for approval: delete) - 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) - rename: Change the title. - move: Move in Drive: workspace_id and/or folder_id. - versions: Earlier saves of the project, and of the Drive image it came from. - restore_version: Make an earlier project save current (version_id), or of the Drive original (original=true). - trash: Move to Drive Trash. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | get | create | open_drive_image | update_layers | move_layer | remove_lay | What to do. |
id | string | Image project id or public id. |
drive_file | string | |
title | string | |
width | number | |
height | number | |
background | boolean | |
layers | array of object | |
layer_id | string | |
index | number | |
version_id | number | |
original | boolean | |
workspace_id | number | |
folder_id | number |
list_database_rowsRead a databaseread only
POST/api/external/tools/list_database_rows
Scopes: docs:read
The columns and rows of a database (sheet). Find databases with list_items types ["sheet"]. Optionally filter rows by text.
| Field | Type | Notes |
|---|---|---|
database required | string | Database id or sheet:<id> ref. |
text | string | Only rows containing this text. |
limit | number | Max rows (default 100, max 500). |
list_docsList docsread only
POST/api/external/tools/list_docs
Scopes: docs:read
List docs in your workspaces, optionally by project or a text search on title and content.
| Field | Type | Notes |
|---|---|---|
workspace_id | number | |
project_id | number | |
query | string | |
limit | number | Default 50. |
list_tagsList tagsread only
POST/api/external/tools/list_tags
Scopes: docs:read
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.
| Field | Type | Notes |
|---|---|---|
scope | crm | work | Which set. Omit for both. |
presentationsPresentationsasks for approval
POST/api/external/tools/presentations
Scopes: docs:read, docs:write
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. Actions: - list: Presentations you can open, newest first. - get: Title, access, theme name and every slide's title, text and notes. - get_slide: One slide (slide_id or slide number) as raw JSON. - create: A new deck: title, slides [{layout, title, subtitle, body, notes}], theme (name), workspace_id or folder_id. - add_slide: Add a slide from fields (fields) at position (1-based; default the end). - update_slide: Change a slide's layout, title, subtitle, body or notes (fields), or hide it (hidden). - set_elements: Replace a slide's elements with raw JSON (elements), for layouts the fields cannot express. - move_slide: Move a slide to position (1-based). - delete_slide: Remove a slide. (waits for approval: delete) - rename: Change the title. - move: Move in Drive: workspace_id and/or folder_id. - versions: Saved .pptx versions (exports, imports). - trash: Move to Drive Trash. (waits for approval: delete) - list_items: A slide's items like the Selection pane: id, kind, name, hidden, locked, group, box (front first). - update_items: Selection pane changes on a slide: items [{id, name, hidden, locked, x, y, w, h}], order (ids front first) to restack. - group: Group items (ids) so they select and move together. - ungroup: Ungroup: every group touching ids comes apart. - 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. - 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. - 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. - add_equation: Add an equation from LaTeX (latex, size), optional x/y; element_id changes an existing one. - 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. - get_master: The slide master: graphics, background, title/body text styles, and per layout its graphics, background, placeholder boxes and hide flag. - 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. - set_slide_options: Slide flags: hide_background_graphics (hide the master graphics on this slide). - 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). - 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.
| Field | Type | Notes |
|---|---|---|
action required | list | get | get_slide | create | add_slide | update_slide | set_elements | move | What to do. |
id | string | Presentation id or public id. |
title | string | |
theme | string | create: Office, Paper, Night, Slate, Forest, Ocean, Sunset, Mono. |
slides | array of object | |
fields | object | {layout, title, subtitle, body, notes} |
elements | array of object | |
slide_id | string | |
slide | number | 1-based slide number. |
position | number | |
hidden | boolean | |
workspace_id | number | |
folder_id | number | |
items | array of object | update_items: [{id, name, hidden, locked, x, y, w, h}] |
order | array of string | update_items: item ids front first. |
ids | array of string | |
animations | array of object | |
transition | object or null | |
all_slides | boolean | |
chart | object | |
diagram | object | |
latex | string | |
size | number | |
element_id | string | |
master | object or null | |
hide_background_graphics | boolean | |
query | string | find / replace_text: the text to look for. |
replacement | string | replace_text: the new text (may be empty). |
match_case | boolean | |
whole_word | boolean | |
include_notes | boolean | find / replace_text: look in speaker notes too (default true). |
x | number | |
y | number | |
w | number | |
h | number |
query_databaseQuery a databaseread only
POST/api/external/tools/query_database
Scopes: docs:read
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).
| Field | Type | Notes |
|---|---|---|
database required | string | Database id or sheet:<id>. |
filters | array of object | |
group_by | string | Column to group by. Multi-select rows count once per label. |
bucket | day | week | month | quarter | year | Date bucket when group_by is a date column. |
metrics | array of object | |
columns | array of string | Columns to show when listing rows. |
sort | object | |
limit | number | Max result rows (default 50 for groups, 100 for rows, max 500). |
chart | bar | line | pie | table | How the app should draw this answer. |
title | string | Short chart title. |
spreadsheetsSpreadsheetsasks for approval
POST/api/external/tools/spreadsheets
Scopes: docs:read, docs:write
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. Actions: - list: Spreadsheets you can open, newest first. - get: Title, access, sheets with their used ranges, named ranges. - read: Cells in range (default the used range) on sheet: values, formulas, text. - write: Write values (rows: a 2D list) starting at cell (default A1) on sheet. Empty string clears a cell; null leaves it. - format: Style a range: style {b, i, u, fc, bg, fs, ha: left|center|right, nf: number format like "#,##0.00" or "0%", wrap}. - evaluate: Compute a formula against the workbook without saving it (formula, sheet). - create: A new spreadsheet (title, workspace_id or folder_id); rows fills the first sheet. - add_sheet: Add a sheet tab (name). - rename_sheet: Rename a sheet tab (sheet, name). - delete_sheet: Remove a sheet tab and its cells (sheet). (waits for approval: delete) - rename: Change the spreadsheet title. - trash: Move the spreadsheet to Trash. (waits for approval: delete) - conditional_formats: The sheet's conditional formatting rules in priority order (earlier wins), each {id, range, kind, v1, v2, style | scale}. - 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%". - charts: The sheet's charts, each {id, kind, range, title, one_axis, cell (top-left anchor), w, h}. - 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. - 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. - 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. - 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. - 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. - 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.
| Field | Type | Notes |
|---|---|---|
action required | list | get | read | write | format | evaluate | create | add_sheet | rename_shee | What to do. |
id | string | Spreadsheet id or public id. |
sheet | string | Sheet tab name. |
range | string | A1 range. |
cell | string | write: the top-left cell (default A1). |
rows | array of array of any | write / create: rows of cell values. |
style | object | |
formula | string | |
name | string | |
title | string | |
workspace_id | number | |
folder_id | number | |
rule | object | add_conditional_format: one rule. |
chart | object | add_chart: one chart. |
charts | array of object | set_charts: every chart (images are kept). |
list | any | set_dropdown: the choices (array of strings, or one comma-separated string). |
source | string | set_dropdown: a range holding the choices instead of list. |
allow_other | boolean | set_dropdown: keep entries that are not a choice (flagged). |
remove | boolean | set_dropdown: remove dropdowns from range. |
set_cell | string | goal_seek: the formula cell (A1). |
to_value | number | goal_seek: the target value for set_cell. |
changing_cell | string | goal_seek: the input cell to change (A1). |
dry_run | boolean | goal_seek: report the answer without writing it. |
rules | array of object | set_conditional_formats: every rule, in priority order. |
update_database_rowUpdate a database row
POST/api/external/tools/update_database_row
Scopes: docs:write
Change cells of one row. Only the columns you pass change.
| Field | Type | Notes |
|---|---|---|
database required | string | Database id or sheet:<id>. |
row_id required | number | |
values required | object | Column name -> value. |
update_docUpdate a doc
POST/api/external/tools/update_doc
Scopes: docs:write
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.
| Field | Type | Notes |
|---|---|---|
doc_id required | number or string | |
title | string | |
content | string | Markdown that replaces the body. |
project_id | number or string or null | Move the doc into this project (same workspace), or null to take it out of its project. |
checklist_filter | object | Set which items a checklist shows: {index (from get_doc checklists), filter: all | open | done}. |
checklist_to_tasks | object | 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. |
whiteboardWhiteboardsasks for approval
POST/api/external/tools/whiteboard
Scopes: docs:read, docs:write
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. Actions: - list: Whiteboards you can open, newest first. - get: The whiteboard: title, your access, and every element as JSON. - create: A new whiteboard (title, workspace_id or folder_id), filed in Drive. - rename: Change the title. - add: Add elements: a list of objects (an id is made when missing). - update: Change elements by id: a list of {id, ...fields to change}. - remove: Remove elements by id (ids). Undo is in the editor's history. - replace: Replace the whole whiteboard content (content = {elements: [...]}). - trash: Move the whiteboard to Trash. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | get | create | rename | add | update | remove | replace | trash | What to do. |
id | string | whiteboard id or public id. |
title | string | |
workspace_id | number | |
folder_id | number | |
elements | array of object | add / update: the elements. |
ids | array of string | remove: ids. |
content | object | replace: the whole document. |
word_docsWord documentsasks for approval
POST/api/external/tools/word_docs
Scopes: docs:read, docs:write
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. Actions: - list: Word docs you can open, newest first. - get: Title, access, where it sits, and the document text. - create: A new Word doc: title, content (markdown), workspace_id or folder_id. - append: Add paragraphs (content) at the end. - write: Replace the whole body with content (headers, footers and styles are kept). - replace_text: Replace old_text with new_text where it sits inside one run of text (all=true for every match). - rename: Change the title (Plodder docs). - move: Move in Drive: workspace_id and/or folder_id (Plodder docs). - versions: Earlier saved versions. - restore_version: Make an earlier version current (version_id); what it replaces is kept as a version. - trash: Move to Drive Trash (Plodder docs). (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | get | create | append | write | replace_text | rename | move | versions | | What to do. |
id | string | Word doc id or public id. |
drive_file | string | A .docx file id in Drive. |
title | string | |
content | string | Markdown-style text. |
old_text | string | |
new_text | string | |
all | boolean | |
version_id | number | |
workspace_id | number | |
folder_id | number | |
max_chars | number |
Meetings 3
get_meetingGet a meetingread only
POST/api/external/tools/get_meeting
Scopes: meetings:read
One meeting: summary, action items, attendees, and the transcript if include_transcript is true.
| Field | Type | Notes |
|---|---|---|
meeting_id required | number | |
include_transcript | boolean |
list_meetingsList meetingsread only
POST/api/external/tools/list_meetings
Scopes: meetings:read
Recorded meetings with their AI summaries, newest first.
| Field | Type | Notes |
|---|---|---|
workspace_id | number | |
query | string | Match in title or summary. |
limit | number | Default 25. |
meetings_adminMeeting links and notesasks for approval
POST/api/external/tools/meetings_admin
Scopes: meetings:read, meetings:write
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. Actions: - create_link: A meeting link (topic, start_time, duration, provider default zoom). Invites nobody by itself. - queue_status: Meeting-notes queue (owner). - process: Queue a transcript for meeting-notes processing (transcript_id, mode). Owner. - set_client: Set a recording's client by name (transcript_id, client, null clears). Owner.
| Field | Type | Notes |
|---|---|---|
action required | create_link | queue_status | process | set_client | What to do. |
topic | string | |
start_time | string | ISO date-time. |
duration | number | Minutes. |
provider | ctrlflow | google_meet | zoom | |
transcript_id | number | |
mode | process | reprocess | |
client | string or null | |
fields | object | Any other body fields the action documents. |
CRM 26
clients_adminClients and businessesasks for approval
POST/api/external/tools/clients_admin
Scopes: crm:read, crm:write
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. Actions: - create_client: A new client profile (name, workspace_id?, set?). - delete_client: Delete a client profile. (waits for approval: delete) - create_business: A new business (name, website?, client_id?, workspace_id?, set?). It is backed by a CRM company. - delete_business: Delete a business. (waits for approval: delete) - client_businesses: A client's businesses. - add_client_business: Add a business to a client (client_id, name, website?, set?). - update_client_business: Change one of a client's businesses (business_id, set). - remove_client_business: Delete one of a client's businesses (business_id). (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | create_client | delete_client | create_business | delete_business | client_busin | What to do. |
client_id | number | |
business_id | number | |
workspace_id | number | |
name | string | |
website | string | |
set | object | Other profile / business fields. |
fields | object | Any other body fields the action documents. |
crm_assignCRM round robinasks for approval
POST/api/external/tools/crm_assign
Scopes: crm:read, crm:write
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). Actions: - list: Every rule. - save: Create, or change with rule_id: fields = {scope: pipeline|source, pipeline_id?, source_kind? form|booking|any, source_ref?, members: [..], away?: [..], enabled?}. - delete: Delete a rule. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | save | delete | What to do. |
rule_id | number | |
fields | object |
crm_bulk_actionsCRM bulk actionsasks for approval
POST/api/external/tools/crm_bulk_actions
Scopes: crm:read, crm:write, messages:send
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. Actions: - list: Bulk actions, newest first: kind, label, status (scheduled, running, paused, done, cancelled, failed), total, done, ok, skipped, failed, next batch, errors. - get: One bulk action (bulk_id). - 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) - pause: Stop a running or scheduled bulk action (bulk_id) until resume. - resume: Carry on (bulk_id): the batch that fell due goes now, the spacing after it stays. (may wait for approval) - cancel: Cancel (bulk_id): the people not reached yet are left as they are.
| Field | Type | Notes |
|---|---|---|
action required | list | get | create | pause | resume | cancel | What to do. |
bulk_id | number | |
kind | tag_add | tag_remove | workflow_add | workflow_remove | email | sms | delete | i | |
contact_ids | array of number | |
filter | object | A People filter (crm_contacts filter_fields). |
segment_id | number | |
params | object | |
when | now | at | batches | |
start_at | string or number | |
batch_size | number | |
batch_every_min | number |
crm_companiesCRM companiesasks for approval
POST/api/external/tools/crm_companies
Scopes: crm:read, crm:write
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. Actions: - create: A new company from a domain (domain, optional name, stage_id). You own it. An existing domain returns that company (existed=true). - update: Change fields: fields = {name, stage_id, deal_owner, revenue, hidden, email_sync, notes, client_id, business_id}. - add_domain: Add a domain (value). - remove_domain: Remove a domain (value). - add_email: List a full email address on it (value). - remove_email: Remove a listed address (value). - add_address: An address lists it, a domain adds the domain (value). - enrich: Look its domain up again (name, icon). - refresh: Re-read its recent mail from your connected mailboxes. - merge: Merge company from_id into company_id (from_id is removed). (waits for approval: delete) - delete: Delete the company. (waits for approval: delete) - link_business: Link (or make) the CRM company for a business (business_id). - link_client: Link company_id (or null to unlink) to a client profile (client_id). - convert_to_client: Make company_id a client (optional workspace_id). - 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. - reactivate: Reactivate a churned company_id: clears the churn and reactivates the deals it churned (back in MRR). Fires Client reactivated. - 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}. - 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).
| Field | Type | Notes |
|---|---|---|
action required | create | update | add_domain | remove_domain | add_email | remove_email | add_ad | What to do. |
filter | object | query: { mode: and|or, items: [{ field, op, value? }] }. |
q | string | 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 | string | |
stage | string | |
activity | string | |
hidden | only | include | |
pipeline | string | |
columns | array of string | |
sort | string | |
dir | asc | desc | |
offset | number | |
limit | number | |
company_id | number | |
domain | string | create: the company domain, like acme.com. |
name | string | |
stage_id | string | |
value | string | The domain, email or address for the add_/remove_ actions. |
fields | object | update: the fields to change. |
from_id | number | merge: the company that goes away. |
business_id | number | |
client_id | number | |
workspace_id | number | |
churned_on | string | mark_churned: the churn day, YYYY-MM-DD (default today). |
reason | string | mark_churned: why (optional). |
crm_contactsCRM peopleasks for approval
POST/api/external/tools/crm_contacts
Scopes: crm:read, crm:write
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. Actions: - 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. - filter_fields: Every people filter field (key, label, group, type, operators, options with their values) and the GHL filters that are not available. - get: One person with company, consent and lead source. - create: A new person (email, optional name, title, company_id). You own them; lead source "manual". - 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}. - 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. - 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). - 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). - make_primary: Make one of their emails or phones the primary (contact_id, channel, value); the old primary becomes an extra. - label_channel: Label an email or phone (contact_id, channel, value, label; empty clears). - 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. - 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. - set_consent: Do-not-contact flags for one or many people: contact_ids + fields {dnc_email?, dnc_sms?, dnc_call?, unsubscribed?, consent_reason?}. - 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) - 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) - deleted: Recently deleted (last 30 days): people (id, name, email, deleted_at, deleted_by, days_left) and deals. q filters by name / email / title. - 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). - duplicates: Groups of companies or people that look like duplicates. - 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) - businesses: Every business the person is part of (primary first, with their role there). - add_business: Add the person to a business (company_id, optional role). Their primary stays. - 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). - set_primary_business: Make one business (company_id) their primary; the old primary stays one of their businesses. - set_business_role: Their role at a business (company_id, role; empty clears).
| Field | Type | Notes |
|---|---|---|
action required | search | filter_fields | get | create | update | channels | add_channel | remove | What to do. |
contact_id | number | |
role | string | add_business / set_business_role: their role there, like Owner. |
contact_ids | array of number | |
batch | string | restore: the batch a delete returned (undo). |
channel | email | phone | add_channel / remove_channel / make_primary / label_channel |
value | string | The email address or phone number. |
label | string | |
primary | boolean | |
field | string | changes: only this field. |
email | string | |
name | string | |
title | string | |
company_id | number | |
fields | object | |
kind | company | person | |
into | number | |
from | array of number | |
filter | object | search: { mode: and|or, items: [{ field, op, value }] } |
q | string | search: text in name, any email or phone (digits match too), business, job title, tag names, text custom fields, deal names |
owner | string | search: an owner key, or none |
columns | array of string | search: field keys to return per person |
sort | string | |
dir | asc | desc | |
limit | number | |
offset | number |
crm_dataCRM data and settingsasks for approval
POST/api/external/tools/crm_data
Scopes: crm:read, crm:write
Bulk edits, CSV import, sync from email, the company stages and the team activity report, as you (/api/crm/bulk, import, sync, settings, report). Actions: - 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. - 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. - import_run: Run the import (same inputs as import_preview). Max 5,000 rows, 5 MB. - sync_status: Email sync state per mailbox. - sync: Sync from email now (runs about 25 s, call again while a backfill is going). - settings: Company stages, closed and won stages, the workspace currency (settings.currency), the unknown-number lead setting and the speed to lead steps. - 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). - 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.
| Field | Type | Notes |
|---|---|---|
action required | bulk_edit | import_preview | import_run | sync_status | sync | settings | save_s | What to do. |
object | people | deals | companies | |
ids | array of number | |
set | object | |
csv | string | |
mapping | array of string | |
dedupe | skip | update | create | |
stages | array of object | |
closed_stage_ids | array of string | |
won_stage_id | string or null | |
unknown_number_lead | object or null | |
speed_to_lead | object or null | |
currency | string or null | save_settings: workspace currency (ISO code) or null. |
from | number | |
to | number |
crm_dealsCRM dealsasks for approval
POST/api/external/tools/crm_deals
Scopes: crm:read, crm:write
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. Actions: - 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. - 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. - get: One deal with its line items and stage history. - 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. - 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). - set_lines: Replace every line item: lines = [{product_id?, name?, qty?, price_cents?, discount_pct?, recurring?}]. - delete: Delete the deal. It stays in Recently deleted for 30 days (restore puts it back). (waits for approval: delete) - deleted: Deals deleted in the last 30 days (merged ones are not listed). q filters by title. - restore: Restore deleted deals: deal_ids. Merged deals and deals whose pipeline was deleted are refused (the reason says so). - 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) - 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. - reactivate: Reactivate a churned deal_id (back in MRR). - 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}. - 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...).
| Field | Type | Notes |
|---|---|---|
action required | list | speed_to_lead | get | create | update | set_lines | delete | deleted | re | What to do. |
filter | object | query: { mode: and|or, items: [{ field, op, value? }] }. |
q | string | 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 | string | |
stage | string | |
activity | string | |
hidden | only | include | |
pipeline | string | |
columns | array of string | |
sort | string | |
dir | asc | desc | |
offset | number | |
limit | number | |
deal_id | number | |
deal_ids | array of number | restore: the deleted deals to put back. |
into_id | number | merge: the deal that is kept. |
person_id | number | list: every deal of this person (plus older deals with no person at their businesses). |
pipeline_id | number | |
pipeline_kind | leads | sales | list: only deals in leads pipelines or in sales pipelines. |
company_id | number | |
contact_id | number | |
status | open | won | lost | abandoned | |
waiting | boolean | list: only open leads with no response by a person yet. |
scope | me | team | speed_to_lead: my deals or the whole CRM. |
from | number | speed_to_lead: epoch seconds. |
to | number | speed_to_lead: epoch seconds. |
fields | object | |
lines | array of object | |
churned_on | string | mark_churned: the churn day, YYYY-MM-DD (default today). |
reason | string | mark_churned: why (optional). |
crm_field_categoriesCRM field categoriesasks for approval
POST/api/external/tools/crm_field_categories
Scopes: crm:read, crm:write
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). Actions: - list: Categories and field placements (entity_type, default crm_contact). - create: New category (name, entity_type?). - rename: Rename (category_id, name). - reorder: Order the categories (order: ids first to last). - place_field: Put a field in a category (definition_id, category_id or null for General, sort_order?). - delete: Delete a category (category_id); its fields go back to General. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | create | rename | reorder | place_field | delete | What to do. |
entity_type | crm_contact | crm_company | crm_deal | |
category_id | number or null | |
name | string | |
order | array of number | reorder: category ids, first to last. |
definition_id | number | place_field: the field (property definition id). |
sort_order | number | place_field: position inside the category. |
fields | object | Any other body fields the action documents. |
crm_forecastDeal forecastasks for approval
POST/api/external/tools/crm_forecast
Scopes: crm:read, crm:write
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. Actions: - 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. - settings: The forecast rules: commit_min, best_min (win chance %), stale_days. - set_settings: Change the rules: commit_min?, best_min? (<= commit_min), stale_days?.
| Field | Type | Notes |
|---|---|---|
action required | get | settings | set_settings | What to do. |
period | month | quarter | |
periods | number | |
group | none | pipeline | owner | |
pipeline | string | |
owner | string | |
currency | string | |
tz | string | |
commit_min | number | |
best_min | number | |
stale_days | number |
crm_get_companyCRM: get a companyread only
POST/api/external/tools/crm_get_company
Scopes: crm:read
One CRM company with its contacts.
| Field | Type | Notes |
|---|---|---|
company_id required | number |
crm_list_companiesCRM: list companiesread only
POST/api/external/tools/crm_list_companies
Scopes: crm:read
Companies in your CRM (built from your email), with stage and deal owner.
| Field | Type | Notes |
|---|---|---|
query | string | |
stage_id | string | |
limit | number |
crm_list_contactsCRM: list contactsread only
POST/api/external/tools/crm_list_contacts
Scopes: crm:read
People in your CRM, optionally for one company or matching text.
| Field | Type | Notes |
|---|---|---|
company_id | number | |
query | string | |
limit | number |
crm_objectsCRM custom objectsasks for approval
POST/api/external/tools/crm_objects
Scopes: crm:read, crm:write
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. Actions: - list_types: Every object type with its fields (id, key, data_type, options) and record count. - get_type: One type (type_id) with its smart lists. - create_type: New type: name, plural?, icon?, description?, labels? (link labels), fields? [{ name, data_type, options?, required? }]. - update_type: Change a type (type_id): name, plural, icon, description, labels. - delete_type: Delete a type and all its records (type_id). (waits for approval: delete) - add_field: Add a field (type_id, name, data_type, options?, required?). - update_field: Rename a field, change its options or required (field_id, name?, options?, required?, sort_order?). A field keeps its type. - delete_field: Delete a field and its values (field_id). (waits for approval: delete) - 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. - get: One record (record_id) with its type and every link. - create: New record: type_id, name, values?, owner?, notes?, associations? [{ target_type, target_id, label? }]. - update: Change a record (record_id): name?, values? (merged), owner?, notes?. - delete: Delete a record (record_id). (waits for approval: delete) - associate: Link a record (record_id) to target_type contact | company | deal | object, target_id, label?. Again = changes the label. - unassociate: Remove a link: association_id, or record_id + target_type + target_id. - related: Records linked to a person / business / deal / record (target_type, target_id), grouped by type. - 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?). - views: Smart lists of a type (type_id) with live counts. - save_view: Save a smart list (type_id, name, config { filter, q?, owner?, sort?, dir? }); with view_id it updates that one. - delete_view: Delete a smart list (view_id). Records stay.
| Field | Type | Notes |
|---|---|---|
action required | list_types | get_type | create_type | update_type | delete_type | add_field | up | What to do. |
type_id | number | |
field_id | number | |
record_id | number | |
view_id | number | |
association_id | number | |
name | string | |
plural | string | |
icon | string | |
description | string | |
labels | array of string | |
fields | array of object | [{ name, data_type, options?, required? }] |
data_type | string | |
options | array of any | Option labels, or { id?, label, color? } |
required | boolean | |
sort_order | number | |
values | object | { field key | id | name: value } |
owner | string | |
notes | string | |
associations | array of object | |
target_type | contact | company | deal | object | |
target_id | number | |
label | string | |
filter | object | |
q | string | |
sort | string | |
dir | asc | desc | |
limit | number | |
offset | number | |
csv | string | |
mapping | object | |
update_existing | boolean | |
config | object |
crm_pipelinesCRM pipelinesasks for approval
POST/api/external/tools/crm_pipelines
Scopes: crm:read, crm:write
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. Actions: - list: Every pipeline with its stages, kind and won_handoff. - 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). - update: Change a pipeline: pipeline_id + any of name, stages, closed_stage_ids, won_stage_id, kind, won_handoff (null clears), access, card_fields. - 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. - 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). - copy: Copy a pipeline (pipeline_id, name optional): the same stages, closed / won stages, kind, access and card fields; no deals and no automations. - reorder: New order: ids = every pipeline id (0 included). - delete: Delete a pipeline; its deals move to move_to (default 0). (waits for approval: delete) - unknown_number_lead: The setting "New texts / calls from unknown numbers create a lead": {on, pipeline_id, stage_id} or null. - set_unknown_number_lead: Set it: setting = {on, pipeline_id, stage_id?} or null to clear.
| Field | Type | Notes |
|---|---|---|
action required | list | create | update | set_access | set_card_fields | copy | reorder | delete | What to do. |
pipeline_id | number | |
name | string | |
stages | array of object | |
closed_stage_ids | array of string | |
won_stage_id | string or null | |
ids | array of number | |
move_to | number | |
kind | leads | sales | |
won_handoff | object or null | {mode: move|create, pipeline_id, stage_id?}; null clears. |
setting | object or null | set_unknown_number_lead: {on, pipeline_id, stage_id?} or null. |
access | object | { mode: everyone | users | owner, users?: string[] } |
card_fields | array or null |
crm_productsCRM productsasks for approval
POST/api/external/tools/crm_products
Scopes: crm:read, crm:write
The product list deal line items pick from, as you (/api/crm/products). Prices are integer cents. Actions: - list: Products (include_archived to see archived ones). - 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.
| Field | Type | Notes |
|---|---|---|
action required | list | save | What to do. |
product_id | number | |
fields | object | |
include_archived | boolean |
crm_tasksCRM tasksasks for approval
POST/api/external/tools/crm_tasks
Scopes: crm:read, crm:write
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). Actions: - mine: Open tasks assigned to you that are linked to a CRM record, by due date. - link: Link task_id to records: links = [{type: crm_deal|crm_contact|crm_company, id}], remind_at? (epoch s, null clears).
| Field | Type | Notes |
|---|---|---|
action required | mine | link | What to do. |
task_id | number | |
links | array of object | |
remind_at | number or null |
crm_update_companyCRM: update a company
POST/api/external/tools/crm_update_company
Scopes: crm:write
Change a CRM company: name, stage, revenue, notes, or hide it.
| Field | Type | Notes |
|---|---|---|
company_id required | number | |
name | string | |
stage_id | string | Stage id from crm_list_companies stages. |
revenue | number | |
notes | string | |
hidden | boolean |
crm_update_contactCRM: update a contact
POST/api/external/tools/crm_update_contact
Scopes: crm:write
Change a CRM contact: name, title, phone or notes.
| Field | Type | Notes |
|---|---|---|
contact_id required | number | |
name | string | |
title | string | |
phone | string | |
notes | string |
crm_viewsCRM views and segmentsasks for approval
POST/api/external/tools/crm_views
Scopes: crm:read, crm:write
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). Actions: - list: Saved views. - segments: Segments with live counts. - smart_lists: Smart lists (people, businesses, deals) with live counts and their kind, and the starter lists you can add. - 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. - tag_list: Add (or with remove: true, take off) a CRM tag on everyone in a smart list (view_id, tag). - segment_people: Who is in a segment right now (view_id). - create: A new view: name, kind companies|people|pipeline, config (filters, columns, sort; segment: true makes it a segment). - update: Change a view (view_id, name?, config?). - 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). - delete: Delete a view. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | segments | smart_lists | add_starters | tag_list | segment_people | creat | What to do. |
view_id | number | |
name | string | |
kind | companies | people | pipeline | |
config | object | |
limit | number | |
keys | array of string | |
tag | string | |
remove | boolean | |
consent | opt_in | customers | cold | not_set |
get_businessGet a businessread only
POST/api/external/tools/get_business
Scopes: crm:read
One business profile: context, offer, audience, location, budgets, channels.
| Field | Type | Notes |
|---|---|---|
business_id required | number or string | Id or slug. |
get_clientGet a clientread only
POST/api/external/tools/get_client
Scopes: crm:read
One client profile (context, goals, offer, audience, channels) and its businesses.
| Field | Type | Notes |
|---|---|---|
client_id required | number or string | Id or slug. |
list_businessesList businessesread only
POST/api/external/tools/list_businesses
Scopes: crm:read
Businesses (a client can have several) in your workspaces.
| Field | Type | Notes |
|---|---|---|
client_id | number or string | |
query | string |
list_clientsList clientsread only
POST/api/external/tools/list_clients
Scopes: crm:read
Clients in your workspaces with their status and industry: the Clients roster with its search, filters and sort (D 10-09).
| Field | Type | Notes |
|---|---|---|
query | string | Search: name, slug, industry, contacts, notes and linked business names. |
where | object or string | 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 | name | contact | status | crm_status | industry | businesses | business_count | | |
dir | asc | desc |
metaMeta (Facebook and Instagram)asks for approval
POST/api/external/tools/meta
Scopes: crm:read, crm:write
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). Actions: - status: Connection, pages, lead forms (questions, mapping, pipeline, on), Conversions API settings, the last 10 leads, the webhook URL. - leads: Recent leads (limit?): form, campaign, ad set, ad, person, deal, status (done | failed | skipped), via (webhook | poll). - ad_accounts: Ad accounts the token reaches. - audiences: Customer list Custom Audiences of the picked ad account. - available_pages: Facebook Pages the connected account reaches: picked (in this CRM), elsewhere (another Plodder account has it). No tokens. - sync: Re-read the pages the token manages (and the forms of pages with leads on). - sync_forms: Re-read one page's lead forms (page_id). - poll: Check every live lead form for leads the webhook missed (each lead lands once). - 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. - 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. - 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. - 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>). - default_mapping: The workspace default lead form mapping: rules [{ by: key | label, pattern (* wildcard; label = contains), target }], auto (apply to newly found forms). - set_default_mapping: Save the default mapping: rules?, auto?, apply_now? (also fill forms whose mapping was never saved). - copy_mapping: Copy the targets and answer rules of another form (from_form_id) onto form_id: same question key, else the same label. - 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. - 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. - set_ad_account: The ad account for Custom Audiences (ad_account_id, act_123...; empty clears). - 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.
| Field | Type | Notes |
|---|---|---|
action required | status | leads | ad_accounts | audiences | available_pages | sync | sync_forms | | What to do. |
page_id | string | |
page_ids | array of string | pick_pages: the Facebook Page ids to keep. |
form_id | string | |
leads_on | boolean | |
messages_on | boolean | |
comments_on | boolean | |
on | boolean | set_form: the form feeds the CRM. |
mapping | object | set_form: { question key: target }. |
rules | any | set_form: { question key: [{ op, value, tags, stage_id? }] } (null clears). set_default_mapping: [{ by, pattern, target }]. |
auto | boolean | set_default_mapping: apply to newly found forms. |
apply_now | boolean | set_default_mapping: also fill forms never mapped. |
from_form_id | string | copy_mapping: the form to copy from. |
question_key | string | new_field: the question. |
name | string | new_field: the field name (default: the question). |
type | string | new_field: text | number | date | select. |
category_id | number | new_field: the person field card. |
answers | object | test_lead: { question key: answer } (others get sample answers). |
platform | string | test_lead: facebook | instagram. |
lead_pipeline | any | set_form: { on, pipeline_id, stage_id?, title? } or null to turn off. |
ad_account_id | string | |
workspace_id | number | |
pixel_id | string | |
test_event_code | string | |
limit | number |
update_businessUpdate a business
POST/api/external/tools/update_business
Scopes: crm:write
Update fields on a business profile. Only the fields you pass change.
| Field | Type | Notes |
|---|---|---|
business_id required | number or string | |
name | string | |
industry | string | |
status | string | |
context | string | |
brand_voice | string | |
goals | string | |
target_audience | string | |
services | string | |
offer | string | |
offer_details | string | |
location | string | |
targeting_area | string | |
website | string | |
instagram_handle | string | |
tiktok_handle | string | |
facebook_page | string | |
monthly_budget | number |
update_clientUpdate a client
POST/api/external/tools/update_client
Scopes: crm:write
Update fields on a client profile. Only the fields you pass change.
| Field | Type | Notes |
|---|---|---|
client_id required | number or string | |
name | string | |
industry | string | |
status | string | |
context | string | |
notes | string | |
brand_voice | string | |
goals | string | |
target_audience | string | |
services | string | |
location | string | |
offer | string | |
offer_details | string | |
website | string | |
instagram_handle | string | |
tiktok_handle | string | |
facebook_page | string | |
monthly_budget | number |
Calendars and bookings 12
booking_linksBooking pagesasks for approval
POST/api/external/tools/booking_links
Scopes: bookings:read, bookings:write
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. Actions: - get: One page with all its settings and hosts. - create: A new page (fields.name required; slug optional, random when left out). (waits for approval: external) - update: Change settings (link_id, fields). Turning it on or changing its link waits for approval. (may wait for approval) - delete: Delete a page. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | get | create | update | delete | What to do. |
link_id | number | |
fields | object |
calendar_eventsCalendar events (more)asks for approval
POST/api/external/tools/calendar_events
Scopes: bookings:read, bookings:write
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. Actions: - search: Find events by words (mode title | description | exact). - create: Create an event with any field. Guests get invitations, so with guests it waits for approval. (may wait for approval) - 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) - move: Move an event to new_start / new_end (ISO). notify_guests emails the guests, so it waits for approval. (may wait for approval) - sync: Pull every connected Google calendar now (and the calendar sync links).
| Field | Type | Notes |
|---|---|---|
action required | search | create | set_recurrence | move | sync | What to do. |
title | string | |
start_time | string | ISO 8601 with offset; all-day: YYYY-MM-DD. |
end_time | string | |
all_day | boolean | |
description | string | |
location | string | |
guests | array of string | |
conferencing | none | meet | zoom | |
busy_status | busy | free | tentative | out_of_office | |
visibility | default | public | private | |
recurrence | array of string | Repeat rules, e.g. ["RRULE:FREQ=WEEKLY;BYDAY=MO"]; [] stops repeating. |
travel_time_before | number | |
travel_time_after | number | |
calendar_id | string | |
project_id | number | |
timezone | string | |
auto_decline | all | new | none | Out of office: decline new or all invites. |
decline_message | string | |
q | string | |
mode | title | description | exact | |
event_id | string | |
new_start | string | |
new_end | string | |
notify_guests | boolean |
calendar_oooOut of officeasks for approval
POST/api/external/tools/calendar_ooo
Scopes: bookings:read, bookings:write
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. Actions: - get: Your out-of-office settings and your upcoming away periods. - team: Who is away between start and end (default last 7 days to next 30). - settings: auto_decline (all | new | none), message, show_team. - add: Mark yourself away: start, end, title, all_day. - remove: Remove one of your away periods (ooo_id; the owner can remove anyone's). (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | get | team | settings | add | remove | What to do. |
start | string or number | |
end | string or number | |
title | string | |
all_day | boolean | |
auto_decline | all | new | none | |
message | string | |
show_team | boolean | |
ooo_id | number |
calendar_sync_linksCalendar sync linksasks for approval
POST/api/external/tools/calendar_sync_links
Scopes: bookings:read, bookings:write
Copy busy time between two connected Google calendars from different accounts (one-way or two-way), so each calendar shows the other's events. Actions: - list: Every sync link with its last sync and error. - create: Link source_calendar_id to destination_calendar_id (direction one_way | two_way). It syncs right away. - update: Turn a link on or off (enabled) or change direction. - delete: Remove a link. (waits for approval: delete) - sync_now: Sync one link now.
| Field | Type | Notes |
|---|---|---|
action required | list | create | update | delete | sync_now | What to do. |
link_id | string | |
source_calendar_id | string | |
destination_calendar_id | string | |
direction | one_way | two_way | |
enabled | boolean |
calendarsCalendars (Automations)asks for approval
POST/api/external/tools/calendars
Scopes: bookings:read, bookings:write
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. Actions: - list: Every calendar with type, hosts, bookings this week and upcoming. - get: One calendar: settings, hosts with their own hours, upcoming date overrides, notifications, look, public URL (calendar_id). - 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). - 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). - 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. - delete_override: Remove a date override (calendar_id, override_id). (waits for approval: delete) - add_holidays: Close a year's public holidays (calendar_id, region us|ca, year?). - 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. - 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) - 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). - 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) - get_theme: The calendar's public page look (calendar_id). - 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) - bookings: Bookings on a calendar with appt_status and needs_status (calendar_id, scope upcoming|past|cancelled|needs). - notices: What was sent for one booking (calendar_id, booking_id). - cancel_booking: Cancel a booking: removes the calendar event and sends the cancel notice (calendar_id, booking_id). (waits for approval: send email) - 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) - 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) - series: The repeating series a booking belongs to: how often, how many, and every booking in it with its status (calendar_id, booking_id). - 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) - 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). - 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). - check_external_calendar: Read an Outlook / iCloud account again: refresh the token and its calendars, set the status (connection_id). - disconnect_external_calendar: Forget an Outlook / iCloud account (connection_id). Events already written stay on that calendar. (waits for approval: delete) - 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) - 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. - 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. - 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) - 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). - 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) - needs_status: Past bookings still Booked / Confirmed (the meeting ended with no outcome), newest first (calendar_id optional). - 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?). - 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?). - 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). - get_default_theme: The workspace default look that calendars and forms without their own follow. - 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) - get_form_theme: A form's public look (form_id). - 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) - list_email_templates: Your email templates. - save_email_template: Create or change an email template (template_id?, name, subject, body with custom values). - delete_email_template: Delete an email template (template_id). (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | get | availability | set_host_hours | set_override | delete_override | ad | What to do. |
calendar_id | number | |
team_member_id | number | |
hours | object or array or null | |
date | string | |
closed | boolean | |
label | string | |
override_id | number | |
region | us | ca | |
year | number | |
duration | number | |
exclude_booking_id | number | |
notifications | object | |
settings | object | set_settings: the calendar settings to change (merged). |
theme | object or null | |
scope | upcoming | past | cancelled | needs | |
booking_id | number | |
start | string | |
end | string | book: the slot end (from availability). |
email | string | book: the guest's email. |
phone | string | book: the guest's phone (optional). |
notes | string | book: notes for the booking. |
recurrence | object | book: { frequency: weekly|biweekly|monthly, count: 2-52 } or { frequency, until: YYYY-MM-DD }. |
skip_conflicts | boolean | book: book the open dates of a series when some are taken. |
series_scope | this | following | all | cancel_series / move_series: which bookings of the series. |
connection_id | number | An Outlook / iCloud account id (external_calendars). |
busy | array of string | set_external_calendars: calendar ids that block booking times. |
write | string or null | set_external_calendars: the calendar id booking events go to, or null. |
outcome | string or null | |
form_id | string | The form id or public id. |
status | booked | confirmed | showed | no_show | cancelled | invalid | |
note | string | |
event_id | string | Calendar event id (or "<eventId>|<calendarId>"). |
event_calendar_id | string | The Google calendar id the event is on. |
contact_id | number | |
company_ids | array of number | |
deal_id | number | |
link_type | crm_contact | crm_company | crm_deal | client | business | |
link_id | number | |
setting | object or null | set_lead_pipeline: the lead setting, or null to turn it off. |
template_id | number | |
name | string | |
subject | string | |
body | string |
create_calendar_eventCreate a calendar eventasks for approval
POST/api/external/tools/create_calendar_event
Scopes: bookings:write
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.
| Field | Type | Notes |
|---|---|---|
title required | string | |
start_time required | string | ISO 8601 with offset, e.g. 2026-10-08T16:00:00-07:00. For all-day: YYYY-MM-DD. |
end_time required | string | ISO 8601. For all-day: the next day YYYY-MM-DD. |
all_day | boolean | |
description | string | |
location | string | |
guests | array of string | Email addresses to invite. Needs approval. |
conferencing | none | meet | zoom | Add a video call. |
busy_status | busy | free | |
timezone | string | IANA zone, e.g. America/Vancouver. |
delete_calendar_eventDelete a calendar eventasks for approval
POST/api/external/tools/delete_calendar_event
Scopes: bookings:write
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.
| Field | Type | Notes |
|---|---|---|
event_id required | string | |
calendar_id | string | |
all_occurrences | boolean | Delete the whole series. |
google_calendarsCalendar settingsasks for approval
POST/api/external/tools/google_calendars
Scopes: bookings:write
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. Actions: - set: visible, default_busy_status (busy | free), use_for_conflicts. Changes reshuffle auto-scheduled tasks. - set_color: color (hex). - set_primary: Make this the calendar new events go to.
| Field | Type | Notes |
|---|---|---|
action required | set | set_color | set_primary | What to do. |
calendar_id required | string | |
visible | boolean | |
default_busy_status | busy | free | |
use_for_conflicts | boolean | |
color | string |
list_booking_linksList booking linksread only
POST/api/external/tools/list_booking_links
Scopes: bookings:read
Your scheduling (booking) pages: name, link, lengths, whether they take bookings. Share a link with someone so they can book time.
| Field | Type | Notes |
|---|---|---|
include_inactive | boolean |
list_calendar_eventsList calendar eventsread only
POST/api/external/tools/list_calendar_events
Scopes: bookings:read
Events on your connected Google calendars between two dates (default: today through the next 7 days). Times are ISO 8601.
| Field | Type | Notes |
|---|---|---|
start | string | ISO date or datetime. Default now. |
end | string | ISO date or datetime. Default start + 7 days. |
query | string | Match in title, location or description. |
include_hidden | boolean | Include calendars hidden in the app. |
limit | number | Default 100. |
list_calendarsList calendarsread only
POST/api/external/tools/list_calendars
Scopes: bookings:read
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.
No arguments.
update_calendar_eventUpdate a calendar eventasks for approval
POST/api/external/tools/update_calendar_event
Scopes: bookings:write
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.
| Field | Type | Notes |
|---|---|---|
event_id required | string | From list_calendar_events. |
calendar_id | string | From list_calendar_events. |
title | string | |
start_time | string | ISO 8601 with offset. Pass end_time too (or the old end is kept). |
end_time | string | |
all_day | boolean | |
description | string | |
location | string | |
busy_status | busy | free | |
guests | array of string | The full guest list after the change (email addresses). |
Forms 1
formsFormsasks for approval
POST/api/external/tools/forms
Scopes: forms:read, forms:write
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. Actions: - get: The form: questions (layout sections), settings, columns, counts, status. - 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). - 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) - trash: Move the form to Trash. (waits for approval: delete) - responses: Every response (the linked database rows) with column names. - 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. - questions: What a respondent sees: sections and questions with their ids, for answer. - answer: Submit a response as you: answers = {questionId: value}. preview=true checks without saving.
| Field | Type | Notes |
|---|---|---|
action required | get | create | edit | trash | responses | stats | questions | answer | What to do. |
form_id | string | Form id or public id. |
name | string | |
workspace_id | number | |
database_id | string | create: a database to build the form on. |
op | meta | layout | settings | public_link | lead_pipeline | add_question | add_exis | edit: which builder change. |
fields | object | edit: the op's fields. |
answers | object | answer: {questionId: value}. |
preview | boolean | |
days | number | stats: only the last N days (omit for all time). |
Messages 25
chat_channel_bookmarksChannel bookmarksasks for approval
POST/api/external/tools/chat_channel_bookmarks
Scopes: messages:read, messages:write
The links bar at the top of a chat channel that everyone in it sees: list, add, remove, reorder. Actions: - list: The channel's bookmarks. - add: Add one: title, and a url or a message_id. - remove: Remove one (bookmark_id). (waits for approval: delete) - reorder: ordered_ids = bookmark ids in order.
| Field | Type | Notes |
|---|---|---|
action required | list | add | remove | reorder | What to do. |
channel_id | number | |
bookmark_id | number | |
title | string | |
url | string | |
message_id | number | |
ordered_ids | array of number |
chat_channelsChat channelsasks for approval
POST/api/external/tools/chat_channels
Scopes: messages:read, messages:write
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. Actions: - get: One channel: name, topic, type, members, your notification level, starred. - create: A new channel (name, optional description, workspace_id, private). You join it. - update: Change name, description, topic or type (public|private). Members only. - archive: Archive (delete) the channel. Members only; the default channel cannot be archived. (waits for approval: delete) - unarchive: Bring an archived channel back. Members only. - reorder: Sidebar order: ordered_ids = channel ids in order (channels you are in). - star: Star the channel for yourself. - unstar: Unstar it. - list_starred: Your starred channel ids. - set_notifications: Your notification level for the channel: level = all | mentions | nothing. - mark_read: Mark the channel read for you. - 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. - open_bot_dm: Find or open the DM with one of your own bots (bot_id).
| Field | Type | Notes |
|---|---|---|
action required | get | create | update | archive | unarchive | reorder | star | unstar | list_sta | What to do. |
channel_id | number | |
name | string | |
description | string | |
topic | string | |
type | public | private | |
private | boolean | create: a private channel. |
workspace_id | number | |
ordered_ids | array of number | |
level | all | mentions | nothing | |
user_ids | array of number | open_dm: the other people (you are added). |
with | string | open_dm: a name, @bot username, or botfather. |
bot_id | number |
chat_invitesChat join linksasks for approval
POST/api/external/tools/chat_invites
Scopes: messages:read, messages:write
Join links for a chat channel: anyone with the link can join, so making one waits for approval. Actions: - list: The channel's join links: url, uses, max uses, expiry. - create: A new join link (max_uses 1-1000 or none, expires_in_days 1-90, default 7). (waits for approval: external) - revoke: Turn a link off (invite_id).
| Field | Type | Notes |
|---|---|---|
action required | list | create | revoke | What to do. |
channel_id required | number | |
invite_id | number | |
max_uses | number | |
expires_in_days | number |
chat_labelsChat sidebar labelsasks for approval
POST/api/external/tools/chat_labels
Scopes: messages:read, messages:write
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?}. Actions: - list: Your labels and which channel sits under which label. - create: A new label (name, optional rule). - update: Rename, change the rule, or collapse (label_id). - reorder: ordered_ids = label ids in order. - delete: Delete a label; its channels go back to Channels / DMs. (waits for approval: delete) - assign: Move a channel under a label (channel_id, label_id; label_id null = back to default).
| Field | Type | Notes |
|---|---|---|
action required | list | create | update | reorder | delete | assign | What to do. |
label_id | number or null | |
name | string | |
rule | object | |
collapsed | boolean | |
ordered_ids | array of number | |
channel_id | number |
chat_membersChat channel membersasks for approval
POST/api/external/tools/chat_members
Scopes: messages:read, messages:write
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. Actions: - list: Members with names and roles. - 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) - remove: Remove a person (user_id) or an agent (agent_id). Waits for approval. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | add | remove | What to do. |
channel_id required | number | |
user_id | number | |
agent_id | string | |
role | member | admin |
chat_messagesChat message actionsasks for approval
POST/api/external/tools/chat_messages
Scopes: messages:read, messages:write
Everything you do to chat messages besides posting: edit or delete your own, reactions, pins, your bookmarks, scheduled messages and reminders, as you. Actions: - edit: Change the text of your own message (message_id, text). Everyone in the channel sees the edit. (waits for approval: post chat) - delete: Delete a message: your own, or any as a channel admin or owner. (waits for approval: delete) - react: Add an emoji reaction (message_id, emoji). - unreact: Remove your reaction. - pin: Pin a message in its channel. - unpin: Unpin it. - list_pins: Pinned messages in a channel (channel_id). - bookmark: Save a message to your bookmarks. - unbookmark: Remove it from your bookmarks. - list_bookmarks: Your bookmarked messages. - 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) - list_scheduled: Your scheduled messages that have not gone out. - cancel_scheduled: Cancel one of your scheduled messages (message_id). Nothing was posted yet. - remind: Remind you about a message later (message_id, at). Only you see it. - list_reminders: Your open reminders (due_only for the ones due now). - dismiss_reminder: Dismiss one of your reminders (reminder_id).
| Field | Type | Notes |
|---|---|---|
action required | edit | delete | react | unreact | pin | unpin | list_pins | bookmark | unbookmar | What to do. |
message_id | number | |
channel_id | number | |
text | string | |
emoji | string | |
at | string or number | ISO time or epoch seconds. |
reminder_id | number | |
due_only | boolean |
chat_pollsChat pollsasks for approval
POST/api/external/tools/chat_polls
Scopes: messages:read, messages:write
Polls in chat: ask one (it posts in the channel), read results, vote, close or reopen. Actions: - create: Post a poll (channel_id, question, options 2-10, multi, anonymous). (waits for approval: post chat) - get: A poll with its tally and (unless anonymous) who voted what. - vote: Your vote: options = option indexes (0-based); an empty list withdraws your vote. - close: Close the poll (the person who asked, or the owner). - reopen: Reopen it.
| Field | Type | Notes |
|---|---|---|
action required | create | get | vote | close | reopen | What to do. |
channel_id | number | |
poll_id | number | |
question | string | |
options | array of string or number | create: option texts; vote: option indexes. |
multi | boolean | |
anonymous | boolean |
crm_conversationsCRM Conversationsasks for approval
POST/api/external/tools/crm_conversations
Scopes: messages:read, messages:send, messages:write
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). Actions: - 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. - views: Saved inbox views (id, name, config). - 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 }. - update_view: Rename a view or change its filters: view_id, name?, config?. - delete_view: Delete a saved view (view_id). The conversations stay. - scheduled: Scheduled messages, soonest first: contact_id? (one person), status? scheduled (default) | sent | failed | cancelled | all. - 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) - update_scheduled: Change a waiting message (scheduled_id): body?, subject?, send_at?, cc?, bcc?, channel?. (may wait for approval) - send_scheduled_now: Send a waiting message now (scheduled_id); a text outside texting hours still waits for the window. (may wait for approval) - cancel_scheduled: Cancel a waiting message (scheduled_id). - settings: Response-time target, business hours and texting hours, with first-response stats over the last 30 days. - 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? }. - response_stats: First response over the last N days (days, default 30): answered, waiting, overdue, avg / median seconds, avg business seconds, % within target.
| Field | Type | Notes |
|---|---|---|
action required | threads | views | save_view | update_view | delete_view | scheduled | schedule | | What to do. |
box | team | mine | unassigned | unread | done | scheduled | |
q | string | |
tag | number | |
owner | string | |
pipeline | number | |
stage | string | |
channel | sms | email | messenger | instagram | |
assignee | string | |
unread | boolean | |
last | them | us | |
view_id | number | |
name | string | |
config | object | |
contact_id | number | |
status | string | |
scheduled_id | number | |
body | string | |
subject | string | |
account_id | number | |
cc | array or string | |
bcc | array or string | |
send_at | string or number | ISO date-time (with offset) or epoch seconds |
settings | object | |
days | number |
crm_sendCRM sendasks for approval
POST/api/external/tools/crm_send
Scopes: messages:read, messages:send
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. Actions: - preview: Dry run: who gets it and who is skipped and why. Nothing is sent. - 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)
| Field | Type | Notes |
|---|---|---|
action required | preview | send | What to do. |
channel | email | sms | |
contact_ids | array of number | |
company_ids | array of number | |
deal_ids | array of number | |
segment_id | number | |
filter | object | People filter: { mode: and|or, items: [{ field, op, value }] } (crm_views smart list schema). |
via | gmail | mailjet | |
account_id | number | Mailbox id to send email from (list_email_threads shows accounts). |
from_email | string | Mailjet: a verified sender (mailjet status lists them). |
from_name | string | |
reply_to | string | |
track | boolean | Mailjet: count opens and clicks. |
format | text | rich | html | |
subject | string | |
body | string |
draft_emailDraft an email or reply
POST/api/external/tools/draft_email
Scopes: messages:write
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.
| Field | Type | Notes |
|---|---|---|
account_id | number | Sending account. Default: your first connected account. |
thread_id | string | Reply in this thread. Recipients and subject come from the newest message unless you pass them. |
reply_all | boolean | With thread_id: cc everyone on the last message. |
to | array of string | |
cc | array of string | |
bcc | array of string | |
subject | string | |
body required | string | Plain text body. |
draft_id | string | Update this existing draft instead of creating one. |
email_campaignsEmail campaignsasks for approval
POST/api/external/tools/email_campaigns
Scopes: messages:read, messages:send, messages:write
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). Actions: - list: Every campaign, newest first, with status, counts (total, sent, skipped, failed), delivered, opened and clicked. - get: One campaign (campaign_id) with stats, clicks per link and its resends. - 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. - update: Change a draft or scheduled campaign (same fields as create). After it starts only the name changes. - duplicate: Copy a campaign into a new draft (campaign_id). - delete: Delete a draft, sent or cancelled campaign (campaign_id). (waits for approval: delete) - 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. - preview_audience: Who it would reach: audience + exclusions (or campaign_id of a resend). Returns total, sendable, skipped reasons with counts, a sample. - test: Send the campaign (campaign_id; subject / body override optional) to yourself only, subject prefixed [Test]. (waits for approval: send email) - send: Send now (campaign_id). Batches go out on their schedule. (waits for approval: send email) - schedule: Schedule (campaign_id, send_local "YYYY-MM-DDTHH:mm", timezone e.g. America/Vancouver). (waits for approval: send email) - unschedule: A scheduled campaign back to draft (campaign_id). - pause: Stop sending (campaign_id). Nothing more goes out until resume. - resume: Carry on (campaign_id). Batches that fell due while paused go now, the rest keep their spacing. (waits for approval: send email) - cancel: Cancel (campaign_id): everyone not sent yet is skipped. - 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) - recipients: Recipients (campaign_id), filter event = sent | delivered | opened | clicked | bounced | unsubscribed | complained | not_opened | skipped | failed | queued, q, limit (<= 1000), offset. - 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)
| Field | Type | Notes |
|---|---|---|
action required | list | get | create | update | duplicate | delete | preview | preview_audience | | What to do. |
campaign_id | number | |
name | string | |
subject | string | |
body | string | |
format | rich | html | text | |
from_email | string | |
from_name | string | |
reply_to | string | |
track | boolean | |
audience | object | { segment_ids?, tag_ids?, contact_ids?, filter? } |
exclusions | object | { segment_ids?, tag_ids? } |
batch | object | { size: people per batch (0 = all at once), every_min: minutes between batches } |
send_local | string | YYYY-MM-DDTHH:mm, wall clock time in timezone |
timezone | string | |
ab | object or null | A/B test: { on, variants: [{ subject, body? }], test_pct, metric open | click, wait_hours } |
after_hours | number | |
event | sent | delivered | opened | clicked | bounced | unsubscribed | complained | not_ | |
q | string | |
limit | number | |
offset | number | |
tag | string | |
workflow_id | number | |
contact_id | number | preview: one person on the list |
email_domainsSending domains and address checksasks for approval
POST/api/external/tools/email_domains
Scopes: messages:read, messages:write
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. Actions: - list: Every sending domain with its records, SPF / DKIM status, client and default From. - add: Add a domain: domain (client.com), business_id? (the client), label?, default_from? (address or local part on the domain), default_name?. - update: Change a domain (domain_id): label, business_id; default_from / default_name set the default sender (made when new). - senders: The domain's senders (domain_id): address, display_name, reply_to, is_default. - 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. - update_sender: Change a sender (domain_id, sender_id): display_name, reply_to, is_default: true makes it the default. - 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) - check: Ask Mailjet to check its DNS now (domain_id). - remove: Take a domain off the list (domain_id); the Mailjet sender stays on the account. (waits for approval: delete) - validate: Check an address (email) or people's primary addresses (contact_ids, <= 2000); fresh = skip the 30-day cache.
| Field | Type | Notes |
|---|---|---|
action required | list | add | update | senders | add_sender | update_sender | remove_sender | che | What to do. |
domain_id | number | |
domain | string | |
business_id | number or null | |
label | string | |
default_from | string | |
default_name | string | |
sender_id | number | |
local_part | string | |
display_name | string | |
reply_to | string | |
is_default | boolean | |
email | string | |
contact_ids | array of number | |
fresh | boolean |
get_email_threadRead an email threadread only
POST/api/external/tools/get_email_thread
Scopes: messages:read
Every message in a Gmail thread as plain text.
| Field | Type | Notes |
|---|---|---|
thread_id required | string | |
account_id required | number | From list_email_threads. |
list_chat_channelsList chat channelsread only
POST/api/external/tools/list_chat_channels
Scopes: messages:read
Plodder chat channels and DMs you can read.
| Field | Type | Notes |
|---|---|---|
include_dms | boolean | Default true. |
list_email_labelsList email labelsread only
POST/api/external/tools/list_email_labels
Scopes: messages:read
The Gmail labels of your connected accounts (system labels like INBOX and your own), with unread counts. Use the names with update_email_thread.
| Field | Type | Notes |
|---|---|---|
account_id | number | One account. Default: all of yours. |
list_email_threadsList email threadsread only
POST/api/external/tools/list_email_threads
Scopes: messages:read
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).
| Field | Type | Notes |
|---|---|---|
folder | inbox | starred | sent | calendar | drafts | archived | all | spam | trash | Default inbox. calendar = threads with invites, archived = out of the inbox. |
unread_only | boolean | |
query | string | Gmail search syntax. |
account_id | number | One account. Default: all of yours merged. |
limit | number | Default 20, max 50. |
page_token | string | next_page_token from a previous call. |
list_inboxList inboxread only
POST/api/external/tools/list_inbox
Scopes: messages:read
Your Plodder inbox: notifications (assignments, mentions, leads...), recent task activity in your workspaces, and AI actions waiting for your approval.
| Field | Type | Notes |
|---|---|---|
unread_only | boolean | |
limit | number | Per section. Default 25. |
mail_organizeEmail organizingasks for approval
POST/api/external/tools/mail_organize
Scopes: messages:read, messages:write
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. Actions: - client_links: Clients a thread is filed to (account_id + thread_id), or counts per client with neither. - file_to_client: File a thread to a client (account_id, thread_id, client_id). - unfile_from_client: Take a thread off a client. - reminders: Live reminders (snoozed and follow-ups), optionally for one thread. - remind: Snooze a thread or set a follow-up (account_id, thread_id, kind, due_at at least a minute out). - reminder_seen: Clear the "back in your inbox" marker on a thread. - cancel_reminder: Unsnooze (back to the inbox now) or cancel a follow-up (reminder_id). - tags: Tags used on your mail with counts. - set_tags: Set a thread's shared tags (account_id, thread_id, tag_ids; subject optional). - create_label: Create a Gmail label in a mailbox (account_id, name). - 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) - delete_draft: Delete a Gmail draft (account_id, draft_id). (waits for approval: delete) - 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)
| Field | Type | Notes |
|---|---|---|
action required | client_links | file_to_client | unfile_from_client | reminders | remind | remind | What to do. |
account_id | number | Your mailbox id (list_email_threads / list_email_labels show it). |
thread_id | string | Gmail thread id. |
client_id | number | |
kind | snooze | followup | remind: snooze (back in the inbox at due_at) or followup (nudge if nobody replied). |
due_at | string or number | remind: ISO time or epoch ms. |
reminder_id | number | |
tag_ids | array of number | set_tags: the thread's full tag list (option ids); [] clears. |
subject | string | |
name | string | |
draft_id | string | |
label_id | string | delete_label: Gmail label id (Label_...). |
channel_id | number | |
note | string | |
fields | object | Any other body fields the action documents. |
mail_scheduledScheduled emailasks for approval
POST/api/external/tools/mail_scheduled
Scopes: messages:read, messages:send, messages:write
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. Actions: - list: Everything waiting to send. - get: One scheduled email with its content. - reschedule: Move it (send_at) and/or replace its content (input). - cancel: Cancel it; nothing is sent (answers with its content). - send_now: Send it now. (waits for approval: send email)
| Field | Type | Notes |
|---|---|---|
action required | list | get | reschedule | cancel | send_now | What to do. |
scheduled_id | number | |
send_at | string or number | reschedule: ISO time or epoch ms. |
input | object | reschedule: the whole email (same shape get returns as input) to replace its content. |
fields | object | Any other body fields the action documents. |
mail_settingsEmail settingsasks for approval
POST/api/external/tools/mail_settings
Scopes: messages:read, messages:write
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). Actions: - get: Your email settings. - set: Change settings: undo_seconds, signature_html, signature_on, signature_replies, split_inbox, or account_id + mailbox_signature_html. - sync_status: How a mailbox's sync stands (account_id): phase backfill / resync / live, status, emails copied, and the Gmail total while copying. - force_sync: Force sync a mailbox (account_id): a live one re-reads everything from Gmail, a stuck one retries now. - gmail_signature: The signature set in Gmail for a mailbox (account_id), to copy in with set (mailbox_signature_html). - list_blocked: Senders you blocked. - block: Block senders (addresses); their inbox mail goes to Trash. (waits for approval: delete) - unblock: Unblock one sender (address). - list_rules: Your Important rules. - set_rule: Always (important true) or never (false) important for an address or domain (kind, pattern). - delete_rule: Delete an Important rule (rule_id, or kind + pattern). (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | get | set | sync_status | force_sync | gmail_signature | list_blocked | block | | What to do. |
undo_seconds | number | |
signature_html | string | |
signature_on | boolean | |
signature_replies | boolean | set: the signature also goes on replies and forwards (false = new emails only). |
split_inbox | boolean | |
account_id | number | Your mailbox id (list_email_threads / list_email_labels show it). |
mailbox_signature_html | string | set: one mailbox's own signature (with account_id); "" removes it. |
addresses | array of string | |
address | string | |
kind | address | domain | |
pattern | string | |
important | boolean | |
rule_id | number | |
fields | object | Any other body fields the action documents. |
mailjetMailjet and email editorasks for approval
POST/api/external/tools/mailjet
Scopes: messages:read, messages:send, messages:write
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. Actions: - 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. - save_settings: Your default marketing sender: from_email (must be a verified Mailjet sender), from_name, reply_to (empty = your connected Gmail). - register_webhook: Point Mailjet's event webhooks (sent, open, click, bounce, spam, blocked, unsub) at this app (app owner only). - 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. - 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)
| Field | Type | Notes |
|---|---|---|
action required | status | save_settings | register_webhook | preview | send_test | What to do. |
refresh | boolean | |
context | workflow | crm | |
subject | string | |
body | string | 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 | rich | html | text | |
from | mailbox | system | |
via | gmail | mailjet | |
mailjet_kind | marketing | transactional | |
from_email | string | |
from_name | string | |
reply_to | string | Empty = your connected Gmail; "none" = no Reply-to. |
account_id | number | |
unsubscribe | boolean | |
cold | boolean | |
contact_id | number | |
workflow_id | number | |
workspace_id | number |
post_chat_messagePost a chat messageasks for approval
POST/api/external/tools/post_chat_message
Scopes: messages:write
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.
| Field | Type | Notes |
|---|---|---|
channel_id required | number | |
text required | string | |
thread_parent_id | number | Reply in this thread. |
read_chat_messagesRead chat messagesread only
POST/api/external/tools/read_chat_messages
Scopes: messages:read
Recent messages in a channel, oldest first.
| Field | Type | Notes |
|---|---|---|
channel_id required | number | |
limit | number | Default 30, max 100. |
before_id | number | Page back from this message id. |
thread_id | number | Read one thread instead: the first message (thread_parent_id) and its replies. |
send_emailSend an emailasks for approval
POST/api/external/tools/send_email
Scopes: messages:send
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.
| Field | Type | Notes |
|---|---|---|
account_id | number | Sending account. Default: your first connected account. |
thread_id | string | Reply in this thread. Recipients and subject come from the newest message unless you pass them. |
reply_all | boolean | With thread_id: cc everyone on the last message. |
to | array of string | |
cc | array of string | |
bcc | array of string | |
subject | string | |
body required | string | Plain text body. |
draft_id | string | Send this saved draft. |
update_email_threadUpdate an email threadasks for approval
POST/api/external/tools/update_email_thread
Scopes: messages:write
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.
| Field | Type | Notes |
|---|---|---|
thread_id required | string | |
account_id required | number | From list_email_threads. |
action | read | unread | archive | inbox | star | unstar | trash | untrash | spam | notsp | |
add_labels | array of string | Label names (or ids) to add. |
remove_labels | array of string | Label names (or ids) to remove. |
Calls 6
crm_activityCRM activityasks for approval
POST/api/external/tools/crm_activity
Scopes: calls:write, crm:read, crm:write
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). Actions: - timeline: Forms, bookings, meetings, signatures, deal moves, logged calls and sends for one company_id, contact_id or deal_id. - 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. - comments: Discussion on a company_id or contact_id. - comment: Post a comment (company_id or contact_id, body, parent_id? to reply). @agent mentions get a reply. - edit_comment: Edit your own comment (comment_id, body). - delete_comment: Delete your own comment. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | timeline | log_call | comments | comment | edit_comment | delete_comment | What to do. |
company_id | number | |
contact_id | number | |
deal_id | number | |
outcome | connected | no-answer | voicemail | busy | wrong-number | |
duration_min | number | |
notes | string | |
at | number | |
direction | in | out | |
comment_id | number | |
parent_id | number | |
body | string |
dialerPower dialerasks for approval
POST/api/external/tools/dialer
Scopes: calls:read, calls:write
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. Actions: - sessions: Dialer sessions, newest first, with stats. - session: One session (session_id): stats, counts, skipped by reason, the current person card and call, the next three. - end_session: End a session (session_id): frees everyone it still holds. - 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. - dispositions: The disposition list (archived too with fields.all = true). - save_disposition: Create or update a disposition (id to update; label, connect, set, kind, actions, archived). - reorder_dispositions: Order the dispositions (order: ids; the first nine are the 1-9 keys). - archive_disposition: Archive a disposition (id). Calls keep their label. (waits for approval: delete) - scripts: Call scripts and the custom values they can use. - save_script: Create or update a script (id to update; name, body, pipeline_id = the default for that pipeline). - delete_script: Delete a script (id). (waits for approval: delete) - drops: Voicemail drops (recorded messages) with their length. - rename_drop: Rename a voicemail drop (id, name). - delete_drop: Delete a voicemail drop (id). (waits for approval: delete) - settings: Dialer settings. - update_settings: Change dialer settings (settings: any of the keys).
| Field | Type | Notes |
|---|---|---|
action required | sessions | session | end_session | set_disposition | dispositions | save_disposi | What to do. |
session_id | number | |
call_id | number | |
key | string | set_disposition: the disposition key from dispositions (e.g. interested). |
note | string | |
callback_at | number | set_disposition with a Call back disposition: epoch seconds. |
id | number | save_script / save_disposition / rename_drop / delete_*: the row id (omit to create). |
name | string | |
body | string | save_script: the script text with custom values. |
pipeline_id | number or null | save_script: the default script for this pipeline. |
label | string | save_disposition: the button name. |
connect | boolean | save_disposition: counts as reaching the person. |
set | boolean | save_disposition: counts as an appointment set. |
kind | no_answer | voicemail | busy | bad_number | contact | dnc | |
actions | object | 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 | boolean | |
order | array of number | reorder_dispositions: ids, first = hotkey 1. |
settings | object | 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 | object | Any other body fields the action documents. |
get_callGet a video callread only
POST/api/external/tools/get_call
Scopes: calls:read
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.
| Field | Type | Notes |
|---|---|---|
call_id required | string | The 12-character call id from list_calls or a /calls/<id> link. |
include_transcript | boolean |
list_callsList video callsread only
POST/api/external/tools/list_calls
Scopes: calls:read
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).
| Field | Type | Notes |
|---|---|---|
tab | all | missed | unattended | live | |
channel_id | number | |
entity_type | string | task, project, crm_company, crm_contact, client ... |
entity_id | string | |
query | string | Match in the title. |
limit | number | Default 25. |
phonePhoneasks for approval
POST/api/external/tools/phone
Scopes: calls:read, calls:write, messages:read, messages:send, messages:write
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. Actions: - conversations: Text threads (scope team or mine) and recent calls. - messages: Texts with a contact (contact_id) or a company (company_id), and whether they opted out. - send_text: Text a contact (contact_id) or a number (to): text, media. (waits for approval: external) - mark_thread_read: Mark a contact's text thread read. - assign: Assign a text thread (contact_id, assignee; null clears). - 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. - 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. - 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. - 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) - mark_done: Mark a conversation done (contact_id): it leaves the open boxes until a newer text comes in. - reopen: Reopen a done conversation (contact_id). - mark_unread: Mark a conversation unread again (contact_id): its newest incoming text. - draft_reply: An AI draft of the next text to a contact (never sent). - calls: Calls with a contact or company, or one call (call_id). - call: Call a contact or number: rings your cell first, then connects them. (waits for approval: external) - hold: Hold or resume a live call (call_id, hold). - hang_up: Hang up a live call (call_id). - 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) - transfer_state: The transfer going on a call (call_id): ringing, talking (warm, answered), completed, cancelled, no-answer. - complete_transfer: Warm transfer: you leave and the customer is connected to the person you transferred to (call_id). - cancel_transfer: Warm transfer called off: the person you rang leaves, the customer comes off hold (call_id). - mark_call_read: Mark a call (missed call, voicemail) read. - add_contact: Add a person known by phone (phone, name, company_id); returns contact_id. - move_contact: Move a person (and their texts and calls) to another company (contact_id, company_id). - inbox: Unread texts, missed calls and voicemails. - mark_inbox_read: Mark inbox items read (items from inbox, or all=true). - templates: Your text templates. - save_template: Create or update a template (template_id to update, name, body). - delete_template: Delete a template. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | conversations | messages | send_text | mark_thread_read | assign | inbox_threads | What to do. |
contact_id | number | |
company_id | number | |
to | string | A full number (+1 604 555 0123) when there is no contact. |
channel | sms | email | messenger | instagram | reply: which channel (messenger / instagram: their Facebook or Instagram DM thread, only inside the 24-hour window after their last message). |
subject | string | reply (email): subject for a new thread. |
account_id | number | reply (email): the mailbox to send from. |
cc | array or string | reply (email): CC addresses (list or comma separated). |
bcc | array or string | reply (email): BCC addresses. |
text | string | |
media | array of string | send_text: media tokens. |
call_id | number | |
hold | boolean | |
transfer_to | object | transfer: { kind: "member", id: team member id } or { kind: "number", phone }. |
mode | cold | warm | transfer: cold = you leave at once; warm = you talk to them first, then complete_transfer. |
assignee | string or null | assign: team member id or name; null clears. |
phone | string | |
name | string | |
hint | string | |
template_id | number | |
body | string | save_template: the text (merge fields {{first_name}}, {{company}}, {{my_name}}). |
all | boolean | |
items | array of object | mark_inbox_read: [{kind, id, contact_id}] from inbox. |
scope | team | mine | |
box | team | mine | unassigned | unread | done | inbox_threads: which box (default team). |
search | string | inbox_threads: name, company, number or text. |
tag_id | number | inbox_threads: a tag option id. |
owner | string | inbox_threads: an owner (team member id) or none. |
pipeline_id | number | inbox_threads: open deal in this pipeline (0 = the default). |
stage_id | string | inbox_threads: open deal in this stage (with pipeline_id). |
fields | object | Any other body fields the action documents. |
video_callsVideo callsasks for approval
POST/api/external/tools/video_calls
Scopes: calls:read, calls:write
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. Actions: - 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) - ring: Ring teammates into a live call (call_id, user_ids). (waits for approval: post chat) - chat: A call's chat. - post_chat: Post in a call's chat (call_id, body). (waits for approval: post chat) - guest_link: A join link for people without an account (call_id). (waits for approval: external) - admit_guest: Let a waiting guest in, or turn them away (call_id, guest_id, admit). Hosts. (waits for approval: external) - participant: Mute or remove someone (call_id, identity, participant_action mute|remove). Hosts. Remove waits for approval. (may wait for approval) - recording: Start or stop recording (call_id, recording start|stop). Starting waits for approval. (may wait for approval) - meetings: Call meetings you organize or are invited to. - 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) - 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) - join_meeting: Open a meeting from its link token (token): the call it runs in. The media join happens in the app. - settings: The calls switch and server (owner). - 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)
| Field | Type | Notes |
|---|---|---|
action required | start | ring | chat | post_chat | guest_link | admit_guest | participant | recor | What to do. |
call_id | string | The call's public id. |
channel_id | number | |
entity | object | {type, id} of the item the call is about. |
title | string | |
user_ids | array of number | |
body | string | |
guest_id | number | |
admit | boolean | |
identity | string | |
participant_action | mute | remove | |
recording | start | stop | |
meeting_id | number | |
description | string | |
scheduled_start | number | |
scheduled_end | number | |
lobby_enabled | boolean | |
invite_user_ids | array of number | |
invitee_emails | array of string | |
cancelled | boolean | |
token | string | join_meeting: the meeting link token. |
enabled | boolean | |
server_url | string | |
test | boolean | |
fields | object | Any other body fields the action documents. |
Automations and agents 26
agent_itemsAgents on items (mention, send to agent, sessions)asks for approval
POST/api/external/tools/agent_items
Scopes: automations:read, automations:write
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). Actions: - mention: Mention agents on an item: item, agent (one) or agents (list), text (what you ask), thread_id? (reply inside a discussion thread). - dispatch: Send an item to an agent: item, agent? (default: your built-in agent), prompt? (what to do; default: work on it). - prompt: The item as a prompt with its discussion threads: item. - sessions: Agent sessions on an item: item.
| Field | Type | Notes |
|---|---|---|
action required | mention | dispatch | prompt | sessions | What to do. |
item | string | |
agent | string or number | |
agents | array of string or number | |
text | string | |
prompt | string | |
thread_id | number |
agent_mcp_serversOutside MCP serversasks for approval
POST/api/external/tools/agent_mcp_servers
Scopes: automations:read, automations:write
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. Actions: - list: Servers and their tools. - add: Add a server (url, name?). Connects and lists its tools. (waits for approval: external) - update: Rename (name) or turn on/off (enabled). Turning on waits for approval. (may wait for approval) - refresh: Reconnect and list its tools again. - delete: Remove a server. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | add | update | refresh | delete | What to do. |
server_id | number | |
url | string | |
name | string | |
enabled | boolean |
agent_memoryAgent memoryasks for approval
POST/api/external/tools/agent_memory
Scopes: automations:read, automations:write
The memory notes your agents read and write (Settings > AI > Memory, /api/ai-agents/memory). Actions: - list: Every note. - add: Add a note by hand (content). - delete: Delete a note (note_id). (waits for approval: delete) - clear: Delete every note. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | add | delete | clear | What to do. |
note_id | number | |
content | string |
agent_runnersPaired Macsasks for approval
POST/api/external/tools/agent_runners
Scopes: automations:read, automations:write
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. Actions: - list: Paired Macs, online state and CLIs. - rename: Rename a Mac (device_id, name). - unpair: Unpair a Mac; its token stops working at once. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | rename | unpair | What to do. |
device_id | number | |
name | string |
agent_runsAgent runs and routinesasks for approval
POST/api/external/tools/agent_runs
Scopes: automations:write
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. Actions: - 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. - delete_routine: Delete a routine (routine_id). (waits for approval: delete) - stop_run: Stop a running agent run (run_id).
| Field | Type | Notes |
|---|---|---|
action required | run_routine | delete_routine | stop_run | What to do. |
routine_id | number | |
run_id | number |
agent_skillsAgent skills and instructionsasks for approval
POST/api/external/tools/agent_skills
Scopes: automations:read, automations:write
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. Actions: - get: One skill (skill_id). - create: A new skill: name, instructions, slug?, description?, share? private|team. - duplicate: Copy a skill (skill_id) to edit it (built-in ones too). - copy_installed: Copy an installed (legacy) skill in (installed_id). Owner only. - update: Change a skill you made (skill_id + any of name, slug, description, instructions, share). - delete: Delete a skill you made. (waits for approval: delete) - instructions: Your AI instructions. - set_instructions: Replace your AI instructions (text).
| Field | Type | Notes |
|---|---|---|
action required | get | create | duplicate | copy_installed | update | delete | instructions | set | What to do. |
skill_id | number | |
installed_id | number | |
name | string | |
slug | string | |
description | string | |
instructions | string | |
share | private | team | |
text | string |
agent_threadsAgent conversationsasks for approval
POST/api/external/tools/agent_threads
Scopes: automations:read, automations:write
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. Actions: - rename: New title (thread_id, title). - 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. - context: Context size (tokens used of the model window, what is summarized) and the agent's permissions for this conversation. - archive: Archive it. - unarchive: Bring it back. - mark_read: Mark it read. - delete: Delete the conversation for good. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | rename | compact | context | archive | unarchive | mark_read | delete | What to do. |
thread_id required | number | |
title | string |
agent_workAgent work (assign to an agent)asks for approval
POST/api/external/tools/agent_work
Scopes: automations:read, automations:write
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. Actions: - list: The queue: work you assigned or that runs as you. status (open | closed | queued | running | waiting | paused | review | done | stopped | error), agent, item. - get: One piece of work: id. - agents: The agents you can assign work to (their assignee values). - 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). - stop: Stop work (ends it and hands the item back): id. - pause: Pause work (a live run stops; resume picks it up): id. - resume: Resume paused work, or try again on finished / failed / stopped work with note?: id. - answer: Answer the agent's question on waiting work: id, note.
| Field | Type | Notes |
|---|---|---|
action required | list | get | agents | assign | stop | pause | resume | answer | What to do. |
id | number | |
item | string | |
agent | string or number | |
note | string | |
finish | review | done | |
status | string | |
limit | number | |
start | boolean |
agents_adminAgentsasks for approval
POST/api/external/tools/agents_admin
Scopes: automations:read, automations:write
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. Actions: - get: One agent with its settings. - create: A new agent: fields = {name, instructions?, model?, ...}. (may wait for approval) - update: Change an agent you made (agent_id, fields). (may wait for approval) - delete: Delete an agent you made. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | get | create | update | delete | What to do. |
agent_id | number | |
fields | object |
botsChat botsasks for approval
POST/api/external/tools/bots
Scopes: automations:read, automations:write
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. Actions: - list: Your bots. - create: A new bot for a runtime agent (agent_id, display_name, username?, description?). Get it running with pair. - 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) - pair: A 10-minute pairing code an outside program trades for the bot token. (waits for approval: external) - delete: Revoke the bot. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | create | update | pair | delete | What to do. |
bot_id | number | |
agent_id | string | |
display_name | string | |
username | string | |
description | string | |
avatar_url | string or null | |
webhook_url | string or null |
coding_sessionsCoding sessionsasks for approval
POST/api/external/tools/coding_sessions
Scopes: automations:read, automations:write
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. Actions: - get: A session (session_id, or thread_id of its conversation) with its change sets. - start: A new coding conversation and its first turn: prompt, repository (owner/name or URL), branch? (default main), kind? (runtime), agent_id?, size?. - turn: The next turn of a session (session_id, prompt). - stop: Stop the session's runtime (session_id). remove=true also deletes the sandbox (waits for approval: delete). (may wait for approval) - link_pr: Link a pull request by hand (session_id, pr_url); its diff is read into Changes. - changes: The diff of a change set (session_id, set_id? default latest), as file paths with added/removed line counts and hunks. - notes: Review notes on the session's changes. - add_note: A review note: file_path, body, line?, side? L|R. - resolve_note: Mark a note resolved or not (note_id, resolved). - delete_note: Delete your own note (note_id). (waits for approval: delete) - runtimes: Which coding runtimes are ready for you, and the Codex environment. - set_codex_environment: Pick the Codex cloud environment (environment_id, environment_label?). The Cursor API key is entered by you in Settings > Coding agents. - clear_codex_environment: Forget the Codex environment choice. - 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. - task_pull_requests: Pull requests linked to a task (task), through coding sessions or a reference in their description, with state. - preferences: Your coding preferences: track work with tasks, open pull requests. - set_preferences: Change coding preferences (create_tasks?, open_pull_requests?).
| Field | Type | Notes |
|---|---|---|
action required | get | start | turn | stop | link_pr | changes | notes | add_note | resolve_note | What to do. |
session_id | number | |
thread_id | number | |
task | string | |
create_tasks | boolean | |
open_pull_requests | boolean | |
prompt | string | |
repository | string | |
branch | string | |
kind | string | |
agent_id | number | |
size | string | |
remove | boolean | |
pr_url | string | |
set_id | number | |
note_id | number | |
file_path | string | |
body | string | |
line | number | |
side | L | R | |
resolved | boolean | |
environment_id | string | |
environment_label | string |
conversation_turnConversation turnasks for approval
POST/api/external/tools/conversation_turn
Scopes: automations:write
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). Actions: - set_vars: Save answers: vars = { name: value } (the variables the current step asks for; numbers as numbers, yes / no as yes or no). - 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. - reply: The message to send them (text). One reply per turn; keep it short on texts. - offer_times: Real open times on the playbook calendar (or the step's). Present them plainly. - book: Book one offered time: slot_id (s1, s2...) and confirmed = true only after they said yes to that exact time. Books once. - 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. - tag: Add a tag (tag), or remove it (remove = true). - update_field: Set a person field: field (name | email | phone | title | lead_source | notes | f:<custom field id>), value. - move_stage: Move their open deal (or make one): pipeline_id, stage_id. - 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. - end: End the conversation: outcome = qualified | disqualified | booked | not_now | other (message optional, your last line). - score: Every turn: level = hot | warm | cold, reason = one short line. - note: A note on the timeline for the team (text). - 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.
| Field | Type | Notes |
|---|---|---|
action required | set_vars | go_to | reply | offer_times | book | send_media | tag | update_field | What to do. |
vars | object | set_vars: { variable: value }. |
target | string | |
text | string | |
slot_id | string | |
confirmed | boolean | |
tag | string | |
remove | boolean | |
field | string | |
value | string | |
pipeline_id | number | |
stage_id | string | |
reason | string | |
outcome | string | |
message | string | |
level | string | |
name | string | |
input | object | call_tool: the tool input. |
media_id | number | send_media: the photo id from PHOTOS. |
create_routineSchedule a routineasks for approval
POST/api/external/tools/create_routine
Scopes: automations:write
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.
| Field | Type | Notes |
|---|---|---|
name required | string | |
instructions required | string | What the agent should do each time it runs. |
cron | string | Repeat on this 5-field cron schedule, for example "0 9 * * 1-5" (9am on weekdays). |
run_at | string | Run once at this ISO date-time (with offset), then turn off. Use instead of cron. |
timezone | string | IANA zone for cron, default America/Vancouver. |
schedule | string | 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 | number | Run as this agent (list_agents). Default: your default agent. |
model | string | Run on this model with no agent setup instead. |
channel_id | number | Post results in this chat channel instead of the Inbox. |
crm_automationsAutomationsasks for approval
POST/api/external/tools/crm_automations
Scopes: automations:read, automations:write
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}}. Actions: - list: Your workflows with status, counts and folder_id, plus your folders. - get: One workflow: draft graph, settings, problems blocking publish, per-step counts, webhook URL (workflow_id). - catalog: Node types with their ports and config fields, trigger events, condition operators, starter templates. - 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?). - create: A new draft: name, template? (booking_reminders | client_onboarding | lead_follow_up | cold_outreach), graph?, settings?, workspace_id?, folder_id?. - 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?. - publish: Publish the draft as a new version and turn the workflow on (workflow_id, note?). (may wait for approval) - pause: Pause: no new people, everyone held where they are (workflow_id). - resume: Resume a paused workflow (workflow_id). (may wait for approval) - delete: Delete a workflow; people still in it are removed (workflow_id). (waits for approval: delete) - versions: Published versions (workflow_id). - restore_version: Load an old version into the draft (workflow_id, version). - enroll: Add people by hand: workflow_id, contact_ids and / or segment_id (max 2,000). (may wait for approval) - remove: Take people out: workflow_id, contact_ids or enrollment_ids. - 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. - enrollments: Who is in it: workflow_id, status? (active | completed | exited | removed | failed), limit?, offset?. - 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. - 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: {...} } }). - values: Workspace custom values ({{workspace.key}}): workspace_id. - set_value: Add or change a workspace custom value: workspace_id, label, key?, value. - delete_value: Remove a workspace custom value: workspace_id, key. - bookings: Your recent and upcoming bookings with their showed / no-show mark. - 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) - folders: Your workflow folders: id, name, parent_id (null = top level), workflows in each. - create_folder: A new folder: name, parent_id? (a folder to nest it in). - update_folder: Rename a folder or move it under another one: folder_id, name?, parent_id? (null = top level). - 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) - move: Put a workflow in a folder: workflow_id, folder_id (null = No folder). - 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. - 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. - delete_connection: Remove a connection (connection_id). Steps using it skip until another is picked. (waits for approval: delete) - 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}}). - create_score: A new named score: name?, preset? (forms | emails | booked | lead | blank: starting rules), rules? [{ event, value?, points }], decay? { points, days }. - 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). - 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) - contact_scores: One person's value in every score (contact_id). - 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. - score_history: One person's score changes, newest first (contact_id, profile_id? for one score). - suppressions: The unsubscribe / bounce list automations never send to. - unsuppress: Take an address off the suppression list: channel (email | sms), address. (waits for approval: send email)
| Field | Type | Notes |
|---|---|---|
action required | list | get | catalog | options | create | update | publish | pause | resume | de | What to do. |
workflow_id | number | |
folder_id | number or null | |
parent_id | number or null | |
name | string | |
description | string | |
template | string | |
graph | object | { nodes, edges } |
settings | object | |
workspace_id | number | |
form_id | number | |
note | string | |
version | number | |
contact_ids | array of number | |
enrollment_ids | array of number | |
segment_id | number | |
status | string | |
enrollment_id | number | |
node_id | string | |
contact_id | number | |
sample | object | |
limit | number | |
offset | number | |
label | string | |
key | string | |
value | string | |
booking_id | number | |
outcome | string or null | |
channel | email | sms | |
address | string | |
connection_id | number | |
rules | array of object | |
profile_id | number | |
preset | forms | emails | booked | lead | blank | |
decay | object | |
points | number | |
set | number | |
reason | string | |
id | number | |
kind | slack | sheets | |
url | string |
get_routineRead a routineread only
POST/api/external/tools/get_routine
Scopes: automations:read
One routine with its full instructions, triggers and its recent runs.
| Field | Type | Notes |
|---|---|---|
routine_id required | number |
github_pullsGitHub pull requestsasks for approval
POST/api/external/tools/github_pulls
Scopes: automations:read, automations:write
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. Actions: - 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. - get: One pull request (repo owner/name, number) with changed files and the conversation. - repositories: Repositories you can hand a coding agent. - comment: Comment on the pull request (body). (waits for approval: external) - review: Submit a review: event APPROVE|REQUEST_CHANGES|COMMENT, body?. (waits for approval: external) - merge: Merge it (method merge|squash|rebase, default squash). Always waits for approval. (waits for approval: external)
| Field | Type | Notes |
|---|---|---|
action required | list | get | repositories | comment | review | merge | What to do. |
repo | string | owner/name. |
number | number | |
body | string | |
event | APPROVE | REQUEST_CHANGES | COMMENT | |
method | merge | squash | rebase | |
scope | all | authored | assigned | involving | review_requests | |
state | open | closed | all | |
repository | array of string | |
author | array of string | |
label | array of string | |
q | string | |
limit | number | |
priority | array of urgent | high | medium | low | none | |
linked | array of agent | ticket | customer | |
sort | recently_updated | least_recently_updated | newest | oldest | priority |
installed_skillsInstalled skill libraryasks for approval
POST/api/external/tools/installed_skills
Scopes: automations:read, automations:write
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. Actions: - list: Every installed skill. - get: One skill (skill_id) and the agents that have it. - set_enabled: Turn a skill on or off (skill_id, enabled). - assign: Give a skill to a runtime agent or take it away (skill_id, agent_id, enabled). - delete: Delete an installed skill (skill_id). (waits for approval: delete) - install_from_github: Install a skill from a GitHub URL (github_url). Downloads outside code. (waits for approval: external)
| Field | Type | Notes |
|---|---|---|
action required | list | get | set_enabled | assign | delete | install_from_github | What to do. |
skill_id | number | |
agent_id | string | |
enabled | boolean | |
github_url | string |
knowledgeKnowledge baseasks for approval
POST/api/external/tools/knowledge
Scopes: automations:read, automations:write
Workspace knowledge entries (text notes and links) agents can read (/api/knowledge). Actions: - list: Entries in a workspace (workspace_id?) or every workspace you are in. - create: A new entry: title, type? text|url, content?, url?, workspace_id?, private?. - update: Change an entry (entry_id + fields). - delete: Delete an entry. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | create | update | delete | What to do. |
entry_id | number | |
workspace_id | number | |
title | string | |
type | string | |
content | string | |
url | string | |
private | boolean | |
fields | object |
list_agentsList agentsread only
POST/api/external/tools/list_agents
Scopes: automations:read
The agents you can use or delegate to (yours and the team's shared ones): id, @handle, what each is for, its model.
No arguments.
list_routinesList routinesread only
POST/api/external/tools/list_routines
Scopes: automations:read
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.
| Field | Type | Notes |
|---|---|---|
text | string | Name or instructions contain this. |
enabled | boolean | Only enabled (true) or paused (false) routines. |
list_skillsList skillsread only
POST/api/external/tools/list_skills
Scopes: automations:read
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.
| Field | Type | Notes |
|---|---|---|
text | string | Name or description contains this. |
memoriesMemory browserasks for approval
POST/api/external/tools/memories
Scopes: automations:read, automations:write
The long-term memory store behind the memory browser (/api/memories): search and delete. Owner only. Agent notes live in agent_memory. Actions: - browse: Search memories (q, sector semantic/episodic, source, client, page). 50 a page, with counts by source and sector. - delete: Delete a memory (memory_id). (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | browse | delete | What to do. |
q | string | |
sector | string | |
source | string | |
client | string | |
page | number | |
memory_id | number |
pathwaysPathways (Conversation AI)asks for approval
POST/api/external/tools/pathways
Scopes: automations:read, automations:write, messages:send
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). Actions: - overview: Playbooks (with what blocks each from going on), pathways, settings, shortcuts, counts, starters. - pickers: Agents, smart lists, calendars, pipelines, team, workflows, person fields for the editors. - playbook: One playbook (playbook_id) with its problems and voice examples. - create_playbook: New playbook (set: { name, agent_id, ...config }); made off. - 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) - delete_playbook: Delete a playbook (playbook_id) with no open conversations. (waits for approval: delete) - reorder_playbooks: Priority order (ids): the first whose rules fit answers. - pathway: One pathway (pathway_id): draft graph, problems, versions, playbooks using it, saved tests. - 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. - agent_home: One agent's Conversation AI home (agent_id): its jobs (with problems), its pathways, shared settings, counts, 30 day results, recent conversations. - 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). - agent_settings: The agent's shared conversation settings (agent_id). - 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. - 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. - agent_media: An agent's media library (agent_id): its photos, its jobs' and the shared ones. - 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. - template: One template with its graph and problems (key: "builtin:<key>" or a saved template id). - 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. - 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. - 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). - delete_template: Delete a saved template (template_id). Copies already on agents stay. (waits for approval: delete) - save_pathway: Save the draft (pathway_id, set: { name?, description?, graph?, require_tests? }). - publish_pathway: Publish the draft as a new version: stage = staging (test first) or live (conversations use it). (may wait for approval) - set_live_version: Make a version live (pathway_id, version): promote staging or roll back. (waits for approval: send email) - duplicate_pathway: Copy a pathway (pathway_id) as a new draft. - delete_pathway: Delete a pathway (pathway_id) no playbook uses. (waits for approval: delete) - settings: Kill switch, auto-pause limits (opt-outs / complaints a day), channel shortcuts. - set_settings: paused (true stops every agent at once; false lets them go again), reason, optout_limit, complaint_limit. (may wait for approval) - 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) - conversations: Conversations (state: open | active | human | handed_off | ended, playbook_id?, agent_id?, q?). - thread: The agent side of one person's thread (contact_id): conversation, log, pending suggested replies. - take_over: A person takes the conversation (conversation_id): the agent stops. - hand_back: Give the conversation back to the agent (conversation_id). (waits for approval: send email) - end_conversation: End a conversation (conversation_id, outcome: qualified | disqualified | booked | not_now | other). - send_draft: Send the agent's suggested reply (draft_id, text? edited). Goes out paced, as the agent. (waits for approval: send email) - discard_draft: Drop a suggested reply (draft_id). - voice_examples: Learned voice examples of a playbook (playbook_id): drafts a person edited. - voice_profile: The texting-voice profile of a playbook (playbook_id): source, style facts and the guidance the agent writes to. - 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. - 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. - 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). - update_media: Change a media item (media_id, set: { title?, when?, tags?, playbook_id? (null = every playbook) }). - delete_media: Take an item out of the media library (media_id); the Drive file stays. (waits for approval: delete) - delete_voice_example: Forget one voice example (example_id). (waits for approval: delete) - 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. - tests: Saved tests of a pathway (pathway_id) with their last runs. - 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) }). - delete_test: Delete a saved test (test_id). (waits for approval: delete) - run_test: Run one saved test (test_id). - run_suite: Run every saved test of a pathway (pathway_id). - results: Results per playbook, pathway and A/B side over days? (default 30): conversations, replied, booked, showed, hand-offs, opt-outs. - campaigns: Agent text campaigns with their stats. - campaign: One campaign (campaign_id): settings, stats, people. - preview_audience: Who an audience is now (set: { segment_ids?, tags?, contact_ids?, exclude_segment_ids? }). - create_campaign: New draft campaign (set: { name, playbook_id, audience, opener: { mode: agent | fixed, text }, pacing: { per_hour, daily_cap } }). - update_campaign: Change a campaign (campaign_id, set); audience / playbook / opener only while a draft. - start_campaign: Start or resume (campaign_id): texts go out by the drip (consent, texting hours, caps). (waits for approval: send email) - pause_campaign: Pause (campaign_id). - stop_campaign: Stop for good (campaign_id); people not reached yet are skipped. - delete_campaign: Delete a draft, stopped or finished campaign (campaign_id). (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | overview | pickers | playbook | create_playbook | update_playbook | delete_playb | What to do. |
playbook_id | number | |
pathway_id | number | |
test_id | number | |
campaign_id | number | |
days | number | |
message | string | |
test_state | object | |
lead | object | |
test_version | string | |
conversation_id | number | |
draft_id | number | |
example_id | number | |
contact_id | number | |
set | object | Fields to set. |
ids | array of number | |
name | string | |
starter | number | |
import_json | any | A Bland pathway export (object or JSON text). |
graph | object | |
stage | string | |
note | string | |
version | number | |
paused | boolean | |
reason | string | |
optout_limit | number | |
complaint_limit | number | |
channel | string | |
on | boolean | |
agent_id | number | |
segment_id | number | |
pipeline_id | number | |
stage_id | string | |
state | string | |
q | string | |
outcome | string | |
text | string | |
template_id | number | |
key | string | Template key: "builtin:<key>" or a saved template id |
from_pathway_id | number | |
job | boolean | |
preset | string | |
job_ids | array of number | |
media_id | number | |
drive_file | string | |
data_base64 | string | |
filename | string | |
mime | string |
read_skillRead a skillread only
POST/api/external/tools/read_skill
Scopes: automations:read
The full instructions of one skill, by id or /command.
| Field | Type | Notes |
|---|---|---|
skill required | string | Skill id or its /command. |
runtime_agent_skillsRuntime agent skillsasks for approval
POST/api/external/tools/runtime_agent_skills
Scopes: automations:read, automations:write
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. Actions: - list: Skills of one runtime agent (agent_id), or all. - create: A new skill: agent_id, name, and any of description, type, trigger_type, trigger_config, prompt, enabled, mode, visibility. - update: Change a skill (skill_id + fields). - delete: Delete a skill (skill_id). (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | create | update | delete | What to do. |
skill_id | number | |
agent_id | string | |
name | string | |
description | string | |
type | string | |
trigger_type | string | |
trigger_config | string | |
prompt | string | |
enabled | boolean | |
mode | string | |
visibility | string |
update_routineChange a routineasks for approval
POST/api/external/tools/update_routine
Scopes: automations:write
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.
| Field | Type | Notes |
|---|---|---|
routine_id required | number | |
enabled | boolean | false pauses it, true resumes it. |
name | string | |
instructions | string | |
cron | string | Repeat on this 5-field cron schedule, for example "0 9 * * 1-5" (9am on weekdays). |
run_at | string | Run once at this ISO date-time (with offset), then turn off. Use instead of cron. |
timezone | string | IANA zone for cron, default America/Vancouver. |
schedule | string | 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 | number |
Reports 2
my_activityMy activityread onlyasks for approval
POST/api/external/tools/my_activity
Scopes: reports:read
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. Actions: - summary: Your actions in the last `days` (1-31, default 7) with the items they touched. - graph: Actions per local day for the past year (tz_offset_min = your getTimezoneOffset). - recent: Your latest actions (limit).
| Field | Type | Notes |
|---|---|---|
action required | summary | graph | recent | What to do. |
days | number | |
tz_offset_min | number | |
limit | number |
reportsReports (CRM > Reports)asks for approval
POST/api/external/tools/reports
Scopes: reports:read, reports:write
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. Actions: - list: Your dashboards and those shared with you, plus the templates. - get: One dashboard with its widgets, filters and grid positions (dashboard_id). - 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. - add_metric: Add a library metric to a dashboard as a widget (dashboard_id, metric_key, position?). - 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. - options: Values for filters you can use: owners, pipelines, stages, businesses, tags, lead sources, calendars, forms, workflows. - metric_list: Saved custom metrics: named equations ("Cost per showed call", "Profit") every dashboard can use (widget config {metric: "formula", custom_metric: id}). - 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"}). - metric_update: Change a saved custom metric (metric_id, name?, formula?); every widget using it follows. - metric_delete: Delete a saved custom metric (metric_id); widgets using it show "This metric was removed." (waits for approval: delete) - 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?). - run_widget: Run a saved widget under its dashboard filters (dashboard_id, widget_id, filters? to override, tz?). - run_dashboard: Run every widget on a dashboard (dashboard_id, filters?, tz?): one answer line per widget. - create: New dashboard, blank or from a template (name?, template?: sales_overview | calls | funnels | funnel | monthly_revenue, description?, shared?, workspace_id?, filters?). - update: Rename, describe, set dashboard filters, or share (dashboard_id, name?, description?, filters?, shared?: private | view | edit, workspace_id?). Sharing is owner only. - duplicate: Your own private copy of a dashboard (dashboard_id, name?). - delete: Delete a dashboard you own (dashboard_id). (waits for approval: delete) - add_widget: Add a widget (dashboard_id, config, position? {x, y, w, h} on the 12-column grid; default: the next free spot). - update_widget: Change a widget's config or position (dashboard_id, widget_id, config?, position?). config replaces the whole config. - delete_widget: Remove a widget (dashboard_id, widget_id). (waits for approval: delete) - set_layout: Move / resize several widgets at once (dashboard_id, positions: [{id, x, y, w, h}]). - 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. - 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. - 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.
| Field | Type | Notes |
|---|---|---|
action required | list | get | library | add_metric | catalog | options | metric_list | metric_cre | What to do. |
dashboard_id | number | |
metric_id | number | metric_update / metric_delete: a saved custom metric id (metric_list). |
formula | object | metric_create / metric_update: {vars: {A: {...}, B: {...}}, expr: "A / B", format?}. |
widget_id | number | |
name | string | |
description | string | |
template | sales_overview | calls | funnels | funnel | monthly_revenue | |
shared | private | view | edit | |
workspace_id | number or null | |
config | object | A widget config (see the tool description). |
filters | object | Dashboard filters: {range: {preset, from?, to?}, compare?, owner?, pipeline?, pipeline_kind?: leads|sales, business?, tag?, lead_source?}. |
position | object | {x, y, w, h} in grid steps (12 columns, rows of 40px). |
positions | array of object | |
metric_key | string | A key from action library (e.g. revenue, speed_to_lead, velocity, show_rate, cpl). |
tz | string | IANA time zone for ranges and buckets (default the dashboard's, else UTC). |
kind | appointments | calls | texts | emails | attribution | list_report / export_list_report: which list. |
range | string | 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 | string | list_report custom range start, YYYY-MM-DD. |
to | string | list_report custom range end (inclusive), YYYY-MM-DD. |
q | string | list_report search (name, email, number, message). |
sort | string | list_report: a column key to sort by. |
dir | asc | desc | |
page | number | |
page_size | number | |
columns | array of string | export_list_report: column keys to include (default all). |
where | object or string | list_report / export_list_report: the filter panel conditions {mode, items: [{field, op, value}]} (fields: list_report_options where_fields). |
Files 4
driveDrive and foldersasks for approval
POST/api/external/tools/drive
Scopes: files:read, files:write
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). Actions: - folders: Folders in a workspace. - create_folder: A folder (workspace_id, name, color, parent_id). - update_folder: Change a folder (folder_id, set). - color_folder_tree: Give a folder and everything in it one colour (folder_id, color). - delete_folder: Delete a folder. (waits for approval: delete) - 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). - move: Move Drive items (items, target). - rename: Rename one Drive item (items: [one], name). - trash: Move Drive items to Trash (items). (waits for approval: delete) - restore: Bring Drive items back from Trash (items). - 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. - delete_forever: Delete Drive items that are already in Trash, for good (items). (waits for approval: delete) - save_attachment: Copy an attachment into Drive (source attachment with attachment, url with url, or mail with mail), to target. - work_trash: Deleted projects, stages and tasks. - restore_work: Restore deleted projects, stages or tasks (project_ids, stage_ids, task_ids). - purge_work: Delete deleted projects, stages or tasks for good (project_ids, stage_ids, task_ids). (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | folders | create_folder | update_folder | color_folder_tree | delete_folder | si | What to do. |
folder_id | number | |
name | string | |
color | string | |
parent_id | number | |
workspace_id | number | |
set | object | update_folder: fields (name, color, parent_id, sort_order, ...). |
items | array of object | [{kind, id}] |
target | object | {folderId?, workspaceId?} |
type | string | sidebar_move: project | doc | folder | database; trash: project | stage | task. |
id | number | |
target_folder_id | number | |
target_project_id | number | |
sort_order | number | |
source | attachment | url | mail | |
attachment | string | save_attachment: msg:<id> | task:<id> |
url | string | |
mail | object | save_attachment from mail: {account, message, id, name, type} |
project_ids | array of number | |
stage_ids | array of number | |
task_ids | array of number | |
fields | object | Any other body fields the action documents. |
importsImportsasks for approval
POST/api/external/tools/imports
Scopes: files:read, files:write
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. Actions: - list: Your imports, newest first. - get: One import with its preview. - create: A new import that previews first (source, workspace_id, options). Nothing is written until start. - set_options: Change an import's options; the preview runs again (job_id, options). - start: Run the import the preview showed (again = update, never duplicate). - cancel: Stop a running import after its current item; what was written stays.
| Field | Type | Notes |
|---|---|---|
action required | list | get | create | set_options | start | cancel | What to do. |
job_id | number | |
source | string | create: github. |
workspace_id | number | |
options | object | create / set_options: source options (repo, picked items, people mapping, filters). |
fields | object | Any other body fields the action documents. |
read_fileRead a fileread only
POST/api/external/tools/read_file
Scopes: files:read
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.
| Field | Type | Notes |
|---|---|---|
file required | string | File id, public id, or file:<id>. |
max_chars | number | Return at most this many characters (default 20000, max 100000). |
save_fileSave a file to Drive
POST/api/external/tools/save_file
Scopes: files:write
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.
| Field | Type | Notes |
|---|---|---|
name required | string | File name with extension, for example "October report.md". |
content required | string | The file's text. |
folder_id | number | Drive folder id (list_items types ["folder"]). |
workspace_id | number | Workspace root, when no folder is given. Default: your own space. |
E-sign 2
documentsDocuments and contractsasks for approval
POST/api/external/tools/documents
Scopes: esign:read, esign:write
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}]. Actions: - templates: List templates (type contract|proposal|agreement|other, query) and the starters. - get_template: One template with its fields, roles, fill-ins and settings. - 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. - update_template: Change a template: set {name?, type?, fields?, roles?, settings? {order, doc_name, subject, message, expires_days, remind_days}, inputs? [{key,label,default}]}. - duplicate_template: Copy a template. - delete_template: Remove a template (documents made from it stay). (waits for approval: delete) - options: The values you can use in templates (incl. Custom values) and who can countersign. - preview: Check a template for a person (contact_id / deal_id, or the sample person): empty values, signer fields, pages, warnings. Nothing is stored. - check_values: Which values are empty for this person, and the fill-ins the template asks for. - 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. - 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) - documents: Documents for a person, deal or template (contact_id, deal_id, template, status), with status and signers. - status: One document: status, each signer, links to the PDF and the signed copy. - cancel: Cancel a sent document (its signing links stop working). (waits for approval: external)
| Field | Type | Notes |
|---|---|---|
action required | templates | get_template | create_template | update_template | duplicate_templat | What to do. |
pay_on_sign | object or boolean | send: collect a payment when the client signs { amount?, currency?, description? }. |
template | string | Template id or public id. |
document | string | Document id or public id. |
kind | pdf | written | |
type | contract | proposal | agreement | other | |
name | string | |
file | string | Drive file id or public id (a PDF or .docx). |
starter | service_agreement | proposal | mutual_nda | |
workspace_id | number | |
query | string | |
set | object | update_template changes. |
contact_id | number | |
deal_id | number | |
inputs | object | Fill-in values {key: text}; text may use {{values}}. |
countersign_user_id | number | |
order | client_first | company_first | parallel | |
subject | string | |
message | string | |
expires_days | number | |
remind_days | number | |
role_emails | object | |
status | string |
pdfPDF annotate and signasks for approval
POST/api/external/tools/pdf
Scopes: esign:read, esign:write, files:read, files:write
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). Actions: - marks: Every mark on the file and whether you can edit. - add_mark: Add a mark: page (1-based) and data. - update_mark: Change a mark's data (mark_id). - delete_mark: Remove a mark (mark_id). (waits for approval: delete) - export: Save a copy with every mark flattened into a new Drive file next to the original. - 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. - requests: Signature requests on the file; options=true also lists who you can ask (teammates, portals, CRM people). - 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) - remind: Remind a teammate signer (request_id; once an hour). - cancel_request: Cancel the whole envelope (request_id). (waits for approval: external) - my_requests: Requests waiting on you to sign. - view_request: A request made to you: the file, marks and fields to fill. - 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) - decline: Decline a request made to you (request_id, reason). (waits for approval: external) - signatures: Your saved signatures (ids and kinds). - save_signature: Save a signature picture: kind draw|type|upload, image (PNG data URL). - delete_signature: Delete a saved signature (signature_id). (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | marks | add_mark | update_mark | delete_mark | export | outline | requests | req | What to do. |
file | string | Drive file id, public id or file:<id> (a PDF). |
page | number | |
data | object | Mark data (see description). |
mark_id | number | |
options | boolean | |
signers | array of object | |
message | string | |
order | sequential | parallel | |
expiresDays | number | |
autoRemindDays | number | |
request_id | number | |
signature_id | number | |
values | array of object | |
placements | array of object | sign without sender fields: [{page, data: signature or text mark}]. |
reason | string | |
kind | draw | type | upload | |
image | string | |
query | string | requests with options: filter who you can ask. |
Payments 1
paymentsPayments (Stripe, Square, Whop, FanBasis)asks for approval
POST/api/external/tools/payments
Scopes: payments:read, payments:write
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. Actions: - status: Which providers are connected (Stripe mode, account names, Square location, last sync, webhook set up) and the default. Never a key. - 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. - list: Payments, newest first (contact_id?, deal_id?, status?, kind?, provider?, limit?). - get: One payment (payment_id). - create_link: A Stripe payment link (single use) for an amount, lines or a deal. Returns the url to share. - 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) - create_subscription: A retainer: monthly / yearly (interval) for a person or deal, collect link (card on Stripe) or invoice. (waits for approval: money) - refund: Refund a paid payment (payment_id, amount? = all that is left, reason?). (waits for approval: money) - cancel: Stop a payment link, void an open invoice, or end a subscription (at_period_end?). (waits for approval: external) - resend: Send an open invoice again (via stripe | email). (waits for approval: external) - 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)
| Field | Type | Notes |
|---|---|---|
action required | status | test_connection | list | get | create_link | create_invoice | create_su | What to do. |
contact_id | number | |
deal_id | number | |
payment_id | number | |
amount | number or string | In the currency, e.g. 1500 = $1,500.00. |
amount_cents | number | Minor units (cents). |
lines | array of object | [{ name, qty, unit_cents }] |
use_deal_lines | boolean | With deal_id: use its line items (default) or false for its value. |
currency | string | 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 | string | |
due_days | number | |
due_date | string | YYYY-MM-DD |
send | stripe | email | none | Invoices: Stripe emails it, our billing email, or none (just the link). |
interval | month | year | |
collect | link | invoice | Subscriptions: link = the client enters a card on Stripe; invoice = Stripe emails an invoice each period. |
status | string | |
kind | link | invoice | subscription | checkout | |
limit | number | |
reason | requested_by_customer | duplicate | fraudulent | |
at_period_end | boolean | |
via | stripe | email | |
idem_key | string | Optional: the same key returns the same payment. |
provider | stripe | square | whop | fanbasis | manual | Which provider makes it (create_*), filters list, or names where a recorded payment was paid (manual = cash / bank / other). |
paid_at | string | record: when it was paid (YYYY-MM-DD), default now. |
Commissions 1
commissionsCommissions (closers and setters)asks for approval
POST/api/external/tools/commissions
Scopes: commissions:read, commissions:write
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". Actions: - overview: The month (period): per payee earned, adjustments, payable, paid, owed and status, plus totals. Brings the ledger up to date first. - list_payees: The sales team with roles, own rules (null = default) and QuickBooks vendor. - get_payee: One payee (payee_id): rules in effect, lines (period or the last 12 months), statements, months. - create_payee: Add a payee: member_key (a team member) or name + email? (an outside person), contact_id?, roles?, rules?, notes?, provider_refs?. - 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). - remove_payee: Remove a payee (payee_id): gone if they never earned, else made inactive (history stays). (waits for approval: delete) - get_deal_roles: Closer and Setter on a deal (deal_id); closer from_owner = the deal owner's payee. - set_deal_roles: Set a deal's closer_payee_id and / or setter_payee_id (null clears; closer cleared = the deal owner). - 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>"). - list_no_person: Paid payments whose buyer email matches no CRM person (email, provider, payments, amount_cents, payment_ids). - 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. - 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. - 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. - create_payee_for_seller: Make a payee from a provider seller (provider, ref, name?, email?, role?, member_key?) with that provider id, then sync. - add_adjustment: A bonus or deduction for a payee (payee_id, period, amount, note). Refused when that month is finalized. - remove_adjustment: Remove an adjustment (line_id) while its month is a draft. (waits for approval: delete) - sync: Bring the ledger up to date now (it also runs every 10 minutes). - recalculate: Rebuild a month's automatic lines after a rate change (period). Draft months only; adjustments stay. - list_statements: Statements (period?, payee_id?). - generate_statements: Draft statements for a month (period), one per payee with lines (all: every active payee too). - finalize_statement: Finalize a statement (statement_id): locks its lines and freezes the totals. Without statement_id: every draft of the period. - reopen_statement: Unlock a finalized statement (statement_id), only before it is a bill in QuickBooks. - get_settings: The workspace default rules, time zone, first month counted, currency and QuickBooks bill settings. - update_settings: Change settings (settings: { rules?, timezone?, start_period?, qbo_account_id?, qbo_account_name?, qbo_line_mode?, qbo_due_days? }). - quickbooks_status: Is QuickBooks connected (company, sandbox or production, home currency, last error). Never a token. - list_vendors: QuickBooks vendors (q? name contains). - list_accounts: QuickBooks expense accounts (for qbo_account_id). - link_vendor: Link a payee (payee_id) to a QuickBooks vendor (vendor_id; null unlinks). - 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) - 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)
| Field | Type | Notes |
|---|---|---|
action required | overview | list_payees | get_payee | create_payee | update_payee | remove_payee | What to do. |
period | string | YYYY-MM, a month name (september) or "last month". Default: this month. |
payee_id | number | |
deal_id | number | |
statement_id | number | |
line_id | number | |
member_key | string | create_payee: a team member: their id from list_team_members, their user_id or their email. Their login sees their own commissions. |
name | string | |
email | string | |
contact_id | number | Optional CRM person for the payee. |
roles | array of closer | setter | |
rules | object | { 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 | boolean | |
notes | string | |
closer_payee_id | number or null | set_deal_roles: null = the deal owner's payee. |
setter_payee_id | number or null | |
amount | number or string | Adjustment in the currency: 250 = bonus, -100 = deduction. |
amount_cents | number | |
note | string | |
all | boolean | generate_statements: also a statement for active payees with no lines. |
vendor_id | string or null | link_vendor: a QuickBooks vendor id (null unlinks). |
q | string | list_vendors: name contains. |
settings | object | 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 | pending | received | part_refunded | refunded | cancelled | list_lines: only lines with this payment mark. |
provider | stripe | square | whop | fanbasis | link_seller / create_payee_for_seller: the provider of the seller. |
ref | string | link_seller / create_payee_for_seller: the seller's id, username or email at the provider (from list_needs_payee). |
role | closer | setter | create_payee_for_seller: their role (default closer). |
provider_refs | array of object | create_payee / update_payee: the payee's ids at each provider (replaces the list on update). |
Instantly 1
instantlyInstantly (cold email)asks for approval
POST/api/external/tools/instantly
Scopes: instantly:read, instantly:write, messages:send
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). Actions: - status: Connection, webhook, settings and 30 day campaign totals. Never the key. - campaigns: The Instantly campaigns (id, name, status), active first. - push: Add people to an Instantly campaign (campaign_id + who). Instantly then emails them on its schedule. (may wait for approval) - stop: Take a person (contact_id) out of every Instantly campaign Plodder knows them in. (waits for approval: external) - sync: Read the daily campaign numbers from Instantly now.
| Field | Type | Notes |
|---|---|---|
action required | status | campaigns | push | stop | sync | What to do. |
campaign_id | string | Instantly campaign id (from campaigns). |
contact_ids | array of number | |
company_ids | array of number | |
deal_ids | array of number | |
segment_id | number | A smart list or segment (CRM saved view) id. |
filter | object | A People filter (as the CRM sends it). |
custom | array of object | [{ field, as }] CRM fields sent as Instantly custom variables. |
dry_run | boolean | |
contact_id | number |
Users 2
get_userGet a userread only
POST/api/external/tools/get_user
Scopes: users:read
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.
| Field | Type | Notes |
|---|---|---|
ref required | string | id, user_id, email or exact name. |
list_team_membersList team membersread only
POST/api/external/tools/list_team_members
Scopes: users:read
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.
| Field | Type | Notes |
|---|---|---|
include_agents | boolean | Include agent team members (default false). |
include_inactive | boolean | Include inactive roster rows (default false). |
q | string | Only people whose name, email or id contains this. |
Admin 23
appsConnected appsasks for approval
POST/api/external/tools/apps
Scopes: admin:read, admin:write
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. Actions: - connected: Apps you connected and their accounts. - search: Find an app to connect (q). - 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. - check: After the person signed in: the app's accounts now (slug).
| Field | Type | Notes |
|---|---|---|
action required | connected | search | connect_link | check | What to do. |
q | string | |
slug | string | App slug, like gmail or github. |
alias | string |
billingBillingread onlyasks for approval
POST/api/external/tools/billing
Scopes: admin:read
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. Actions: - 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?). - 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. - invoices: Recent invoices and credit ledger (owners and admins). - 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).
| Field | Type | Notes |
|---|---|---|
action required | overview | preview | invoices | unsettled | What to do. |
workspace_id | number | |
state | string |
calendar_teamTeam calendarsasks for approval
POST/api/external/tools/calendar_team
Scopes: admin:read, admin:write
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. Actions: - view: Teammates and their shared events between start and end (default this week; 62 days at most). user_ids narrows it. - 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. - get_sharing: What teammates can see of your calendar: busy_only | all | none. - set_sharing: Set what teammates can see of your calendar: sharing busy_only (default) | all | none.
| Field | Type | Notes |
|---|---|---|
action required | view | availability | get_sharing | set_sharing | What to do. |
start | string or number | |
end | string or number | |
user_ids | array of number | |
emails | array of string | |
duration_minutes | number | |
sharing | busy_only | all | none |
data_cleanupData cleanup (Trash and retention)asks for approval
POST/api/external/tools/data_cleanup
Scopes: admin:read, admin:write
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. Actions: - 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. - preview: What a job would do right now (job, retention_days?). Nothing is written. - 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)
| Field | Type | Notes |
|---|---|---|
action required | get | preview | set | What to do. |
workspace_id | number | |
trash_purge | boolean | set: Empty Trash after 30 days on/off. |
retention_days | number or null | set: 30-3650 days, or null / 0 for off. preview: days to check (default the saved setting). |
job | trash_purge | retention | preview: which job. |
fields | object | Any other body fields the action documents. |
database_questionsDatabase SQL and saved questionsasks for approval
POST/api/external/tools/database_questions
Scopes: admin:read, admin:write
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. Actions: - sql: Run a read-only SELECT (database_id, sql) and get its columns and rows. - list: Saved questions you can see (database_id to limit to one database, query to filter by name). - run: Run a saved question live (question_id): its rows, chart and any error. - save: Save a question (database_id, name, sql, chart). The SQL must run. - update: Rename a question or change its SQL or chart (question_id, name / sql / chart). - delete: Delete a saved question (question_id). (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | sql | list | run | save | update | delete | What to do. |
database_id | string | Database id, public id or sheet:<id>. |
question_id | number | A saved question (question:<id>). |
sql | string | |
name | string | save / update: the question's name. |
chart | table | bar | line | pie | How the question is drawn. |
query | string | list: filter by name. |
fields | object | Any other body fields the action documents. |
favoritesFavoritesasks for approval
POST/api/external/tools/favorites
Scopes: admin:read, admin:write
Your favorites (the starred items in the palette and lists). Items are "type:id" refs, e.g. "task:12", "doc:abc". Actions: - list: Your favorites with titles and links. - add: Favorite items (refs). - remove: Unfavorite items (refs).
| Field | Type | Notes |
|---|---|---|
action required | list | add | remove | What to do. |
refs | array of string |
home_feedHome feedread only
POST/api/external/tools/home_feed
Scopes: admin:read
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.
| Field | Type | Notes |
|---|---|---|
status | all | unread | read | |
types | array of tasks | email | chat | notifications | files | projects | agents | |
tz_offset_min | number | Minutes east of UTC for the date groups (default 0). |
limit | number | Rows, default 50, max 200. |
invitationsInvitationsasks for approval
POST/api/external/tools/invitations
Scopes: admin:read, admin:write
Email invitations to join a workspace (people who are not users yet). Sending one emails an outside person, so it waits for approval. Actions: - list: Invitations for a workspace. - send: Invite someone by email (email, name?, role admin|member|client). Owners and admins. (waits for approval: external) - revoke: Revoke a pending invitation (invitation_id). (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | send | revoke | What to do. |
workspace_id | number | |
email | string | |
name | string | |
role | admin | member | client | |
invitation_id | number | |
fields | object | Any other body fields the action documents. |
list_pending_approvalsList approval requestsread only
POST/api/external/tools/list_pending_approvals
Scopes: admin:read
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.
| Field | Type | Notes |
|---|---|---|
status | pending | executed | rejected | failed | all | Default all. |
limit | number |
list_workspacesList workspacesread only
POST/api/external/tools/list_workspaces
Scopes: admin:read
The workspaces you belong to. Other tools take workspace_id from here; your primary one is the default.
No arguments.
my_settingsMy profile and settingsasks for approval
POST/api/external/tools/my_settings
Scopes: admin:read, admin:write
Your profile (name, bio, pronouns, display role), your app settings (rail, ai_default_model, accent) and the interactive tutorial progress. Actions: - profile: Your profile. - update_profile: Change your profile (name, bio, pronouns, display_role). - setting: One app setting (key). - set_setting: Save one app setting (key, value). - tutorial: Tutorial progress. - set_tutorial: Mark a tutorial lesson done or not (lesson, done?), or reset=true.
| Field | Type | Notes |
|---|---|---|
action required | profile | update_profile | setting | set_setting | tutorial | set_tutorial | What to do. |
name | string | |
bio | string | |
pronouns | string | |
display_role | string | |
key | rail | ai_default_model | accent | |
value | any | |
lesson | move | select | create | palette | mention | |
done | boolean | |
reset | boolean | |
fields | object | Any other body fields the action documents. |
notification_settingsNotification settingsasks for approval
POST/api/external/tools/notification_settings
Scopes: admin:read, admin:write
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. Actions: - get: Your per-kind preferences, snooze, and digest settings. - set_pref: One kind: in_app, email, push on or off. - snooze: Hold every notification until `until` (ISO or epoch seconds; max 30 days). - resume: End the snooze now. - mute_status: Is this item muted (type, id)? - mute: Mute an item (type, id), optional until; a chat channel muted with no end also goes to Nothing. - unmute: Unmute it (a chat channel goes back to All). - set_digest: Digest: frequency off | daily | weekly, send_hour 0-23, weekday 0-6, timezone. - test_digest: Email yourself your digest now (one every 5 minutes). (waits for approval: send email)
| Field | Type | Notes |
|---|---|---|
action required | get | set_pref | snooze | resume | mute_status | mute | unmute | set_digest | te | What to do. |
kind | new_lead | inbound_reply | mention | assignment | appointment_booked | appointme | |
in_app | boolean | |
email | boolean | |
push | boolean | |
until | string or number | |
type | string | |
id | string or number | |
frequency | off | daily | weekly | |
send_hour | number | |
weekday | number | |
timezone | string |
notificationsNotificationsasks for approval
POST/api/external/tools/notifications
Scopes: admin:read, admin:write
Your notification feed (bell and Inbox) and its state: unseen, seen, done. Keys look like "u:12" (yours) or "w:3" (workspace bell, owner). Actions: - list: Your feed, newest first (filter all | active | unseen, limit), with the unseen count. - set_state: op = seen | done | reopen on keys, or all=true. - mark_read: Mark your notifications read: ids, or none for all. - mark_workspace_read: Owner: mark the workspace bell read (ids, or all=true). - mark_task_activity_read: Mark task activity in the Inbox read (activity ids, or all=true).
| Field | Type | Notes |
|---|---|---|
action required | list | set_state | mark_read | mark_workspace_read | mark_task_activity_read | What to do. |
filter | all | active | unseen | |
limit | number | |
op | seen | done | reopen | |
keys | array of string | |
ids | array of number | |
all | boolean |
phone_setupPhone setupasks for approval
POST/api/external/tools/phone_setup
Scopes: admin:read, admin:write
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. Actions: - get: Your phone account settings and numbers in use. - set: Change settings (settings object). - 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) - disconnect: Forget the Twilio credentials (texts and calls stay). (waits for approval: delete) - numbers: Every number on your Twilio account. - search_numbers: Numbers you could buy (country, area_code, contains, type), with the monthly price. - buy_number: Buy a number (phone_number from search_numbers). This COSTS MONEY: Twilio bills it every month. (waits for approval: external) - use_number: Point a number already on your Twilio account here (sid). - default_number: Texts and calls go out from this number (sid). - repair_number: Set a number's SMS and voice webhooks back to Plodder (sid; numbers with needs_repair). - 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. - 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. - a2p_save: Save registration answers (brand and / or campaign objects; partial is fine). Only while it is a draft or Twilio asked for changes. - 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) - a2p_check: Read the registration status from Twilio now (and take the next step when one is due). - a2p_link: Track a registration already made in the Twilio Console (finds the brand and its campaign). - a2p_attach: Add every US number in use to the registered campaign. - a2p_reset: Start the registration over here (nothing is deleted at Twilio; the answers stay). (waits for approval: delete) - 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. - save_flow: Create or update a call flow (flow object). Numbers can only follow a flow without problems. - number_flow: A number (sid) follows a call flow (flow_id; null = ring the app, then the cell, then voicemail). - delete_flow: Delete a call flow (flow_id). Its numbers and tracking sources ring the business line again. (waits for approval: delete) - 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). - 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. - tracking_number: Put one number in use here on a tracking source (sid, source_id; null takes it off). - 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). - delete_tracking_source: Delete a tracking source (source_id). Its numbers become plain lines; calls already tracked keep the name. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | get | set | setup_browser_calling | disconnect | numbers | search_numbers | buy_ | What to do. |
settings | object | set: cell_phone, call_cell_first, forward_unanswered, ring_seconds, record_calls, record_notice, voicemail_greeting, help_reply, a2p. |
country | US | CA | |
area_code | string | |
contains | string | |
type | Local | TollFree | |
phone_number | string | buy_number: a number from search_numbers. |
sid | string | A number's Twilio sid. |
assignee | string or null | assign_number: team member id (from the team list); null = everyone. |
brand | object | 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 | object | 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 | number or null | A tracking source id (tracking_number: null takes the number off its source). |
flow | object | 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 | number or null | A call flow id (number_flow: null = default ringing). |
target | string | tracking_swap_target: the number written on the website that the swap script replaces (empty turns it off). |
campaign | object | 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 | object | Any other body fields the action documents. |
property_adminProperties and tags adminasks for approval
POST/api/external/tools/property_admin
Scopes: admin:read, admin:write
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. Actions: - definitions: Definitions that apply to a type (target type in property, workspace_id). - create_definition: A property (display_name, data_type, target_types, ref_entity_type, workspace_id or personal). - update_definition: Rename or retarget a property (id, display_name, target_types, sort_order). - archive_definition: Archive a property (id); restore brings it back. (waits for approval: delete) - restore_definition: Bring an archived property back (id). - options: A property's options (property; stage needs project_id; tags take scope crm|work). - add_option: Add an option (property, label, color; tags take scope crm|work). - update_option: Change an option (property, id, label, color, sort_order). - delete_option: Remove an option (property, id). (waits for approval: delete) - 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. - create_tag: A tag (label, color, scope crm|work; default work). The same name may exist once per scope. - update_tag: Rename or recolour a tag everywhere it is used (id, label, color). - merge_tags: Fold one tag into another (from, into). (waits for approval: delete) - delete_tag: Remove a tag from every item and list (id). (waits for approval: delete) - clear_value: Clear one property on an item (item, property).
| Field | Type | Notes |
|---|---|---|
action required | definitions | create_definition | update_definition | archive_definition | resto | What to do. |
id | string | Definition id, option id or tag id. |
display_name | string | |
data_type | string | text, number, select, multi_select, date, checkbox, url, email, person, relation, ... |
target_types | array of string | |
ref_entity_type | string | |
workspace_id | number | |
personal | boolean | |
sort_order | number | |
property | string | options: the property id or system key (status, stage, tags, ...). |
project_id | number | |
label | string | |
color | string | |
from | number | |
into | number | |
scope | crm | work | Tags: CRM tags (people, businesses, deals, conversations, bookings, forms) or Work tags (everything else). |
item | string | clear_value: <type>:<id> |
fields | object | Any other body fields the action documents. |
recent_itemsRecently openedasks for approval
POST/api/external/tools/recent_items
Scopes: admin:write
Tell the app you opened an item, so it rises in your "recently viewed" and "frequent" sorts (list_items sort viewed / frecency). Actions: - record_open: Record that you opened item type + id (only items you can see).
| Field | Type | Notes |
|---|---|---|
action required | record_open | What to do. |
type required | string | Item type: task, doc, project, form, sheet, file, ... |
id required | string or number |
referralsReferral invite linksasks for approval
POST/api/external/tools/referrals
Scopes: admin:read, admin:write
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. Actions: - list: Invite links and who joined through them (workspace_id?). - 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) - turn_off: Turn a link off (link_id).
| Field | Type | Notes |
|---|---|---|
action required | list | create | turn_off | What to do. |
workspace_id | number | |
max_uses | number | |
ttl_hours | number | |
link_id | number | |
recipient_email | string | |
note | string | |
free_months | number |
sharingSharingasks for approval
POST/api/external/tools/sharing
Scopes: admin:read, admin:write
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. Actions: - get: Who has access and how. - set: Give or change access (principal, level; level none removes). An email principal can get an email (notify). (may wait for approval) - set_general: Anyone with the link (docs, databases): none, view or edit. (may wait for approval) - create_link: A share link (level, expires); people who open it get that level after signing in. (waits for approval: external) - update_link: Change a share link (link_id, level, expires, enabled). (may wait for approval) - disable_link: Turn a share link off (link_id).
| Field | Type | Notes |
|---|---|---|
action required | get | set | set_general | create_link | update_link | disable_link | What to do. |
item | string | |
principal | object | {type: user|email|workspace, id | email} |
level | string | |
notify | boolean | |
general | none | view | edit | |
link_id | number | |
expires | string | "7d", an ISO date, or null for never. |
enabled | boolean | |
fields | object | Any other body fields the action documents. |
team_directoryTeam directoryasks for approval
POST/api/external/tools/team_directory
Scopes: admin:read, admin:write
The team roster used for assignees (/api/team). Owner only for changes. Setting a teammate's role or permissions waits for approval. Actions: - list: Teammates with their workspaces. - create: A new teammate (name, email?, role?, workspace_ids?). With role or permissions it waits for approval. (may wait for approval) - update: Change a teammate (member_id, set). Changing role or permissions waits for approval. (may wait for approval) - delete: Remove a teammate. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | create | update | delete | What to do. |
member_id | number | |
name | string | |
email | string | |
role | string | |
permissions | any | Permission list. |
workspace_ids | array of number | |
set | object | |
include_inactive | boolean | |
fields | object | Any other body fields the action documents. |
webhooksWebhooks and API keysasks for approval
POST/api/external/tools/webhooks
Scopes: admin:read, admin:write, webhooks:read, webhooks:write
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. Actions: - list: Webhooks (status, failures, pending retries) and the events you can send. - events: Every event with its area and when it fires. - log: A webhook's delivery log, newest first (webhook_id, limit?): event, attempt, status code, time taken, response snippet. - create: A webhook (name, url, events? default all). Only public URLs. (waits for approval: external) - update: Change a webhook (webhook_id, set). (may wait for approval) - test: Post a sample event to the webhook now (webhook_id, event?). Returns the status code and response. (waits for approval: external) - resend: Post one delivery again now (delivery_id from the log). (waits for approval: external) - delete: Delete a webhook. (waits for approval: delete) - list_keys: API keys: name, scopes, last used (never the key). - revoke_key: Revoke an API key (key_id). Anything using it stops working. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | events | log | create | update | test | resend | delete | list_keys | rev | What to do. |
webhook_id | number | |
name | string | |
url | string | |
events | array of string | Event names (see events), "family.*" or "*". |
set | object | update: name, url, events, active. |
event | string | test: the sample event to send (default webhook.test). |
delivery_id | number | |
key_id | number | |
limit | number | |
fields | object | Any other body fields the action documents. |
workspace_settingsWorkspace labels, statuses and fieldsasks for approval
POST/api/external/tools/workspace_settings
Scopes: admin:read, admin:write
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). Actions: - labels: Workspace labels. - add_label: A workspace label (name, color?). - update_label: Change a label (id, set {name, color, sort_order}). - delete_label: Delete a label (id). (waits for approval: delete) - statuses: Workspace statuses. - add_status: A workspace status (name, color?). - update_status: Change a status (id, set {name, color, sort_order}). - delete_status: Delete a status (id). (waits for approval: delete) - fields: Workspace custom fields. - add_field: A custom field (name, field_type?, options?). - update_field: Change a custom field (id, set). - delete_field: Delete a custom field (id). (waits for approval: delete) - set_task_field: Set a custom field on a task (task_id, field_id, value). - 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. - 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) - set_project_prefix: A project's own task code prefix (project_id, project_prefix; blank = use the workspace prefix). Owners and admins. - task_code: A task's short code (ENG-42) and git branch name (task_id). - global_labels: Global labels. - add_global_label: A global label (name, color?). - delete_global_label: Delete a global label (id). (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | labels | add_label | update_label | delete_label | statuses | add_status | updat | What to do. |
workspace_id | number | |
id | number | The label, status or field id. |
name | string | |
color | string | |
field_type | string | text, number, date, select, ... |
options | any | select options. |
set | object | |
task_id | number | |
field_id | number | |
value | any | |
team_name | string | set_team_settings: the team (workspace) name. |
members_can_invite | boolean | |
share_link_level | view | comment | edit | |
share_link_expiry | 1d | 7d | 30d | never | |
task_prefix | string | |
project_id | number | |
project_prefix | string | |
fields | object | Any other body fields the action documents. |
workspacesWorkspaces and teamasks for approval
POST/api/external/tools/workspaces
Scopes: admin:read, admin:write
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. Actions: - list: Your workspaces. - create: A new workspace (name, color?, copy_from_id?). You own it. - update: Change a workspace you own (workspace_id, set). - delete: Delete a workspace you own. (waits for approval: delete) - set_primary: Make a workspace you own your primary one. - team: The team with roles, your role and whether you can manage it. - add_to_team: Add an existing Plodder user to the team (email, role). Waits for approval. (waits for approval: external) - set_role: Change someone's role (user_id, role). Any role change (raising or lowering, yours included) waits for approval. (may wait for approval) - remove_from_team: Take someone off the team (user_id). (waits for approval: delete) - members: Workspace members (people). - add_member: Add an existing user as a member by email (email, role?). Owners and admins. Waits for approval. (waits for approval: external) - remove_member: Remove a member (user_id). Owners and admins. (waits for approval: delete)
| Field | Type | Notes |
|---|---|---|
action required | list | create | update | delete | set_primary | team | add_to_team | set_role | | What to do. |
workspace_id | number | |
name | string | |
color | string | |
copy_from_id | number | create: copy settings from this workspace. |
set | object | update: workspace fields (name, color, ...). |
email | string | |
role | owner | admin | member | guest | |
user_id | number | |
fields | object | Any other body fields the action documents. |
ctrl-flow 7
fetchFetch an itemread only
POST/api/external/tools/fetch
Scopes: ctrl-flow:read
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>.
| Field | Type | Notes |
|---|---|---|
id required | string | For example task:123 or doc:45. |
get_propertiesRead propertiesread only
POST/api/external/tools/get_properties
Scopes: ctrl-flow:read
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.
| Field | Type | Notes |
|---|---|---|
item required | string | 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 | boolean | Include the options of select properties (default true). |
list_itemsList itemsread only
POST/api/external/tools/list_items
Scopes: ctrl-flow:read
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.
| Field | Type | Notes |
|---|---|---|
types | array of task | project | doc | sheet | channel | meeting | client | business | crm_compa | Only these kinds. Default: everything except CRM contacts. |
text | string | Title contains these words. |
project_ids | array of number | Only items in these projects (tasks, docs). |
tags | array of string | Tag names (from list_tags). Items with any of them. |
updated_after | string | ISO date or datetime: changed on or after this. |
sort | updated | created | viewed | frecency | updated (default), created, viewed (recently opened by the user) or frecency. |
favorites | boolean | Only the user's favourites. |
limit | number | Max results (default 25, max 100). |
cursor | string | The cursor from a previous page. |
list_linksList linked itemsread only
POST/api/external/tools/list_links
Scopes: ctrl-flow:read
What an item links to (mentions, attachments, hand-made relations) and what links to it (backlinks).
| Field | Type | Notes |
|---|---|---|
item required | string | 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. |
read_activityRead activityread only
POST/api/external/tools/read_activity
Scopes: ctrl-flow:read
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.
| Field | Type | Notes |
|---|---|---|
item | string | 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 | number | Max events (default 30). |
read_discussionRead commentsread only
POST/api/external/tools/read_discussion
Scopes: ctrl-flow:read
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.
| Field | Type | Notes |
|---|---|---|
item required | string | 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 | number | Newest threads to return (default 30). |
searchSearch Plodderread only
POST/api/external/tools/search
Scopes: ctrl-flow:read
Search everything you can access in Plodder: tasks, projects, docs, meetings and clients. Returns ids you can pass to fetch for the full item.
| Field | Type | Notes |
|---|---|---|
query required | string | Words to look for. |
types | array of task | project | doc | meeting | client | Limit to these kinds. |
limit | number | Max results (default 20). |
* 5
link_itemsLink two items
POST/api/external/tools/link_items
Scopes: *
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.
| Field | Type | Notes |
|---|---|---|
source required | string | 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 required | string | 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. |
post_discussionComment on an itemasks for approval
POST/api/external/tools/post_discussion
Scopes: *
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.
| Field | Type | Notes |
|---|---|---|
item required | string | 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 required | string | The comment (markdown). |
thread_id | number | Reply in this thread instead of starting a new one. |
set_propertySet a property
POST/api/external/tools/set_property
Scopes: *
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.
| Field | Type | Notes |
|---|---|---|
item | string | 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 | array of string | Several items (type:id) to set the same value on, instead of item. Max 50. |
property required | string | Property name or key, for example status, Priority, Deal stage, due_date. |
value | any | The new value: a string, number, boolean, a list for multi-value properties, or null to clear. |
set_tagsTag an item
POST/api/external/tools/set_tags
Scopes: *
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.
| Field | Type | Notes |
|---|---|---|
item required | string | 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 | array of string | Tag names to add. |
remove | array of string | Tag names to remove. |
unlink_itemsUnlink two items
POST/api/external/tools/unlink_items
Scopes: *
Remove a hand-made relation between two items (mentions in content stay until the content changes).
| Field | Type | Notes |
|---|---|---|
source required | string | 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 required | string | 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. |
Webhooks
Add a webhook in Settings, Integrations and API, Webhooks: your URL and the events you want. Plodder sends a signed POST for each one and retries up to 5 times. Subscribe to one event, a family like contact.*, or everything with *.
Payload
POST <your URL>
Content-Type: application/json
X-CtrlFlow-Event: deal.stage_changed
X-CtrlFlow-Delivery: 8812
X-CtrlFlow-Signature: t=1791504000,v1=<hex HMAC-SHA256 of "t.body">
{
"id": "evt_3f1c...",
"event": "deal.stage_changed",
"created_at": "2026-10-08T17:20:00.000Z",
"workspace": { "id": 1 },
"owner": { "id": 1 },
"data": {
"deal": { "id": 77, "title": "Heat pump install", "stage_id": "proposal" },
"changes": { "stage_id": { "from": "qualified", "to": "proposal" } }
}
}
Check the signature
import crypto from 'node:crypto'
// rawBody: the request body as a string, before JSON.parse
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map(p => p.split('=')))
const t = Number(parts.t)
if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false // 5 minute window
const want = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex')
return want.length === parts.v1.length &&
crypto.timingSafeEqual(Buffer.from(want), Buffer.from(parts.v1))
}
Answer with a 2xx quickly and do the work after. The same id can arrive more than once, so skip ids you have already handled.
Events
CRM
contact.created | A person is added to the CRM (app, form, import, agent, API). |
contact.updated | A person's fields change. data.changes has { field: { from, to } }. |
contact.deleted | A person is deleted (they stay in Recently deleted for 30 days). |
contact.restored | A deleted person is restored from Recently deleted. |
contact.tag_added | A tag is put on a person. data.tag names it. |
contact.tag_removed | A tag comes off a person. |
business.created | A business is added to the CRM. |
business.updated | A business's fields change. |
business.stage_changed | A business moves to another stage. |
deal.created | A deal is created. |
deal.updated | A deal's fields change. |
deal.stage_changed | A deal moves to another stage or pipeline. |
deal.won | A deal is marked won. |
deal.lost | A deal is marked lost. |
deal.status_changed | A deal turns open, won, lost or abandoned. data.status. |
bulk_action.finished | A bulk action on people (tag, workflow, email, text, delete) finished, stopped or was cancelled. data.bulk_action: kind, status, total, ok, skipped, failed. |
deal.stale | An open deal had no stage or field change for 14 days (once per stale stretch). |
contact.dnd_changed | Do not email / text / call or unsubscribed turns on or off (also STOP / START). data.dnd. |
contact.score_changed | 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). |
contact.replied | A person texts or emails in (any channel). data.channel. |
deal.deleted | A deal is deleted. |
deal.restored | A deleted deal is restored from Recently deleted. |
deal.merged | A deal is merged into another. data.merged_into is the one kept. |
note.added | A note or comment is added on a person, business or deal. |
client.created | A client is made (Make client on a business, or a new client). |
client.churned | A business is marked churned. data.churned_on and data.reason; its recurring deals churn with it. |
client.reactivated | A churned business is reactivated. |
deal.churned | A recurring deal is marked churned (it stops counting in MRR from data.churned_on). |
deal.reactivated | A churned deal is reactivated (back in MRR). |
Conversations
conversation.response_overdue | 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. |
conversation.scheduled_sent | A text, email or DM scheduled from a conversation went out. data.scheduled. |
conversation.scheduled_failed | A scheduled message could not go (consent, STOP, Meta's 24-hour window, mailbox). data.scheduled.error. |
email.domain_verified | A client sending domain passed Mailjet's SPF and DKIM check. data.domain. |
email.campaign_winner | An email campaign's A/B test picked its winner and sends it to everyone else. data.winner, data.results per version. |
Speed to lead
lead.waiting_amber | A new lead has had no text, call or email for the amber time. |
lead.waiting_red | A new lead has had no reply for the red time. |
lead.responded | Someone answered a lead for the first time. |
Bookings
booking.created | Someone books a time on a booking calendar. |
booking.rescheduled | A booking moves to a new time. |
booking.cancelled | A booking is cancelled. |
booking.confirmed | A booking is confirmed (the guest's Confirm link, or by hand). |
booking.showed | A booking is marked showed. |
booking.no_show | A booking is marked no-show. |
booking.invalid | A booking is marked invalid. |
booking.status_changed | Any booking status change. data.from and data.status. |
booking.series_created | Someone books a repeating series (weekly, every 2 weeks, monthly). data.series, data.bookings (each is also a booking.created), data.skipped dates. |
booking.calendar_sync_broken | A connected Outlook or iCloud calendar stops answering (signed out, password changed). data.connection, data.error, data.reconnect. |
booking.series_cancelled | This and the following, or every upcoming booking of a series is cancelled at once. data.scope, data.cancelled booking ids. |
Forms
form.submitted | A form is submitted. data.answers holds the answers by question. |
form.abandoned | Someone typed an email on a public form and left it unsent for 30 minutes. |
Messages
sms.received | A text comes in on one of your numbers. |
sms.failed | A text we sent failed or was not delivered. data.sms.error. |
sms.sent | A text goes out (you, a workflow, an agent). |
phone.number_added | A Twilio number is bought or put to use in Plodder (webhooks set on it). data.number. |
phone.a2p_changed | The A2P 10DLC registration moves stage (in review, approved, changes asked). data.registration.stage, reasons. |
phone.call_transferred | A call is transferred from the call bar (cold or warm). data.transfer.mode, label. |
phone.tracking_call | 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. |
email.received | Email from a CRM person arrives in a connected mailbox. |
email.sent | The app sends an email (you, scheduled, a workflow, an agent). |
email.opened | A workflow email is opened (first open). |
email.replied | Someone replies to a workflow email. |
email.delivered | Mailjet confirms an email reached the person's mail server. |
email.clicked | A person clicks a link in an email we sent (first click per link). data.email.url. |
email.bounced | An email to a person bounces. data.email.hard is true for a hard bounce. |
email.unsubscribed | A person unsubscribes (our link, the one-click header, or Mailjet). |
email.campaign_sent | An email campaign finishes sending. data.campaign has the counts (sent, skipped, failed). |
Custom objects
object.record_created | A record of a custom object (CRM > Objects) is made: app, import, workflow, agent or API. data.record has its fields by key. |
object.record_updated | A custom object record changes. data.changes has { field key: { from, to } }. |
object.record_deleted | A custom object record is deleted. |
object.associated | A custom object record is linked to a person, business, deal or another record. data.association. |
Facebook and Instagram
meta.lead_received | 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. |
meta.dm_received | A person messages your page on Messenger or your Instagram account. data.channel, data.message. |
meta.comment | A person comments on your page's posts or your Instagram media. data.comment. |
meta.connection_broken | 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. |
Conversation AI
conversation.booked | An agent booked a time on a calendar in a conversation. data.booking_id, data.when, data.playbook. |
conversation.handed_off | An agent handed a conversation to a person. data.reason, data.summary (two lines). |
conversation.ended | A conversation ended. data.outcome, data.summary, data.outcomes (post-conversation fields), data.tags (pathway tags), data.variables. |
conversation.qualified | The qualifying answers passed. data.answers. |
conversation.disqualified | The qualifying answers did not pass. data.answers. |
Calls
call.started | A phone call starts (in or out). |
call.completed | A phone call ends after it was answered. duration, direction, recording_url. |
call.missed | A call ends unanswered (missed, busy, no answer, voicemail). |
call.logged | A call is logged by hand on a CRM record. |
Automations
workflow.enrolled | A person enters a workflow. |
workflow.completed | A person reaches the end of a workflow. |
workflow.failed | A workflow stops on an error. |
workflow.goal_reached | A person meets a workflow goal. |
E-sign
signature.requested | A document is sent for signature (one event per signer). |
signature.signed | A signer signs. |
signature.declined | A signer declines (the envelope closes). |
signature.completed | Every signer has signed; the signed copy is ready. |
Payments
payment.received | A client pays (payment link, invoice, subscription invoice, booking, document). data.payment. |
payment.failed | A payment attempt fails (card declined, bank debit failed, renewal failed). |
invoice.paid | A Stripe invoice is paid (one-off or a subscription's). |
invoice.overdue | An open invoice passes its due date (once per invoice). |
subscription.started | A client starts a retainer / subscription. |
subscription.cancelled | A subscription ends. |
subscription.renewed | A subscription renewal invoice is paid. |
refund.issued | A payment is refunded, fully or in part. data.payment.refund_cents. |
Commissions
commission.pending | 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. |
commission.earned | 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. |
commission.refunded | A refund claws a commission back (negative line in the refund's month; the payment's line is marked Refunded / Part refunded). data.commission. |
commission.statement_finalized | A payee's month is finalized (its lines are locked). data.statement. |
commission.statement_sent | A finalized month becomes a bill in QuickBooks Online. data.statement.qbo_bill_id. |
commission.statement_paid | QuickBooks shows the payee's bill paid (once per statement). data.statement. |
Instantly
instantly.reply_received | A lead replied to an Instantly campaign email. data.instantly.reply_text when Instantly sends it. |
instantly.lead_interested | A lead is marked interested in Instantly (once per person per campaign). data.instantly.deal_id = the lead deal made. |
instantly.lead_not_interested | A lead is marked not interested in Instantly. |
instantly.meeting_booked | A lead is marked meeting booked in Instantly. |
instantly.out_of_office | An auto reply or out of office from a lead. |
instantly.unsubscribed | A lead unsubscribed in Instantly (the address is suppressed here too). |
instantly.bounced | An Instantly email bounced (the person is marked Do not email). |
instantly.email_sent | Instantly sent a campaign email to a person in the CRM. |
instantly.email_opened | A person opened an Instantly campaign email. |
instantly.link_clicked | A person clicked a tracked link in an Instantly campaign email. |
instantly.lead_status | Any other Instantly lead label (neutral, closed, wrong person, a custom label) or campaign event. data.instantly.event_type. |
Work
task.created | A task is created. |
task.updated | A task changes. |
task.completed | A task is marked done. |
task.reminder | A task on a CRM record reaches its reminder time. |
task.deleted | A task is deleted. |
task.agent_started | An agent starts work it was assigned (a task, project or doc given to it). data.work, data.agent, data.assigned_by. |
task.agent_finished | An agent finishes assigned work and hands it back. data.work, data.agent, data.outcome (review | done), data.summary. |
project.created | A project is created. |
doc.created | A doc is created. |
doc.updated | A doc is edited. |
Webhooks
webhook.test | Send test event in Settings, or the webhooks tool action test. |
MCP server
Connect Claude, ChatGPT or any MCP client straight to your workspace. Add this server URL in the client and sign in with your Plodder account:
https://app.useplodder.com/mcp
The client gets the same tools as the API, limited to what your account can reach. The full spec is at openapi.json (OpenAPI 3.1).