TrustPager Docs

API Reference

1007 endpoints across 82 resources. Every endpoint below is statically rendered — AI agents can read this page directly via WebFetch.

Base URL & auth

Full auth details — scopes, approval queue, pagination, idempotency, rate limits — are on the Authentication & scopes page.

Discovery surface — machine-readable everything

Every endpoint on this page is also available in machine-readable form for AI agents and tooling:

Resources

Jump to a resource:

Universal Search (1) Contacts (19) Companies (13) Opportunities (58) Pipelines (12) Products (9) Supplier Catalog (5) Activities (5) Stage Changes (1) Tasks (10) Work Orders (6) Work Order Settings (15) STAKEHOLDERS (17) Automations (26) Custom Scripts (14) Sensitive Fields (1) Skills (6) Auto Queues (15) Auto Schedules (9) SCHEDULING (26) Email (23) Email Templates (2) Email Campaigns (22) SMS (6) WhatsApp (14) Chat (13) Phone (17) Agents (39) Agent Knowledge Bases (8) VOICE_AGENT_TEMPLATES (3) VOICE_CALL_DIRECTIVES (5) Voice Agent Toolkit (16) Knowledge Bases (14) Text Agents (6) Transcripts (10) Document Templates (14) Signing (5) Forms (25) Websites (34) SEO Research (16) Portals (25) Order Forms (6) Stores (9) Orders (5) Invoices (14) Recurring Invoices (5) Files (15) Images (1) Notepads (18) Whiteboards (8) Spreadsheets (16) Reports (18) AI Knowledge (7) Agent Memory (7) Company (33) CRM Templates (5) Integrations (6) Webhooks (17) AI (15) Billing (4) Service Requests (8) Agency (11) Approvals (6) AI Instructions (1) Voices (8) Agent Proposals (6) Lead Generation (10) Lead Gen Initiatives (12) Playbooks (8) Roles & Permissions (9) Scheduled Communications (7) Schemas (2) Reputation (22) Referrals (18) FEEDBACK (8) CRM Export (1) Export Templates (13) Evie (In-App Agent) (2) Agent Ops (28) Auto Responders (6) INVENTORY (15) COMPONENTS (12)

Contacts (19 endpoints)

Manage individual contacts (people) in the CRM. Supports search, filtering, sub-resources (opportunities, activities, employers), and AI enrichment.

📄 Full markdown: /api/contacts.md

POST /contacts/:contact_id/employers/:customer_id Scopes: contacts:write

Link a contact to a company as an employer.

3 parameters
Name In Type Required Description
contact_id path string yes
customer_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/contacts/post-contacts-contact-id-employers-customer-id.md

POST /contacts/bulk-create Scopes: contacts:write

Create up to 100 contacts in one request. Each record must have at least first_name, and takes the same fields as create_contact — including customer_id to link that contact to a company as it is created.

3 parameters
Name In Type Required Description
records body array yes Array of contact objects. Accepts every create_contact field.
skip_automations body boolean no Set true to suppress contact_created automation triggers for every record in this batch. Use for imports and backfills.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/contacts/post-contacts-bulk-create.md

POST /contacts/bulk-delete Scopes: contacts:delete

Delete up to 100 contacts in one request. Destructive.

2 parameters
Name In Type Required Description
ids body array yes Contact UUIDs
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/contacts/post-contacts-bulk-delete.md

POST /contacts/bulk-link Scopes: contacts:write

Link up to 100 existing contacts to companies in one request. Use this to repair an import whose contacts were created without their company, instead of calling add_contact_employer once per contact. Already-linked pairs come back under `skipped`, never as errors, so re-running is safe.

2 parameters
Name In Type Required Description
records body array yes Array of {contact_id, customer_id} pairs
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/contacts/post-contacts-bulk-link.md

POST /contacts/bulk-update Scopes: contacts:write

Update up to 100 contacts in one request. Each record must include id plus fields to change. Include archived:true to bulk-archive (or archived:false to bulk-restore).

2 parameters
Name In Type Required Description
records body array yes Array of contact objects. Each needs id plus any subset of the update_contact fields.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/contacts/post-contacts-bulk-update.md

POST /contacts Scopes: contacts:write

Create a new contact. first_name is required. To link to a company pass customer_id.

32 parameters
Name In Type Required Description
first_name body string yes Given name (required on create).
last_name body string no
email body string no
phone body string no Mobile, any format, normalised to E.164.
landline body string no
job_title body string no
contact_type body string no
date_of_birth body string no ISO date (YYYY-MM-DD). Pass "" to clear.
notes body string no
source body string no How this contact entered the CRM. One of: manual, form_submission, voice_call, order_form, import, api. Defaults to "manual"; use "api" for anything you create programmatically. This is NOT campaign attribution: to record which ad, channel or campaign a lead came from, set the opportunity's lead_source instead.
alternate_emails body array no Other email addresses this record uses. Bookings, public forms and inbound email match on ANY of them, so a person or business with two addresses resolves to this one record instead of creating a duplicate. Stored lowercased; the main `email` is never duplicated in here. RECOGNITION ONLY - outbound email always goes to `email`. Replaces the whole list, so read the record and merge first.
address_line1 body string no Street address, first line.
address_line2 body string no Street address, second line (unit, level).
city body string no Suburb or city.
state body string no State or region, e.g. "VIC", "NSW". Also used as the fallback for the contact's local time when `timezone` is not set, so it is worth filling in even when the rest of the address is unknown.
postal_code body string no Postcode.
country body string no Country. Defaults to Australia when omitted.
timezone body string no The contact's own timezone ("Timezone" on the contact page). Sets the local time used to decide when the dialler may call them and when business-hours-aware email, SMS and WhatsApp may reach them. Pass an IANA zone such as "Australia/Perth", or a state we can map such as "WA" or "Western Australia". Leave unset (or pass null to clear) to fall back to their state, then their phone country, then the workspace default. Set this whenever you know where a lead actually is: an Australian mobile carries no region, so without it a Perth lead is treated as east coast and called hours early.
source_id body string no UUID of the workspace lead source this contact came through, from the lead sources configured at https://app.trustpager.com/settings/crm. This is the structured counterpart to `source`, which only records the mechanism (form, call, import).
is_primary body boolean no Marks this contact as the primary contact for the company they are linked to. Only one contact per company should carry it.
metadata body object no Custom field values on the contact, keyed by field name, as configured at https://app.trustpager.com/settings/crm. Each value is stored in its declared type so the API, the UI and automations agree on the shape. On update, the keys you send merge into the existing set: other fields are kept, and a key sent as null is removed (see metadata_mode).
relationship_started_at body string no When the real-world relationship actually began, as an ISO date or timestamp. Defaults to when the record was created, so set it explicitly on anything imported from another CRM: birthday tiering, anniversaries and tenure all read this rather than the creation date. Pass "" or null to clear. A future date is rejected.
email_unsubscribed body boolean no true = stop all email to this contact (same effect as unsubscribe_contact). false = re-opt them in: clears their unsubscribe or do-not-contact. A hard bounce or spam complaint on the address stays blocked until unblock_recipient clears it.
sms_unsubscribed body boolean no true = stop all SMS to this contact. false = re-opt them in.
voice_unsubscribed body boolean no true = stop all voice calls to this contact. false = re-opt them in.
whatsapp_unsubscribed body boolean no true = stop all WhatsApp messages to this contact. false = re-opt them in.
is_referrer body boolean no Flags this contact as a referral partner so they appear in Referrals even before their first referral.
referrer_category body string no The partner's tier, matching the workspace's own referral categories (e.g. "Gold", "Introducer"). Tiered fee rates are looked up from this, so an off-list value breaks the lookup. Free text: the workspace decides the vocabulary.
referrer_opportunity_id body string no UUID of the referrer's designated opportunity, the anchor for statements, portal and partner documents. Setting it also marks the contact as a referrer.
customer_id body string no UUID of the company to link this contact to. The link is written to the contact-company join, and echoed back on the created record so you can confirm it. Read it later via get_contact_employers, or list_contacts?customer_id=. An unknown company is a 400 and nothing is created.
skip_automations body boolean no Set true to suppress contact_created automation triggers for this write. Use for bulk imports and backfills that should not fire client-facing automations.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/contacts/post-contacts.md

DELETE /contacts/:contact_id Scopes: contacts:delete

Delete a contact by UUID. Destructive.

2 parameters
Name In Type Required Description
contact_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/contacts/delete-contacts-contact-id.md

GET /contacts/:contact_id Scopes: contacts:read

Fetch a single contact by UUID. Returns the full record including custom fields.

1 parameter
Name In Type Required Description
contact_id path string yes

📄 /api/contacts/get-contacts-contact-id.md

GET /contacts/:contact_id/activities Scopes: contacts:readactivities:read

List all activities (calls, meetings, notes, emails) logged against a contact.

1 parameter
Name In Type Required Description
contact_id path string yes

📄 /api/contacts/get-contacts-contact-id-activities.md

GET /contacts/:contact_id/deals Scopes: contacts:readopportunities:read

List all opportunities/deals associated with a contact.

1 parameter
Name In Type Required Description
contact_id path string yes

📄 /api/contacts/get-contacts-contact-id-deals.md

GET /contacts Scopes: contacts:read

List contacts in the current workspace. Returns active (non-archived) contacts by default. Supports cursor-based pagination, free-text search via `search`, and filtering by source, customer_id, created date range, and unsubscribe flags. Pass archived="true" for only archived, or archived="all" for both.

12 parameters
Name In Type Required Description
limit query number no Max contacts (1-100, default 25)
after query string no Pagination cursor
search query string no Free-text across first_name, last_name, email, phone
created_after query string no Only contacts created on or after this ISO date/timestamp.
created_before query string no Only contacts created on or before this ISO date/timestamp.
email_unsubscribed query string no Filter by email unsubscribe flag.
sms_unsubscribed query string no Filter by SMS unsubscribe flag.
voice_unsubscribed query string no Filter by voice unsubscribe flag.
fields query string no Comma-separated field list to return instead of the default set, e.g. "id,email,phone".
source query string no
customer_id query string no Filter to contacts linked to a specific company
archived query string no Archive scope. "false" (default) = active only, "true" = archived only, "all" = both.

📄 /api/contacts/get-contacts.md

POST /contacts/merge Scopes: contacts:delete

Merge duplicate contacts into one. Every child record on the duplicates is re-parented onto the winner before the duplicates are deleted, across the complete foreign-key graph, so nothing is orphaned or destroyed. Pass preview: true to see what would move without writing. This is the only safe way to dedupe: deleting a duplicate directly destroys its linked records.

8 parameters
Name In Type Required Description
winner_id body string yes The contact that survives. Every duplicate is folded into this one, and it keeps its own values wherever both records have something.
loser_ids body array yes The duplicate contacts to absorb, up to 100. Each is emptied onto the winner and then deleted. Ids that are not in this workspace are rejected before anything is written.
preview body boolean no Set true to get the per-table summary of what WOULD move, without writing anything. Use it to review a large dedupe before committing.
fill_empty_fields body boolean no Fill blank fields on the winner from the duplicates. Defaults to true. The winner never loses a value it already had.
transfer body object no Per-category opt-outs, e.g. { "activities": false } leaves activities behind. Anything not named is carried. A category cannot switch off a relationship the database deletes on its own (work orders, stage history, stakeholder links and the like): those are always carried, because skipping them would destroy the rows rather than leave them alone, and the response lists them under forced_categories.
field_overrides body object no Explicit keep-this-value picks from a choose-what-survives review, as field name to value. Applied before the blank-fill pass, which never talks over one.
append_notes body boolean no Append notes from each duplicate onto the winner, separated by a merged marker, instead of letting them die with the row. Defaults to false.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/contacts/post-contacts-merge.md

DELETE /contacts/:contact_id/employers/:customer_id Scopes: contacts:delete

Unlink a contact from a company employer.

3 parameters
Name In Type Required Description
contact_id path string yes
customer_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/contacts/delete-contacts-contact-id-employers-customer-id.md

POST /contacts/search Scopes: contacts:read

Fuzzy search across contacts by name, email, or phone. Returns matching contact records.

2 parameters
Name In Type Required Description
query body string yes Search query
limit body number no

📄 /api/contacts/post-contacts-search.md

POST /contacts/:contact_id/unsubscribe Scopes: contacts:write

Unsubscribe a contact across email/sms/voice/whatsapp channels. Cancels queued comms and auto-enrolments, tags linked opportunities, and writes an audit activity.

5 parameters
Name In Type Required Description
contact_id path string yes
channels body array no Channels to suppress. Omit for every channel. Suppress one channel only (e.g. ["voice"]) when the contact objected to AI calls but still wants email.
reason body string no Why they unsubscribed. Stored on the contact and the audit activity.
source body string no Where the opt-out came from, for ops triage (e.g. "voice_objection").
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/contacts/post-contacts-contact-id-unsubscribe.md

PATCH /contacts/:contact_id Scopes: contacts:write

Update one or more fields on a contact. Pass only the fields you want to change. Set archived=true to archive (hide from active lists) or archived=false to restore.

33 parameters
Name In Type Required Description
contact_id path string yes
first_name body string no Given name (required on create).
last_name body string no
email body string no
phone body string no Mobile, any format, normalised to E.164.
landline body string no
job_title body string no
contact_type body string no
date_of_birth body string no ISO date (YYYY-MM-DD). Pass "" to clear.
notes body string no
source body string no How this contact entered the CRM. One of: manual, form_submission, voice_call, order_form, import, api. Defaults to "manual"; use "api" for anything you create programmatically. This is NOT campaign attribution: to record which ad, channel or campaign a lead came from, set the opportunity's lead_source instead.
alternate_emails body array no Other email addresses this record uses. Bookings, public forms and inbound email match on ANY of them, so a person or business with two addresses resolves to this one record instead of creating a duplicate. Stored lowercased; the main `email` is never duplicated in here. RECOGNITION ONLY - outbound email always goes to `email`. Replaces the whole list, so read the record and merge first.
address_line1 body string no Street address, first line.
address_line2 body string no Street address, second line (unit, level).
city body string no Suburb or city.
state body string no State or region, e.g. "VIC", "NSW". Also used as the fallback for the contact's local time when `timezone` is not set, so it is worth filling in even when the rest of the address is unknown.
postal_code body string no Postcode.
country body string no Country. Defaults to Australia when omitted.
timezone body string no The contact's own timezone ("Timezone" on the contact page). Sets the local time used to decide when the dialler may call them and when business-hours-aware email, SMS and WhatsApp may reach them. Pass an IANA zone such as "Australia/Perth", or a state we can map such as "WA" or "Western Australia". Leave unset (or pass null to clear) to fall back to their state, then their phone country, then the workspace default. Set this whenever you know where a lead actually is: an Australian mobile carries no region, so without it a Perth lead is treated as east coast and called hours early.
source_id body string no UUID of the workspace lead source this contact came through, from the lead sources configured at https://app.trustpager.com/settings/crm. This is the structured counterpart to `source`, which only records the mechanism (form, call, import).
is_primary body boolean no Marks this contact as the primary contact for the company they are linked to. Only one contact per company should carry it.
metadata body object no Custom field values on the contact, keyed by field name, as configured at https://app.trustpager.com/settings/crm. Each value is stored in its declared type so the API, the UI and automations agree on the shape. On update, the keys you send merge into the existing set: other fields are kept, and a key sent as null is removed (see metadata_mode).
relationship_started_at body string no When the real-world relationship actually began, as an ISO date or timestamp. Defaults to when the record was created, so set it explicitly on anything imported from another CRM: birthday tiering, anniversaries and tenure all read this rather than the creation date. Pass "" or null to clear. A future date is rejected.
email_unsubscribed body boolean no true = stop all email to this contact (same effect as unsubscribe_contact). false = re-opt them in: clears their unsubscribe or do-not-contact. A hard bounce or spam complaint on the address stays blocked until unblock_recipient clears it.
sms_unsubscribed body boolean no true = stop all SMS to this contact. false = re-opt them in.
voice_unsubscribed body boolean no true = stop all voice calls to this contact. false = re-opt them in.
whatsapp_unsubscribed body boolean no true = stop all WhatsApp messages to this contact. false = re-opt them in.
is_referrer body boolean no Flags this contact as a referral partner so they appear in Referrals even before their first referral.
referrer_category body string no The partner's tier, matching the workspace's own referral categories (e.g. "Gold", "Introducer"). Tiered fee rates are looked up from this, so an off-list value breaks the lookup. Free text: the workspace decides the vocabulary.
referrer_opportunity_id body string no UUID of the referrer's designated opportunity, the anchor for statements, portal and partner documents. Setting it also marks the contact as a referrer.
archived body boolean no true = archive, false = restore. Archived contacts stay searchable and API-accessible but drop out of active lists.
metadata_mode body string no How metadata lands on update. "merge" (default): the keys you send replace those keys, every other custom field is kept, and a key sent as null is removed. "replace": the object you send becomes the whole set, deleting every field you leave out.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/contacts/patch-contacts-contact-id.md

POST /contacts/voice/unsubscribe Scopes: contacts:write

AI voice agent unsubscribe — looks up contact by caller phone and flips voice_unsubscribed flag.

3 parameters
Name In Type Required Description
phone body string no Caller phone in E.164, e.g. "+61431377068". Optional: falls back to the number on the call payload, and the handler returns a spoken prompt asking for it when neither is present.
reason body string no Why they unsubscribed. Defaults to "voice_call:<call_id>".
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/contacts/post-contacts-voice-unsubscribe.md

Companies (13 endpoints)

Companies (customers) in the workspace, their contacts and their opportunities. The legacy /customers/* paths still work.

📄 Full markdown: /api/companies.md

POST /companies/bulk-create Scopes: companies:write

Create up to 100 companies in one request.

2 parameters
Name In Type Required Description
records body array yes Array of company objects. Accepts every create_company field.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/companies/post-companies-bulk-create.md

POST /companies/bulk-delete Scopes: companies:delete

Delete up to 100 companies in one request. Destructive.

2 parameters
Name In Type Required Description
ids body array yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/companies/post-companies-bulk-delete.md

POST /companies/bulk-update Scopes: companies:write

Update up to 100 companies in one request. Each record is { id, ...fields }. Include archived:true to bulk-archive (or archived:false to bulk-restore).

2 parameters
Name In Type Required Description
records body array yes Array of company objects. Each needs id plus any subset of the update_company fields.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/companies/post-companies-bulk-update.md

POST /companies Scopes: companies:write

Create a new CRM company (customer/account). Requires name. This creates a CRM account record — NOT the workspace org profile.

22 parameters
Name In Type Required Description
name body string yes Company name (required on create).
email body string no
alternate_emails body array no Other email addresses this record uses. Bookings, public forms and inbound email match on ANY of them, so a person or business with two addresses resolves to this one record instead of creating a duplicate. Stored lowercased; the main `email` is never duplicated in here. RECOGNITION ONLY - outbound email always goes to `email`. Replaces the whole list, so read the record and merge first.
phone body string no Mobile in E.164, e.g. +61412345678.
landline body string no Landline in E.164, e.g. +61299991234. Set null to clear.
website body string no
industry body string no
account_type body string no Account/organisation type (e.g. Client, Trustee-Liquidator). Get options from get_crm_settings.account_type_options. Set null to clear.
is_customer body boolean no Is this a customer/client (default true).
is_supplier body boolean no Is this a supplier/vendor (default false).
tax_number body string no Tax number (e.g. ABN).
relationship_started_at body string no When the real-world relationship began (YYYY-MM-DD or ISO 8601). Set null to clear. Cannot be in the future.
address_line1 body string no
address_line2 body string no
city body string no
state body string no
postal_code body string no
country body string no
timezone body string no The company's own timezone, as an IANA zone such as "Australia/Perth", or a state we can map such as "WA". Sets the local time used to decide when the dialler may call and when business-hours-aware sends may reach them.
notes body string no
metadata body object no Custom field values keyed by field_id (get ids from get_crm_settings.custom_fields.account). On update, the keys you send merge into the existing set: other fields are kept, and a key sent as null is removed (see metadata_mode).
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/companies/post-companies.md

DELETE /companies/:company_id Scopes: companies:delete

Delete a company by UUID. Destructive.

2 parameters
Name In Type Required Description
company_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/companies/delete-companies-company-id.md

GET /companies/:company_id Scopes: companies:read

Fetch a single company by UUID. Returns the full record including custom fields.

1 parameter
Name In Type Required Description
company_id path string yes

📄 /api/companies/get-companies-company-id.md

GET /companies/:company_id/activities Scopes: companies:readactivities:read

List activities (calls, meetings, notes, emails) on a company.

1 parameter
Name In Type Required Description
company_id path string yes

📄 /api/companies/get-companies-company-id-activities.md

GET /companies/:company_id/deals Scopes: companies:readopportunities:read

List all opportunities/deals tied to a company.

1 parameter
Name In Type Required Description
company_id path string yes

📄 /api/companies/get-companies-company-id-deals.md

GET /companies Scopes: companies:read

List companies (customers) in the workspace. Returns active (non-archived) companies by default. Supports cursor-based pagination, search, and filters. Pass archived="true" for only archived, or archived="all" for both.

5 parameters
Name In Type Required Description
limit query number no
after query string no
search query string no
industry query string no
archived query string no Archive scope. "false" (default) = active only, "true" = archived only, "all" = both.

📄 /api/companies/get-companies.md

POST /companies/merge Scopes: companies:delete

Merge duplicate companys into one. Every child record on the duplicates is re-parented onto the winner before the duplicates are deleted, across the complete foreign-key graph, so nothing is orphaned or destroyed. Pass preview: true to see what would move without writing. This is the only safe way to dedupe: deleting a duplicate directly destroys its linked records.

8 parameters
Name In Type Required Description
winner_id body string yes The company that survives. Every duplicate is folded into this one, and it keeps its own values wherever both records have something.
loser_ids body array yes The duplicate companys to absorb, up to 100. Each is emptied onto the winner and then deleted. Ids that are not in this workspace are rejected before anything is written.
preview body boolean no Set true to get the per-table summary of what WOULD move, without writing anything. Use it to review a large dedupe before committing.
fill_empty_fields body boolean no Fill blank fields on the winner from the duplicates. Defaults to true. The winner never loses a value it already had.
transfer body object no Per-category opt-outs, e.g. { "activities": false } leaves activities behind. Anything not named is carried. A category cannot switch off a relationship the database deletes on its own (work orders, stage history, stakeholder links and the like): those are always carried, because skipping them would destroy the rows rather than leave them alone, and the response lists them under forced_categories.
field_overrides body object no Explicit keep-this-value picks from a choose-what-survives review, as field name to value. Applied before the blank-fill pass, which never talks over one.
append_notes body boolean no Append notes from each duplicate onto the winner, separated by a merged marker, instead of letting them die with the row. Defaults to false.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/companies/post-companies-merge.md

POST /companies/search Scopes: companies:read

Fuzzy search across companies by name, email, phone, website. Returns matching company records.

2 parameters
Name In Type Required Description
query body string yes
limit body number no

📄 /api/companies/post-companies-search.md

PATCH /companies/:company_id Scopes: companies:write

Update a CRM company (customer/account). Pass only the fields you want to change. Set archived=true to archive (hide from active lists) or archived=false to restore. This edits a CRM account record — NOT the workspace org profile (use update_our_company for that).

25 parameters
Name In Type Required Description
company_id path string yes
name body string no Company name (required on create).
email body string no
alternate_emails body array no Other email addresses this record uses. Bookings, public forms and inbound email match on ANY of them, so a person or business with two addresses resolves to this one record instead of creating a duplicate. Stored lowercased; the main `email` is never duplicated in here. RECOGNITION ONLY - outbound email always goes to `email`. Replaces the whole list, so read the record and merge first.
phone body string no Mobile in E.164, e.g. +61412345678.
landline body string no Landline in E.164, e.g. +61299991234. Set null to clear.
website body string no
industry body string no
account_type body string no Account/organisation type (e.g. Client, Trustee-Liquidator). Get options from get_crm_settings.account_type_options. Set null to clear.
is_customer body boolean no Is this a customer/client (default true).
is_supplier body boolean no Is this a supplier/vendor (default false).
tax_number body string no Tax number (e.g. ABN).
relationship_started_at body string no When the real-world relationship began (YYYY-MM-DD or ISO 8601). Set null to clear. Cannot be in the future.
address_line1 body string no
address_line2 body string no
city body string no
state body string no
postal_code body string no
country body string no
timezone body string no The company's own timezone, as an IANA zone such as "Australia/Perth", or a state we can map such as "WA". Sets the local time used to decide when the dialler may call and when business-hours-aware sends may reach them.
notes body string no
metadata body object no Custom field values keyed by field_id (get ids from get_crm_settings.custom_fields.account). On update, the keys you send merge into the existing set: other fields are kept, and a key sent as null is removed (see metadata_mode).
archived body boolean no true = archive, false = restore. Archived companies stay searchable and API-accessible but drop out of active lists.
metadata_mode body string no How metadata lands on update. "merge" (default): the keys you send replace those keys, every other custom field is kept, and a key sent as null is removed. "replace": the object you send becomes the whole set, deleting every field you leave out.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/companies/patch-companies-company-id.md

Opportunities (58 endpoints)

Opportunities (deals) and everything hanging off them: products, contacts, invoices, documents and pipeline placement. The legacy /deals/* paths still work.

📄 Full markdown: /api/opportunities.md

POST /opportunities/:opportunity_id/billing-parties/:customer_id Scopes: opportunities:writeinvoices:write

Add a billing party (payer company) to an opportunity. Invoices raised in your accounting integration to that company then surface on this opportunity in addition to the client’s, without changing the opportunity’s client. Use when a related entity pays the fees for the matter. A manually pinned invoice (linked directly to another opportunity) is never pulled in.

4 parameters
Name In Type Required Description
opportunity_id path string yes
customer_id path string yes The crm_customers id of the paying company.
notes body string no Optional note, e.g. why this entity pays the fees.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-opportunity-id-billing-parties-customer-id.md

POST /opportunities/:opportunity_id/add-card Scopes: opportunities:write

Add an additional pipeline placement card to an opportunity (multi-pipeline workspaces).

4 parameters
Name In Type Required Description
opportunity_id path string yes
pipeline_id body string yes
stage_id body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-opportunity-id-add-card.md

POST /opportunities/:opportunity_id/contacts/:contact_id Scopes: opportunities:write

Link a contact to an opportunity.

3 parameters
Name In Type Required Description
opportunity_id path string yes
contact_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-opportunity-id-contacts-contact-id.md

POST /opportunities/:opportunity_id/documents/:attachment_id Scopes: opportunities:writedocuments:read

Attach a document to an opportunity.

3 parameters
Name In Type Required Description
opportunity_id path string yes
attachment_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-opportunity-id-documents-attachment-id.md

POST /opportunities/:opportunity_id/files/:attachment_id Scopes: opportunities:writefiles:read

Attach a file to an opportunity.

3 parameters
Name In Type Required Description
opportunity_id path string yes
attachment_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-opportunity-id-files-attachment-id.md

POST /opportunities/:opportunity_id/images/:attachment_id Scopes: opportunities:writefiles:read

Attach an image to an opportunity.

3 parameters
Name In Type Required Description
opportunity_id path string yes
attachment_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-opportunity-id-images-attachment-id.md

POST /opportunities/:opportunity_id/products Scopes: opportunities:write

Attach a product to an opportunity.

9 parameters
Name In Type Required Description
opportunity_id path string yes
product_id body string no
quantity body number no
unit_price body number no
discount_percent body number no
deposit_percent body number no
bucket_id body string no Optional product bucket id to file this line item under (create buckets with create_opportunity_product_bucket). Buckets group line items by area (e.g. "Main Bathroom") with their own subtotal, and let the same product appear in more than one bucket.
invoice_description body string no The exact text this line prints on the invoice: the "Invoice description" field on the line item's Details tab. Use it to describe the work actually done for this customer instead of the generic catalogue wording. Leave it unset and the line falls back to the catalogue product name plus its description, as before. Pass an empty string to clear an override and go back to the catalogue text.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-opportunity-id-products.md

POST /opportunities/:opportunity_id/spreadsheets/:spreadsheet_id Scopes: opportunities:writespreadsheets:write

Attach a spreadsheet to an opportunity.

3 parameters
Name In Type Required Description
opportunity_id path string yes
spreadsheet_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-opportunity-id-spreadsheets-spreadsheet-id.md

POST /opportunities/:opportunity_id/users/:user_id Scopes: opportunities:write

Assign a workspace user to an opportunity.

3 parameters
Name In Type Required Description
opportunity_id path string yes
user_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-opportunity-id-users-user-id.md

POST /opportunities/bulk-link-contacts Scopes: opportunities:write

Link up to 100 contact-to-opportunity pairs in one request. Each record is { deal_id, contact_id, role?, notes? }. Already-linked pairs are returned in skipped[] (idempotent), unknown ids in errors[]. Returns created[], skipped[], errors[].

2 parameters
Name In Type Required Description
records body array yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-bulk-link-contacts.md

POST /opportunities/:opportunity_id/products/bulk Scopes: opportunities:write

Add multiple products to an opportunity in a single request.

3 parameters
Name In Type Required Description
opportunity_id path string yes
products body array yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-opportunity-id-products-bulk.md

POST /opportunities/bulk-products Scopes: opportunities:write

Add product line items to up to 100 opportunities in a single request (cross-opportunity bulk). Each item is { opportunity_id, products: [{ product_id, quantity, unit_price, discount_percent?, sort_order?, section? }] }. Per-item errors are isolated; each affected opportunity's value rolls up automatically. Built for high-volume migrations / batch quoting.

2 parameters
Name In Type Required Description
items body array yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-bulk-products.md

POST /opportunities/bulk-create Scopes: opportunities:write

Create up to 100 opportunities in one request. Pass dedup_key (a metadata field name, e.g. "external_id") to make the call idempotent: any record whose metadata[dedup_key] already exists on an opportunity is skipped, so a half-finished import can be re-run safely. Returns created[], skipped[], errors[].

6 parameters
Name In Type Required Description
records body array yes Array of opportunity objects. Accepts every create_opportunity field.
pipeline_id body string no Default pipeline for every record, overridable per record. Must be passed together with stage_id. Get a real id from list_pipelines: a pipeline_id that does not exist in this workspace fails the whole record rather than creating an unplaced opportunity.
stage_id body string no Default stage for every record, overridable per record. Must be passed together with pipeline_id and must be a stage OF that pipeline. Get a real id from list_pipeline_stages: a wrong or invented stage_id fails the record instead of creating an opportunity that appears on no board.
skip_automations body boolean no
dedup_key body string no Name of a metadata field to treat as a stable external id for idempotent re-runs.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-bulk-create.md

POST /opportunities/bulk-delete Scopes: opportunities:delete

Delete up to 100 opportunities in one request. Destructive.

2 parameters
Name In Type Required Description
ids body array yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-bulk-delete.md

POST /opportunities/bulk-move Scopes: opportunities:write

Move up to 100 opportunities into a target pipeline + stage at once.

6 parameters
Name In Type Required Description
ids body array yes
pipeline_id body string yes
stage_id body string yes
skip_automations body boolean no Set true to suppress stage-change automation triggers across every opportunity in this batch.
skip_action_ids body array no Automation action UUIDs to skip for this batch, when you want most of the stage-change automations to run but not these.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-bulk-move.md

POST /opportunities/bulk-update Scopes: opportunities:write

Update up to 100 opportunities in one request. Each record is { id, ...fields }. Include archived:true to bulk-archive (or archived:false to bulk-restore).

2 parameters
Name In Type Required Description
records body array yes Array of opportunity objects. Each needs id plus any subset of the update_opportunity fields.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-bulk-update.md

POST /opportunities Scopes: opportunities:write

Create a new opportunity (deal). Requires name. To place it on a pipeline board, pass pipeline_id and stage_id together; omit both to create it unplaced. Both ids are validated, so a wrong stage_id returns an error rather than creating an opportunity that appears on no board.

26 parameters
Name In Type Required Description
name body string yes
contact_id body string no
customer_id body string no
pipeline_id body string no Pipeline to place the opportunity on. Must be passed together with stage_id. Get a real id from list_pipelines.
stage_id body string no Stage to place the opportunity in. Must be passed together with pipeline_id and must be a stage OF that pipeline. Get a real id from list_pipeline_stages: never construct or pad a UUID by hand.
value body number no
currency body string no
value_is_manual body boolean no Force this opportunity to manual value (true, locked from product auto-sum) or auto (false). Defaults to the workspace CRM setting for opportunity value.
status body string no
notes body string no
owner_id body string no Deprecated alias for assigned_to, kept working for existing integrations. Prefer assigned_to.
skip_automations body boolean no Set true to suppress opportunity_created automation triggers for this write. Use for bulk imports and backfills.
lead_source body string no Campaign attribution: which ad, channel or campaign this opportunity came from. Distinct from a contact's `source`, which records how the contact entered the CRM.
tags body array no Tags on the opportunity. Full-replace array, so read the record and merge first.
metadata body object no Custom field values on the opportunity, keyed by field name, as configured at https://app.trustpager.com/settings/crm. `metadata.dedup_key` is treated as a stable external id for de-duplication. On update, the keys you send merge into the existing set: other fields are kept, and a key sent as null is removed (see metadata_mode).
assigned_to body string no UUID of the team member who owns this opportunity. Get real ids from list_company_users, and use the `user_id` field on each row: the row's own `id` is the membership record, not the user.
opportunity_type body string no The workspace's own classification for this opportunity, matching the opportunity types configured at https://app.trustpager.com/settings/crm.
timezone body string no The timezone belonging to this opportunity. Used for date and time variables rendered on its documents and messages when neither the contact nor the company has one of their own. Pass an IANA zone such as "Australia/Perth", or a state we can map such as "WA" or "Western Australia". Pass null to clear it. Prefer setting the timezone on the CONTACT when you know where the person is: that is the one the dialler and business-hours sends read first.
next_action_name body string no Title of the opportunity's next action, e.g. "Follow-up call". Shows on the opportunity card and on the calendar at https://app.trustpager.com/tasks/calendar.
next_action_date body string no Date of the next action as ISO "YYYY-MM-DD". Pass null to clear the reminder.
next_action_time body string no Time of the next action as a UTC wall time, "HH:MM" or "HH:MM:SS". Paired with next_action_date — a date with no time renders as an untimed reminder.
next_action_duration body number no Length of the next action in minutes. Drives the end time shown on the calendar and the block height in Day view. Omit for a point-in-time reminder: with no duration the calendar shows a start time only and never invents an end time.
next_action_type body string no "next_action" = a plain reminder. "google_meeting" = a Google Meet (a meeting URL is generated on save). A custom event type id is also accepted.
next_action_description body string no Longer notes for the next action, shown in the reminder editor.
next_action_color body string no Colour of the calendar chip: blue, red, green, purple, orange, teal, pink, yellow, indigo, or gray.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities.md

POST /opportunities/:opportunity_id/invoices Scopes: opportunities:writeinvoices:write

Create an invoice on this opportunity via the workspace accounting integration (Xero today). The invoice is created as a DRAFT unless you pass status, so someone approves it before it can be paid or emailed. Bill the opportunity products with line_items_source "deal_products", or pass line_items.

22 parameters
Name In Type Required Description
opportunity_id path string yes
status body string no Status the invoice is created with. Defaults to DRAFT, which holds it for someone to approve before it can be paid or emailed. SUBMITTED sends it for approval; AUTHORISED makes it payable straight away. Invoice email only goes out for SUBMITTED or AUTHORISED.
line_items_source body string no Set to "deal_products" to bill the products on this opportunity. Either this or line_items must be given, or no invoice is created.
invoiced_deal_product_ids body array no With line_items_source "deal_products", bill only these opportunity product ids instead of all of them.
line_items body array no Explicit invoice lines, used instead of line_items_source.
custom_line_items body array no Extra manual lines appended after the opportunity products.
invoice_portion body string no Which part of the opportunity this invoice covers. Defaults to full. deposit and balance add a label to the reference.
due_days body number no Days from today until the invoice is due. 0 means due on receipt. An AUTHORISED invoice with no due date is rejected, so set this whenever status is AUTHORISED.
reference body string no Reference printed on the invoice, e.g. a job or quote number.
account_code body string no Default accounting code for every line. Defaults to "200".
tax_type body string no Default tax type for every line. Defaults to "OUTPUT".
contact_id body string no Accounting contact id to bill. Leave out to use the contact on the opportunity, created in the accounting system if it does not exist yet.
email_mode body string no How the invoice is emailed after it is created. Defaults to none. Only sends for SUBMITTED or AUTHORISED invoices.
email_subject body string no Subject line when email_mode is "trustpager".
email_body body string no Email body when email_mode is "trustpager".
email_cc body array no Addresses copied on the invoice email when email_mode is "trustpager".
email_attach_pdf body boolean no Attach the invoice PDF when email_mode is "trustpager". Defaults to true.
email_recipient_source body string no Who receives the invoice email when email_mode is "trustpager". Defaults to primary_contact.
email_recipient_contact_id body string no CRM contact id to email when email_recipient_source is "secondary_contact".
email_recipient_email body string no Send the invoice email to this address instead of a contact.
auto_send_email body boolean no Legacy. true is the same as email_mode "xero". Use email_mode instead.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-opportunity-id-invoices.md

POST /opportunities/:opportunity_id/buckets Scopes: opportunities:write

Create a product bucket on an opportunity. A bucket is a labelled grouping (e.g. "Main Bathroom") that line items file under, so the quote breaks down per area with its own subtotal on the products list, invoices, and the rendered estimate.

4 parameters
Name In Type Required Description
opportunity_id path string yes
label body string yes The bucket heading shown everywhere this opportunity is priced (e.g. "Main Bathroom").
sort_order body number no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-opportunity-id-buckets.md

POST /opportunities/:opportunity_id/product-costs/:deal_product_id Scopes: opportunities:write

Add a supplier cost to an opportunity product line.

12 parameters
Name In Type Required Description
opportunity_id path string yes
deal_product_id path string yes
label body string no Label for the cost line, e.g. "Standard Cost" or "Labour". Shown as Product Name when the cost comes from a supplier catalog product.
unit_cost body number no Cost Price per unit, in the cost line's currency.
quantity body number no Qty: how many units this cost line covers.
currency body string no Currency code, e.g. AUD.
unit body string no Unit the quantity counts: once-off, each, minute, hour, day, month, year, kg or unit. Omit or send an empty string for no unit.
sku body string no SKU: the supplier's reference code.
supplier_id body string no Supplier: UUID of a company marked as a supplier in this workspace.
supplier_product_id body string no Supplier Catalog Product: UUID of the item in that supplier's catalog this cost comes from.
notes body string no Notes about the cost line.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-opportunity-id-product-costs-deal-product-id.md

DELETE /opportunities/:opportunity_id Scopes: opportunities:delete

Delete an opportunity by UUID. Destructive.

2 parameters
Name In Type Required Description
opportunity_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/delete-opportunities-opportunity-id.md

DELETE /opportunities/:opportunity_id/buckets/:bucket_id Scopes: opportunities:delete

Delete a product bucket from an opportunity. Line items in the bucket are kept but become ungrouped; their prices are unchanged.

3 parameters
Name In Type Required Description
opportunity_id path string yes
bucket_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/delete-opportunities-opportunity-id-buckets-bucket-id.md

DELETE /opportunities/:opportunity_id/product-costs/:cost_id Scopes: opportunities:delete

Delete a supplier cost from an opportunity product line. Get cost_id from list_opportunity_product_costs.

3 parameters
Name In Type Required Description
opportunity_id path string yes
cost_id path string yes The cost line to delete, from list_opportunity_product_costs.
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/delete-opportunities-opportunity-id-product-costs-cost-id.md

GET /opportunities/:opportunity_id Scopes: opportunities:read

Fetch a single opportunity by UUID. Returns the full record including custom fields, products, contacts, and current stage placement.

1 parameter
Name In Type Required Description
opportunity_id path string yes

📄 /api/opportunities/get-opportunities-opportunity-id.md

GET /opportunities Scopes: opportunities:read

List opportunities (deals) in the workspace. Returns active (non-archived) opportunities by default. Supports cursor pagination, search, and filtering by pipeline, stage, status, assigned_to, contact, company, dates. Pass archived="true" for only archived, or archived="all" for both.

12 parameters
Name In Type Required Description
limit query number no
after query string no
search query string no
pipeline_id query string no
stage_id query string no
status query string no
assigned_to query string no Owner. User UUID of the assigned team member.
contact_id query string no
customer_id query string no
created_after query string no ISO timestamp. Only opportunities created on or after this.
created_before query string no ISO timestamp. Only opportunities created on or before this.
archived query string no Archive scope. "false" (default) = active only, "true" = archived only, "all" = both.

📄 /api/opportunities/get-opportunities.md

GET /opportunities/:opportunity_id/billing-parties Scopes: opportunities:readinvoices:read

List the billing parties (payer companies) on an opportunity. A billing party is a separate company that pays the fees for this matter; invoices raised to it surface on this opportunity in addition to the client’s, without changing who the client is.

1 parameter
Name In Type Required Description
opportunity_id path string yes

📄 /api/opportunities/get-opportunities-opportunity-id-billing-parties.md

GET /opportunities/:opportunity_id/invoices Scopes: opportunities:readinvoices:read

List native invoices linked to an opportunity. Returns invoice headers (invoice_number, status, total, amount_due, amount_paid, due_date, …); use get_invoice for line items.

1 parameter
Name In Type Required Description
opportunity_id path string yes

📄 /api/opportunities/get-opportunities-opportunity-id-invoices.md

GET /opportunities/:opportunity_id/buckets Scopes: opportunities:read

List the product buckets (groupings like "Main Bathroom", "Ensuite") on an opportunity, in display order.

1 parameter
Name In Type Required Description
opportunity_id path string yes

📄 /api/opportunities/get-opportunities-opportunity-id-buckets.md

POST /opportunities/merge Scopes: opportunities:delete

Merge duplicate opportunitys into one. Every child record on the duplicates is re-parented onto the winner before the duplicates are deleted, across the complete foreign-key graph, so nothing is orphaned or destroyed. Pass preview: true to see what would move without writing. This is the only safe way to dedupe: deleting a duplicate directly destroys its linked records.

9 parameters
Name In Type Required Description
winner_id body string yes The opportunity that survives. Every duplicate is folded into this one, and it keeps its own values wherever both records have something.
loser_ids body array yes The duplicate opportunitys to absorb, up to 100. Each is emptied onto the winner and then deleted. Ids that are not in this workspace are rejected before anything is written.
preview body boolean no Set true to get the per-table summary of what WOULD move, without writing anything. Use it to review a large dedupe before committing.
fill_empty_fields body boolean no Fill blank fields on the winner from the duplicates. Defaults to true. The winner never loses a value it already had.
transfer body object no Per-category opt-outs, e.g. { "activities": false } leaves activities behind. Anything not named is carried. A category cannot switch off a relationship the database deletes on its own (work orders, stage history, stakeholder links and the like): those are always carried, because skipping them would destroy the rows rather than leave them alone, and the response lists them under forced_categories.
field_overrides body object no Explicit keep-this-value picks from a choose-what-survives review, as field name to value. Applied before the blank-fill pass, which never talks over one.
append_notes body boolean no Append notes from each duplicate onto the winner, separated by a merged marker, instead of letting them die with the row. Defaults to false.
union_tags body boolean no Union tags from each duplicate onto the winner, matching on tag name so an existing tag is not duplicated. Tags live on the row, so without this they die with it. Defaults to false.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-merge.md

POST /opportunities/:opportunity_id/move Scopes: opportunities:write

Move an opportunity to a different stage, on its current pipeline or another one. stage_id is enough: pipeline_id is optional and worked out from the stage when left out. The destination stage's automations run whenever the card lands on a new stage, including when it arrives from another pipeline; send skip_automations to move it quietly. Moving into a won or lost stage also marks the opportunity won or lost; send lost_reasons / won_reasons to record why.

8 parameters
Name In Type Required Description
opportunity_id path string yes
pipeline_id body string no
stage_id body string yes
lost_reasons body array no Why the opportunity was lost, e.g. ["No Response"]. Saved when the opportunity is (or is being set to) lost. Values must come from the workspace's lost reasons list at https://app.trustpager.com/settings/crm (matched ignoring case). Anything not on the list is not saved and is named in _warnings along with the valid options. Full-replace; null clears.
won_reasons body array no Why the opportunity was won, e.g. ["Referral"]. Saved when the opportunity is (or is being set to) won. Values must come from the workspace's won reasons list at https://app.trustpager.com/settings/crm (matched ignoring case). Anything not on the list is not saved and is named in _warnings along with the valid options. Full-replace; null clears.
skip_automations body boolean no Set true to move the card without running the destination stage's automations. By default they run whenever the card lands on a new stage, including when it arrives from another pipeline.
skip_action_ids body array no Automation action UUIDs to skip on this move, when you want most of the destination stage's automations to run but not these.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-opportunity-id-move.md

POST /opportunities/:opportunity_id/move-card Scopes: opportunities:write

Alias of move_opportunity (legacy path). The destination stage's automations run whenever the card lands on a new stage, including when it arrives from another pipeline; send skip_automations to move it quietly.

8 parameters
Name In Type Required Description
opportunity_id path string yes
pipeline_id body string yes
stage_id body string yes
lost_reasons body array no Why the opportunity was lost, e.g. ["No Response"]. Saved when the opportunity is (or is being set to) lost. Values must come from the workspace's lost reasons list at https://app.trustpager.com/settings/crm (matched ignoring case). Anything not on the list is not saved and is named in _warnings along with the valid options. Full-replace; null clears.
won_reasons body array no Why the opportunity was won, e.g. ["Referral"]. Saved when the opportunity is (or is being set to) won. Values must come from the workspace's won reasons list at https://app.trustpager.com/settings/crm (matched ignoring case). Anything not on the list is not saved and is named in _warnings along with the valid options. Full-replace; null clears.
skip_automations body boolean no Set true to move the card without running the destination stage's automations. By default they run whenever the card lands on a new stage, including when it arrives from another pipeline.
skip_action_ids body array no Automation action UUIDs to skip on this move, when you want most of the destination stage's automations to run but not these.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-opportunity-id-move-card.md

DELETE /opportunities/:opportunity_id/billing-parties/:customer_id Scopes: opportunities:writeinvoices:delete

Remove a billing party from an opportunity. The paying company and its invoices are untouched; only the link that surfaces them on this opportunity is removed.

3 parameters
Name In Type Required Description
opportunity_id path string yes
customer_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/delete-opportunities-opportunity-id-billing-parties-customer-id.md

POST /opportunities/:opportunity_id/remove-card Scopes: opportunities:write

Remove an opportunity card from a pipeline.

3 parameters
Name In Type Required Description
opportunity_id path string yes
pipeline_id body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-opportunity-id-remove-card.md

DELETE /opportunities/:opportunity_id/contacts/:contact_id Scopes: opportunities:delete

Unlink a contact from an opportunity.

3 parameters
Name In Type Required Description
opportunity_id path string yes
contact_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/delete-opportunities-opportunity-id-contacts-contact-id.md

DELETE /opportunities/:opportunity_id/documents/:attachment_id Scopes: opportunities:write

Detach a document from an opportunity.

3 parameters
Name In Type Required Description
opportunity_id path string yes
attachment_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/delete-opportunities-opportunity-id-documents-attachment-id.md

DELETE /opportunities/:opportunity_id/files/:attachment_id Scopes: opportunities:write

Detach a file from an opportunity.

3 parameters
Name In Type Required Description
opportunity_id path string yes
attachment_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/delete-opportunities-opportunity-id-files-attachment-id.md

DELETE /opportunities/:opportunity_id/images/:attachment_id Scopes: opportunities:write

Detach an image from an opportunity.

3 parameters
Name In Type Required Description
opportunity_id path string yes
attachment_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/delete-opportunities-opportunity-id-images-attachment-id.md

DELETE /opportunities/:opportunity_id/products/:deal_product_id Scopes: opportunities:delete

Remove a product from an opportunity.

3 parameters
Name In Type Required Description
opportunity_id path string yes
deal_product_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/delete-opportunities-opportunity-id-products-deal-product-id.md

DELETE /opportunities/:opportunity_id/spreadsheets/:spreadsheet_id Scopes: opportunities:writespreadsheets:write

Detach a spreadsheet from an opportunity.

3 parameters
Name In Type Required Description
opportunity_id path string yes
spreadsheet_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/delete-opportunities-opportunity-id-spreadsheets-spreadsheet-id.md

DELETE /opportunities/:opportunity_id/users/:user_id Scopes: opportunities:delete

Unassign a user from an opportunity.

3 parameters
Name In Type Required Description
opportunity_id path string yes
user_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/delete-opportunities-opportunity-id-users-user-id.md

POST /opportunities/:opportunity_id/buckets/reorder Scopes: opportunities:write

Reorder the product buckets on an opportunity. Pass bucket_ids in the order you want them displayed.

3 parameters
Name In Type Required Description
opportunity_id path string yes
bucket_ids body array yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-opportunity-id-buckets-reorder.md

POST /opportunities/:opportunity_id/products/reorder Scopes: opportunities:write

Reorder products on an opportunity.

3 parameters
Name In Type Required Description
opportunity_id path string yes
product_ids body array yes Opportunity-product row UUIDs in the desired display order.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/post-opportunities-opportunity-id-products-reorder.md

POST /opportunities/search Scopes: opportunities:read

Fuzzy search across opportunities by name, contact, or company. Returns matching opportunity records. Optionally narrow the search to a pipeline, stage, or status.

5 parameters
Name In Type Required Description
query body string yes
limit body number no
pipeline_id body string no
stage_id body string no
status body string no

📄 /api/opportunities/post-opportunities-search.md

PATCH /opportunities/:opportunity_id Scopes: opportunities:write

Update one or more fields on an opportunity. Pass only the fields you want to change. Set archived=true to archive (hide from active lists, pipeline, and dashboard counts) or archived=false to restore. The next_action_* fields set the opportunity's reminder, which appears on its card and on the calendar.

28 parameters
Name In Type Required Description
opportunity_id path string yes
name body string no
value body number no
currency body string no
value_is_manual body boolean no Whether this opportunity's value is set manually (locked) or summed automatically from its products. Setting value also sets this to true, so products stop overwriting it. Pass false to revert to auto — the value is immediately recomputed from the products.
status body string no Opportunity status: "open", "won" or "lost". When closing, send the reason in the same call with lost_reasons or won_reasons.
notes body string no
owner_id body string no Deprecated alias for assigned_to, kept working for existing integrations. Prefer assigned_to.
contact_id body string no
customer_id body string no
archived body boolean no true = archive, false = restore. Archived opportunities stay searchable and API-accessible but drop out of active lists, the pipeline board, and dashboard counts.
metadata_mode body string no How metadata lands on update. "merge" (default): the keys you send replace those keys, every other custom field is kept, and a key sent as null is removed. "replace": the object you send becomes the whole set, deleting every field you leave out.
lead_source body string no Campaign attribution: which ad, channel or campaign this opportunity came from. Distinct from a contact's `source`, which records how the contact entered the CRM.
tags body array no Tags on the opportunity. Full-replace array, so read the record and merge first.
metadata body object no Custom field values on the opportunity, keyed by field name, as configured at https://app.trustpager.com/settings/crm. `metadata.dedup_key` is treated as a stable external id for de-duplication. On update, the keys you send merge into the existing set: other fields are kept, and a key sent as null is removed (see metadata_mode).
assigned_to body string no UUID of the team member who owns this opportunity. Get real ids from list_company_users, and use the `user_id` field on each row: the row's own `id` is the membership record, not the user.
opportunity_type body string no The workspace's own classification for this opportunity, matching the opportunity types configured at https://app.trustpager.com/settings/crm.
timezone body string no The timezone belonging to this opportunity. Used for date and time variables rendered on its documents and messages when neither the contact nor the company has one of their own. Pass an IANA zone such as "Australia/Perth", or a state we can map such as "WA" or "Western Australia". Pass null to clear it. Prefer setting the timezone on the CONTACT when you know where the person is: that is the one the dialler and business-hours sends read first.
next_action_name body string no Title of the opportunity's next action, e.g. "Follow-up call". Shows on the opportunity card and on the calendar at https://app.trustpager.com/tasks/calendar.
next_action_date body string no Date of the next action as ISO "YYYY-MM-DD". Pass null to clear the reminder.
next_action_time body string no Time of the next action as a UTC wall time, "HH:MM" or "HH:MM:SS". Paired with next_action_date — a date with no time renders as an untimed reminder.
next_action_duration body number no Length of the next action in minutes. Drives the end time shown on the calendar and the block height in Day view. Omit for a point-in-time reminder: with no duration the calendar shows a start time only and never invents an end time.
next_action_type body string no "next_action" = a plain reminder. "google_meeting" = a Google Meet (a meeting URL is generated on save). A custom event type id is also accepted.
next_action_description body string no Longer notes for the next action, shown in the reminder editor.
next_action_color body string no Colour of the calendar chip: blue, red, green, purple, orange, teal, pink, yellow, indigo, or gray.
lost_reasons body array no Why the opportunity was lost, e.g. ["No Response"]. Saved when the opportunity is (or is being set to) lost. Values must come from the workspace's lost reasons list at https://app.trustpager.com/settings/crm (matched ignoring case). Anything not on the list is not saved and is named in _warnings along with the valid options. Full-replace; null clears.
won_reasons body array no Why the opportunity was won, e.g. ["Referral"]. Saved when the opportunity is (or is being set to) won. Values must come from the workspace's won reasons list at https://app.trustpager.com/settings/crm (matched ignoring case). Anything not on the list is not saved and is named in _warnings along with the valid options. Full-replace; null clears.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/patch-opportunities-opportunity-id.md

PATCH /opportunities/:opportunity_id/products/:deal_product_id Scopes: opportunities:write

Update an opportunity product line item.

9 parameters
Name In Type Required Description
opportunity_id path string yes
deal_product_id path string yes
quantity body number no
unit_price body number no
discount_percent body number no
deposit_percent body number no
bucket_id body string no Product bucket id to file this line item under. Pass an empty string or null to unbucket it. Buckets group line items by area (e.g. "Main Bathroom") with their own subtotal.
invoice_description body string no The exact text this line prints on the invoice: the "Invoice description" field on the line item's Details tab. Use it to describe the work actually done for this customer instead of the generic catalogue wording. Leave it unset and the line falls back to the catalogue product name plus its description, as before. Pass an empty string to clear an override and go back to the catalogue text.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/patch-opportunities-opportunity-id-products-deal-product-id.md

PATCH /opportunities/:opportunity_id/buckets/:bucket_id Scopes: opportunities:write

Rename or reorder a single product bucket on an opportunity.

5 parameters
Name In Type Required Description
opportunity_id path string yes
bucket_id path string yes
label body string no
sort_order body number no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/patch-opportunities-opportunity-id-buckets-bucket-id.md

PATCH /opportunities/:opportunity_id/product-costs/:cost_id Scopes: opportunities:write

Update a supplier cost on an opportunity product line. Only the fields you send change; send null to clear one. Get cost_id from list_opportunity_product_costs.

12 parameters
Name In Type Required Description
opportunity_id path string yes
cost_id path string yes The cost line to update, from list_opportunity_product_costs.
label body string no Label for the cost line, e.g. "Standard Cost" or "Labour". Shown as Product Name when the cost comes from a supplier catalog product.
unit_cost body number no Cost Price per unit, in the cost line's currency.
quantity body number no Qty: how many units this cost line covers.
currency body string no Currency code, e.g. AUD.
unit body string no Unit the quantity counts: once-off, each, minute, hour, day, month, year, kg or unit. Omit or send an empty string for no unit.
sku body string no SKU: the supplier's reference code.
supplier_id body string no Supplier: UUID of a company marked as a supplier in this workspace.
supplier_product_id body string no Supplier Catalog Product: UUID of the item in that supplier's catalog this cost comes from.
notes body string no Notes about the cost line.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/opportunities/patch-opportunities-opportunity-id-product-costs-cost-id.md

Pipelines (12 endpoints)

Manage sales pipelines and their stages. Opportunities move through pipeline stages to track progress.

📄 Full markdown: /api/pipelines.md

POST /pipelines Scopes: pipelines:write

Create a new pipeline in the workspace. Requires a name; optional fields include description, is_active, is_default, position, sop_url, learning_hub_canvas_id, connected_pipeline_ids, exclude_from_lead_reporting.

10 parameters
Name In Type Required Description
name body string yes Pipeline name (e.g. "Sales", "Onboarding")
description body string no Optional plain-text description of what this pipeline tracks
is_active body boolean no Whether the pipeline is currently active (default true)
is_default body boolean no Make this the workspace default pipeline
position body number no Display order across all pipelines
learning_hub_canvas_id body string no Optional UUID of a Playbook to attach as this pipeline's training resource. Mutually exclusive with sop_url. When set, the "View Training" button opens the Playbook in-app rather than an external URL.
exclude_from_lead_reporting body boolean no When true, opportunities in this pipeline are excluded from the Lead Sources leaderboard (totals, per-source cards, conversion rate). Defaults to false. Use for non-lead pipelines like supplier or referral-partner networks stored as opportunities.
sop_url body string no External URL for this pipeline training resource. Mutually exclusive with learning_hub_canvas_id: when set, the View Training button opens this link instead of a Playbook.
connected_pipeline_ids body array no UUIDs of other pipelines this one hands work to, so an opportunity can move on when it finishes here.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/pipelines/post-pipelines.md

POST /pipelines/:pipeline_id/stages Scopes: pipelines:write

Add a new stage to a pipeline. Requires a name. Optional: position (defaults to end), color (hex), is_won_stage, is_lost_stage.

7 parameters
Name In Type Required Description
pipeline_id path string yes UUID of the parent pipeline
name body string yes Stage name
position body number no Display order within the pipeline (0-based)
color body string no Hex colour code for the stage chip
is_won_stage body boolean no Mark this stage as a "won" terminal stage
is_lost_stage body boolean no Mark this stage as a "lost" terminal stage
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/pipelines/post-pipelines-pipeline-id-stages.md

DELETE /pipelines/:pipeline_id Scopes: pipelines:delete

Delete a pipeline by UUID. Destructive — pipeline cannot be deleted if it has opportunities placed in any of its stages.

2 parameters
Name In Type Required Description
pipeline_id path string yes UUID of the pipeline to delete
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/pipelines/delete-pipelines-pipeline-id.md

DELETE /pipelines/:pipeline_id/stages/:stage_id Scopes: pipelines:delete

Delete a single stage from a pipeline. Destructive — fails if opportunities are placed in this stage.

3 parameters
Name In Type Required Description
pipeline_id path string yes
stage_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/pipelines/delete-pipelines-pipeline-id-stages-stage-id.md

GET /pipelines/:pipeline_id Scopes: pipelines:read

Fetch a single pipeline by UUID. Returns the pipeline record (does not include stages — use list_pipeline_stages for that).

1 parameter
Name In Type Required Description
pipeline_id path string yes UUID of the pipeline

📄 /api/pipelines/get-pipelines-pipeline-id.md

GET /pipelines/:pipeline_id/summary Scopes: pipelines:read

Fetch summary statistics for a pipeline: deal counts and value totals per stage. Use this for "pipeline health" or "what is in stage X" questions.

1 parameter
Name In Type Required Description
pipeline_id path string yes

📄 /api/pipelines/get-pipelines-pipeline-id-summary.md

GET /pipelines/:pipeline_id/deals Scopes: pipelines:readopportunities:read

List all opportunities (deals) placed in any stage of a pipeline. Returns opportunity records with their current stage placement.

3 parameters
Name In Type Required Description
pipeline_id path string yes
limit query number no Max rows (1-100, default 25)
after query string no Pagination cursor

📄 /api/pipelines/get-pipelines-pipeline-id-deals.md

GET /pipelines/:pipeline_id/stages Scopes: pipelines:read

List all stages of a pipeline in display order. Each stage includes a deal_count. Use this to find a stage UUID for create_opportunity or move_opportunity.

1 parameter
Name In Type Required Description
pipeline_id path string yes UUID of the pipeline

📄 /api/pipelines/get-pipelines-pipeline-id-stages.md

GET /pipelines Scopes: pipelines:read

List all pipelines in the workspace. Each pipeline represents a workflow (e.g. "Sales", "Onboarding", "Support Tickets"). Use this when you need a pipeline UUID for creating or moving an opportunity.

📄 /api/pipelines/get-pipelines.md

POST /pipelines/:pipeline_id/stages/reorder Scopes: pipelines:write

Reorder the stages of a pipeline. Pass an array of stage UUIDs in the desired new order — position numbers are reassigned in array order.

3 parameters
Name In Type Required Description
pipeline_id path string yes UUID of the pipeline
stage_ids body array yes Stage UUIDs in their new display order
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/pipelines/post-pipelines-pipeline-id-stages-reorder.md

PATCH /pipelines/:pipeline_id Scopes: pipelines:write

Update a pipeline by UUID. Pass only the fields you want to change. Writable: name, description, is_active, is_default, position, sop_url, learning_hub_canvas_id, connected_pipeline_ids, exclude_from_lead_reporting.

11 parameters
Name In Type Required Description
pipeline_id path string yes UUID of the pipeline to update
name body string no
description body string no
is_active body boolean no
is_default body boolean no
position body number no
learning_hub_canvas_id body string no UUID of a Playbook to link as this pipeline's training resource, or null to clear. Mutually exclusive with sop_url.
exclude_from_lead_reporting body boolean no When true, opportunities in this pipeline are excluded from the Lead Sources leaderboard (totals, per-source cards, conversion rate). Use for non-lead pipelines like supplier or referral-partner networks stored as opportunities.
sop_url body string no External URL for this pipeline training resource, or null to clear. Mutually exclusive with learning_hub_canvas_id.
connected_pipeline_ids body array no UUIDs of other pipelines this one hands work to. Replaces the whole list.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/pipelines/patch-pipelines-pipeline-id.md

PATCH /pipelines/:pipeline_id/stages/:stage_id Scopes: pipelines:write

Update a single stage within a pipeline. Pass only the fields you want to change.

8 parameters
Name In Type Required Description
pipeline_id path string yes
stage_id path string yes
name body string no
position body number no
color body string no
is_won_stage body boolean no
is_lost_stage body boolean no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/pipelines/patch-pipelines-pipeline-id-stages-stage-id.md

Products (9 endpoints)

Manage products and services that can be added to deals.

📄 Full markdown: /api/products.md

POST /products Scopes: products:write

Create a new product/service. Requires name and price.

17 parameters
Name In Type Required Description
name body string yes Product or service name, as it appears in the catalogue at https://app.trustpager.com/settings/products.
sku body string no Stock keeping unit or product code. Free text, and searched by list_products `search` alongside the name.
price body number yes Sell price for one `unit`, in `currency`, excluding tax.
currency body string no ISO currency code for `price`, e.g. "AUD". Defaults to AUD.
category body string no Catalogue category. Groups the product in the picker and filters list_products.
description body string no Customer-facing description. Shown on quotes, proposals and store listings.
unit body string no The unit `price` is quoted per, e.g. "each", "hour", "m2". Defaults to "each".
benefits body array no Bullet-point benefits, shown under "Benefits" on the product page and pulled into proposals. Replaces the whole list.
deposit_percent body number no Default deposit on this product as a percentage of its line total, 0 to 100. 100 means the full amount is due up front.
default_expiry_days body number no Shelf life in days, used to date batches when batch tracking is on.
images body array no Image URLs for the product. The first is the primary image. Replaces the whole list.
track_inventory body boolean no true turns on Stock & Inventory for this product, so stock on hand and movements are tracked.
track_batches body boolean no true tracks individual batches with their own expiry dates. Needs `track_inventory` on as well.
low_stock_threshold body number no Stock on hand at or below this number flags the product as low stock.
is_active body boolean no false hides the product from pickers and stores without deleting it.
metadata body object no Free-form JSON attributes on the product: taxonomy, supplier codes, external ids, anything the catalogue columns do not cover. Returned on every read, so read the current object and merge before writing: a write REPLACES the whole object, and TrustPager stores product links under `metadata.linked_product_ids`, which a blind replace would wipe. UUID-shaped keys at the root are reserved.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/products/post-products.md

POST /products/:product_id/costs Scopes: products:write

Add a cost entry to a product (e.g. supplier price).

8 parameters
Name In Type Required Description
product_id path string yes
label body string no
cost_price body number no
quantity body number no
currency body string no
supplier_id body string no
notes body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/products/post-products-product-id-costs.md

DELETE /products/:product_id Scopes: products:delete

Delete a product by UUID. Destructive.

2 parameters
Name In Type Required Description
product_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/products/delete-products-product-id.md

DELETE /products/:product_id/costs/:cost_id Scopes: products:delete

Delete a product cost entry by UUID. Destructive.

3 parameters
Name In Type Required Description
product_id path string yes
cost_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/products/delete-products-product-id-costs-cost-id.md

GET /products/:product_id Scopes: products:read

Fetch a single product by UUID, including pricing, inventory settings and its free-form metadata.

2 parameters
Name In Type Required Description
product_id path string yes
fields query string no Comma-separated field list to return instead of the default set, e.g. "id,name,price". Pass "all" for every readable field, which adds documents, files and created_by.

📄 /api/products/get-products-product-id.md

GET /products/:product_id/costs Scopes: products:read

List all cost entries (supplier costs) for a product.

1 parameter
Name In Type Required Description
product_id path string yes

📄 /api/products/get-products-product-id-costs.md

GET /products Scopes: products:read

List products / services in the workspace catalog. Use for quotes, line items on opportunities, or pricing questions.

6 parameters
Name In Type Required Description
limit query number no
after query string no
search query string no Free-text across name and sku
category query string no
is_active query boolean no
fields query string no Comma-separated field list to return instead of the default set, e.g. "id,name,price". Pass "all" for every readable field, which adds documents, files and created_by.

📄 /api/products/get-products.md

PATCH /products/:product_id Scopes: products:write

Update a product. Pass only the fields you want to change.

18 parameters
Name In Type Required Description
product_id path string yes UUID of the product to update.
name body string no Product or service name, as it appears in the catalogue at https://app.trustpager.com/settings/products.
sku body string no Stock keeping unit or product code. Free text, and searched by list_products `search` alongside the name.
price body number no Sell price for one `unit`, in `currency`, excluding tax.
currency body string no ISO currency code for `price`, e.g. "AUD". Defaults to AUD.
category body string no Catalogue category. Groups the product in the picker and filters list_products.
description body string no Customer-facing description. Shown on quotes, proposals and store listings.
unit body string no The unit `price` is quoted per, e.g. "each", "hour", "m2". Defaults to "each".
benefits body array no Bullet-point benefits, shown under "Benefits" on the product page and pulled into proposals. Replaces the whole list.
deposit_percent body number no Default deposit on this product as a percentage of its line total, 0 to 100. 100 means the full amount is due up front.
default_expiry_days body number no Shelf life in days, used to date batches when batch tracking is on.
images body array no Image URLs for the product. The first is the primary image. Replaces the whole list.
track_inventory body boolean no true turns on Stock & Inventory for this product, so stock on hand and movements are tracked.
track_batches body boolean no true tracks individual batches with their own expiry dates. Needs `track_inventory` on as well.
low_stock_threshold body number no Stock on hand at or below this number flags the product as low stock.
is_active body boolean no false hides the product from pickers and stores without deleting it.
metadata body object no Free-form JSON attributes on the product: taxonomy, supplier codes, external ids, anything the catalogue columns do not cover. Returned on every read, so read the current object and merge before writing: a write REPLACES the whole object, and TrustPager stores product links under `metadata.linked_product_ids`, which a blind replace would wipe. UUID-shaped keys at the root are reserved.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/products/patch-products-product-id.md

PATCH /products/:product_id/costs/:cost_id Scopes: products:write

Update a product cost entry. Pass only the fields you want to change.

6 parameters
Name In Type Required Description
product_id path string yes
cost_id path string yes
label body string no
cost_price body number no
quantity body number no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/products/patch-products-product-id-costs-cost-id.md

Supplier Catalog (5 endpoints)

Supplier catalog products you can pull into your own product list and quote from.

📄 Full markdown: /api/supplier-catalog.md

POST /supplier-catalog Scopes: products:write

Add a supplier catalog entry.

14 parameters
Name In Type Required Description
supplier_id body string yes UUID of the supplier company this price belongs to.
name body string no What the supplier calls this item. Stored as product_name.
sku body string no The product code the supplier uses. Stored as supplier_sku.
min_order_quantity body number no Smallest quantity this supplier will accept on an order. Stored as minimum_order_qty.
product_id body string no UUID of the catalogue product this supplier line prices. Optional: a supplier line can stand alone with just a name.
supplier_price body number yes What this supplier charges, per unit, in supplier_currency.
supplier_currency body string no ISO currency code the supplier prices in, e.g. "AUD". Defaults to the workspace currency.
unit body string no Unit the price is per, e.g. "each", "m2", "hour".
description body string no What the supplier actually supplies, in their words.
notes body string no Internal notes about this supplier line. Not shown to the customer.
is_active body boolean no Whether this supplier line is still offered. Set false to retire it without deleting the price history.
sort_order body number no Position of this line in the supplier catalogue, ascending.
lead_time_days body number no How many days this supplier takes to deliver after an order is placed.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/supplier-catalog/post-supplier-catalog.md

DELETE /supplier-catalog/:supplier_product_id Scopes: products:delete

Delete a supplier catalog entry.

2 parameters
Name In Type Required Description
supplier_product_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/supplier-catalog/delete-supplier-catalog-supplier-product-id.md

GET /supplier-catalog Scopes: products:read

List supplier catalog entries — products available from suppliers (prices, SKUs, lead times).

2 parameters
Name In Type Required Description
limit query number no
supplier_id query string no

📄 /api/supplier-catalog/get-supplier-catalog.md

PATCH /supplier-catalog/:supplier_product_id Scopes: products:write

Update a supplier catalog entry.

15 parameters
Name In Type Required Description
supplier_product_id path string yes
supplier_id body string no UUID of the supplier company this price belongs to. Set it to move the line to a different supplier.
name body string no What the supplier calls this item. Stored as product_name.
sku body string no The product code the supplier uses. Stored as supplier_sku.
min_order_quantity body number no Smallest quantity this supplier will accept on an order. Stored as minimum_order_qty.
product_id body string no UUID of the catalogue product this supplier line prices. Optional: a supplier line can stand alone with just a name.
supplier_price body number no What this supplier charges, per unit, in supplier_currency.
supplier_currency body string no ISO currency code the supplier prices in, e.g. "AUD". Defaults to the workspace currency.
unit body string no Unit the price is per, e.g. "each", "m2", "hour".
description body string no What the supplier actually supplies, in their words.
notes body string no Internal notes about this supplier line. Not shown to the customer.
is_active body boolean no Whether this supplier line is still offered. Set false to retire it without deleting the price history.
sort_order body number no Position of this line in the supplier catalogue, ascending.
lead_time_days body number no How many days this supplier takes to deliver after an order is placed.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/supplier-catalog/patch-supplier-catalog-supplier-product-id.md

Activities (5 endpoints)

Log and manage CRM activities (calls, meetings, notes) linked to contacts, deals, and customers.

📄 Full markdown: /api/activities.md

DELETE /activities/:activity_id Scopes: activities:delete

Delete an activity by UUID. Destructive.

2 parameters
Name In Type Required Description
activity_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/activities/delete-activities-activity-id.md

GET /activities/:activity_id Scopes: activities:read

Fetch a single activity by UUID — the full record including description, occurred_at, and linked entities.

1 parameter
Name In Type Required Description
activity_id path string yes UUID of the activity

📄 /api/activities/get-activities-activity-id.md

GET /activities Scopes: activities:read

List CRM activity log entries (free-text notes and one-line records of calls, meetings, sms, emails, tasks) anchored to contacts/companies/opportunities. Use for "recent notes", "what was logged", "activity history on this deal/contact". DO NOT use for phone-specific queries — list_phone_call_logs returns the actual call records with duration, recording, direction. DO NOT use for inbox queries — list_email_threads / list_sms_conversations / list_bookings / list_transcripts have richer per-channel data.

7 parameters
Name In Type Required Description
limit query number no Max activities (1-100, default 25)
after query string no Pagination cursor
contact_id query string no Filter to activities on a specific contact
customer_id query string no Filter to activities on a specific company
deal_id query string no Filter to activities on a specific opportunity
task_id query string no Filter to activities logged from a specific task
activity_type query string no Filter by type (call, meeting, note, email)

📄 /api/activities/get-activities.md

POST /activities Scopes: activities:write

Log an activity (call, meeting, note, email) against a contact, company, opportunity, or task. Use after a phone call, meeting, or to add a note. When task_id is set, the activity appears on the task detail page AND on the linked opportunity timeline.

9 parameters
Name In Type Required Description
activity_type body string yes Type: call, meeting, note, email, sms, task
subject body string yes Short title / summary, e.g. "Discovery call". Required: an activity cannot be saved without one.
description body string no Detail / notes content
contact_id body string no UUID of the contact this is about
customer_id body string no UUID of the company this is about
deal_id body string no UUID of the opportunity this is about
deal_ids body array no Multiple opportunity UUIDs to fan out
task_id body string no UUID of the task this activity came from. Shows on the task detail page; pair with deal_id to also surface on the opportunity timeline.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/activities/post-activities.md

PATCH /activities/:activity_id Scopes: activities:write

Update an activity. Pass only the fields you want to change. Pass deal_ids array to replace linked opportunities atomically.

9 parameters
Name In Type Required Description
activity_id path string yes
activity_type body string no
subject body string no
description body string no
contact_id body string no
customer_id body string no
task_id body string no
deal_ids body array no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/activities/patch-activities-activity-id.md

Stage Changes (1 endpoint)

The audit trail of every time an opportunity moved between pipeline stages.

📄 Full markdown: /api/stage-changes.md

GET /stage-changes Scopes: opportunities:read

List pipeline stage-change (card-movement) events — the audit trail of every time an opportunity moved between stages, on any pipeline. Each event records who moved it (moved_by, null = automation/system), the from-stage and to-stage (id + name), the pipeline, and when (moved_at). This is the source for "who moved this card out of <stage>, and when", card-movement reports, and stage-velocity analysis — human and automated moves alike, told apart by moved_by. Use human_only=true for people-only moves. Covers manual kanban drags, API/MCP moves, and automation moves.

8 parameters
Name In Type Required Description
limit query number no Max events (1-100, default 25)
after query string no Pagination cursor
opportunity_id query string no Filter to one opportunity — its full move history in chronological order
pipeline_id query string no Filter to moves within a specific pipeline
moved_by query string no Filter to moves made by a specific user (UUID)
human_only query boolean no When true, return only moves made by a real user (excludes automation/system moves where moved_by is null)
moved_after query string no ISO timestamp — only events at/after this time
moved_before query string no ISO timestamp — only events at/before this time

📄 /api/stage-changes/get-stage-changes.md

Tasks (10 endpoints)

Create, assign, and manage tasks linked to deals and contacts.

📄 Full markdown: /api/tasks.md

POST /tasks Scopes: tasks:write

Create a new task. Requires title. The task modal has two text fields: "Summary" (short one-liner — maps to the `description` field) and "Detailed Description" (long-form notes — maps to the `body` field). Use the right one for the length of content. Link to records via deal_id / contact_id / customer_id.

15 parameters
Name In Type Required Description
title body string yes Title (required). Matches the "Title" field on the task modal.
description body string no Summary — short one-line summary (target under ~200 chars). Matches the "Summary" field on the task modal. For multi-paragraph content use `body` instead.
body body string no Detailed Description — long-form notes, acceptance criteria, multi-paragraph content. Matches the "Detailed Description" field on the task modal.
due_date body string no ISO date (YYYY-MM-DD) when the task is due
due_time body string no Time of day (HH:MM) when the task is due
assigned_to body string no UUID of the user to assign
category body string no Category name
status body string no todo, in_progress, completed, or cancelled. Defaults to todo. An unrecognised value is rejected with a 400, not coerced.
priority body string no low, medium, high, or urgent. Defaults to medium.
deal_id body string no UUID of the opportunity this task belongs to. Pass null to detach it.
contact_id body string no UUID of the contact this task is about. Pass null to detach it.
customer_id body string no UUID of the company this task is about. Pass null to detach it.
completed_at body string no When the task was completed, as an ISO timestamp. Setting `status` to completed is the normal way to finish a task; set this directly only when importing history and the real completion date matters.
sort_order body number no Position of this task within its list, ascending.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/tasks/post-tasks.md

POST /tasks/categories Scopes: tasks:write

Create a new task category in the workspace.

2 parameters
Name In Type Required Description
name body string yes Category name
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/tasks/post-tasks-categories.md

DELETE /tasks/:task_id Scopes: tasks:delete

Delete a task by UUID. Destructive.

2 parameters
Name In Type Required Description
task_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/tasks/delete-tasks-task-id.md

DELETE /tasks/categories/:category_id Scopes: tasks:delete

Delete a task category by UUID. Destructive.

2 parameters
Name In Type Required Description
category_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/tasks/delete-tasks-categories-category-id.md

GET /tasks/:task_id Scopes: tasks:read

Fetch a single task by UUID. Returns the full task record.

1 parameter
Name In Type Required Description
task_id path string yes UUID of the task

📄 /api/tasks/get-tasks-task-id.md

GET /tasks/categories Scopes: tasks:read

List task categories defined in the workspace. Use this to find a category UUID for assigning to a task.

📄 /api/tasks/get-tasks-categories.md

GET /tasks Scopes: tasks:read

List tasks in the workspace. Supports filtering by status, assigned_to, priority, category, deal_id, contact_id, customer_id.

9 parameters
Name In Type Required Description
limit query number no Max number of tasks to return (1-100, default 25)
after query string no Pagination cursor
status query string no Filter by status: todo, in_progress, completed, or cancelled
assigned_to query string no UUID of the assignee
priority query string no Filter by priority
category query string no Filter by category
deal_id query string no Filter to a specific opportunity
contact_id query string no Filter to a specific contact
customer_id query string no Filter to a specific company

📄 /api/tasks/get-tasks.md

POST /tasks/reorder Scopes: tasks:write

Reorder a set of tasks. Pass task_ids array in desired display order; sort_order is reassigned from the array index.

2 parameters
Name In Type Required Description
task_ids body array yes Task UUIDs in new display order
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/tasks/post-tasks-reorder.md

PATCH /tasks/:task_id Scopes: tasks:write

Update a task. Set status to "completed" to mark complete. Pass only fields you want to change. Note: the task modal has two text fields — "Summary" (maps to `description`) and "Detailed Description" (maps to `body`). Pick the right one for the length of content.

16 parameters
Name In Type Required Description
task_id path string yes
title body string no Title. Matches the "Title" field on the task modal.
description body string no Summary — short one-line summary (target under ~200 chars). Matches the "Summary" field on the task modal. For multi-paragraph content use `body` instead.
body body string no Detailed Description — long-form notes, acceptance criteria, multi-paragraph content. Matches the "Detailed Description" field on the task modal.
due_date body string no
due_time body string no
assigned_to body string no
status body string no todo, in_progress, completed, or cancelled. Set to completed when the work was done; set to cancelled when it stopped being relevant (comes off overdue counts without counting as done). An unrecognised value is rejected with a 400, not coerced.
priority body string no low, medium, high, or urgent
category body string no
deal_id body string no UUID of the opportunity this task belongs to. Pass null to detach it.
contact_id body string no UUID of the contact this task is about. Pass null to detach it.
customer_id body string no UUID of the company this task is about. Pass null to detach it.
completed_at body string no When the task was completed, as an ISO timestamp. Setting `status` to completed is the normal way to finish a task; set this directly only when importing history and the real completion date matters.
sort_order body number no Position of this task within its list, ascending.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/tasks/patch-tasks-task-id.md

PATCH /tasks/categories/:category_id Scopes: tasks:write

Rename a task category.

3 parameters
Name In Type Required Description
category_id path string yes
name body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/tasks/patch-tasks-categories-category-id.md

Work Orders (6 endpoints)

Manage work orders linked to deals for tracking project execution. Work orders appear on the CRM Calendar, support team assignment, and scheduled dates.

📄 Full markdown: /api/work-orders.md

POST /work-orders Scopes: work-orders:write

Create a work order. Pass deal_id for a deal-level work order, or deal_product_id to attach it to a specific product line. At least one is required. Pass assignee_ids (array of user UUIDs) to assign a crew — the first becomes the primary assignee.

10 parameters
Name In Type Required Description
deal_id body string no
deal_product_id body string no
status_id body string no
status body string no
data body object no
assignee_ids body array no User UUIDs to assign as the crew; the first is kept as the primary assignee.
schedule_date body string no Day the job is booked for (YYYY-MM-DD).
schedule_time body string no Start time on schedule_date (HH:MM). Omit for a job with no fixed start.
sort_order body number no Position of this work order within the job list, ascending.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/work-orders/post-work-orders.md

DELETE /work-orders/:work_order_id Scopes: work-orders:delete

Delete a work order by UUID.

2 parameters
Name In Type Required Description
work_order_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/work-orders/delete-work-orders-work-order-id.md

GET /work-orders/:work_order_id Scopes: work-orders:read

Fetch a single work order by UUID, including its assignee crew (assignees[]).

1 parameter
Name In Type Required Description
work_order_id path string yes

📄 /api/work-orders/get-work-orders-work-order-id.md

GET /work-orders Scopes: work-orders:read

List work orders. Filter by deal_id (returns deal-level work orders plus any attached to that deal's product lines), deal_product_id, status_id, or assigned_to. Each work order includes its assignee crew (assignees[]).

4 parameters
Name In Type Required Description
limit query number no
deal_id query string no
deal_product_id query string no
status_id query string no

📄 /api/work-orders/get-work-orders.md

POST /work-orders/send-work-status Scopes: work-orders:write

Send a work-status update email to a contact for an opportunity. Costs credits.

6 parameters
Name In Type Required Description
deal_id body string yes
recipient_email body string yes
recipient_name body string yes
personal_message body string no Optional note included in the work-status email.
expires_in_days body number no How long the portal link stays valid. Defaults to 30.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/work-orders/post-work-orders-send-work-status.md

PATCH /work-orders/:work_order_id Scopes: work-orders:write

Update a work order (status, schedule, data fields, and/or its assignee crew). `data` MERGES: send only the field slugs you are changing and the rest are left alone. Pass assignee_ids (array of user UUIDs) to replace the crew; the first becomes the primary assignee.

11 parameters
Name In Type Required Description
work_order_id path string yes
status_id body string no
status body string no
data body object no Work-order field slugs to values. MERGED into the existing data, so send only what you are changing — omitted fields keep their current values and job photos are preserved. Set a key to null to clear just that field.
schedule_date body string no Day the job is booked for (YYYY-MM-DD).
schedule_time body string no Start time on schedule_date (HH:MM). Omit for a job with no fixed start.
assignee_ids body array no User UUIDs for the crew; replaces the current assignees. First is kept as the primary.
deal_id body string no UUID of the opportunity this work order belongs to. Set it to move a deal-level work order to a different opportunity.
deal_product_id body string no UUID of the opportunity product line this work order is attached to. Set it to move the job to a different line.
sort_order body number no Position of this work order within the job list, ascending.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/work-orders/patch-work-orders-work-order-id.md

Work Order Settings (15 endpoints)

Work order settings: statuses, field definitions and their ordering.

📄 Full markdown: /api/work-order-config.md

POST /work-order-fields Scopes: work-orders:write

Add a work-order custom field. The generated slug is what automation configs address the field by (see the create_work_orders action's `data` object), so read it back off the response.

10 parameters
Name In Type Required Description
label body string yes Display label, e.g. "Offer Amount (AUD)".
slug body string no Machine key that automations address this field by (the create_work_orders action's `data` object). Auto-generated from the label when omitted, and immutable afterwards.
product_id body string no Scope the field to one catalogue product instead of the whole workspace.
type body string no Field type. Drives both the input rendered on the work order and the coercion applied to values an automation prefills into it. Defaults to text. "select" stores one of the configured options as a string; "multiselect" stores several of them as an array of strings, for a job that covers more than one of your priced services in a single visit.
options body array no Choices for a select or multiselect field. Required for those two types. Without them the field renders an empty dropdown.
required body boolean no Marks the field as expected: it renders with an asterisk and counts toward the work order's "scoped" progress indicator. It does NOT block anything on its own — a work order can still change status with it empty. To actually gate a status change, use requiredFieldsByStatus on a portal's work_orders sharing rule (set_portal_data_sharing).
is_title body boolean no Make this the field rendered as the work order title. Only one field per workspace should carry it.
internal_only body boolean no Internal only: keep this field and its value out of the work-status portal entirely. The portal link is scoped to the opportunity, not to one recipient, so everyone you send it to sees the same work orders. Mark anything a customer or a subcontractor must never read (what you pay another trade, internal margins, private notes) as internal_only. The value is stripped server-side, so it is not merely hidden in the page. Defaults to false.
position body number no Display order among the workspace fields.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/work-order-config/post-work-order-fields.md

POST /work-order-statuses Scopes: work-orders:write

Create a work-order status.

3 parameters
Name In Type Required Description
label body string yes
color body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/work-order-config/post-work-order-statuses.md

POST /work-order-templates Scopes: work-orders:write

Add a templated work order to a product. The data object keys should match your work order field slugs (e.g. deliverable, scope, dependencies).

3 parameters
Name In Type Required Description
product_id body string yes
data body object no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/work-order-config/post-work-order-templates.md

DELETE /work-order-fields/:field_id Scopes: work-orders:delete

Delete a work-order field.

2 parameters
Name In Type Required Description
field_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/work-order-config/delete-work-order-fields-field-id.md

DELETE /work-order-statuses/:status_id Scopes: work-orders:delete

Delete a work-order status.

2 parameters
Name In Type Required Description
status_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/work-order-config/delete-work-order-statuses-status-id.md

DELETE /work-order-templates/:template_id Scopes: work-orders:delete

Delete a templated work order.

2 parameters
Name In Type Required Description
template_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/work-order-config/delete-work-order-templates-template-id.md

GET /work-order-templates Scopes: work-orders:read

List the templated work orders for a product. These are stamped onto a real work order automatically the moment that product is added to an opportunity. Requires product_id.

1 parameter
Name In Type Required Description
product_id query string yes

📄 /api/work-order-config/get-work-order-templates.md

POST /work-order-fields/reorder Scopes: work-orders:write

Reorder work-order fields.

2 parameters
Name In Type Required Description
field_ids body array yes Work-order field UUIDs in the desired display order.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/work-order-config/post-work-order-fields-reorder.md

POST /work-order-statuses/reorder Scopes: work-orders:write

Reorder work-order statuses.

2 parameters
Name In Type Required Description
status_ids body array yes Work-order status UUIDs in the desired display order.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/work-order-config/post-work-order-statuses-reorder.md

POST /work-order-templates/reorder Scopes: work-orders:write

Reorder the templated work orders for a product.

2 parameters
Name In Type Required Description
template_ids body array yes Work-order template UUIDs in the desired display order.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/work-order-config/post-work-order-templates-reorder.md

PATCH /work-order-fields/:field_id Scopes: work-orders:write

Update a work-order field. `slug` is immutable after creation. Everything else can be changed.

9 parameters
Name In Type Required Description
field_id path string yes
label body string no Display label.
type body string no Field type. Drives both the input rendered on the work order and the coercion applied to values an automation prefills into it. Defaults to text. "select" stores one of the configured options as a string; "multiselect" stores several of them as an array of strings, for a job that covers more than one of your priced services in a single visit.
options body array no Choices for a select or multiselect field. Required for those two types. Without them the field renders an empty dropdown.
required body boolean no Marks the field as expected: it renders with an asterisk and counts toward the work order's "scoped" progress indicator. It does NOT block anything on its own — a work order can still change status with it empty. To actually gate a status change, use requiredFieldsByStatus on a portal's work_orders sharing rule (set_portal_data_sharing).
is_title body boolean no Make this the field rendered as the work order title. Only one field per workspace should carry it.
internal_only body boolean no Internal only: keep this field and its value out of the work-status portal entirely. The portal link is scoped to the opportunity, not to one recipient, so everyone you send it to sees the same work orders. Mark anything a customer or a subcontractor must never read (what you pay another trade, internal margins, private notes) as internal_only. The value is stripped server-side, so it is not merely hidden in the page. Defaults to false.
position body number no Display order among the workspace fields.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/work-order-config/patch-work-order-fields-field-id.md

PATCH /work-order-statuses/:status_id Scopes: work-orders:write

Update a work-order status.

2 parameters
Name In Type Required Description
status_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/work-order-config/patch-work-order-statuses-status-id.md

PATCH /work-order-templates/:template_id Scopes: work-orders:write

Update a templated work order.

2 parameters
Name In Type Required Description
template_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/work-order-config/patch-work-order-templates-template-id.md

STAKEHOLDERS (17 endpoints)

stakeholders endpoints.

📄 Full markdown: /api/stakeholders.md

POST /stakeholder-types/:type_id/members Scopes: contacts:write

Add a contact to a stakeholder type. Requires contact_id.

3 parameters
Name In Type Required Description
type_id path string yes
contact_id body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/stakeholders/post-stakeholder-types-type-id-members.md

POST /tasks/:task_id/stakeholders Scopes: opportunities:write

Assign a stakeholder to a task. The contact must belong to a stakeholder type with "assignable to work" enabled. The task appears in that stakeholder's portal My Tasks tab.

3 parameters
Name In Type Required Description
task_id path string yes
contact_id body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/stakeholders/post-tasks-task-id-stakeholders.md

POST /work-orders/:work_order_id/stakeholders Scopes: opportunities:write

Assign a stakeholder to a work order. The contact must belong to a stakeholder type with "assignable to work" enabled. Also shares the work order into that stakeholder's portal so it shows in their Work Orders tab.

3 parameters
Name In Type Required Description
work_order_id path string yes
contact_id body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/stakeholders/post-work-orders-work-order-id-stakeholders.md

POST /deal-stakeholders Scopes: opportunities:write

Attach a stakeholder contact to an opportunity. Requires deal_id and contact_id; optional bucket_id (the stakeholder type) and role.

5 parameters
Name In Type Required Description
deal_id body string yes
contact_id body string yes
bucket_id body string no Stakeholder type UUID this attachment belongs to.
role body string no Free-text role on this opportunity, e.g. Lead tiler.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/stakeholders/post-deal-stakeholders.md

POST /stakeholder-types Scopes: contacts:write

Create a stakeholder type. Requires name; optional capability flags default to off.

7 parameters
Name In Type Required Description
name body string yes
color body string no Hex colour for the type chip, e.g. #6366f1.
portal_eligible body boolean no People in this type can be given a scoped portal login.
commission_eligible body boolean no People in this type earn trackable commissions/payouts.
assignable_to_work_orders body boolean no People in this type can be assigned jobs/work orders.
sort_order body number no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/stakeholders/post-stakeholder-types.md

DELETE /stakeholder-types/:type_id Scopes: contacts:delete

Delete a stakeholder type by UUID. Destructive; its membership links go too.

2 parameters
Name In Type Required Description
type_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/stakeholders/delete-stakeholder-types-type-id.md

DELETE /deal-stakeholders/:id Scopes: opportunities:delete

Detach a stakeholder from an opportunity by the attachment row UUID.

2 parameters
Name In Type Required Description
id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/stakeholders/delete-deal-stakeholders-id.md

GET /deal-stakeholders Scopes: opportunities:read

List the external stakeholders attached to an opportunity. Requires the deal_id query parameter.

1 parameter
Name In Type Required Description
deal_id query string yes

📄 /api/stakeholders/get-deal-stakeholders.md

GET /stakeholder-types Scopes: contacts:read

List stakeholder types (external-people groups such as Contractors, Referrers, Partners) with their capability flags (portal_eligible, commission_eligible, assignable_to_work_orders).

1 parameter
Name In Type Required Description
limit query number no

📄 /api/stakeholders/get-stakeholder-types.md

DELETE /stakeholder-types/:type_id/members/:member_id Scopes: contacts:delete

Remove a contact from a stakeholder type by the membership row UUID.

3 parameters
Name In Type Required Description
type_id path string yes
member_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/stakeholders/delete-stakeholder-types-type-id-members-member-id.md

DELETE /tasks/:task_id/stakeholders/:contact_id Scopes: opportunities:delete

Unassign a stakeholder from a task by contact UUID.

3 parameters
Name In Type Required Description
task_id path string yes
contact_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/stakeholders/delete-tasks-task-id-stakeholders-contact-id.md

DELETE /work-orders/:work_order_id/stakeholders/:contact_id Scopes: opportunities:delete

Unassign a stakeholder from a work order by contact UUID.

3 parameters
Name In Type Required Description
work_order_id path string yes
contact_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/stakeholders/delete-work-orders-work-order-id-stakeholders-contact-id.md

PATCH /stakeholder-types/:type_id Scopes: contacts:write

Update a stakeholder type (name, colour, or the capability flags). Pass only the fields you want to change.

8 parameters
Name In Type Required Description
type_id path string yes
name body string no
color body string no Hex colour for the type chip, e.g. #6366f1.
portal_eligible body boolean no People in this type can be given a scoped portal login.
commission_eligible body boolean no People in this type earn trackable commissions/payouts.
assignable_to_work_orders body boolean no People in this type can be assigned jobs/work orders.
sort_order body number no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/stakeholders/patch-stakeholder-types-type-id.md

Automations (26 endpoints)

Create and manage workflow automations. Includes sub-resources for triggers, actions, and execution runs. Supports enable/disable and manual triggering.

📄 Full markdown: /api/automations.md

POST /automations/:automation_id/actions Scopes: automations:write

Add an action to an automation. Omit run_priority and the action is appended to run LAST, which is almost always what you want; pass it only to place the action deliberately, and note it counts DOWN (higher runs first). `config` carries the action's settings and is REQUIRED (the column is NOT NULL) — pass {} for an action that needs none, or call describe_action_type first to get the exact config fields for this action_type. Use list_action_types to see what is available. The response includes chain_run_order so you can confirm where the action landed.

7 parameters
Name In Type Required Description
automation_id path string yes
action_type body string yes Which action to run, e.g. send_email, send_sms, create_task, update_deal. Call list_action_types for the full vocabulary, then describe_action_type for one action's config shape.
run_priority body number no Higher run_priority runs first. The action with the highest value executes at the start of the chain, counting down. Omit this and the actions run in the order you supply them.
config body object yes Settings for this action type (recipients, template ids, field values). The required shape depends on action_type. Call describe_action_type for its config schema before writing. Three keys work on EVERY action type regardless of action_type: "run_if" gates just this step (same condition shape as the automation's own conditions, e.g. { "contact.phone": { "starts_with": "+61" } }; when it does not match the step is SKIPPED, the rest of the chain still runs, and no error is raised), "stop_on_failure" halts the remaining chain if this step errors, and "unskippable" (stage automations only) marks this step mandatory so it is locked on in the stage-move window and still runs when the mover declines the automation or clicks "Move without running" — use it on the state half of a mixed automation and leave the customer-facing send declinable. A trigger payload field is addressable both flattened and namespaced — `form_id` and `trigger.form_id` are the same field — because the payload is copied to the top level and mirrored under `trigger`. Enriched CRM records use their own namespaces: `contact.*`, `deal.*`, `company.*`. Supported run_if operators: exists, not_exists, eq, neq, gt, lt, gte, lte, contains, not_contains, contains_any, not_contains_any, contains_text, not_contains_text, starts_with, not_starts_with, in, not_in. An unrecognized operator is rejected with a 400 at write time. To gate a step, the key is "run_if" — "conditions" on an ACTION is rejected, because it means the automation-level field and nothing reads it here.
integration_id body string no UUID of the connected integration this action runs through, when the action type needs one.
integration_type body string no Which integration platform the action targets, when the action type needs one.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/automations/post-automations-automation-id-actions.md

POST /automations/:automation_id/triggers Scopes: automations:write

Add a trigger to an automation. An automation may have multiple triggers - it fires when ANY of them matches (OR logic). Pass trigger_type to give this trigger its own event class (e.g. add a webhook_received trigger to a form_completed automation so both entry points run the same actions); omit it to inherit the automation's primary trigger_type.

6 parameters
Name In Type Required Description
automation_id path string yes
trigger_type body string no Optional. This trigger's own event class. Omit to inherit the automation's primary trigger_type. Set it to fire one automation from multiple different event types (OR-matched).
source_type body string no
source_id body string no
config body object no Trigger-specific settings. For opportunity events (deal_created / deal_updated), scope the trigger with { scope_pipeline_id, scope_stage_id } so it only fires for that pipeline/stage.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/automations/post-automations-automation-id-triggers.md

POST /automations Scopes: automations:write

Create a new automation.

17 parameters
Name In Type Required Description
name body string yes Automation name shown in the Automations list.
description body string no What this automation does. Internal note for your team; never shown to a customer.
trigger_type body string yes What fires the automation, e.g. stage_changed, deal_created, form_completed, webhook_received, facebook_lead_ad, scheduled. Call list_trigger_schemas for the full list, or get_trigger_schema for one trigger's trigger_data shape.
stage_id body string no Pipeline stage UUID this automation fires on. Only meaningful when trigger_type is stage_changed, where it is the ONLY way to bind the automation: add_automation_trigger rejects stage_changed on purpose. Pass null to unbind.
enabled body boolean no Whether the automation runs. False leaves it configured but dormant.
priority body number no Run order when several automations match the same event. Lower runs first.
max_executions_per_day body number no Safety cap on how many times this automation may run per day across the workspace. Null means uncapped.
show_in_quick_links body boolean no Show this automation as a manually-fireable Quick Link button on the opportunity page.
conditions body object no Optional AND-conditions gating whether the automation fires. Shape: { "field": { "operator": value } }, or plain equality shorthand { "field": value }. Supported operators: exists, not_exists, eq, neq, gt, lt, gte, lte, contains, not_contains, contains_any, not_contains_any, contains_text, not_contains_text, starts_with, not_starts_with, in, not_in. An unrecognized operator is rejected with a 400 at write time (it used to fail silently at runtime). For "field has a value", use { "field": { "exists": true } }. A trigger payload field is addressable both flattened and namespaced — `form_id` and `trigger.form_id` are the same field — because the payload is copied to the top level and mirrored under `trigger`. Enriched CRM records use their own namespaces: `contact.*`, `deal.*`, `company.*`. To gate a SINGLE action rather than the whole automation, put the same shape in that action's config.run_if instead.
crm_integration body object no Maps incoming trigger payload fields onto CRM records for webhook-style triggers (which pipeline/stage to create in, which fields map where). Full-replace on write. CARD PLACEMENT: card_behavior + update_card_strategy decide what happens to the workflow card of an opportunity that already exists. The default pair (find_or_create + target_pipeline) uses the card already on pipeline_id and adds one when there is none, so cards on other boards stay put; most_recent and oldest MOVE a card off another board instead. Use card_behavior "ignore" to never touch placement at all. An unrecognized value on any of these is rejected with a 400 (SR 5628b3a7). Custom fields and quick links need BOTH halves: the value bag (custom_fields_deal / quick_links_deal, and the contact/account equivalents) AND a matching target in field_mappings (custom.deal.<field_id> / quicklink.deal.<type_id>). With the bag but no target, the whole group is written on a NEWLY CREATED record and silently dropped on an EXISTING one, with no error on the run, and the write returns _warnings naming the affected field ids (SR 77381a31).
folder body string no Folder name this automation is filed under in the Automations list. Pass null to move it back to the root.
dedup_enabled body boolean no Suppress repeat runs for the same subject inside dedup_window_minutes. Use when a noisy source can fire the same event twice.
dedup_window_minutes body number no How long the deduplication window lasts, in minutes. Only applies when dedup_enabled is true.
unskippable body boolean no Stage automations only. Every automation on a stage already starts ticked in the stage-move window, so this is the lock on top of that: when true it cannot be unticked, and it still runs if the mover declines everything else. Defaults to false, which lets whoever moves the opportunity untick it for that one move.
triggers body array no Optional trigger rows to create alongside the automation.
actions body array no Optional action chain to create alongside the automation. Supply them in the order you want them to RUN — the first element executes first — and leave run_priority off each one; the API numbers them for you. The response returns the chain in run order so you can confirm it. Each action needs action_type and a config object: pass {} for an action that needs no settings.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/automations/post-automations.md

POST /automations/folders Scopes: automations:write

Create an automation folder. kind defaults to general; pass workflow, queue, or schedule to file it under another primitive.

3 parameters
Name In Type Required Description
name body string yes
kind body string no general (default), workflow, queue, or schedule.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/automations/post-automations-folders.md

DELETE /automations/:automation_id Scopes: automations:delete

Delete an automation.

2 parameters
Name In Type Required Description
automation_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/automations/delete-automations-automation-id.md

DELETE /automations/:automation_id/actions/:action_id Scopes: automations:delete

Delete an automation action.

3 parameters
Name In Type Required Description
automation_id path string yes
action_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/automations/delete-automations-automation-id-actions-action-id.md

DELETE /automations/folders/:folder_id Scopes: automations:delete

Delete an automation folder. Items keep their folder name string; clear it via update_automation if needed.

2 parameters
Name In Type Required Description
folder_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/automations/delete-automations-folders-folder-id.md

DELETE /automations/:automation_id/triggers/:trigger_id Scopes: automations:delete

Delete a trigger.

3 parameters
Name In Type Required Description
automation_id path string yes
trigger_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/automations/delete-automations-automation-id-triggers-trigger-id.md

GET /automations/action-types/:action_type

Return the full config schema, example, warnings, and related actions for one automation action_type. Call this RIGHT BEFORE writing that action via add_automation_action — do not memorise multiple types upfront. Free read.

1 parameter
Name In Type Required Description
action_type path string yes Canonical action_type key, e.g. "add_tasks", "send_custom_email", "move_deal". Aliases (e.g. "set_crm_field" → "set_custom_field") are accepted.

📄 /api/automations/get-automations-action-types-action-type.md

POST /automations/:automation_id/disable Scopes: automations:write

Disable an automation.

2 parameters
Name In Type Required Description
automation_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/automations/post-automations-automation-id-disable.md

POST /automations/:automation_id/enable Scopes: automations:write

Enable an automation.

2 parameters
Name In Type Required Description
automation_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/automations/post-automations-automation-id-enable.md

POST /automations/:automation_id/actions/:action_id/execute Scopes: automations-trigger:trigger

Execute a single automation action (testing).

5 parameters
Name In Type Required Description
automation_id path string yes
action_id path string yes
trigger_data body object no Data passed to the trigger, available to the action as variables (e.g. contact_id, deal_id, email).
payload body object no Alias for trigger_data. Either name works; trigger_data is preferred.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/automations/post-automations-automation-id-actions-action-id-execute.md

GET /automations/:automation_id Scopes: automations:read

Fetch an automation by UUID. Triggers and actions are always included. Pass expand=dispatchability to also get whether a switched-on automation can actually fire.

2 parameters
Name In Type Required Description
automation_id path string yes
expand query string no Pass "dispatchability" to add { can_fire, reason, detail } when the automation is enabled.

📄 /api/automations/get-automations-automation-id.md

GET /automations/:automation_id/actions Scopes: automations:read

List actions on an automation, in RUN ORDER: the first action returned is the first one that executes. Each row carries run_priority, which counts DOWN (highest runs first).

1 parameter
Name In Type Required Description
automation_id path string yes

📄 /api/automations/get-automations-automation-id-actions.md

GET /automations/folders Scopes: automations:read

List automation folders. Pass kind (general | workflow | queue | schedule) to scope to one primitive.

1 parameter
Name In Type Required Description
kind query string no Optional. Filter to one primitive: general, workflow, queue, or schedule.

📄 /api/automations/get-automations-folders.md

GET /automations Scopes: automations:read

List automations in the workspace. Pass expand=dispatchability to also get, for every switched-on automation, whether it can actually fire — use this to audit a workspace for automations that look healthy but are silently inert. expand=triggers and expand=actions embed the child rows so you can read a whole workspace a page at a time instead of one call per automation.

5 parameters
Name In Type Required Description
limit query number no
trigger_type query string no Filter to one trigger_type.
enabled query string no "true" or "false" to filter by switched-on state.
search query string no Case-insensitive match on name.
expand query string no Comma-separated: triggers, actions, dispatchability. dispatchability returns { can_fire, reason, detail } on every enabled row — reason is one of no_trigger_row, stage_missing, stage_deleted, no_actions, and detail is a plain sentence you can show a customer.

📄 /api/automations/get-automations.md

POST /automations/:automation_id/actions/reorder Scopes: automations:write

Reorder an automation's actions. Pass action_ids in the order you want them to RUN — first id runs first. The response returns the resulting chain in run order so you can confirm it.

3 parameters
Name In Type Required Description
automation_id path string yes
action_ids body array yes Every action on the automation, in the order they should run: the first id executes first. Must list the whole chain, not a subset — get the ids from list_automation_actions.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/automations/post-automations-automation-id-actions-reorder.md

POST /automations/:automation_id/trigger Scopes: automations-trigger:trigger

Manually trigger an automation.

4 parameters
Name In Type Required Description
automation_id path string yes
trigger_data body object no Data passed to the trigger, available to the automation's actions as variables (e.g. contact_id, deal_id, email). Reserved keys: skip_action_ids (string[]) suppresses those actions, only_action_ids (string[]) runs ONLY those.
payload body object no Alias for trigger_data. Either name works; trigger_data is preferred.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/automations/post-automations-automation-id-trigger.md

PATCH /automations/:automation_id Scopes: automations:write

Update an automation. For a stage automation, this is where stage_id is set: stage automations are wired via automations.stage_id, NOT by adding a trigger row.

16 parameters
Name In Type Required Description
automation_id path string yes UUID of the automation to update. Get ids from list_automations.
name body string no Automation name shown in the Automations list.
description body string no What this automation does. Internal note for your team; never shown to a customer.
trigger_type body string no What fires the automation, e.g. stage_changed, deal_created, form_completed, webhook_received, facebook_lead_ad, scheduled. Call list_trigger_schemas for the full list, or get_trigger_schema for one trigger's trigger_data shape.
stage_id body string no Pipeline stage UUID this automation fires on. Only meaningful when trigger_type is stage_changed, where it is the ONLY way to bind the automation: add_automation_trigger rejects stage_changed on purpose. Pass null to unbind.
enabled body boolean no Whether the automation runs. False leaves it configured but dormant.
priority body number no Run order when several automations match the same event. Lower runs first.
max_executions_per_day body number no Safety cap on how many times this automation may run per day across the workspace. Null means uncapped.
show_in_quick_links body boolean no Show this automation as a manually-fireable Quick Link button on the opportunity page.
conditions body object no Optional AND-conditions gating whether the automation fires. Shape: { "field": { "operator": value } }, or plain equality shorthand { "field": value }. Supported operators: exists, not_exists, eq, neq, gt, lt, gte, lte, contains, not_contains, contains_any, not_contains_any, contains_text, not_contains_text, starts_with, not_starts_with, in, not_in. An unrecognized operator is rejected with a 400 at write time (it used to fail silently at runtime). For "field has a value", use { "field": { "exists": true } }. A trigger payload field is addressable both flattened and namespaced — `form_id` and `trigger.form_id` are the same field — because the payload is copied to the top level and mirrored under `trigger`. Enriched CRM records use their own namespaces: `contact.*`, `deal.*`, `company.*`. To gate a SINGLE action rather than the whole automation, put the same shape in that action's config.run_if instead.
crm_integration body object no Maps incoming trigger payload fields onto CRM records for webhook-style triggers (which pipeline/stage to create in, which fields map where). Full-replace on write. CARD PLACEMENT: card_behavior + update_card_strategy decide what happens to the workflow card of an opportunity that already exists. The default pair (find_or_create + target_pipeline) uses the card already on pipeline_id and adds one when there is none, so cards on other boards stay put; most_recent and oldest MOVE a card off another board instead. Use card_behavior "ignore" to never touch placement at all. An unrecognized value on any of these is rejected with a 400 (SR 5628b3a7). Custom fields and quick links need BOTH halves: the value bag (custom_fields_deal / quick_links_deal, and the contact/account equivalents) AND a matching target in field_mappings (custom.deal.<field_id> / quicklink.deal.<type_id>). With the bag but no target, the whole group is written on a NEWLY CREATED record and silently dropped on an EXISTING one, with no error on the run, and the write returns _warnings naming the affected field ids (SR 77381a31).
folder body string no Folder name this automation is filed under in the Automations list. Pass null to move it back to the root.
dedup_enabled body boolean no Suppress repeat runs for the same subject inside dedup_window_minutes. Use when a noisy source can fire the same event twice.
dedup_window_minutes body number no How long the deduplication window lasts, in minutes. Only applies when dedup_enabled is true.
unskippable body boolean no Stage automations only. Every automation on a stage already starts ticked in the stage-move window, so this is the lock on top of that: when true it cannot be unticked, and it still runs if the mover declines everything else. Defaults to false, which lets whoever moves the opportunity untick it for that one move.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/automations/patch-automations-automation-id.md

PATCH /automations/:automation_id/actions/:action_id Scopes: automations:write

Update an automation action: its config (settings), its action_type, or its run_priority (position in the chain). To move an action, prefer reorder_automation_actions, which takes the order you want rather than a number. `config` REPLACES the stored object rather than merging into it, so read the action first via list_automation_actions and pass the full object back with your change applied. The response includes chain_run_order.

8 parameters
Name In Type Required Description
automation_id path string yes
action_id path string yes
action_type body string no Which action to run, e.g. send_email, send_sms, create_task, update_deal. Call list_action_types for the full vocabulary, then describe_action_type for one action's config shape.
run_priority body number no Higher run_priority runs first. The action with the highest value executes at the start of the chain, counting down. Omit this and the actions run in the order you supply them.
config body object no Settings for this action type (recipients, template ids, field values). The required shape depends on action_type. Call describe_action_type for its config schema before writing. Three keys work on EVERY action type regardless of action_type: "run_if" gates just this step (same condition shape as the automation's own conditions, e.g. { "contact.phone": { "starts_with": "+61" } }; when it does not match the step is SKIPPED, the rest of the chain still runs, and no error is raised), "stop_on_failure" halts the remaining chain if this step errors, and "unskippable" (stage automations only) marks this step mandatory so it is locked on in the stage-move window and still runs when the mover declines the automation or clicks "Move without running" — use it on the state half of a mixed automation and leave the customer-facing send declinable. A trigger payload field is addressable both flattened and namespaced — `form_id` and `trigger.form_id` are the same field — because the payload is copied to the top level and mirrored under `trigger`. Enriched CRM records use their own namespaces: `contact.*`, `deal.*`, `company.*`. Supported run_if operators: exists, not_exists, eq, neq, gt, lt, gte, lte, contains, not_contains, contains_any, not_contains_any, contains_text, not_contains_text, starts_with, not_starts_with, in, not_in. An unrecognized operator is rejected with a 400 at write time. To gate a step, the key is "run_if" — "conditions" on an ACTION is rejected, because it means the automation-level field and nothing reads it here.
integration_id body string no UUID of the connected integration this action runs through, when the action type needs one.
integration_type body string no Which integration platform the action targets, when the action type needs one.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/automations/patch-automations-automation-id-actions-action-id.md

PATCH /automations/folders/:folder_id Scopes: automations:write

Rename an automation folder. To move automations into it, set their folder field via update_automation.

4 parameters
Name In Type Required Description
folder_id path string yes UUID of the automation folder to update. Get ids from list_automation_folders.
name body string no Folder name shown in the Automations list.
kind body string no What the folder groups: general (plain folder), workflow, queue, or schedule. Drives how the folder is presented in the Automations UI.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/automations/patch-automations-folders-folder-id.md

PATCH /automations/:automation_id/triggers/:trigger_id Scopes: automations:write

Update a trigger - its event class, source, or config (e.g. the pipeline/stage scope on an opportunity trigger).

7 parameters
Name In Type Required Description
automation_id path string yes
trigger_id path string yes
trigger_type body string no Optional. This trigger's own event class (OR-matched with the automation's other triggers).
source_type body string no
source_id body string no
config body object no Trigger-specific settings. For opportunity events (deal_created / deal_updated), scope the trigger with { scope_pipeline_id, scope_stage_id } so it only fires for that pipeline/stage; omit or clear them to fire for every opportunity.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/automations/patch-automations-automation-id-triggers-trigger-id.md

Custom Scripts (14 endpoints)

Generate, edit, secure, test, and approve a Custom Script automation action -- an AI-written (or hand-written) JavaScript module that runs on its own automation as the final action.

📄 Full markdown: /api/custom-scripts.md

POST /automations/:automation_id/actions/:action_id/custom-script/approve-code Scopes: automations:write

Approve a Custom Script's current generated code for live execution (Gate B — separate from saving the action itself).

3 parameters
Name In Type Required Description
automation_id path string yes
action_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/custom-scripts/post-automations-automation-id-actions-action-id-custom-script-approve-code.md

PUT /custom-scripts/:script_id/tp-key Scopes: custom-scripts:write

Attach a TrustPager API key (tp_live_…) to a Custom Script so its code can call the TrustPager API via tp.*. The key's own scopes decide what the script may do - mint it in Settings → API with exactly the scopes needed. Write-only; the value is never returned. Redeploy (Test) the script to apply.

3 parameters
Name In Type Required Description
script_id path string yes
api_key body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/custom-scripts/put-custom-scripts-script-id-tp-key.md

DELETE /automations/:automation_id/actions/:action_id/custom-script/secrets/:secret_name Scopes: automations:write

Remove a third-party secret from a Custom Script.

4 parameters
Name In Type Required Description
automation_id path string yes
action_id path string yes
secret_name path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/custom-scripts/delete-automations-automation-id-actions-action-id-custom-script-secrets-secret-name.md

DELETE /custom-scripts/:script_id/tp-key Scopes: custom-scripts:write

Remove the TrustPager API key from a Custom Script - tp.* becomes unavailable to it on the next deploy.

2 parameters
Name In Type Required Description
script_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/custom-scripts/delete-custom-scripts-script-id-tp-key.md

POST /automations/:automation_id/actions/:action_id/custom-script/generate Scopes: automations:write

Generate (or regenerate) a Custom Script action's code from a plain-English description.

4 parameters
Name In Type Required Description
automation_id path string yes
action_id path string yes
description body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/custom-scripts/post-automations-automation-id-actions-action-id-custom-script-generate.md

GET /custom-scripts/:script_id Scopes: custom-scripts:read

Get one Custom Script by its id — description, generated code, approval/deploy status, secret names, last test result.

1 parameter
Name In Type Required Description
script_id path string yes

📄 /api/custom-scripts/get-custom-scripts-script-id.md

GET /custom-scripts/:script_id/invocations Scopes: custom-scripts:read

List recent runs of a Custom Script — status (dispatched/completed/failed/timed_out), result, error, who invoked it, and timing.

2 parameters
Name In Type Required Description
script_id path string yes
limit query number no

📄 /api/custom-scripts/get-custom-scripts-script-id-invocations.md

GET /custom-scripts Scopes: custom-scripts:read

List all Custom Scripts in the workspace — id, description, approval/deploy status, secret names, and the owning automation (if any).

1 parameter
Name In Type Required Description
limit query number no

📄 /api/custom-scripts/get-custom-scripts.md

POST /custom-scripts/:script_id/restore-approved Scopes: custom-scripts:write

Put a Custom Script back to its last approved version and re-approve it. Use this when a newer version cannot be published (a syntax error, a size limit, a bad credential) and has left the script unable to run: it republishes code a human already approved, so the automation goes live again without waiting for the broken version to be fixed.

2 parameters
Name In Type Required Description
script_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/custom-scripts/post-custom-scripts-script-id-restore-approved.md

POST /custom-scripts/:script_id/run Scopes: custom-scripts:write

Run an approved Custom Script by its id right now (a live run — enforces the code-approval gate). Optionally pass contact_id/deal_id/trigger so the script can resolve a real record. Async: poll get_custom_script_invocations for the result.

5 parameters
Name In Type Required Description
script_id path string yes
contact_id body string no
deal_id body string no
trigger body object no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/custom-scripts/post-custom-scripts-script-id-run.md

PATCH /automations/:automation_id/actions/:action_id/custom-script/code Scopes: automations:write

Save hand-edited Custom Script code verbatim (marks it manually_edited so future regenerations never silently overwrite it).

4 parameters
Name In Type Required Description
automation_id path string yes
action_id path string yes
code body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/custom-scripts/patch-automations-automation-id-actions-action-id-custom-script-code.md

PUT /automations/:automation_id/actions/:action_id/custom-script/secrets/:secret_name Scopes: automations:write

Add or replace a third-party secret a Custom Script needs (write-only — the value is never returned).

5 parameters
Name In Type Required Description
automation_id path string yes
action_id path string yes
secret_name path string yes
value body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/custom-scripts/put-automations-automation-id-actions-action-id-custom-script-secrets-secret-name.md

POST /automations/:automation_id/actions/:action_id/custom-script/test Scopes: automations:write

Deploy and dry-run a Custom Script against sample trigger data. Bypasses both approval gates; never touches automation run history. Optionally pass contact_id/deal_id so tp.contact()/tp.deal()/tp.context() have a real record to resolve.

5 parameters
Name In Type Required Description
automation_id path string yes
action_id path string yes
contact_id body string no
deal_id body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/custom-scripts/post-automations-automation-id-actions-action-id-custom-script-test.md

Sensitive Fields (1 endpoint)

Read the real value of a field marked Sensitive (such as a tax file number). Everywhere else these fields only return a masked copy, and every reveal is logged.

📄 Full markdown: /api/sensitive-fields.md

POST /sensitive-fields/reveal Scopes: sensitive-fields:read

Read the real value of a field marked Sensitive, such as a tax file number. Everywhere else (get_contact, list and search results, exports, merge fields, automations, form submissions) a Sensitive field only ever returns its masked copy, e.g. "••••••782". Every call is logged with who asked and when. Requires the sensitive-fields:read permission ticked explicitly on the key: full (admin) access does not include it, and AI connectors never get it by default. Only call this when the person you are helping genuinely needs the number.

3 parameters
Name In Type Required Description
record_type body string yes What the value belongs to: "contact", "opportunity" or "account" for a custom field, or "form_submission" for a form answer.
record_id body string yes The contact, opportunity, account or form submission id.
field body string yes For a custom field, its id (the metadata key, e.g. "tax_file_number"). For a form answer, the form field id.

📄 /api/sensitive-fields/post-sensitive-fields-reveal.md

Skills (6 endpoints)

Reusable skills: a model, a prompt, an allowed tool set and an input/output contract you can run on demand.

📄 Full markdown: /api/skills.md

POST /skills Scopes: agent-ops:write

Create a skill owned by your workspace. A skill is one tool_loop: a model, a system prompt, an allowed tool set, and an input/output contract. Set visibility "public" to make it usable by every workspace (only you can still edit it).

14 parameters
Name In Type Required Description
name body string yes Stable handle used to run it, e.g. "curate_memory". Unique per owner; unique globally when public.
display_name body string yes
description body string no
when_to_use body string yes When an agent should reach for this skill. This IS the tool description the agent reads to decide whether to invoke it (agent-invocation skills are compiled into its tool catalog and picked by this text).
visibility body string no Default "private".
invocation body string no Default "agent" (the agent chooses it as a callable tool, selected by when_to_use). "lifecycle" = always-on: fires on an event (e.g. after a chat turn), never chosen, and NOT exposed as a tool.
execution body string no How it runs. Default "tool_loop" (a general agentic loop). "briefing" fetches every allowlisted read once, in parallel, with the input, then does one grounded summarize call — fast, for gather-then-summarize skills.
model body string yes The TrustPager AI model the loop runs on, as a '@cf/...' model id. Leave it out to use the default model.
system_prompt body string yes The skill's instructions.
tool_allowlist body array no Tool names the loop may call (e.g. search_memory, write_memory).
input_schema body object no JSON Schema for the skill's input.
output_schema body object no JSON Schema for the skill's output.
status body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/skills/post-skills.md

DELETE /skills/:skill_id Scopes: agent-ops:delete

Delete a skill your workspace owns.

2 parameters
Name In Type Required Description
skill_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/skills/delete-skills-skill-id.md

GET /skills/:skill_id Scopes: agent-ops:read

Fetch a single skill by id (must be public or owned by your workspace).

1 parameter
Name In Type Required Description
skill_id path string yes

📄 /api/skills/get-skills-skill-id.md

GET /skills Scopes: agent-ops:read

List skills available to this workspace: every public skill plus the workspace's own private skills. Filter by visibility or status.

4 parameters
Name In Type Required Description
visibility query string no
status query string no
limit query number no
after query string no

📄 /api/skills/get-skills.md

POST /skills/run Scopes: agent-ops:write

Run a skill (by id or name) against an input and return its output, tool trace, and token usage. Runs one bounded tool_loop on the skill's configured model with its allowed tools. Use this to test a skill or to invoke one on demand. Only public skills and skills your workspace owns can be run.

3 parameters
Name In Type Required Description
skill body string yes The skill id (uuid) or its name (e.g. "curate_memory").
input body object no The input object matching the skill's input_schema.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/skills/post-skills-run.md

PATCH /skills/:skill_id Scopes: agent-ops:write

Update a skill your workspace owns (model, prompt, tools, visibility, status, etc.). You cannot edit another workspace's public skill.

15 parameters
Name In Type Required Description
skill_id path string yes
name body string no
display_name body string no
description body string no
when_to_use body string no
visibility body string no
invocation body string no
execution body string no
model body string no
system_prompt body string no
tool_allowlist body array no
input_schema body object no
output_schema body object no
status body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/skills/patch-skills-skill-id.md

Auto Queues (15 endpoints)

Auto queues: multi-step sequences contacts are enrolled into, with their steps and enrollments. The legacy /event-queues/* paths still work.

📄 Full markdown: /api/auto-queues.md

POST /auto-queues/:queue_id/steps Scopes: automations:write

Add a step to an event queue.

9 parameters
Name In Type Required Description
queue_id path string yes
automation_id body string yes UUID of the automation this step runs. Get real ids from list_automations.
step_order body number yes Position of this step in the queue, ascending. Steps run in this order.
delay_days body number no Days after enrolment this step runs (cumulative from enrolment, not from the step before it: Day 2 / 4 / 6 is 2, 4, 6). Adds to delay_hours and delay_minutes. Counted as open business days when the queue has count_business_days on. Defaults to 0.
delay_hours body number no Hours after enrolment, on top of delay_days. Defaults to 0.
delay_minutes body number no Minutes after enrolment, on top of delay_days and delay_hours. Defaults to 0.
description body string no What this step is for, shown in the queue builder. Internal only.
is_active body boolean no Whether this step runs. Set false to skip it without removing it from the queue or renumbering the rest.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/auto-queues/post-auto-queues-queue-id-steps.md

POST /auto-queues/:queue_id/bulk-enroll Scopes: automations:write

Bulk-enrol contacts or opportunities in an event queue. Pass ids directly, or a filter to enrol everything matching it. At least one of contact_ids, deal_ids, contact_filter or customer_filter is required.

8 parameters
Name In Type Required Description
queue_id path string yes
contact_ids body array no Contact UUIDs to enrol.
deal_ids body array no Opportunity UUIDs to enrol.
contact_filter body object no Enrol every contact matching this filter instead of listing ids.
customer_filter body object no Enrol every company matching this filter instead of listing ids.
enrollment_time body string no When enrolment takes effect. Defaults to now.
skip_if_already_enrolled body boolean no Skip records already in the queue instead of re-enrolling them.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/auto-queues/post-auto-queues-queue-id-bulk-enroll.md

POST /auto-queues Scopes: automations:write

Create an automation event queue.

6 parameters
Name In Type Required Description
name body string yes
description body string no
respect_business_hours body boolean no Respect business hours: when true, steps only send during the workspace business-hours window (held to the next window otherwise). Defaults to false.
count_business_days body boolean no Count step delays in business days: when true, the days in each step delay count only open days in the workspace business hours (closed weekdays and holidays are skipped), so Day 2 / 4 / 6 never lands on a weekend or two steps on the same Monday. Applies to new enrolments only; contacts already in the queue keep their dates. Defaults to false (calendar days).
is_active body boolean no Whether the queue is running. Set false to pause it: enrolled contacts stay enrolled and nothing sends until it is switched back on.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/auto-queues/post-auto-queues.md

DELETE /auto-queues/:queue_id Scopes: automations:delete

Delete an event queue.

2 parameters
Name In Type Required Description
queue_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/auto-queues/delete-auto-queues-queue-id.md

DELETE /auto-queues/:queue_id/steps/:step_id Scopes: automations:delete

Delete a step from an event queue.

3 parameters
Name In Type Required Description
queue_id path string yes
step_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/auto-queues/delete-auto-queues-queue-id-steps-step-id.md

GET /auto-queues/:queue_id/board Scopes: automations:read

Read-only kanban-shaped state for an Auto Queue. Returns active enrolments bucketed into columns by the step they last completed (column 0 = "Awaiting Step 1"; column N = after step N), plus terminal counts (completed / cancelled). Use this to answer "where is contact X in queue Y" or "how many contacts are at each step" without grouping per-task rows from list_auto_queue_enrollments client-side. Each column returns up to 50 cards — for terminal-state drill-in, call list_auto_queue_enrollments with status=completed or status=cancelled.

1 parameter
Name In Type Required Description
queue_id path string yes

📄 /api/auto-queues/get-auto-queues-queue-id-board.md

GET /auto-queues Scopes: automations:read

List automation event queues (drip sequences).

1 parameter
Name In Type Required Description
limit query number no

📄 /api/auto-queues/get-auto-queues.md

POST /auto-queues/remove-all-enrollments Scopes: automations:write

Remove a contact or deal from EVERY Auto Queue (drip sequence) in the workspace at once, cancelling all of their still-pending steps across all queues. Pass ONE of contact_id or deal_id. Use this instead of calling remove_from_auto_queue once per queue when you want a full unenrol (e.g. the customer unsubscribed or churned). Returns cancelled_count (total pending steps stopped) and queues_affected; cancelled_count: 0 means nothing was pending for that contact/deal in any queue.

3 parameters
Name In Type Required Description
contact_id body string no Cancel this contact's pending steps across all queues.
deal_id body string no Cancel this deal's pending steps across all queues.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/auto-queues/post-auto-queues-remove-all-enrollments.md

POST /auto-queues/:queue_id/remove-enrollment Scopes: automations:write

Remove a contact or deal from an auto queue (drip sequence) by cancelling their pending steps. Pass queue_id plus ONE of: enrollment_id (most precise — from get_auto_queue_board cards or list_auto_queue_enrollments), contact_id, or deal_id. This is the direct unenrol — no need to build, enable, and trigger a remove_from_event_queue automation. Returns cancelled_count (the real number of pending steps stopped) and a message; cancelled_count: 0 with an explanatory message means nothing was pending for that enrolment (already completed, already removed, or never enrolled).

5 parameters
Name In Type Required Description
queue_id path string yes The auto queue UUID.
enrollment_id body string no Preferred. The enrolment UUID from the board or enrollments list.
contact_id body string no Cancel this contact's pending steps in the queue.
deal_id body string no Cancel this deal's pending steps in the queue.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/auto-queues/post-auto-queues-queue-id-remove-enrollment.md

POST /auto-queues/:queue_id/retry-failed Scopes: automations:write

Retry failed steps in an auto queue (drip sequence), in bulk. Use this when steps show status "failed" with a reason saying they were never sent, which happens when the dispatcher stopped part-way through a large batch. Find them first with list_auto_queue_enrollments using status="failed" and read each last_error. Pass queue_id alone to consider every failed step in the queue, or narrow with event_queue_step_id (re-send one step to everyone it missed), contact_id, enrollment_id, or an explicit task_ids list. SAFE TO REPEAT: every step is checked against its automation run record first, and only steps proven never to have reached the send path are requeued, so this cannot message someone twice. Returns retried, skipped_already_sent, skipped_unconfirmed and considered. skipped_already_sent means those recipients did get the message and were deliberately left alone; skipped_unconfirmed means delivery could not be established either way, so they were not re-sent and need a human to check.

7 parameters
Name In Type Required Description
queue_id path string yes The auto queue UUID.
task_ids body array no Optional. Retry only these specific step UUIDs (the "id" values from list_auto_queue_enrollments). Omit to consider every failed step in the queue.
contact_id body string no Optional. Only retry failed steps for this contact.
enrollment_id body string no Optional. Only retry failed steps for this enrolment.
event_queue_step_id body string no Optional. Only retry failed steps for this one step of the queue, e.g. re-send step 1 to everyone it never reached.
limit body number no Optional. Maximum steps to retry in this call (default 200, max 500). Steps are taken oldest-scheduled first.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/auto-queues/post-auto-queues-queue-id-retry-failed.md

PATCH /auto-queues/:queue_id Scopes: automations:write

Update an event queue.

7 parameters
Name In Type Required Description
queue_id path string yes
name body string no
description body string no
respect_business_hours body boolean no Respect business hours: when true, steps only send during the workspace business-hours window (held to the next window otherwise). Defaults to false.
count_business_days body boolean no Count step delays in business days: when true, the days in each step delay count only open days in the workspace business hours (closed weekdays and holidays are skipped), so Day 2 / 4 / 6 never lands on a weekend or two steps on the same Monday. Applies to new enrolments only; contacts already in the queue keep their dates. Defaults to false (calendar days).
is_active body boolean no Whether the queue is running. Set false to pause it: enrolled contacts stay enrolled and nothing sends until it is switched back on.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/auto-queues/patch-auto-queues-queue-id.md

PATCH /auto-queues/:queue_id/steps/:step_id Scopes: automations:write

Update a step in an event queue.

10 parameters
Name In Type Required Description
queue_id path string yes
step_id path string yes
automation_id body string no UUID of the automation this step runs. Get real ids from list_automations.
step_order body number no Position of this step in the queue, ascending. Steps run in this order.
delay_days body number no Days after enrolment this step runs (cumulative from enrolment, not from the step before it: Day 2 / 4 / 6 is 2, 4, 6). Adds to delay_hours and delay_minutes. Counted as open business days when the queue has count_business_days on. Defaults to 0.
delay_hours body number no Hours after enrolment, on top of delay_days. Defaults to 0.
delay_minutes body number no Minutes after enrolment, on top of delay_days and delay_hours. Defaults to 0.
description body string no What this step is for, shown in the queue builder. Internal only.
is_active body boolean no Whether this step runs. Set false to skip it without removing it from the queue or renumbering the rest.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/auto-queues/patch-auto-queues-queue-id-steps-step-id.md

Auto Schedules (9 endpoints)

Auto schedules: recurring jobs that fire on a cron, with their run history and audience preview. The legacy /event-schedules/* paths still work.

📄 Full markdown: /api/auto-schedules.md

POST /auto-schedules Scopes: schedules:write

Create an Auto Schedule: a cron (WHEN) plus a Source Data primitive (WHAT DATA) plus an automation (WHAT HAPPENS). Each row the Source resolves fires the automation once with that row as trigger_data. Every Source is bounded by its own data shape, so pick the one that matches what you want to act on. Use "report" when the answer is a number and a list rather than an action per record.

14 parameters
Name In Type Required Description
name body string yes
automation_id body string yes
cron_expression body string no
time_of_day body string no
timezone body string no
source_kind body string yes Required. Which Source Data primitive feeds the automation. 'report' = ONE email per named recipient carrying a count and a list; never fans out per record, and costs the same whether 18 or 25,000 records match. 'workflow_stage' = opportunities sitting in named stages of one pipeline (archived, won, lost and archived pipelines are always excluded). 'expiry_date' = records whose custom date field lands in a day window, or work orders whose scheduled date does (e.g. remind each trade the day before their own job). 'staff' = workspace people, bounded by headcount (accounts that never accepted their invite are excluded). 'stakeholder_group' = the members of one curated stakeholder group. 'external_feed' = new records polled from a JSON URL, deduped. 'static' = fixed values authored on the schedule. The old free-form 'audience' source is RETIRED and cannot be created: an arbitrary filter over a growing table on a cron has no ceiling. For a mass contact send, use Email Blasts instead.
source_config body object no Config for the chosen source_kind. report: { recipients:{ user_ids?:[], emails?:[] } (required, max 20 total), count_of?:{ kind:'workflow_stage'|'expiry_date'|'staff'|'stakeholder_group', ...that kind's config } to attach a count plus a list of the matching records, list_limit? (rows listed in the email, default 25, max 200), dashboard_id? (target of the 'see the full report' link) }. workflow_stage: { pipeline_id (required), stage_ids[] (required, at least one — a whole pipeline is not allowed because it grows without bound), assigned_to_ids?, stale_days?, batch_size? (rows dispatched per run, default 100, max 500) }. expiry_date: { field_id (required, the custom date field), min_days? and/or max_days? (at least one required — the window is the bound; negatives allowed, e.g. max_days:0 = already lapsed; min_days:30,max_days:30 = exactly 30 days out), entity?:'contacts'|'deals'|'work_orders', contact_type?, has_email?, respect_unsubscribes? (default true), batch_size? }. With entity:'work_orders' there is no field_id: the window applies to each work order's own scheduled date, and each matched work order runs the automation once with the same payload work_order_status_changed sends (work_order_id, deal_id, stakeholder_name, stakeholder_email, stakeholder_contact_id, schedule_date, schedule_time, work_order_status, and the work order's fields by slug), so {{stakeholder_email}} reaches exactly that trade. Extra options: work_order_status_in:[status names or ids] (REQUIRED for work orders, at least one, e.g. ['Accepted']: without it a trade who declined or was never offered the job would be reminded too. Optional only inside a report's count_of, where an empty list counts every status. A list that matches no status fails the run rather than going quiet), require_stakeholder? (default true: skip work orders with no trade attached). Work orders on archived or lost opportunities are always excluded. The repeat interval counts per work order per date, so a rescheduled job is reminded again for its new day. Example, remind each accepted trade the day before: { entity:'work_orders', min_days:1, max_days:1, work_order_status_in:['Accepted'] } with row_cooldown_days:1. staff: { user_ids? (empty = everyone), roles?, with_open_tasks? (digest mode: only people who have open tasks, with their tasks attached as related_tasks), task_statuses?, overdue_only?, max_tasks_per_user?, batch_size? }. stakeholder_group: { bucket_id (required), batch_size? }. external_feed: { feed_url (required, tokenised JSON URL), format:'json_array', dedupe_key (required, the record key holding the stable external id), first_run:'seed'|'import_all' (default 'seed' = record everything as seen and import nothing on the first fire), filters:[{ field, op:'not_empty'|'equals'|'not_equals'|'in', value? }] }. static: { trigger_data:{...} } (one fixed row per fire) or { rows:[{...}] }.
row_cooldown_days body number no Repeat interval: how many days before the SAME record may fire again. REQUIRED for the record-selecting sources (workflow_stage, expiry_date, staff, stakeholder_group) and must be answered explicitly, including 0. 0 means 'every run', which is correct when the automation moves the record out of the source (e.g. a move_deal that empties the stage). A positive number means each record fires at most once per that many days, which is what an alert wants. It cannot be left unset because a record that matches today usually still matches tomorrow: leaving it unset is what turned one NSJ schedule into 7,060 duplicate tasks in four days. Ignored for report, external_feed and static, which are self-limiting.
description body string no Free-text note on what this schedule is for. Shown in the Auto Schedules list.
is_active body boolean no Whether the schedule fires. Defaults to true on create; set false to author a schedule without starting it.
end_at body string no ISO timestamp after which the schedule stops firing. Omit for no end date.
max_runs body number no Stop the schedule after this many total runs. Omit for unlimited.
days_of_week body array no Days of the week (0 = Sunday) the schedule fires. Combined with time_of_day and derived into cron_expression at save time; an alternative to writing cron_expression yourself.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/auto-schedules/post-auto-schedules.md

DELETE /auto-schedules/:schedule_id Scopes: schedules:delete

Delete an event schedule.

2 parameters
Name In Type Required Description
schedule_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/auto-schedules/delete-auto-schedules-schedule-id.md

POST /auto-schedules/:schedule_id/fire-now Scopes: schedules:write

Fire an event schedule immediately (out of band).

2 parameters
Name In Type Required Description
schedule_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/auto-schedules/post-auto-schedules-schedule-id-fire-now.md

GET /auto-schedules Scopes: schedules:read

List automation event schedules (fire at wall-clock cron expressions).

2 parameters
Name In Type Required Description
limit query number no
after query string no Pagination cursor from a previous page.

📄 /api/auto-schedules/get-auto-schedules.md

POST /auto-schedules/preview-cron Scopes: schedules:read

Validate a cron expression and return the next N firing times.

4 parameters
Name In Type Required Description
cron_expression body string yes
timezone body string no
count body number no
n body number no

📄 /api/auto-schedules/post-auto-schedules-preview-cron.md

PATCH /auto-schedules/:schedule_id Scopes: schedules:write

Update an Auto Schedule. source_kind and source_config are validated as a MERGED state, so a patch that only clears the repeat interval or only widens the source config is refused the same way a bad create would be. A schedule still on the retired 'audience' source can be renamed, paused or deleted, but cannot be edited to stay on it.

15 parameters
Name In Type Required Description
schedule_id path string yes
name body string no
cron_expression body string no
time_of_day body string no
timezone body string no
source_kind body string no The Source Data primitive. See create_auto_schedule for what each one selects. 'audience' is retired and cannot be set.
source_config body object no Config for the chosen source_kind. See create_auto_schedule for the full per-source shape. Replaces the stored config wholesale, so send the complete object.
is_active body boolean no
end_at body string no
max_runs body number no
description body string no What this schedule is for, shown in the schedule list. Internal only.
automation_id body string no UUID of the automation this schedule runs. Get real ids from list_automations.
days_of_week body array no Days the schedule runs, 0 (Sunday) to 6 (Saturday). Paired with time_of_day as the plain-language alternative to cron_expression, which is derived from the two at save time.
row_cooldown_days body number no Repeat interval: how many days before the SAME record may fire again. 0 means every run. Required for workflow_stage, expiry_date, staff and stakeholder_group, and a patch cannot clear it back to null. See create_auto_schedule.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/auto-schedules/patch-auto-schedules-schedule-id.md

SCHEDULING (26 endpoints)

scheduling endpoints.

📄 Full markdown: /api/scheduling.md

POST /scheduling/bookings/:booking_id/cancel Scopes: scheduling:write

Cancel a booking. Invitee gets cancellation notice.

4 parameters
Name In Type Required Description
booking_id path string yes
reason body string no Why the booking was cancelled. Stored as cancellation_reason.
cancellation_reason body string no Alias of reason, the name it is stored under.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduling/post-scheduling-bookings-booking-id-cancel.md

POST /scheduling/check-availability Scopes: scheduling:read

Quick check whether a specific slot is available. Returns nearest available alternatives, and if unavailable, why: reason_code, plus for a Google Calendar conflict the blocking event's name (blocking_event) AND its exact times (blocking_event_start / blocking_event_end as ISO instants, blocking_event_window rendered in the requested timezone) so you can verify the event genuinely overlaps the slot.

3 parameters
Name In Type Required Description
event_type_id body string yes
date body string yes YYYY-MM-DD
time body string yes HH:MM, 24-hour

📄 /api/scheduling/post-scheduling-check-availability.md

POST /scheduling/bookings Scopes: scheduling:write

Create a calendar booking. Use get_available_slots first to find a valid event_type_id + date + time. Provide email + full_name for a new booker, OR attach_to_opportunity_id / attach_to_contact_id / attach_to_company_id to attach the booking to an existing record (booker email + name are then derived from that record's primary contact). IN-PERSON: if the event type has location_type "in_person" and in_person_direction "at_customer" (we travel to the customer), you MUST pass booker_address or the booking is rejected with MISSING_ADDRESS — putting the address in message does not work. Check those two fields on the event type first.

28 parameters
Name In Type Required Description
event_type_id body string yes Event type UUID, from list_scheduled_event_types / get_available_slots. Alternatively pass event_type_slug or event_type_name.
event_type_slug body string no Event type slug. Alternative to event_type_id.
event_type_name body string no Event type name. Alternative to event_type_id.
date body string yes YYYY-MM-DD, from get_available_slots.
time body string yes HH:MM (24h), from get_available_slots.
timezone body string no IANA timezone the date/time are expressed in, e.g. "Australia/Sydney". Defaults to the availability timezone of the event type. Pass the zone the wall-clock you are sending is ACTUALLY in: get_available_slots returns host-local times unless you pass visitor_timezone, in which case send its booking_time and leave this alone.
visitor_timezone body string no IANA timezone to SHOW the booker their own times in, e.g. "Australia/Perth". Display only, and separate from timezone, which says how to READ the time you sent. Set it when the booker is in a different zone from the host and you are sending host-local times: the confirmation, its calendar invite, every reminder and the booking_created automation payload are then all written in the clock of the booker, with the zone named. Defaults to timezone.
email body string no Booker email. Required unless attach_to_opportunity_id / attach_to_contact_id / attach_to_company_id is provided (then derived from that record's primary contact).
full_name body string no Booker full name. Required unless attach_to_opportunity_id / attach_to_contact_id / attach_to_company_id is provided (then derived from that record's primary contact). Also accepted as fullName.
phone body string no Booker phone. Normalised to E.164.
message body string no Notes from or about the booker, stored on the booking. NOT a substitute for booker_address — an address left only in here is ignored by an at_customer event type.
booker_state body string no Booker state/region, e.g. "VIC", "NSW". Written to the CRM contact's state only when that field is currently empty.
booker_timezone body string no IANA timezone to store on the CRM contact, e.g. "Australia/Perth". Falls back to timezone when omitted. Written only when the contact's timezone is currently empty.
booker_address body string no The booker's address — labelled "Your address" on the booking form. REQUIRED when the event type has location_type "in_person" and in_person_direction "at_customer" (we travel to the customer); the booking is rejected with MISSING_ADDRESS without it. Putting the address in message does NOT work. Becomes the calendar event location and appears in confirmations and reminders. Check location_type + in_person_direction on the event type before booking.
booker_address_notes body string no Arrival notes for that address — labelled "Access notes (optional)" on the booking form, e.g. "Parking in rear. Ring buzzer 3." Appended to the calendar event instructions.
attach_to_contact_id body string no Attach the booking to an existing contact instead of matching/creating one. Booker email + name are derived from the contact.
attach_to_opportunity_id body string no Attach the booking to an existing opportunity instead of matching/creating one. Booker email + name are derived from the opportunity's primary contact.
attach_to_company_id body string no Attach the booking to an existing company instead of matching/creating one. Booker email + name are derived from the company's primary contact, or its first contact.
attendees body array no Additional attendees beyond the booker: [{ "email": "...", "name": "...", "phone": "..." }]. All receive calendar invites. Each email must be a complete valid address or INVALID_ATTENDEE_EMAIL is returned.
internal body boolean no Set true for an internal staff meeting (the event type must have meeting_mode "internal"). Skips availability gating and CRM opportunity creation; still creates the meeting link and Notetaker. Omit date/time for an instant meeting on the next 5-minute boundary.
is_recurring_series body boolean no Set true only when creating the single series-anchor booking for a recurring internal meeting type. The anchor holds the shared meeting link and is topped up by the occurrence scheduler. Requires recurrence and event_timezone.
recurrence body array no RRULE strings for the recurring calendar event, e.g. ["RRULE:FREQ=WEEKLY;BYDAY=MO"]. Required when is_recurring_series is true; normally computed from the event type's recurrence_rule.
event_timezone body string no IANA timezone anchoring a recurring event's wall-clock time across DST, e.g. "Australia/Sydney". Required when is_recurring_series is true.
form_answers body object no Answers to the event type's booking_form_fields questions, keyed by each field's key, e.g. { "birth_date": "1988-04-02", "birth_time": "14:30", "consent": true }. A time field with allow_unknown may send { "unknown": true }. Optional here (required questions are only enforced on the booker's own booking page), but what is sent must be valid for the field type or INVALID_FORM_ANSWERS is returned. Stored on the booking as metadata.form_answers.
metadata body object no Arbitrary JSON merged into the booking's metadata alongside the message. Cannot set form_answers: send those as form_answers.
website_id body string no Website UUID to record as the source of the booking, when it came from an embedded booking widget.
site_key body string no Site key to record as the source of the booking, when it came from an embedded booking widget.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduling/post-scheduling-bookings.md

POST /scheduling/event-types Scopes: scheduling:write

Create a new scheduled event type (meeting template).

63 parameters
Name In Type Required Description
name body string yes Display name of the event type, e.g. "Discovery Call".
description body string no Shown to the booker on the public booking page, under the name.
slug body string no URL segment for the public booking link. Auto-derived from name on create when omitted.
icon body string no Lucide icon name shown on the event-type card and calendar, e.g. "calendar", "phone", "video".
color body string no Theme colour key for the card and calendar entries, e.g. "primary".
sort_order body integer no Position in the event-type list. Lower sorts first.
is_active body boolean no When false the event type is archived: hidden from the list and not bookable. Default true.
is_bookable body boolean no When true the event type has a public booking page and appears on booking links. When false it is internal-only. Setting this true defaults location_type to "video".
is_meeting body boolean no Legacy flag, kept in sync with location_type automatically (video = true, phone/in_person = false). Set location_type instead.
default_duration_minutes body number no How long a booking of this type runs, in minutes (15, 30, 45, 60...). This is the field returned when you read the event type back, and it sets each booking's end time.
buffer_before_minutes body integer no Padding held free immediately before each booking, in minutes. Blocks slots that would start too close to an existing one.
buffer_after_minutes body integer no Padding held free immediately after each booking, in minutes.
min_notice_hours body integer no How far ahead a booker must book, in hours. A slot closer than this is not offered.
max_advance_days body integer no How far into the future slots are offered, in days.
slot_interval_minutes body integer no Spacing between offered start times, in minutes. 30 offers 9:00, 9:30, 10:00; 15 offers 9:00, 9:15, 9:30.
meeting_mode body string no "booking" = public 1:1 booking (one attendee per slot, the default). "group" = public Group Session (multiple attendees per slot up to capacity; set capacity + waitlist_enabled). "internal" = internal meeting, not publicly bookable.
email_config_id body string no Optional sending identity (email_config UUID) for this event type's booking notifications. Confirmation and all reminders send FROM this config's name/address/reply. Unset (or null to clear) = the workspace default sending address. Use for a workspace running multiple brands so one brand's bookings send from its own sender.
event_queue_id body string no Auto Queue (UUID) a confirmed booking enrols the booker into, for reminder or follow-up sequences. Enrolment is deduped: a booker who already has pending steps in that queue is skipped rather than enrolled twice, so rebooking does not double the reminders. Pass null to unlink.
enrollment_offset_minutes body integer no Shifts when the Auto Queue clock starts, relative to the booking start time, in minutes. Negative counts backwards from the meeting (-1440 anchors step delays to 24 hours before it). Default 0, meaning the meeting start.
booking_confirmation_message body string no Custom text shown on the confirmation screen after a booker confirms a slot.
booking_form_fields body array no Extra questions shown on the public and /embed booking page (paid checkout included), after name/email/phone. Answers are stored on the booking only (metadata.form_answers, returned by get_booking and shown on the booking in the Portal): never copied to the contact, the booking_created trigger payload or any email. Bookers send them as form_answers: { <key>: value }; a time field with allow_unknown may send { "unknown": true }. Send [] to remove all questions.
booking_notifications body object no Per-event booking emails and SMS, keyed by moment. The keys that actually send: "confirmation" (immediate, supports attach_ics), "pre_meeting" (offset_minutes BEFORE the start), "meeting_start", "late", "no_show", "rebooking" (offset_minutes AFTER now), and "rsvp_declined" (immediate, fires the moment the booker declines the calendar invite). Each key holds an array of { label, channels, email_subject, email_body, sms_body, recipients, chromeless, offset_minutes } entries. A "pre_meeting" entry also accepts only_if_not_accepted: true, which skips anyone who has already accepted their calendar invite so a firmer second nudge only reaches the people who have not. Recipients are "booker" (the person who booked), "booker_and_attendees" (the booker plus the attendees on the booking, but NOT the assigned host), "team" (only the users in booking_assigned_user_ids, i.e. the assigned host), or "all" (booker, attendees and assigned host). The assigned host is only notified by "team" or "all". Once a booker declines, they are dropped from every later send aimed at them (pre_meeting, meeting_start, late, no_show and rebooking), so the "rsvp_declined" offer is the last thing they hear unless they rebook; "team" and "all" entries still reach the host. Note "reminder", "cancellation" and "reschedule" were previously documented here but have never had a consumer — "pre_meeting" is the reminder.
booking_default_products body array no Products attached to the opportunity a booking creates, as [{ id, quantity }].
booking_default_tags body array no Tags applied to the created opportunity and contact, as [{ name, color }].
booking_assigned_user_ids body array no Host user UUIDs. Their connected calendars gate availability and one of them is assigned each booking. A bookable event type needs at least one.
booking_default_attendee_contact_ids body array no Contact UUIDs added as attendees on every booking of this type, on top of the booker.
booking_default_attendee_emails body array no Plain email addresses added as attendees on every booking of this type, for people with no contact record.
auto_record_enabled body boolean no When true, the TrustPager Notetaker joins each booking of this type to record and transcribe it. Video bookings only. Default false.
recurrence_rule body object no Recurrence for internal meeting series (meeting_mode "internal"): { frequency, interval, days_of_week, until }. Not used by public booking types.
embed_config body object no Embed theme for the /embed booking page (colours, card style, width). Keys: surface, pageBg, field, text, muted, border, primary, secondary, cardStyle ("card"|"flat"), showLogoHeader (bool), maxWidth ("sm"|"md"|"lg"|"full"), align ("left"|"center"). Only affects the embeddable variant, not the public booking link.
location_type body string no Where the booking happens. "video" = each booking gets its own Google Meet link when an assigned host has Google Calendar connected, otherwise it uses location_meeting_url. "phone" = you call the booker (or they call you). "in_person" = a physical address, which also requires in_person_direction. Determines the icon and location shown on the calendar and in booking confirmations. Bookable event types default to "video".
in_person_direction body string no Required when location_type is "in_person". "at_business" = "Customer comes to us" — the booker travels to you, so set location_address. "at_customer" = "We travel to customer" — the booking form asks the booker for their address, and a booking then requires booker_address.
location_address body string no Your physical address, shown to the booker before they confirm. Used when location_type is "in_person" and in_person_direction is "at_business".
location_instructions body string no Free-text arrival notes shown with the address, e.g. "Parking at rear, buzz unit 4".
location_meeting_url body string no A fixed video meeting link (your Zoom, Microsoft Teams or Google Meet room, must start with https://). Used for location_type "video" bookings when no assigned host has Google Calendar connected: the booker gets this link in their confirmation and reminders, and the TrustPager Notetaker joins it. Leave empty to rely on per-booking Google Meet links.
booking_ignore_all_day_events body boolean no When true (the default), all-day entries on a host's connected calendar (leave, reminders, OOO markers) do NOT block bookings. Set false to let all-day events count as busy. To block a specific date, use a date override in Availability instead.
booking_ignore_free_events body boolean no When true (the default), timed entries the host has marked "Show as Free" in Google do NOT block bookings, matching how Calendly and Cal.com read that flag. Marking an entry Free is a deliberate act meaning the time is still bookable, so it is honoured. Set false to make every calendar entry block, even one flagged Free, for a host who marks real commitments Free out of habit.
capacity body integer no Group Session only. Maximum number of independent bookings a single time slot accepts (e.g. 40 seats in a workshop). Each booker gets their own record and confirmation. Leave unset/null for a 1:1 event type (one booker per slot).
waitlist_enabled body boolean no Group Session only. When true, once a slot reaches capacity the public booking page offers a free waitlist (name + email, no booking) instead of blocking. Default false.
is_paid body boolean no When true, this event requires payment at the point of booking via the workspace's connected Stripe. The slot is held during checkout and the booking is only confirmed once payment succeeds. Requires price_amount and an active Stripe connection to publish. Default false.
price_amount body integer no Price in the smallest currency unit (e.g. cents): 9700 = $97.00. Required when is_paid is true. Each booker pays this amount for their own seat.
price_currency body string no ISO 4217 currency code for the price, e.g. "AUD", "USD". Defaults to AUD.
booking_abandoned_stage_id body string,null no Optional, paid events only. A stage (UUID) in booking_pipeline_id. When set, a booker whose checkout expires unpaid (the held slot is released ~35 min after they start checkout) is captured as a contact + opportunity in this stage, named with the slot they tried for and tagged "Checkout abandoned", and deal_created fires so a follow-up automation scoped to this stage can run. No calendar event or meeting link is created. Null (default) = abandoned checkouts are released silently.
allow_promotion_codes body boolean no Optional, paid events only. When true, the Stripe checkout shows an "Add promotion code" field. The codes and their limits (discount, total uses, expiry, first-time order only, which products/event types) are created in the workspace’s own Stripe; each promo-enabled event type sells a named Stripe Product so a coupon can be limited to it. The code used is saved on the booking (metadata.promotion) and the opportunity (metadata.promotion_code). Default false.
checkout_success_url body string,null no Optional, paid events only. An https URL on your own site that Stripe sends the booker to after a successful payment, instead of the TrustPager confirmation page. tp_booking=paid and tp_event=<event type slug> are added to it. Useful when the booking page is embedded on your site (/embed/book/<company>/<event>). Null (default) = TrustPager confirmation page.
checkout_cancel_url body string,null no Optional, paid events only. An https URL Stripe sends the booker to if they back out of payment. Null (default) = back to the booking page.
booking_lead_source_from_utm body boolean no Optional. When true and the booking link carries utm_source, a new opportunity’s lead source is that value instead of booking_lead_source. UTM / click-id attribution is saved on the contact and opportunity metadata either way. Default false.
cancellation_policy body object no Paid event types only (SR 0bc488ed). The booker’s self-service cancel/reschedule rules, shown before checkout and on the booking management link. Keys: full_refund_hours (cancel at least this many hours before the start for a full refund via the workspace’s connected Stripe), late_refund_percent (0-100, refund for a later cancel, default 0), reschedule_cutoff_hours (booker can reschedule only until this many hours before), max_reschedules (how many times the booker can reschedule; 0 = never online), note (extra text). A host cancelling from the Portal is never restricted and no automatic refund is issued there. Send null to remove.
booking_limits body object no Per-contact booking limits for this event type (SR 95e858d8). Keys: max_bookings + period_days (at most max_bookings bookings within any period_days stretch), min_gap_days (at least this many days between two bookings), match_by ("email" | "phone" | "email_or_phone", default email_or_phone), message (shown to the booker when blocked), alternative_url (optional link offered with the message), also_counts_event_type_ids (optional array of other event type ids in this workspace whose bookings also count towards this limit, e.g. one welcome offer across several event types). Enforced on the public booking page, paid checkout, client portal, voice agent and email/SMS assistant; a host booking from the Portal or API is not limited. Send null to remove.
booking_auto_create_deal body boolean no Whether a booking creates/links an opportunity at all. Default true.
booking_deal_behavior body string no How a booking relates to an existing opportunity for the booker. "create" (or "always_create") = always mint a new opportunity. "update_or_create" (or "find_or_create") = match the booker's existing opportunity first, create one only if none matches. "never_create" (or "match_only") = only ever attach to a matching opportunity, never create. Matched by email/phone.
booking_match_fields body array no Identity fields used to match an existing contact/opportunity, e.g. ["email"] or ["email","phone"]. "name" is never honoured (fuzzy-name-collision risk).
booking_match_pipeline_id body string no Restrict opportunity matching to one pipeline (UUID). Unset = search all pipelines.
booking_match_status_filter body string no "all" | "open" | "won" | "lost" - which opportunity statuses are eligible to match. Default "open".
booking_match_strategy body string no "most_recent" | "highest_value" | "oldest" - tie-breaker when multiple opportunities match. Default "most_recent".
booking_card_behavior body string no "ignore" | "find_or_create" | "always_create" - what to do with the pipeline card placement once the opportunity is resolved.
booking_pipeline_id body string no Destination pipeline (UUID) for the opportunity when one is created. Falls back to the workspace default pipeline.
booking_stage_id body string no Destination stage (UUID) within booking_pipeline_id. Falls back to the first stage.
booking_lead_source body string no lead_source recorded on the created opportunity. Defaults to "booking".
booking_deal_name_template body string no Opportunity-name template. Tokens: {event_type} {customer_name} (or {booker_name}) {date} {time}. Any unrecognised token falls back to the booker name.
booking_update_next_action body boolean no When true, a booking stamps the linked opportunity's Next Action with this meeting (name, date, time) so it shows on the opportunity card, and a reschedule moves it to the new time. Default false.
duration_minutes body number no Alias for default_duration_minutes. Accepted for backwards compatibility; default_duration_minutes wins if both are sent.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduling/post-scheduling-event-types.md

DELETE /scheduling/event-types/:event_type_id Scopes: scheduling:delete

Delete a scheduled event type by UUID. Destructive.

2 parameters
Name In Type Required Description
event_type_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduling/delete-scheduling-event-types-event-type-id.md

POST /scheduling/available-slots Scopes: scheduling:read

Get available booking slots for a scheduled event type within a date range. Returns slot start times in host timezone. Set include_blocked_slots to also see why in-window times are NOT offered (too soon, already booked, outside a host's personal hours, or a named event on their Google Calendar).

5 parameters
Name In Type Required Description
event_type_id body string yes
date_from body string yes YYYY-MM-DD
date_to body string yes YYYY-MM-DD
timezone body string no
include_blocked_slots body boolean no When true, each date also returns a blocked_slots array: { time, formatted, reason_code, summary?, busy_start?, busy_end?, busy_window?, detail } explaining every unavailable in-window time. For a host_calendar_busy block, busy_start/busy_end are the blocking event's exact ISO instants and busy_window is them rendered in the response timezone, so you can confirm the event really does overlap the slot.

📄 /api/scheduling/post-scheduling-available-slots.md

GET /scheduling/bookings/:booking_id Scopes: scheduling:read

Fetch a single booking by UUID with event details, invitee, host, status, linked records.

1 parameter
Name In Type Required Description
booking_id path string yes

📄 /api/scheduling/get-scheduling-bookings-booking-id.md

GET /scheduling/availability Scopes: scheduling:read

Get working-hours availability and date overrides. Defaults to the company-wide schedule; pass event_type_id to get one event type's own schedule, or user_id to get one host's personal schedule. Every response names which schedule actually governs via "source" ("event_type" | "company_default" | "user_default" | "default" | "unset") plus a "constrains_bookings" boolean. An event type with no schedule of its own reports the real company default it inherits, never a generic placeholder. A host who has saved no personal hours reports source "unset" with EMPTY weekly_hours and constrains_bookings false, which means they narrow nothing and are bookable across the whole base window — it does NOT mean they are never available.

2 parameters
Name In Type Required Description
event_type_id query string no Get this event type's own schedule instead of the company default.
user_id query string no Get this host's personal schedule instead of the company default. Ignored if event_type_id is set.

📄 /api/scheduling/get-scheduling-availability.md

POST /scheduling/bookings/:booking_id/link-opportunity Scopes: scheduling:write

Attach an existing booking to an opportunity — repairs a mis-linked booking (e.g. deal_id came back null) without cancelling and recreating it, which would re-fire client-facing confirmations. Only sets deal_id; contact_id/customer_id are backfilled from the opportunity only when the booking is missing them.

3 parameters
Name In Type Required Description
booking_id path string yes Booking UUID to attach (required).
opportunity_id body string yes Opportunity UUID to attach the booking to (required).
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduling/post-scheduling-bookings-booking-id-link-opportunity.md

GET /scheduling/bookings Scopes: scheduling:read

List bookings (calendar appointments). Filter by date range, contact, company, opportunity, event type, status, or where the booking came from.

11 parameters
Name In Type Required Description
limit query number no
after query string no
contact_id query string no
customer_id query string no
opportunity_id query string no
event_type_id query string no
status query string no
booker_rsvp_status query string no The booker's response to the calendar invite. "no_response" means the invite reached them and they have not replied — the useful filter for chasing likely no-shows. Absent entirely (null) on bookings with no connected calendar, which is different from "no_response".
booked_via query string no Where the booking came from. "voice_agent" is the AI phone agent (those rows also carry call_id, so a booking can be traced back to its recording), "website" the public booking page, "client_portal" a signed-in portal member, "staff_calendar" a booking made by a team member, "api" the API or an AI agent over MCP (those rows also carry api_credential_type and api_credential_id), "auto_responder" the inbox AI replying to a customer, "internal" an internal team meeting. "none" selects bookings with no source recorded, which means bookings made before 29 Sept 2026 for the api, auto_responder and internal sources, and before 23 Sept 2026 for the rest.
date_from query string no YYYY-MM-DD. Bookings starting on or after this date.
date_to query string no YYYY-MM-DD. Bookings starting on or before this date.

📄 /api/scheduling/get-scheduling-bookings.md

GET /scheduling/event-types Scopes: scheduling:read

List the workspace scheduled event types (meeting templates). Use to find event_type_id for get_available_slots and create_booking.

2 parameters
Name In Type Required Description
is_bookable query boolean no Filter to publicly bookable event types (true) or internal-only ones (false). Omit for both. Read by listTypes but never published until SR da88447b, so no MCP caller could narrow the list.
is_active query boolean no Filter to live event types (true) or archived ones (false). Omit for both.

📄 /api/scheduling/get-scheduling-event-types.md

POST /scheduling/bookings/:booking_id/mark-attended Scopes: scheduling:write

Mark a booking as attended.

2 parameters
Name In Type Required Description
booking_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduling/post-scheduling-bookings-booking-id-mark-attended.md

POST /scheduling/bookings/:booking_id/mark-late Scopes: scheduling:write

Mark a booking as late.

2 parameters
Name In Type Required Description
booking_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduling/post-scheduling-bookings-booking-id-mark-late.md

POST /scheduling/bookings/:booking_id/mark-no-show Scopes: scheduling:write

Mark a booking as no-show.

2 parameters
Name In Type Required Description
booking_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduling/post-scheduling-bookings-booking-id-mark-no-show.md

POST /scheduling/bookings/:booking_id/reschedule-pending Scopes: scheduling:write

Mark an upcoming booking "To be rescheduled" while a new time is agreed with the booker. Frees the slot and removes the calendar event WITHOUT notifying anyone, and does NOT fire booking_cancelled (so cancel-recovery automations stay quiet); fires booking_reschedule_pending instead. The booking stays in Upcoming with status reschedule_pending until reschedule_booking picks the new time, which sends the normal invite. Refused for recurring meetings and group-session seats.

3 parameters
Name In Type Required Description
booking_id path string yes
reason body string no Optional note on why, e.g. "Client travelling, confirming a new time next week". Shown on the activity and in the trigger.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduling/post-scheduling-bookings-booking-id-reschedule-pending.md

POST /scheduling/bookings/:booking_id/hosts Scopes: scheduling:write

Change who hosts ONE booking (a quick reassign) without changing its event type. The calendar invite follows: if the current invite owner stays a host, the new hosts are added as guests; if the owner is removed and a new host has Google Calendar connected, the invite moves to that host's calendar keeping the same time and video link. A past or not-yet-confirmed booking only has its record updated. Group sessions and recurring meetings are refused (change those on the event type). Returns calendar (moved / guests_updated / not_connected / record_only / no_invite / failed) and busy: new hosts who already have something on at that time (advisory; the change still happens). Get user UUIDs from list_company_users; get_booking returns the current host_user_ids.

3 parameters
Name In Type Required Description
booking_id path string yes
host_ids body array yes User UUIDs of the team members who should host this one booking (at least one). They must be members of this workspace. Passing exactly the event type's own hosts puts the booking back to following its event type.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduling/post-scheduling-bookings-booking-id-hosts.md

POST /scheduling/bookings/:booking_id/reschedule Scopes: scheduling:write

Reschedule a booking. Use get_available_slots to find a valid new slot. Also picks the new time for a booking marked To be rescheduled (status reschedule_pending): that sends a fresh invite and sets it back to confirmed.

5 parameters
Name In Type Required Description
booking_id path string yes
slot body string yes
event_type_id body string no Defaults to the booking's current event type.
timezone body string no Defaults to the booking's current timezone.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduling/post-scheduling-bookings-booking-id-reschedule.md

POST /scheduling/bookings/:booking_id/notetaker Scopes: scheduling:write

Schedule the TrustPager Notetaker to join a booking.

2 parameters
Name In Type Required Description
booking_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduling/post-scheduling-bookings-booking-id-notetaker.md

PATCH /scheduling/event-types/:event_type_id Scopes: scheduling:write

Update a scheduled event type.

64 parameters
Name In Type Required Description
event_type_id path string yes UUID of the event type to update.
name body string no Display name of the event type, e.g. "Discovery Call".
description body string no Shown to the booker on the public booking page, under the name.
slug body string no URL segment for the public booking link. Auto-derived from name on create when omitted.
icon body string no Lucide icon name shown on the event-type card and calendar, e.g. "calendar", "phone", "video".
color body string no Theme colour key for the card and calendar entries, e.g. "primary".
sort_order body integer no Position in the event-type list. Lower sorts first.
is_active body boolean no When false the event type is archived: hidden from the list and not bookable. Default true.
is_bookable body boolean no When true the event type has a public booking page and appears on booking links. When false it is internal-only. Setting this true defaults location_type to "video".
is_meeting body boolean no Legacy flag, kept in sync with location_type automatically (video = true, phone/in_person = false). Set location_type instead.
default_duration_minutes body number no How long a booking of this type runs, in minutes (15, 30, 45, 60...). This is the field returned when you read the event type back, and it sets each booking's end time.
buffer_before_minutes body integer no Padding held free immediately before each booking, in minutes. Blocks slots that would start too close to an existing one.
buffer_after_minutes body integer no Padding held free immediately after each booking, in minutes.
min_notice_hours body integer no How far ahead a booker must book, in hours. A slot closer than this is not offered.
max_advance_days body integer no How far into the future slots are offered, in days.
slot_interval_minutes body integer no Spacing between offered start times, in minutes. 30 offers 9:00, 9:30, 10:00; 15 offers 9:00, 9:15, 9:30.
meeting_mode body string no "booking" = public 1:1 booking (one attendee per slot, the default). "group" = public Group Session (multiple attendees per slot up to capacity; set capacity + waitlist_enabled). "internal" = internal meeting, not publicly bookable.
email_config_id body string no Optional sending identity (email_config UUID) for this event type's booking notifications. Confirmation and all reminders send FROM this config's name/address/reply. Unset (or null to clear) = the workspace default sending address. Use for a workspace running multiple brands so one brand's bookings send from its own sender.
event_queue_id body string no Auto Queue (UUID) a confirmed booking enrols the booker into, for reminder or follow-up sequences. Enrolment is deduped: a booker who already has pending steps in that queue is skipped rather than enrolled twice, so rebooking does not double the reminders. Pass null to unlink.
enrollment_offset_minutes body integer no Shifts when the Auto Queue clock starts, relative to the booking start time, in minutes. Negative counts backwards from the meeting (-1440 anchors step delays to 24 hours before it). Default 0, meaning the meeting start.
booking_confirmation_message body string no Custom text shown on the confirmation screen after a booker confirms a slot.
booking_form_fields body array no Extra questions shown on the public and /embed booking page (paid checkout included), after name/email/phone. Answers are stored on the booking only (metadata.form_answers, returned by get_booking and shown on the booking in the Portal): never copied to the contact, the booking_created trigger payload or any email. Bookers send them as form_answers: { <key>: value }; a time field with allow_unknown may send { "unknown": true }. Send [] to remove all questions.
booking_notifications body object no Per-event booking emails and SMS, keyed by moment. The keys that actually send: "confirmation" (immediate, supports attach_ics), "pre_meeting" (offset_minutes BEFORE the start), "meeting_start", "late", "no_show", "rebooking" (offset_minutes AFTER now), and "rsvp_declined" (immediate, fires the moment the booker declines the calendar invite). Each key holds an array of { label, channels, email_subject, email_body, sms_body, recipients, chromeless, offset_minutes } entries. A "pre_meeting" entry also accepts only_if_not_accepted: true, which skips anyone who has already accepted their calendar invite so a firmer second nudge only reaches the people who have not. Recipients are "booker" (the person who booked), "booker_and_attendees" (the booker plus the attendees on the booking, but NOT the assigned host), "team" (only the users in booking_assigned_user_ids, i.e. the assigned host), or "all" (booker, attendees and assigned host). The assigned host is only notified by "team" or "all". Once a booker declines, they are dropped from every later send aimed at them (pre_meeting, meeting_start, late, no_show and rebooking), so the "rsvp_declined" offer is the last thing they hear unless they rebook; "team" and "all" entries still reach the host. Note "reminder", "cancellation" and "reschedule" were previously documented here but have never had a consumer — "pre_meeting" is the reminder.
booking_default_products body array no Products attached to the opportunity a booking creates, as [{ id, quantity }].
booking_default_tags body array no Tags applied to the created opportunity and contact, as [{ name, color }].
booking_assigned_user_ids body array no Host user UUIDs. Their connected calendars gate availability and one of them is assigned each booking. A bookable event type needs at least one.
booking_default_attendee_contact_ids body array no Contact UUIDs added as attendees on every booking of this type, on top of the booker.
booking_default_attendee_emails body array no Plain email addresses added as attendees on every booking of this type, for people with no contact record.
auto_record_enabled body boolean no When true, the TrustPager Notetaker joins each booking of this type to record and transcribe it. Video bookings only. Default false.
recurrence_rule body object no Recurrence for internal meeting series (meeting_mode "internal"): { frequency, interval, days_of_week, until }. Not used by public booking types.
embed_config body object no Embed theme for the /embed booking page (colours, card style, width). Keys: surface, pageBg, field, text, muted, border, primary, secondary, cardStyle ("card"|"flat"), showLogoHeader (bool), maxWidth ("sm"|"md"|"lg"|"full"), align ("left"|"center"). Only affects the embeddable variant, not the public booking link.
location_type body string no Where the booking happens. "video" = each booking gets its own Google Meet link when an assigned host has Google Calendar connected, otherwise it uses location_meeting_url. "phone" = you call the booker (or they call you). "in_person" = a physical address, which also requires in_person_direction. Determines the icon and location shown on the calendar and in booking confirmations. Bookable event types default to "video".
in_person_direction body string no Required when location_type is "in_person". "at_business" = "Customer comes to us" — the booker travels to you, so set location_address. "at_customer" = "We travel to customer" — the booking form asks the booker for their address, and a booking then requires booker_address.
location_address body string no Your physical address, shown to the booker before they confirm. Used when location_type is "in_person" and in_person_direction is "at_business".
location_instructions body string no Free-text arrival notes shown with the address, e.g. "Parking at rear, buzz unit 4".
location_meeting_url body string no A fixed video meeting link (your Zoom, Microsoft Teams or Google Meet room, must start with https://). Used for location_type "video" bookings when no assigned host has Google Calendar connected: the booker gets this link in their confirmation and reminders, and the TrustPager Notetaker joins it. Leave empty to rely on per-booking Google Meet links.
booking_ignore_all_day_events body boolean no When true (the default), all-day entries on a host's connected calendar (leave, reminders, OOO markers) do NOT block bookings. Set false to let all-day events count as busy. To block a specific date, use a date override in Availability instead.
booking_ignore_free_events body boolean no When true (the default), timed entries the host has marked "Show as Free" in Google do NOT block bookings, matching how Calendly and Cal.com read that flag. Marking an entry Free is a deliberate act meaning the time is still bookable, so it is honoured. Set false to make every calendar entry block, even one flagged Free, for a host who marks real commitments Free out of habit.
capacity body integer no Group Session only. Maximum number of independent bookings a single time slot accepts (e.g. 40 seats in a workshop). Each booker gets their own record and confirmation. Leave unset/null for a 1:1 event type (one booker per slot).
waitlist_enabled body boolean no Group Session only. When true, once a slot reaches capacity the public booking page offers a free waitlist (name + email, no booking) instead of blocking. Default false.
is_paid body boolean no When true, this event requires payment at the point of booking via the workspace's connected Stripe. The slot is held during checkout and the booking is only confirmed once payment succeeds. Requires price_amount and an active Stripe connection to publish. Default false.
price_amount body integer no Price in the smallest currency unit (e.g. cents): 9700 = $97.00. Required when is_paid is true. Each booker pays this amount for their own seat.
price_currency body string no ISO 4217 currency code for the price, e.g. "AUD", "USD". Defaults to AUD.
booking_abandoned_stage_id body string,null no Optional, paid events only. A stage (UUID) in booking_pipeline_id. When set, a booker whose checkout expires unpaid (the held slot is released ~35 min after they start checkout) is captured as a contact + opportunity in this stage, named with the slot they tried for and tagged "Checkout abandoned", and deal_created fires so a follow-up automation scoped to this stage can run. No calendar event or meeting link is created. Null (default) = abandoned checkouts are released silently.
allow_promotion_codes body boolean no Optional, paid events only. When true, the Stripe checkout shows an "Add promotion code" field. The codes and their limits (discount, total uses, expiry, first-time order only, which products/event types) are created in the workspace’s own Stripe; each promo-enabled event type sells a named Stripe Product so a coupon can be limited to it. The code used is saved on the booking (metadata.promotion) and the opportunity (metadata.promotion_code). Default false.
checkout_success_url body string,null no Optional, paid events only. An https URL on your own site that Stripe sends the booker to after a successful payment, instead of the TrustPager confirmation page. tp_booking=paid and tp_event=<event type slug> are added to it. Useful when the booking page is embedded on your site (/embed/book/<company>/<event>). Null (default) = TrustPager confirmation page.
checkout_cancel_url body string,null no Optional, paid events only. An https URL Stripe sends the booker to if they back out of payment. Null (default) = back to the booking page.
booking_lead_source_from_utm body boolean no Optional. When true and the booking link carries utm_source, a new opportunity’s lead source is that value instead of booking_lead_source. UTM / click-id attribution is saved on the contact and opportunity metadata either way. Default false.
cancellation_policy body object no Paid event types only (SR 0bc488ed). The booker’s self-service cancel/reschedule rules, shown before checkout and on the booking management link. Keys: full_refund_hours (cancel at least this many hours before the start for a full refund via the workspace’s connected Stripe), late_refund_percent (0-100, refund for a later cancel, default 0), reschedule_cutoff_hours (booker can reschedule only until this many hours before), max_reschedules (how many times the booker can reschedule; 0 = never online), note (extra text). A host cancelling from the Portal is never restricted and no automatic refund is issued there. Send null to remove.
booking_limits body object no Per-contact booking limits for this event type (SR 95e858d8). Keys: max_bookings + period_days (at most max_bookings bookings within any period_days stretch), min_gap_days (at least this many days between two bookings), match_by ("email" | "phone" | "email_or_phone", default email_or_phone), message (shown to the booker when blocked), alternative_url (optional link offered with the message), also_counts_event_type_ids (optional array of other event type ids in this workspace whose bookings also count towards this limit, e.g. one welcome offer across several event types). Enforced on the public booking page, paid checkout, client portal, voice agent and email/SMS assistant; a host booking from the Portal or API is not limited. Send null to remove.
booking_auto_create_deal body boolean no Whether a booking creates/links an opportunity at all. Default true.
booking_deal_behavior body string no How a booking relates to an existing opportunity for the booker. "create" (or "always_create") = always mint a new opportunity. "update_or_create" (or "find_or_create") = match the booker's existing opportunity first, create one only if none matches. "never_create" (or "match_only") = only ever attach to a matching opportunity, never create. Matched by email/phone.
booking_match_fields body array no Identity fields used to match an existing contact/opportunity, e.g. ["email"] or ["email","phone"]. "name" is never honoured (fuzzy-name-collision risk).
booking_match_pipeline_id body string no Restrict opportunity matching to one pipeline (UUID). Unset = search all pipelines.
booking_match_status_filter body string no "all" | "open" | "won" | "lost" - which opportunity statuses are eligible to match. Default "open".
booking_match_strategy body string no "most_recent" | "highest_value" | "oldest" - tie-breaker when multiple opportunities match. Default "most_recent".
booking_card_behavior body string no "ignore" | "find_or_create" | "always_create" - what to do with the pipeline card placement once the opportunity is resolved.
booking_pipeline_id body string no Destination pipeline (UUID) for the opportunity when one is created. Falls back to the workspace default pipeline.
booking_stage_id body string no Destination stage (UUID) within booking_pipeline_id. Falls back to the first stage.
booking_lead_source body string no lead_source recorded on the created opportunity. Defaults to "booking".
booking_deal_name_template body string no Opportunity-name template. Tokens: {event_type} {customer_name} (or {booker_name}) {date} {time}. Any unrecognised token falls back to the booker name.
booking_update_next_action body boolean no When true, a booking stamps the linked opportunity's Next Action with this meeting (name, date, time) so it shows on the opportunity card, and a reschedule moves it to the new time. Default false.
duration_minutes body number no Alias for default_duration_minutes. Accepted for backwards compatibility; default_duration_minutes wins if both are sent.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduling/patch-scheduling-event-types-event-type-id.md

PUT /scheduling/availability Scopes: scheduling:write

Update working hours and date overrides. Defaults to the company-wide schedule; pass event_type_id to set one event type's own schedule, or user_id to set one host's personal schedule. Over REST both PUT and PATCH are accepted on /scheduling/availability. Note this replaces the whole record for the target schedule, so read it first with get_scheduling_availability and send back every field you want to keep.

6 parameters
Name In Type Required Description
event_type_id body string no Set this event type's own schedule instead of the company default.
user_id body string no Set this host's personal schedule instead of the company default. Ignored if event_type_id is set.
weekly_hours body array no Array of { day: 0-6 (Sunday-Saturday), start: "HH:MM", end: "HH:MM" }.
timezone body string no IANA timezone, e.g. "Australia/Sydney".
date_overrides body array no Array of { date: "YYYY-MM-DD", type: "blocked"|"custom", start?, end?, reason? }.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduling/put-scheduling-availability.md

POST /scheduling/voice/:event_type_id/book Scopes: scheduling:write

AI voice agent — create a booking from voice flow.

4 parameters
Name In Type Required Description
event_type_id path string yes
slot body string yes
name body string no Name of the person the booking is for. Declared as `invitee_name` until 2026-09-06, which the handler never read, so a voice booking made by an agent following the published schema was saved with no name against it.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduling/post-scheduling-voice-event-type-id-book.md

POST /scheduling/voice/cancel-booking Scopes: scheduling:write

AI voice agent — cancel a booking keyed by caller phone.

1 parameter
Name In Type Required Description
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduling/post-scheduling-voice-cancel-booking.md

POST /scheduling/voice/reschedule-booking Scopes: scheduling:write

AI voice agent — reschedule a booking keyed by caller phone.

2 parameters
Name In Type Required Description
slot body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduling/post-scheduling-voice-reschedule-booking.md

POST /scheduling/voice/schedule-callback Scopes: scheduling:write

AI voice agent — schedule an automatic AI callback at the time the caller requested. Rings them back from the agent's outbound number; phone is auto-resolved from the call. Pass callback_day + callback_time + state; the server works out the absolute date in the caller's timezone, so never compute a date yourself.

4 parameters
Name In Type Required Description
callback_day body string no Day to call back: "today", "tomorrow", a weekday, or YYYY-MM-DD. Do NOT compute a date.
callback_time body string yes Clock time the caller wants, e.g. "3pm", "2:30pm", "14:00".
state body string no Caller state/territory for timezone (e.g. QLD, NSW, VIC).
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduling/post-scheduling-voice-schedule-callback.md

Email (23 endpoints)

Send and receive emails via TrustPager Mail or Gmail, manage threads, view logs, and configure email settings. Supports provider selection for Gmail integration.

📄 Full markdown: /api/email.md

POST /email/threads/mark-read Scopes: email:write

Mark multiple email threads as read.

2 parameters
Name In Type Required Description
thread_ids body array yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email/post-email-threads-mark-read.md

POST /email/configs Scopes: email-config:write

Create a new outbound email sender config.

23 parameters
Name In Type Required Description
from_email_handle body string yes Local-part of the sender address (before the @). Alone it composes <handle>@mail.trustpager.net.
from_email_domain body string no A verified sending domain for this workspace (see list_sending_domains). Composes from_email = <handle>@<domain>. Must be verified first.
from_name body string yes Sender name recipients see in the From field ("From Name" in the UI).
staff_email body string yes Where internal form/lead notifications are sent ("Staff Notification Email" in the UI).
config_name body string no Label for this sending address, e.g. "Lead Form" or "Company Default".
website_id body string no Scope this config to one website. Omit for a company-level (workspace-wide) config.
is_default body boolean no Make this the default config for its scope. Sends that do not name a config resolve to the default.
logo_url body string no Logo shown in this sender's branded emails. Leave unset to inherit the company logo.
primary_color body string no Brand primary colour as a hex value, e.g. "#3b82f6".
secondary_color body string no Brand secondary colour as a hex value, e.g. "#8b5cf6".
send_to_user body boolean no "Send to User" — email a confirmation to whoever submitted the form.
send_to_staff body boolean no "Send to Staff" — email a notification to staff_email on submission.
crm_auto_create_deal body boolean no Auto-create a CRM opportunity from each submission.
crm_pipeline_id body string no Workflow the auto-created opportunity lands in. Required when crm_auto_create_deal is true.
crm_initial_stage_id body string no Stage within that workflow. Required when crm_auto_create_deal is true.
gmail_sender_user_id body string no Team member whose connected Gmail sends for this config, when the rail is "gmail".
gmail_sender_alias body string no Gmail send-as alias to use. Must be unique across companies.
preferred_provider body string no Sending rail: "trustpager_mail" (your verified domain, never expires) or "gmail" (connected Team Gmail).
use_chromeless_confirmation body boolean no "On-brand confirmation email" — send the confirmation and staff notification on a clean branded card using your logo and colours, with no standard email frame. False keeps the default template.
crm_default_products body array no Product UUIDs attached to every opportunity auto-created from this form. Each must belong to this workspace.
signature body string no Signature appended to emails sent from this address.
reply_to_email body string,null no "Send replies to": the address a customer reaches when they press Reply on an email from this sender. Leave unset (or null) to keep replies coming back into the TrustPager Inbox.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email/post-email-configs.md

DELETE /email/configs/:config_id Scopes: email-config:delete

Delete an email config by UUID. Destructive.

2 parameters
Name In Type Required Description
config_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email/delete-email-configs-config-id.md

DELETE /email/threads/:thread_id Scopes: email:write

Delete an email thread by UUID.

2 parameters
Name In Type Required Description
thread_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email/delete-email-threads-thread-id.md

GET /email/capabilities Scopes: email:read

Get the workspace email send capability summary; connected accounts, daily limits, configured aliases.

📄 /api/email/get-email-capabilities.md

GET /email/configs/:config_id Scopes: email:read

Fetch a single email config by UUID.

1 parameter
Name In Type Required Description
config_id path string yes

📄 /api/email/get-email-configs-config-id.md

GET /email/threads/:thread_id Scopes: email:read

Fetch a single email thread by UUID: subject, participants, message count, read/status and linked entities. Returns thread metadata only; use list_email_thread_messages for the messages themselves.

1 parameter
Name In Type Required Description
thread_id path string yes

📄 /api/email/get-email-threads-thread-id.md

GET /email/blocked-recipients Scopes: email:read

List the email addresses this workspace can no longer send to. An address is blocked after it hard-bounces, files a spam complaint, or is set to do-not-contact, and from then on every email to it is skipped (shown as "Blocked" in email logs). Each row gives the reason, when it was blocked, can_reactivate, and marketing_opt_out (they also unsubscribed from marketing, which unblocking keeps). Pass query to search by part of an address. Use this when someone says they are not receiving emails.

1 parameter
Name In Type Required Description
query query string no Optional. Part of an email address to search for, e.g. "resolve.net.au". Omit to list every blocked address.

📄 /api/email/get-email-blocked-recipients.md

GET /email/logs Scopes: email:read

List recent outbound email send log entries. Includes message_id, the RFC Message-ID of the sent message, which can be passed back as in_reply_to on send_email / reply_to_email, and error_message, the reason a failed, bounced or held-back send did not go through.

1 parameter
Name In Type Required Description
limit query number no

📄 /api/email/get-email-logs.md

GET /email/threads/:thread_id/messages Scopes: email:read

List all messages within an email thread, inbound and outbound, in chronological order. Each message returns message_id (its RFC Message-ID), which is what in_reply_to on send_email / reply_to_email expects, plus in_reply_to and references_header on inbound messages and html_body on outbound ones.

1 parameter
Name In Type Required Description
thread_id path string yes

📄 /api/email/get-email-threads-thread-id-messages.md

GET /email/threads Scopes: email:read

List email threads in the workspace inbox. Filter by contact_id, customer_id, opportunity_id, unread, status, direction, participant, or free-text search on the subject.

12 parameters
Name In Type Required Description
limit query number no
after query string no
contact_id query string no
customer_id query string no
opportunity_id query string no
unread query boolean no true returns only unread threads; false only read ones.
is_read query boolean no Inverse of unread. Wins if both are sent.
status query string no
is_automated query boolean no
direction query string no Direction of the most recent message, inbound or outbound.
search query string no Free-text search on the thread subject.
participant query string no Exact email address of a thread participant.

📄 /api/email/get-email-threads.md

GET /email/sending-domains Scopes: email:read

List the workspace's bring-your-own sending domains and their verification status. A verified domain lets you send from your own address (e.g. support@yourcompany.com) instead of @mail.trustpager.net.

📄 /api/email/get-email-sending-domains.md

POST /email/sending-domains Scopes: email-config:write

Register your own domain for sending. Returns two DNS records (a DKIM TXT and a Return-Path CNAME) to add at your DNS host; no SPF or MX change, your existing inbox keeps receiving. After adding them, call verify_sending_domain. Once verified, set it as the sending address via update_email_config { from_email_domain }.

2 parameters
Name In Type Required Description
domain body string yes Bare domain, e.g. "yourcompany.com". No scheme, no @, no path.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email/post-email-sending-domains.md

DELETE /email/sending-domains/:domain_id Scopes: email-config:delete

Remove a sending domain. Refused if it is currently your sending address (change update_email_config { from_email_domain } first). Otherwise removes it from TrustPager Mail entirely.

2 parameters
Name In Type Required Description
domain_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email/delete-email-sending-domains-domain-id.md

POST /email/reply Scopes: email:send

Reply to an existing email thread, preserving threading. Costs credits. Supports the same attachments as send_email, inline or by file_id.

11 parameters
Name In Type Required Description
thread_id body string yes
to_email body string yes
to_name body string no
reply_html body string yes
cc body array no
mode body string no Sending identity. Omit or "default" sends from the workspace default outbound address. "personal" sends as a specific user via their connected Gmail; pair with sender_user_id and/or from_email.
sender_user_id body string no With mode "personal", the UUID of the workspace user to send as. Must belong to this company and have Gmail connected.
from_email body string no With mode "personal", the exact address or alias to send from; the system resolves the matching user.
in_reply_to body string no Optional. RFC Message-ID of the specific earlier message being replied to, with or without angle brackets; read it off message_id from list_email_thread_messages. Only needed to reply to a message other than the most recent one on the thread: In-Reply-To and References are otherwise resolved from thread_id automatically.
attachments body array no File attachments. Max 20 items. Supported on TrustPager Mail (<=7MB total) and Gmail (<=10MB total); a larger set is refused before it is sent or queued for approval, so share a link to big files instead. Each item is ONE of two shapes: { file_id } to attach a file already stored in this workspace (name, type and bytes are read server-side), or { filename, mime_type, content } to supply base64 content inline. If a file_id cannot be read, the whole send is refused rather than delivering an email that promises a document it does not carry. Attaching by file_id also requires the files:read scope.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email/post-email-reply.md

POST /email/save-attachment Scopes: email:readfiles:write

Save an attachment from a received email onto a CRM record. The file is attached to the contact, customer or opportunity you name: PDFs land in the documents array on that record, everything else in the files array. Works for email received via Gmail and for kept TrustPager Mail attachments (those with a storage_path), up to 5MB. Calling it twice for the same attachment and record is a no-op success, not an error.

5 parameters
Name In Type Required Description
inbound_email_id body string yes UUID of the received email holding the attachment. Get ids from list_inbound_emails or the thread messages. Works for email received via Gmail, and for email received at a TrustPager Mail address whose attachments were kept (each kept file carries a storage_path on its attachments entry).
attachment_name body string yes Exact filename of the attachment as it appears on the email, e.g. "invoice_406154.pdf". Must match one of the names listed in the attachments array on that email. Limit 5MB.
entity_type body string yes Which kind of CRM record to attach the file to. "deal" is an opportunity. "customer" is a company/account.
entity_id body string yes UUID of the contact, customer or opportunity to attach the file to. Must belong to this workspace.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email/post-email-save-attachment.md

POST /email/send Scopes: email:send

Send a new email. Defaults to the workspace default outbound address; pass mode:"personal" with sender_user_id and/or from_email to send as a specific user via their connected Gmail. For replies use reply_to_email, or pass thread_id here; either way the reply threads correctly in the recipient mail client without you supplying in_reply_to. Costs credits. Pass opportunity_id (canonical) or deal_id (legacy alias) to auto-link the resulting thread to a deal. Set raw_html: true to send a fully-designed HTML email verbatim with no TrustPager header/footer chrome (for pixel-perfect branded sends). Attachments are supported on both the TrustPager Mail rail (<=7MB total) and the Gmail rail (<=10MB total), either as inline base64 or by file_id for a file already stored in this workspace. cc and bcc must be valid addresses: a bad entry or an over-limit attachment set is refused with a 400 before anything is sent or queued for approval.

21 parameters
Name In Type Required Description
to_email body string yes
to_name body string no
subject body string yes
html_body body string yes
cc body array no Visible carbon-copy recipients. A single comma-separated string is also accepted.
bcc body array no Blind carbon-copy recipients; the visible recipients never see them, and they are not added to the thread participants. A single comma-separated string is also accepted.
contact_id body string no
customer_id body string no Link to an account/company UUID. Stamps email_logs.customer_id and attributes the send on that account.
mode body string no Sending identity. Omit or "default" sends from the workspace default outbound address. "personal" sends as a specific user via their connected Gmail; pair with sender_user_id and/or from_email.
sender_user_id body string no With mode "personal", the UUID of the workspace user to send as. Must belong to this company and have Gmail connected. Provide sender_user_id and/or from_email.
from_email body string no With mode "personal", the exact address or alias to send from; the system resolves the matching user. Use GET /email/capabilities to see available senders and aliases.
email_config_id body string no Optional: send using a specific workspace email configuration instead of the default.
opportunity_id body string no Link to an opportunity UUID. Stamps email_logs.deal_id and registers the thread under the opportunity activity feed.
deal_id body string no Legacy alias for opportunity_id.
raw_html body boolean no When true, send html_body verbatim with no header/footer chrome for fully-designed branded emails. Default false wraps the body in the standard branded template.
thread_id body string no Continue an existing email_threads conversation (personal/Gmail rail only). The matching Gmail thread is resolved so the send threads correctly for the recipient. Use list_email_threads to find it.
in_reply_to body string no Optional. RFC Message-ID of the specific earlier message being replied to, with or without angle brackets; read it off message_id from list_email_thread_messages. Only needed to reply to a message other than the most recent one: when thread_id is given, In-Reply-To and References are resolved from the thread automatically so the message threads in the recipient mail client.
email_type body string no Tag persisted on email_logs.email_type so automated workflows can filter their own sends, e.g. "nurture". Personal/Gmail rail only; the TrustPager Mail rail always records "api_send". Defaults to "api_send".
metadata body object no Arbitrary JSON persisted on email_logs.template_data.metadata for later querying, e.g. {"campaign_id":"..."}. Personal/Gmail rail only. Keep values to JSON scalars and objects.
attachments body array no File attachments. Max 20 items. Supported on TrustPager Mail (<=7MB total) and Gmail (<=10MB total); a larger set is refused before it is sent or queued for approval, so share a link to big files instead. Each item is ONE of two shapes: { file_id } to attach a file already stored in this workspace (name, type and bytes are read server-side), or { filename, mime_type, content } to supply base64 content inline. If a file_id cannot be read, the whole send is refused rather than delivering an email that promises a document it does not carry. Attaching by file_id also requires the files:read scope.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email/post-email-send.md

POST /email/send-with-file Scopes: files:reademail:send

Send an email with a workspace CRM file attached. Costs credits. Works on both TrustPager Mail (<=7MB) and Gmail (<=10MB) rails; a larger file is refused before anything is sent or queued for approval. Pass opportunity_id (canonical) or deal_id (legacy alias) to auto-link the resulting thread to a deal.

15 parameters
Name In Type Required Description
file_id body string yes
to_email body string yes
to_name body string no
subject body string yes
html_body body string yes
cc body array no Visible carbon-copy recipients. A single comma-separated string is also accepted.
bcc body array no Blind carbon-copy recipients; the visible recipients never see them. A single comma-separated string is also accepted.
contact_id body string no
mode body string no Sending identity. Omit or "default" sends from the workspace default outbound address. "personal" sends as a specific user via their connected Gmail; pair with sender_user_id and/or from_email.
sender_user_id body string no With mode "personal", the UUID of the workspace user to send as. Must belong to this company and have Gmail connected.
from_email body string no With mode "personal", the exact address or alias to send from; the system resolves the matching user.
email_config_id body string no Optional: send using a specific workspace email configuration instead of the default.
opportunity_id body string no Link to an opportunity UUID. Stamps email_logs.deal_id and registers the thread under the opportunity activity feed.
deal_id body string no Legacy alias for opportunity_id.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email/post-email-send-with-file.md

POST /email/blocked-recipients/reactivate Scopes: email-config:write

Unblock an email address so this workspace can email it again. Clears the TrustPager Mail block and this workspace's own block. Only do this when the address is known to work: a hard bounce means the receiving server said the mailbox does not exist, so unblocking a dead address just bounces again. Marketing unsubscribes are never cleared: someone who unsubscribed and then bounced gets transactional email again after an unblock, but not marketing (marketing_opt_out_kept: true). A spam complaint cannot be lifted on the TrustPager Mail side (provider_cleared comes back false).

2 parameters
Name In Type Required Description
email body string yes The blocked address to unblock, exactly as list_blocked_recipients returned it.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email/post-email-blocked-recipients-reactivate.md

PATCH /email/configs/:config_id Scopes: email-config:write

Update an email config. To send from your own verified domain, pass from_email_handle (local-part) + from_email_domain (a verified sending domain). Set preferred_provider to choose the workspace rail.

24 parameters
Name In Type Required Description
config_id path string yes
from_name body string no Sender name recipients see in the From field ("From Name" in the UI).
staff_email body string no Where internal form/lead notifications are sent ("Staff Notification Email" in the UI).
from_email_handle body string no Local-part of the sender address (before the @). With no from_email_domain it composes <handle>@mail.trustpager.net.
from_email_domain body string no A verified sending domain for this workspace (see list_sending_domains). Composes from_email = <handle>@<domain>, e.g. support@yourcompany.com. Must be verified first.
config_name body string no Label for this sending address, e.g. "Lead Form" or "Company Default".
website_id body string no Scope this config to one website. Omit for a company-level (workspace-wide) config.
is_default body boolean no Make this the default config for its scope. Sends that do not name a config resolve to the default.
logo_url body string no Logo shown in this sender's branded emails. Pass null to clear it and inherit the company logo.
primary_color body string no Brand primary colour as a hex value, e.g. "#3b82f6".
secondary_color body string no Brand secondary colour as a hex value, e.g. "#8b5cf6".
send_to_user body boolean no "Send to User" — email a confirmation to whoever submitted the form.
send_to_staff body boolean no "Send to Staff" — email a notification to staff_email on submission.
crm_auto_create_deal body boolean no Auto-create a CRM opportunity from each submission.
crm_pipeline_id body string no Workflow the auto-created opportunity lands in. Required when crm_auto_create_deal is true.
crm_initial_stage_id body string no Stage within that workflow. Required when crm_auto_create_deal is true.
gmail_sender_user_id body string no Team member whose connected Gmail sends for this config, when the rail is "gmail".
gmail_sender_alias body string no Gmail send-as alias to use. Must be unique across companies.
preferred_provider body string no Default Workspace Email Address rail: "trustpager_mail" (send via your verified domain, never expires) or "gmail" (send via connected Team Gmail). Governs general/automation email, form invitations, and signing invitations alike.
use_chromeless_confirmation body boolean no "On-brand confirmation email" — send the confirmation and staff notification on a clean branded card using your logo and colours, with no standard email frame. False keeps the default template.
crm_default_products body array no Product UUIDs attached to every opportunity auto-created from this form. Each must belong to this workspace.
signature body string no Signature appended to emails sent from this address.
reply_to_email body string,null no "Send replies to": the address a customer reaches when they press Reply on an email from this sender. Leave unset (or null) to keep replies coming back into the TrustPager Inbox.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email/patch-email-configs-config-id.md

PATCH /email/threads/:thread_id Scopes: email:write

Update an email thread. Mutate read/status, or link the thread to an opportunity / contact / customer so it appears on the deal activity feed and in list_email_threads?opportunity_id=. Useful when send_email was called without an opportunity_id and you need to attach the thread after the fact.

7 parameters
Name In Type Required Description
thread_id path string yes
is_read body boolean no
status body string no active | archived | spam
link_opportunity_id body string no Link this thread to an opportunity UUID. Idempotent; safe to call repeatedly.
link_contact_id body string no Link this thread to a contact UUID. Idempotent.
link_customer_id body string no Link this thread to a customer UUID. Idempotent.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email/patch-email-threads-thread-id.md

POST /email/sending-domains/:domain_id/verify Scopes: email-config:write

Re-check DNS for a sending domain and return its current verification state. Call after adding the DNS records. DNS can take a few minutes to propagate. status becomes "verified" once both DKIM and Return-Path resolve.

2 parameters
Name In Type Required Description
domain_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email/post-email-sending-domains-domain-id-verify.md

Email Templates (2 endpoints)

Reusable email templates for the workspace.

📄 Full markdown: /api/email-templates.md

POST /email-templates/preview Scopes: company:read

Preview the rendered HTML for a channel email template, applying optional workspace and per-template chrome overrides. Free read.

3 parameters
Name In Type Required Description
channel body string yes Channel template key
workspace_settings body object no
per_template_settings body object no

📄 /api/email-templates/post-email-templates-preview.md

Email Campaigns (22 endpoints)

Create and send bulk broadcast email campaigns to segmented audiences. Supports tag-based and pipeline-based audience filters, delivery tracking (opens, clicks, bounces), and automatic unsubscribe management.

📄 Full markdown: /api/email-campaigns.md

DELETE /email-campaigns/:campaign_id/sends/:send_id Scopes: email-blasts:delete

Discard a run that has NOT sent yet, removing it from the Runs list entirely. Only a run with status 'scheduled' or 'paused' can be discarded this way. To halt a run that is already sending, use stop_email_blast_run instead: a part-sent run is kept, not deleted, because it records who was emailed.

3 parameters
Name In Type Required Description
campaign_id path string yes
send_id path string yes The scheduled run to cancel, from list_blast_runs.
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email-campaigns/delete-email-campaigns-campaign-id-sends-send-id.md

POST /email-campaigns Scopes: email-blasts:write

Create an email marketing campaign.

15 parameters
Name In Type Required Description
name body string yes
subject body string no
body_html body string no
segment_filter body object no Audience for the blast: which contacts it resolves at send time. Shape is the shared opportunity-contact filter: { pipeline_id, stage_ids[], status('open'|'won'|'lost'|'all'), tags[{name}], stale_days, assigned_to_ids[], contact_type, has_email, has_phone, metadata_date_within }. A deliverable email address is always required and unsubscribed contacts are always excluded, so neither needs setting. Omit for an empty audience, which you can fill in later before scheduling a run.
folder body string no Optional folder name to file this blast under (must be an existing folder from list_email_blast_folders, or a new name).
design_mode body string no How the blast body is edited: "text" (rich-text editor, plain announcements), "html" (raw HTML source with live preview — designed table-based emails should use this so the editor never reflows the markup), or "guided" (Visual mode: edit only the designer-marked editable regions of a locked HTML design). "visual" is the legacy alias for "text".
intro_text body string no Short lead-in shown above the main body, e.g. a one-line summary of the announcement.
cta_text body string no Label on the call-to-action button, e.g. "Read the update". Needs cta_url to render.
cta_url body string no Where the call-to-action button goes. Needs cta_text to render.
show_reply_button body boolean no Whether the email offers a reply button. Turn it off for announcements you do not want replies to.
footer_config body object no Footer overrides for this blast: which of the workspace footer blocks to show and what they say. Omit to use the workspace default footer.
email_config_id body string no UUID of the sending identity this blast goes out from. Get real ids from list_email_configs. Omit to use the workspace default.
attachments body array no Files attached to every email in the blast. Replaces the whole list, so read the blast and merge first.
scheduled_for body string no When to send, as an ISO timestamp. Leave unset to keep the blast as a draft you send manually.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email-campaigns/post-email-campaigns.md

POST /email-campaigns/folders Scopes: email-blasts:write

Create an email blast folder.

2 parameters
Name In Type Required Description
name body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email-campaigns/post-email-campaigns-folders.md

DELETE /email-campaigns/:campaign_id Scopes: email-blasts:delete

Delete an email campaign.

2 parameters
Name In Type Required Description
campaign_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email-campaigns/delete-email-campaigns-campaign-id.md

DELETE /email-campaigns/folders/:folder_id Scopes: email-blasts:delete

Delete an email blast folder. Blasts filed under it are moved to unfiled (not deleted).

2 parameters
Name In Type Required Description
folder_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email-campaigns/delete-email-campaigns-folders-folder-id.md

POST /email-campaigns/:campaign_id/sends/:send_id/duplicate Scopes: email-blasts:write

Copy a run into a new PAUSED run, offset by offset_days (default 7). This is how a send series is built: schedule the invite, duplicate it twice, then rewrite each copy as the reminder and the last call. The copy is independent from the moment it exists, so editing it never touches the original. It is always created paused, because a duplicate exists to be edited, and is armed with resume_email_blast_run once it says what it should.

4 parameters
Name In Type Required Description
campaign_id path string yes
send_id path string yes The run to copy.
offset_days body number no Days after the source run's send time to place the copy. Defaults to 7. Negative values place it earlier.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email-campaigns/post-email-campaigns-campaign-id-sends-send-id-duplicate.md

GET /email-campaigns/:campaign_id/recipients Scopes: email-blasts:read

List recipients of an email blast with delivery status. Add ?send_id=… to scope to one run.

2 parameters
Name In Type Required Description
campaign_id path string yes
send_id query string no

📄 /api/email-campaigns/get-email-campaigns-campaign-id-recipients.md

GET /email-campaigns Scopes: email-blasts:read

List email marketing campaigns (blasts). Pass folder to list only the blasts filed under that folder name.

3 parameters
Name In Type Required Description
limit query number no
status query string no
folder query string no Filter to blasts filed under this folder name.

📄 /api/email-campaigns/get-email-campaigns.md

POST /email-campaigns/:campaign_id/sends/:send_id/pause Scopes: email-blasts:write

Pause a scheduled run so it cannot send. The run keeps its content, its audience and its intended send time, and stays editable; it simply is not queued, so nothing can fire it. Use this to park a send while people review or approve it, instead of cancelling and rebuilding it later. Arm it again with resume_email_blast_run.

3 parameters
Name In Type Required Description
campaign_id path string yes
send_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email-campaigns/post-email-campaigns-campaign-id-sends-send-id-pause.md

POST /email-campaigns/:campaign_id/sends/:send_id/resume Scopes: email-blasts:write

Arm a paused run so it will send at its scheduled time. If that time has already passed while the run was paused, it is clamped to now and sends promptly rather than never. Costs credits per recipient when it fires. Returns the run with the instant it will actually send.

3 parameters
Name In Type Required Description
campaign_id path string yes
send_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email-campaigns/post-email-campaigns-campaign-id-sends-send-id-resume.md

POST /email-campaigns/:campaign_id/schedule Scopes: email-blasts:write

Schedule a run of an email blast for a future time, or immediately. Creates a scheduled run that appears in the blast's Runs list and snapshots the blast's current subject, body, CTA and audience filter, so editing the blast afterwards does not change what this run sends. Omit scheduled_for (or pass a past time) to send as soon as possible. The audience is re-resolved when the run fires, so contacts added between scheduling and sending are included. Costs credits per recipient at fire time. Edit a scheduled run with update_email_blast_run, or cancel it with cancel_email_blast_run.

4 parameters
Name In Type Required Description
campaign_id path string yes
scheduled_for body string no ISO 8601 instant to send at, e.g. 2026-08-07T01:00:00Z. This is an absolute moment, not a local wall-clock time, so convert from the workspace timezone first. Omit to send now.
paused body boolean no Create the run PAUSED: it keeps its content, audience and intended send time but is not queued, so it cannot fire until someone arms it with resume_email_blast_run. Use this when a send needs sign-off before it goes out. Defaults to false.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email-campaigns/post-email-campaigns-campaign-id-schedule.md

POST /email-campaigns/:campaign_id/send Scopes: email-blasts:write

Trigger sending of an email campaign to its saved audience. Costs credits per recipient. Optionally override the content for this send (subject, body_html, intro_text, cta_text, cta_url, show_reply_button) — the overrides are written onto the blast before it fires. The audience is NOT overridable here; it stays set on the blast. Duplicate guard: if the content is byte-identical to the last successful send, the request is refused with HTTP 409 { duplicate: true, last_sent_at } and nothing is sent — pass confirm_duplicate: true to override. To preview a blast in a real inbox without sending to the audience, use send_email_blast_test.

9 parameters
Name In Type Required Description
campaign_id path string yes
subject body string no Optional. Override the blast subject for this send.
body_html body string no Optional. Override the blast HTML body for this send.
intro_text body string no Optional. Override the intro text for this send.
cta_text body string no Optional. Override the CTA button text.
cta_url body string no Optional. Override the CTA button URL.
show_reply_button body boolean no Optional. Toggle the reply button for this send.
confirm_duplicate body boolean no Set true to override the duplicate-content guard and re-send content identical to a previous send. Omit for the default safe behaviour.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email-campaigns/post-email-campaigns-campaign-id-send.md

POST /email-campaigns/:campaign_id/send-test Scopes: email-blasts:write

Send a test copy of a blast to up to 5 specific email addresses. Renders exactly like a real send but resolves no audience, creates no contacts, and does not affect stats. Pass send_id to test one specific RUN's copy instead of the blast's current copy, which is what you want before arming a run in a series, since each run carries its own subject, body, attachments and sender.

4 parameters
Name In Type Required Description
campaign_id path string yes
recipients body array yes Up to 5 email addresses to receive the test.
send_id body string no Optional. Test this run's exact content rather than the blast's current content.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email-campaigns/post-email-campaigns-campaign-id-send-test.md

POST /email-campaigns/:campaign_id/sends/:send_id/stop Scopes: email-blasts:write

STOP a run that is already sending. This is the handbrake for a blast going to the wrong people: it halts the send at the next batch boundary, so it stops within 250 recipients rather than instantly, and the run then records what actually went out ('stopped after 120 of 754'). Recipients who had not been reached are left untouched and are never sent to. Emails already delivered cannot be recalled. Also works on a scheduled or paused run that has not started. Unlike cancel_email_blast_run this KEEPS the run in the Runs list, because a part-sent run is a record of who was emailed.

3 parameters
Name In Type Required Description
campaign_id path string yes
send_id path string yes The run to stop, from list_blast_runs. Use the one whose status is "sending".
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email-campaigns/post-email-campaigns-campaign-id-sends-send-id-stop.md

PATCH /email-campaigns/:campaign_id Scopes: email-blasts:write

Update an email blast: its content, its audience (segment_filter), its design mode, or which folder it is filed under.

16 parameters
Name In Type Required Description
campaign_id path string yes
name body string no
subject body string no
body_html body string no
design_mode body string no How the blast body is edited: "text" (rich-text editor, plain announcements), "html" (raw HTML source with live preview — designed table-based emails should use this so the editor never reflows the markup), or "guided" (Visual mode: edit only the designer-marked editable regions of a locked HTML design). "visual" is the legacy alias for "text".
folder body string no Folder name to file this blast under (pass "" or null to unfile).
segment_filter body object no Audience for the blast: which contacts it resolves at send time. Shape is the shared opportunity-contact filter: { pipeline_id, stage_ids[], status('open'|'won'|'lost'|'all'), tags[{name}], stale_days, assigned_to_ids[], contact_type, has_email, has_phone, metadata_date_within }. A deliverable email address is always required and unsubscribed contacts are always excluded, so neither needs setting. Omit for an empty audience, which you can fill in later before scheduling a run.
intro_text body string no Short lead-in shown above the main body, e.g. a one-line summary of the announcement.
cta_text body string no Label on the call-to-action button, e.g. "Read the update". Needs cta_url to render.
cta_url body string no Where the call-to-action button goes. Needs cta_text to render.
show_reply_button body boolean no Whether the email offers a reply button. Turn it off for announcements you do not want replies to.
footer_config body object no Footer overrides for this blast: which of the workspace footer blocks to show and what they say. Omit to use the workspace default footer.
email_config_id body string no UUID of the sending identity this blast goes out from. Get real ids from list_email_configs. Omit to use the workspace default.
attachments body array no Files attached to every email in the blast. Replaces the whole list, so read the blast and merge first.
scheduled_for body string no When to send, as an ISO timestamp. Leave unset to keep the blast as a draft you send manually.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email-campaigns/patch-email-campaigns-campaign-id.md

PATCH /email-campaigns/folders/:folder_id Scopes: email-blasts:write

Rename an email blast folder. Blasts filed under it move to the new name.

3 parameters
Name In Type Required Description
folder_id path string yes
name body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email-campaigns/patch-email-campaigns-folders-folder-id.md

PATCH /email-campaigns/:campaign_id/sends/:send_id Scopes: email-blasts:write

Edit a SCHEDULED run of an email blast before it sends: its content (subject, intro_text, body_html, cta_text, cta_url), its audience (segment_filter), or its send time (scheduled_for). Each scheduled run carries its own copy, which is how a series of sends can say something different each time (invite, then reminder, then last call) without touching the blast itself. Only runs with status 'scheduled' can be edited; a run that has already sent is a permanent record.

10 parameters
Name In Type Required Description
campaign_id path string yes
send_id path string yes The run to edit, from list_blast_runs.
subject body string no Subject line for this run only.
intro_text body string no Intro text for this run only.
body_html body string no HTML body for this run only.
cta_text body string no CTA button text for this run only.
cta_url body string no CTA button URL for this run only.
segment_filter body object no Audience filter for this run only, same shape as the blast's segment_filter.
scheduled_for body string no New ISO 8601 send instant. Also moves the queued send.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/email-campaigns/patch-email-campaigns-campaign-id-sends-send-id.md

SMS (6 endpoints)

Send and receive SMS messages. View conversations and message history.

📄 Full markdown: /api/sms.md

POST /sms/conversations/mark-read Scopes: sms:write

Mark multiple SMS conversations as read at once.

2 parameters
Name In Type Required Description
conversation_ids body array yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/sms/post-sms-conversations-mark-read.md

GET /sms/conversations/:conversation_id Scopes: sms:read

Fetch a single SMS conversation by UUID, including the full message history.

1 parameter
Name In Type Required Description
conversation_id path string yes

📄 /api/sms/get-sms-conversations-conversation-id.md

GET /sms/conversations Scopes: sms:read

List SMS conversations in the workspace inbox. Filter by contact_id, opportunity_id, external_phone_number, phone_number_id, status, or unread. external_phone_number is the most reliable way to find someone's SMS history, because it also finds conversations that were never linked to a contact record.

8 parameters
Name In Type Required Description
limit query number no
after query string no
contact_id query string no Only conversations linked to this contact UUID.
opportunity_id query string no Only conversations linked to this opportunity UUID.
external_phone_number query string no The other party's number in E.164, e.g. +61431377068. Exact match.
phone_number_id query string no Only conversations on this workspace phone number UUID.
status query string no Conversation status, e.g. active or archived.
unread query boolean no true returns only conversations with unread messages; false only those fully read.

📄 /api/sms/get-sms-conversations.md

POST /sms/send Scopes: sms:send

Send an SMS message from a workspace phone number. `phone_number_id` is OPTIONAL and usually best left out: the sending number is resolved per recipient, preferring a number in the recipient's own country, then the workspace default line, then the oldest active number. This works whether the workspace has one SMS number or several. Only pass `phone_number_id` to force a specific line, and call `list_phone_numbers` first if you do rather than inventing a UUID. Keep under 160 chars where possible. Costs credits.

6 parameters
Name In Type Required Description
to_number body string yes Recipient phone in E.164, e.g. +61431377068
message body string yes SMS body. Keep concise — ideally under 160 chars.
phone_number_id body string no OPTIONAL. UUID of the workspace phone number to send from. Omit it and the line is chosen for you, preferring one in the recipient's own country. Pass it only to force a specific line; get the UUID from list_phone_numbers, never invent one.
contact_id body string no
deal_id body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/sms/post-sms-send.md

PATCH /sms/conversations/:conversation_id Scopes: sms:write

Update an SMS conversation, e.g. mark it read.

5 parameters
Name In Type Required Description
conversation_id path string yes
is_read body boolean no true marks the conversation read (clears unread_count).
unread_count body number no
status body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/sms/patch-sms-conversations-conversation-id.md

WhatsApp (14 endpoints)

Send and receive WhatsApp messages. Pair a phone number, view 1:1 conversations and group conversations, send messages, and manage CRM links.

📄 Full markdown: /api/whatsapp.md

POST /whatsapp/disconnect Scopes: whatsapp:write

Disconnect the workspace's WhatsApp pairing. Deletes the Evolution instance and clears the pairing row. Use to re-pair against a different phone.

1 parameter
Name In Type Required Description
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/whatsapp/post-whatsapp-disconnect.md

GET /whatsapp/groups/:group_id Scopes: whatsapp:read

Fetch a single WhatsApp group by UUID, including member list snapshot.

1 parameter
Name In Type Required Description
group_id path string yes

📄 /api/whatsapp/get-whatsapp-groups-group-id.md

GET /whatsapp/pairing Scopes: whatsapp:read

Get the workspace's WhatsApp pairing status (connected / awaiting_qr / disconnected / etc.) and paired phone identity.

📄 /api/whatsapp/get-whatsapp-pairing.md

POST /whatsapp/groups/:group_id/link Scopes: whatsapp:write

Link a WhatsApp group to a deal or customer so its messages appear in the deal/customer timeline.

4 parameters
Name In Type Required Description
group_id path string yes
deal_id body string no
customer_id body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/whatsapp/post-whatsapp-groups-group-id-link.md

GET /whatsapp/conversations Scopes: whatsapp:read

List WhatsApp 1:1 conversations in the workspace inbox. Supports filtering by external_phone_number and unread.

4 parameters
Name In Type Required Description
limit query number no
after query string no
external_phone_number query string no
unread query boolean no

📄 /api/whatsapp/get-whatsapp-conversations.md

GET /whatsapp/groups Scopes: whatsapp:read

List WhatsApp groups the workspace's paired account is in. Filter by linked_deal_id, linked_customer_id, or unlinked.

5 parameters
Name In Type Required Description
limit query number no
after query string no
linked_deal_id query string no
linked_customer_id query string no
linked query boolean no true = only linked groups, false = only unlinked

📄 /api/whatsapp/get-whatsapp-groups.md

POST /whatsapp/send Scopes: whatsapp:send

Send a WhatsApp message to a contact. Provide either to_phone (E.164) or conversation_id. Costs credits.

7 parameters
Name In Type Required Description
to_phone body string no Recipient phone in E.164, e.g. +61431377068
body body string yes WhatsApp message body (text only in v1).
conversation_id body string no Existing conversation UUID. Mutually exclusive with to_phone.
contact_id body string no
deal_id body string no
customer_id body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/whatsapp/post-whatsapp-send.md

POST /whatsapp/groups/send Scopes: whatsapp:send

Send a WhatsApp message into a registered group. Group must be visible to the workspace (paired account is a member). Costs credits.

3 parameters
Name In Type Required Description
group_id body string yes TrustPager whatsapp_groups UUID (not the WhatsApp JID).
body body string yes Message body (text only in v1).
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/whatsapp/post-whatsapp-groups-send.md

POST /whatsapp/pair Scopes: whatsapp:write

Begin pairing a WhatsApp account to the workspace. Returns a QR code data URL the user scans with their phone (Settings → Linked Devices → Link a Device). The pairing becomes "connected" automatically once they scan.

1 parameter
Name In Type Required Description
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/whatsapp/post-whatsapp-pair.md

POST /whatsapp/groups/:group_id/unlink Scopes: whatsapp:write

Remove a WhatsApp group's deal/customer link. The group remains visible in the inbox.

2 parameters
Name In Type Required Description
group_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/whatsapp/post-whatsapp-groups-group-id-unlink.md

PATCH /whatsapp/conversations/:conversation_id Scopes: whatsapp:write

Update a WhatsApp conversation (e.g. mark read, archive).

4 parameters
Name In Type Required Description
conversation_id path string yes
unread_count body number no
status body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/whatsapp/patch-whatsapp-conversations-conversation-id.md

Chat (13 endpoints)

Team chat rooms, members, and messages. Workspace-scoped in Phase 0; portal and cross-workspace rooms in later phases.

📄 Full markdown: /api/chat.md

POST /chat/rooms/:room_id/members Scopes: chat:write

Add a workspace user to a chat room. In Phase 0 the user must belong to this workspace; cross-workspace adds (which need a consent handshake) come later.

4 parameters
Name In Type Required Description
room_id path string yes
user_id body string yes UUID of the workspace user to add.
member_role body string no owner or member (default member).
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/chat/post-chat-rooms-room-id-members.md

POST /chat/rooms Scopes: chat:write

Create a chat room. The API key owner joins as owner; pass member_user_ids to add teammates (they must belong to this workspace). Use room_type "dm" for 1:1, "group" for a named group, "self" for a private notes-to-self room, "workspace" for a team-wide channel.

4 parameters
Name In Type Required Description
room_type body string no dm, group, self, or workspace. Defaults to group.
title body string no Room title (optional for dm/self).
member_user_ids body array no UUIDs of workspace users to add as members. Non-workspace UUIDs are skipped and returned in skipped_member_ids.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/chat/post-chat-rooms.md

DELETE /chat/rooms/:room_id/messages/:message_id Scopes: chat:write

Soft-delete one of your own chat messages (sets deleted_at; history preserved). Only the original sender can delete.

3 parameters
Name In Type Required Description
room_id path string yes
message_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/chat/delete-chat-rooms-room-id-messages-message-id.md

PATCH /chat/rooms/:room_id/messages/:message_id Scopes: chat:write

Edit one of your own chat messages (sets edited_at). Only the original sender can edit. Pass the new `body` and/or `content`.

5 parameters
Name In Type Required Description
room_id path string yes
message_id path string yes
body body string no New message text.
content body object no New structured payload.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/chat/patch-chat-rooms-room-id-messages-message-id.md

GET /chat/rooms/:room_id Scopes: chat:read

Fetch a single chat room by UUID.

1 parameter
Name In Type Required Description
room_id path string yes UUID of the room.

📄 /api/chat/get-chat-rooms-room-id.md

POST /chat/rooms/:room_id/invite Scopes: chat:write

Invite someone to a chat room by email — the cross-workspace path. Existing TrustPager users are added immediately; a new email is provisioned an account and added so they land in the room when they accept. Optionally set member_role "host" so they can invite their own people.

4 parameters
Name In Type Required Description
room_id path string yes
email body string yes Email address of the person to invite.
member_role body string no member (default) or host (can invite others + manage the room).
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/chat/post-chat-rooms-room-id-invite.md

GET /chat/rooms/:room_id/messages Scopes: chat:read

List messages in a chat room, newest first, paginated. Soft-deleted messages are excluded.

3 parameters
Name In Type Required Description
room_id path string yes
limit query number no Max messages (1-100, default 25).
after query string no Pagination cursor.

📄 /api/chat/get-chat-rooms-room-id-messages.md

GET /chat/rooms/:room_id/members Scopes: chat:read

List the members of a chat room, with each member's user profile (name, email, avatar).

1 parameter
Name In Type Required Description
room_id path string yes

📄 /api/chat/get-chat-rooms-room-id-members.md

GET /chat/rooms Scopes: chat:read

List chat rooms in the workspace. A room is a set of participants (a DM, a group, a self-note, or a workspace channel). Filter by room_type or is_archived. Newest activity first.

4 parameters
Name In Type Required Description
room_type query string no Filter by type: dm, group, self, or workspace.
is_archived query string no "true" for archived only, "all" for both. Defaults to active only.
limit query number no Max rooms to return (1-100, default 25).
after query string no Pagination cursor.

📄 /api/chat/get-chat-rooms.md

POST /chat/rooms/:room_id/read Scopes: chat:write

Mark a chat room as read up to now for the API key owner, updating their last_read_at. You can only mark your own read state.

2 parameters
Name In Type Required Description
room_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/chat/post-chat-rooms-room-id-read.md

DELETE /chat/rooms/:room_id/members/:user_id Scopes: chat:write

Remove a member from a chat room (marks them removed; message history is preserved).

3 parameters
Name In Type Required Description
room_id path string yes
user_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/chat/delete-chat-rooms-room-id-members-user-id.md

POST /chat/rooms/:room_id/messages Scopes: chat:write

Post a message to a chat room. When sent via API/MCP the message is attributed as an AI actor on the workspace's behalf (actor_type defaults to "ai"); pass actor_type "human" only when relaying a real person. Provide `body` text and/or structured `content`.

7 parameters
Name In Type Required Description
room_id path string yes
body body string yes The message text.
content body object no Optional structured payload (attachments, rich content).
actor_type body string no human, ai, or system. Defaults to ai for API/MCP sends.
sender_label body string no Display label for an AI/system sender (e.g. "TrustPager AI").
reply_to_message_id body string no UUID of the message being replied to.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/chat/post-chat-rooms-room-id-messages.md

PATCH /chat/rooms/:room_id Scopes: chat:write

Update a chat room title or archive it. Pass only the fields to change.

4 parameters
Name In Type Required Description
room_id path string yes
title body string no
is_archived body boolean no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/chat/patch-chat-rooms-room-id.md

Phone (17 endpoints)

Manage phone numbers and call logs. Search for available numbers to purchase.

📄 Full markdown: /api/phone.md

POST /phone/numbers/buy Scopes: phone:write

Buy a phone number found with search_phone_numbers. Costs credits and recurring fees. Australian numbers need an approved regulatory bundle for their number type (mobile, toll-free, local): the workspace's approved bundle for that type is used automatically, so bundle_id is only needed if more than one approved bundle covers the same type. If none does, create one with create_phone_bundle and submit_phone_bundle first. A US number whose search result shows address_requirements other than "none" needs address_id (from list_phone_addresses, created with create_phone_address).

5 parameters
Name In Type Required Description
phone_number body string yes The number to buy in E.164 format, exactly as search_phone_numbers returned it.
friendly_name body string no A label for the number, shown in the workspace.
bundle_id body string no Optional. The id (from list_phone_bundles) of the approved regulatory bundle to buy under. Leave out to use the approved bundle matching the number type.
address_id body string no Optional. The id (from list_phone_addresses) of the address to register the number to. Only needed when the number's address_requirements is not "none".
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/phone/post-phone-numbers-buy.md

POST /phone/addresses Scopes: phone:write

Create a regulatory address.

9 parameters
Name In Type Required Description
customer_name body string yes Legal name of the person or business the address belongs to. Must match the regulatory paperwork.
street body string yes Street address line 1.
street_secondary body string no Street address line 2 (unit, level, suite).
city body string yes City or suburb.
region body string yes State, province or territory, e.g. "VIC".
postal_code body string yes Postcode or ZIP.
iso_country body string yes Two-letter ISO country code, e.g. "AU".
emergency_enabled body boolean no Register this address for emergency calling. Only valid where the number type supports it.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/phone/post-phone-addresses.md

POST /phone/bundles Scopes: phone:write

Create a regulatory bundle: the compliance paperwork a country requires before a workspace may hold a number of that type. Australian mobile numbers cannot be bought until an approved bundle exists, so this comes first, then submit_phone_bundle, then the regulator reviews it, then buy_phone_number. WHICH FIELDS: friendly_name and email are always required, and email must be a monitored mailbox because approvals and rejection notices go there. Beyond that it depends on end_user_type: a "business" bundle needs business_name, business_registration_number, business_address and business_phone; an "individual" bundle needs individual_first_name, individual_last_name, individual_dob, individual_address and individual_phone. business_name must be the exact legal entity that owns business_registration_number, since a mismatch between the two is a standard rejection and costs another review cycle. Do NOT send a regulation_sid: the right regulation is looked up for you from country_code, number_type and end_user_type. Check progress afterwards with get_phone_bundle, which also returns the live regulator state.

16 parameters
Name In Type Required Description
friendly_name body string yes Label for this bundle in the phone settings list, e.g. "Acme Pty Ltd - Mobile Bundle".
email body string yes Where the regulator sends approval updates for this bundle. A real monitored mailbox: approval can take days and a rejection notice arrives here.
country_code body string no Two-letter ISO country code the bundle is for. Defaults to "AU".
number_type body string no Which class of number the bundle covers. Requirements differ per class. Defaults to "mobile".
end_user_type body string no Whether the number is held by a business or a person. Decides which of the two field sets below is required. Defaults to "business".
address_sid body string no Identifier of a regulatory address to record against the bundle. Create one first with create_phone_address.
business_name body string no REQUIRED when end_user_type is "business". The exact legal entity name tied to business_registration_number. A mismatch between the two is a standard rejection.
business_registration_number body string no REQUIRED when end_user_type is "business". ABN or the local equivalent.
business_address body object no REQUIRED when end_user_type is "business". Shape: { street, city, region, postal_code }.
business_phone body string no REQUIRED when end_user_type is "business". Contact number for the business.
individual_first_name body string no REQUIRED when end_user_type is "individual".
individual_last_name body string no REQUIRED when end_user_type is "individual".
individual_dob body string no REQUIRED when end_user_type is "individual". Date of birth as YYYY-MM-DD.
individual_address body object no REQUIRED when end_user_type is "individual". Shape: { street, street_secondary?, city, region, postal_code }.
individual_phone body string no REQUIRED when end_user_type is "individual".
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/phone/post-phone-bundles.md

GET /phone/a2p Scopes: phone:read

Check where this workspace's US SMS registration is up to. US numbers cannot send SMS at all until this clears, and clearance is a carrier review that takes roughly two weeks and cannot be hurried, so this is the endpoint that answers "why is their US texting not working yet". Returns null when the workspace has never started registration. Otherwise returns status (draft | pending_profile | pending_brand | approved | failed) plus the per-stage detail behind it: profile_status (business profile), trust_product_status (A2P profile: draft | pending-review | in-review | twilio-approved | twilio-rejected) and brand_status say which review the sequence is actually waiting on, campaign_status covers the final step, status_message carries the reason on a failure, and mock tells you whether this is a free test registration (which can never send) rather than a real one. This is a cached read: it reports what the last update recorded and makes no provider call, so call sync_a2p_status first if you need the live position.

📄 /api/phone/get-phone-a2p.md

GET /phone/numbers/:phone_number_id Scopes: phone:read

Fetch a phone number by UUID. Includes after_hours_enabled/after_hours_config and the derived active_inbound_mode field — see list_phone_numbers.

1 parameter
Name In Type Required Description
phone_number_id path string yes

📄 /api/phone/get-phone-numbers-phone-number-id.md

GET /phone/call-logs Scopes: calls:read

List phone-call records (inbound + outbound). USE THIS for any "recent calls", "phone call history", "missed calls", "who called", or similar phone-specific query. Returns the raw call log (timestamps, duration, direction, phone number, recording url, voice-agent linkage) — richer than list_activities for calls. Prefer this over list_activities when the user asks specifically about phone activity.

6 parameters
Name In Type Required Description
limit query number no Max call logs to return.
after query string no Pagination cursor from a previous response.
direction query string no Filter to calls placed TO the workspace ("inbound") or FROM it ("outbound"). These are the only two stored values.
status query string no Filter by call status, e.g. "completed", "no-answer", "busy", "failed".
phone_number_id query string no Filter to calls on one workspace phone number (UUID).
contact_id query string no Filter to calls linked to a specific contact (UUID).

📄 /api/phone/get-phone-call-logs.md

GET /phone/numbers Scopes: phone:read

List workspace phone numbers. Each number includes after_hours_enabled/after_hours_config (a second, clock-gated inbound mode that overrides the primary routing fields outside its schedule) and active_inbound_mode — a derived field answering "what actually answers a call on this number right now" (agent / app / forward / none), already accounting for after-hours. Read active_inbound_mode instead of reasoning about the primary fields plus after_hours_config yourself.

📄 /api/phone/get-phone-numbers.md

POST /phone/a2p/register Scopes: phone:write

Register this workspace to send SMS from US numbers (US A2P 10DLC). Lodge this the moment a client buys a US number: carrier review takes roughly two weeks, is outside our control, and their US numbers cannot send a single message until it clears, so any delay in lodging is added straight onto that clock. COST AND RISK, read before calling without mock: a real registration charges 35,000 credits once and then 17,500 credits per month for as long as the campaign is live, and it creates records at the carrier registry that we cannot take back. Worse, a rejected registration cannot be edited, it has to be rebuilt from scratch, which restarts the two-week wait. The most common rejection is business_name not exactly matching the legal entity that owns business_registration_number, so check that against an approved regulatory bundle rather than the workspace name. ALWAYS run once with mock true first: that proves the whole sequence accepts your payload for free, with no carrier vetting and nothing billed. Registration is asynchronous and staged, so a successful call returns a pending status, not a finished one. Follow it with get_a2p_registration to watch progress and sync_a2p_status to advance it. Re-calling this for a workspace whose last attempt FAILED rebuilds from scratch; re-calling it mid-flight resumes in place. Switching the mock flag always rebuilds, because a test registration can never become a real one. A 402 means the payload was fine and the workspace simply needs a credit top-up.

6 parameters
Name In Type Required Description
business body object yes The legal business being registered. Required keys: business_name (the EXACT legal entity name tied to business_registration_number — a mismatch is the single most common rejection, and it costs another full review cycle), business_registration_number (ABN, EIN or equivalent), rep_first_name, rep_last_name, rep_email, rep_phone (E.164), and address { customer_name, street, street_secondary?, city, region, postal_code, iso_country }. Optional keys: business_type, business_industry, business_regions_of_operation, company_type ("private" | "public" | "non_profit" | "government", defaults to "private"), website_url, social_media_profile_url, rep_business_title, rep_job_position, notifications_email. If the workspace already has an approved regulatory bundle, take business_name, business_registration_number and the address from its end_user_data rather than from the workspace name — the workspace is often a trading name, not the legal entity.
campaign body object yes How the workspace will actually use SMS. Required keys: privacy_policy_url and terms_and_conditions_url (both must be live, publicly reachable pages — they are read during review). Strongly recommended: message_samples (array of real message bodies you will send), message_flow (how people opt in, in plain words), description, opt_out_keywords / opt_out_message, opt_in_keywords / opt_in_message, help_keywords / help_message, has_embedded_links, has_embedded_phone, usecase. Thin or invented samples are a common rejection reason, so send the wording you really use.
phone_number_id body string no UUID of the workspace phone number to attach to the campaign sender pool once it goes live. Must be a US, SMS-capable number in this workspace; ignored if it belongs to another workspace. Omit to register the business without attaching a number yet.
mock body boolean no Run the whole sequence against a free test registration instead of a real one: no fees, no carrier vetting, auto-approves, and it can never send. ALWAYS do this first to prove the payload is accepted before spending anything. A test registration can never become a real one — switching the flag rebuilds from scratch. Defaults to false.
low_volume body boolean no Register as a sole trader / low-volume sender instead of a standard business. Cheaper and faster to vet, but caps throughput hard. Only correct for genuinely low-volume senders. Defaults to false.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/phone/post-phone-a2p-register.md

POST /phone/numbers/:phone_number_id/release Scopes: phone:write

Release a phone number (stops billing).

2 parameters
Name In Type Required Description
phone_number_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/phone/post-phone-numbers-phone-number-id-release.md

POST /phone/numbers/search Scopes: phone:read

Search for available phone numbers to purchase. country_code is an ISO 3166-1 alpha-2 code (e.g. "US", "AU"). By default the search returns SMS-capable numbers of the right type for the country (US/CA return Local numbers, AU/GB/NZ return Mobile); pass number_type to force a specific type. Each result includes address_requirements ("none" | "any" | "local" | "foreign") indicating whether an address must be supplied to buy that number.

6 parameters
Name In Type Required Description
country_code body string no ISO 3166-1 alpha-2 country code, e.g. "US" or "AU". Defaults to US.
area_code body string no Area/region code to filter by (e.g. "415" for US, "2" for AU).
contains body string no Digit sequence the number must contain.
number_type body string no Force a specific number type. Omit to let the search pick the SMS-capable type for the country.
sms_enabled body boolean no Only return SMS-capable numbers. Defaults to true.
voice_enabled body boolean no Only return voice-capable numbers.

📄 /api/phone/post-phone-numbers-search.md

POST /phone/bundles/:bundle_id/submit Scopes: phone:write

Submit a draft regulatory bundle for review. Review takes 1 to 3 business days and a rejected bundle cannot be edited, only rebuilt, so this refuses (400) an Australian mobile business bundle that has no business document, or whose document holds no file. Upload it first with upload_phone_bundle_document.

2 parameters
Name In Type Required Description
bundle_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/phone/post-phone-bundles-bundle-id-submit.md

POST /phone/a2p/sync Scopes: phone:write

Refresh this workspace's US SMS registration against the carrier registry and push it to its next stage if it is ready. Safe to call repeatedly — it does nothing when nothing has changed. Call this when get_a2p_registration looks stuck, or before reporting progress to a client, so you are quoting the live position rather than the last recorded one. It needs the write scope because it does more than read: once a review passes it performs the next step of the sequence for real, which on the final stage means creating the campaign, attaching the number and starting the monthly charge. Returns the same shape as register_a2p: the current status, the stage it reached, and an error if a stage failed. A registration the registry has REJECTED comes back as a normal 200 carrying status "failed" plus the reason, not as an error, because the refresh itself worked and that status is the answer. Always read status rather than assuming a 200 means healthy.

1 parameter
Name In Type Required Description
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/phone/post-phone-a2p-sync.md

PATCH /phone/numbers/:phone_number_id Scopes: phone:write

Update a phone number: friendly_name, voice-agent routing, the outbound in-app-dialler allowlist, and the "When Busy" no-answer fallback. IMPORTANT: binding an agent here (inbound_voice_agent_id / outbound_voice_agent_id) controls INBOUND answering (who picks up calls TO this number) and is one-agent-per-number. OUTBOUND dialling does NOT use this binding; set the caller-ID number on the agent via create_voice_agent_outbound_config (default_phone_number_id), which lets one number serve many agents. outbound_caller_user_ids restricts which workspace users may present THIS number as caller ID from the in-app dialler ("Call from TrustPager"): pass an array of user IDs to restrict to exactly those people, or [] / null to let anyone with calling permission dial from it (the default). Every ID must be a member of this workspace. Toll-free numbers (1800/1300) cannot dial out at all. WHEN BUSY (Answer-in-TrustPager numbers only): when the number rings staff browsers ("Answer in TrustPager") and nobody answers within ring_timeout_seconds, no_answer_action decides what happens next instead of dropping the call: "voicemail" (record a message; needs voicemail_greeting_text or voicemail_greeting_audio_url, else a default greeting is spoken; the recording is transcribed and attached to the caller's contact), "forward" (ring no_answer_forward_number), "agent" (hand to the AI voice agent no_answer_agent_config_id), or "none"/null to disable the fallback. no_answer_agent_config_id must be a voice agent in this workspace. AFTER HOURS: after_hours_enabled + after_hours_config define a SECOND inbound mode that overrides everything above (inbound_voice_agent_id, inbound ring routing, transfer_number) whenever the current moment falls OUTSIDE the configured schedule. Setting after_hours_enabled true requires after_hours_config to be provided (either in this same request or already stored from a prior one). Use get_phone_number's active_inbound_mode field to check what a number actually does right now instead of reasoning about business hours yourself.

17 parameters
Name In Type Required Description
phone_number_id path string yes
friendly_name body string no
inbound_voice_agent_id body string no
outbound_voice_agent_id body string no
outbound_caller_user_ids body array no User IDs allowed to dial out from this number via the in-app dialler. [] or null = anyone with calling permission.
ring_timeout_seconds body integer no How long (5-120s) to ring before the When Busy fallback fires: staff browsers (default 25) or the forward number (default 30).
screen_forwarded_calls body boolean no Forwarded calls only: the person answering hears "Press 1 to take this call" and must press 1 to be connected. A mobile voicemail that answers first presses nothing, so the When Busy fallback (no_answer_action) gets the call instead. Needs a no_answer_action. Default false.
no_answer_action body string no What to do when no one answers an Answer-in-TrustPager call. null/"none" = drop (no fallback).
no_answer_forward_number body string no E.164 number to ring when no_answer_action = "forward".
transfer_number body string no E.164 number a voice agent transfers the caller to when it hands off to a human.
no_answer_agent_config_id body string no Voice agent (this workspace) to hand the call to when no_answer_action = "agent".
voicemail_greeting_text body string no Spoken greeting for no_answer_action = "voicemail" (max 1000 chars). Ignored if voicemail_greeting_audio_url is set.
voicemail_greeting_audio_url body string no http(s) URL of a pre-generated greeting audio to play for voicemail (e.g. from ai_generate_speech). Takes priority over voicemail_greeting_text.
allowed_inbound_country_list body array no Restrict which countries may call this number IN. Array of ISO 3166-1 alpha-2 codes (e.g. ["AU"] for Australia only). Calls from any country not listed are rejected before reaching the agent (spam/fraud control). [] or null = allow all countries (the default).
after_hours_enabled body boolean no Turn the after-hours mode on/off. Turning it on requires after_hours_config to exist (in this request or already stored). Turning it off keeps the stored config so re-enabling restores the same schedule.
after_hours_config body object no The after-hours schedule and routing target. Shape: { schedule: { days: array of "mon".."sun" (at least one), start_hour: integer 0-23, end_hour: integer 1-24 and greater than start_hour, timezone: IANA name e.g. "Australia/Brisbane" (default "Australia/Sydney"), holidays: optional array of "YYYY-MM-DD" strings }, mode: "agent" | "forward" | "app" | "none", plus exactly the one field that mode needs — voice_agent_config_id (mode "agent", must be a voice agent in this workspace), transfer_number (mode "forward", E.164), or ring_user_ids (mode "app", non-empty array of workspace member user ids) }. Required together with after_hours_enabled the first time it is turned on.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/phone/patch-phone-numbers-phone-number-id.md

POST /phone/bundles/:bundle_id/document Scopes: phone:write

Upload the business document to a DRAFT regulatory bundle. Order: create_phone_address, create_phone_bundle, this, then submit_phone_bundle. An Australian mobile business bundle is always rejected without it, and this step is also what attaches the address to the bundle, so skipping it fails review on two counts. For an Australian business send the ASIC company extract as ONE PDF (merge any extra pages in): upload once per bundle, and only while it is a draft. Fails with a 400, and nothing is recorded, if the file does not reach the provider, so a 201 means the file is really there (check get_phone_bundle: the supporting_document item shows mime_type).

5 parameters
Name In Type Required Description
bundle_id path string yes UUID of the draft bundle, from create_phone_bundle or list_phone_bundles.
file_base64 body string yes The business document, base64-encoded with no "data:" prefix. For an Australian business this is the company extract from ASIC. Merge every page you want reviewed into ONE PDF: a bundle holds one document. 5 MB maximum.
file_name body string no File name shown to the reviewer, e.g. "Acme Pty Ltd - ASIC company extract.pdf". Defaults to "document.pdf".
file_type body string no MIME type of the file. Defaults to "application/pdf". PNG and JPEG images are also accepted.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/phone/post-phone-bundles-bundle-id-document.md

Agents (39 endpoints)

Manage AI voice agents and text agents under the unified /agents endpoint. Use ?type=voice or ?type=text to filter listings. The type field is required in the create body.

📄 Full markdown: /api/voice-agents.md

POST /voice-agents/blocklist Scopes: voice-agents:write

Stop placing AI voice calls to a number. Takes effect on the next call attempt, across every dispatch path. Idempotent: re-adding an existing number updates its reason.

3 parameters
Name In Type Required Description
phone body string yes The number to stop calling, ideally E.164 (e.g. "+61431377068"). AU local form ("0431377068") also matches at dial time. Adding a number already on the list updates its reason instead of creating a duplicate.
reason body string no Why this number is on the list, shown in the workspace. In-call opt-outs record "Caller asked to be removed during a call".
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/post-voice-agents-blocklist.md

POST /voice-agents/:agent_id/tests/add-template-tests Scopes: voice-agents:write

Add the ready-made chat tests from the template this agent was built from. Agents created from a template get them automatically; use this for older agents or to restore deleted ones. Tests already there are left alone.

2 parameters
Name In Type Required Description
agent_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/post-voice-agents-agent-id-tests-add-template-tests.md

POST /voice-agents/:agent_id/apply-template Scopes: voice-agents:write

Stamp a profile/source template onto a target voice agent.

4 parameters
Name In Type Required Description
agent_id path string yes
source_id body string no Optional. Another voice agent in this workspace to copy the voice and call settings from. Omit to apply the standard TrustPager profile.
fields body array no Optional. Only copy these settings, e.g. ["voice_id"]. Valid: voice_id, voice_model, voice_speed, voice_temperature, volume, language, fallback_voice_ids, responsiveness, interruption_sensitivity, enable_backchannel, backchannel_frequency, backchannel_words, ambient_sound, ambient_sound_volume, normalize_for_speech, max_call_duration_ms, end_call_after_silence_ms, reminder_trigger_ms, reminder_max_count, data_storage_setting, model_choice.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/post-voice-agents-agent-id-apply-template.md

POST /voice-agents Scopes: voice-agents:write

Create a voice agent.

37 parameters
Name In Type Required Description
agent_name body string yes The agent's display name. Also the name stamped on every call it reports back, which is what call logs and transcript titles show, so renaming needs a republish.
language body string no Language — primary spoken language and accent, as a BCP-47 tag (e.g. "en-AU", "en-US"). Defaults to en-AU.
voice_id body string no The voice this agent speaks in. Accepts a TrustPager voice UUID from list_voices, or a catalogue voice id. New agents come up on the standard TrustPager receptionist voice, so leave it unset unless you want a different one.
voice_model body string no Speech model used to render the voice. Leave unset to take the default that pairs with the chosen voice; pin it only if you have a reason to.
voice_speed body number no Speed — how fast the agent speaks. 0.5 (slow) to 2.0 (fast), default 1.
voice_temperature body number no Stability — voice consistency. 0 to 2; lower is more varied, higher is more stable. Default 1.32.
volume body number no Volume — output level. 0 to 2, default 1.44.
ambient_sound body string no Background Sound played under the call. One of coffee-shop, convention-hall, summer-outdoor, mountain-outdoor, static-noise, call-center. Pass null or "" for none (the default).
ambient_sound_volume body number no Ambient Volume — loudness of the Background Sound. 0 to 2, default 0.48. Only matters when ambient_sound is set.
backchannel_frequency body number no Backchannel Frequency — how often the agent interjects with "mm-hm", "right" and similar. 0 to 1. Only applies when enable_backchannel is true.
backchannel_words body array no The specific words the agent uses to backchannel, e.g. ["mm-hm", "right", "I see"]. Replaces the whole list. Only applies when enable_backchannel is true.
enable_backchannel body boolean no Whether the agent makes short acknowledgement noises while the caller is talking. Default false.
conversation_flow_config body object no ADVANCED. The full conversation flow (nodes, edges, prompts and model choice) that drives what the agent says. Normally built in the Voice Agent Builder rather than written here; replacing it wholesale replaces the agent's entire script.
data_storage_setting body string no What the call recording and transcript retain. "everything" stores audio and transcripts (the default), "no_pii" masks personal data, "nothing" keeps no recording or transcript. Pass "" to fall back to the workspace default.
default_dynamic_variables body object no Fallback values for the {{variables}} used in the agent's prompts, applied when a call does not supply them. A flat object of string keys to string values.
end_call_after_silence_ms body number no Silence Timeout — end the call after this much silence, in milliseconds. Default 61000 (about one minute).
fallback_voice_ids body array no Voices to fall back to if the primary voice_id is unavailable mid-call. Accepts TrustPager voice UUIDs or catalogue ids from list_voices. Replaces the whole list. New agents come up with one standard fallback already set.
interruption_sensitivity body number no Interruption Sensitivity — how easily the caller can talk over the agent. 0 to 1, higher is easier. Default 0.85.
max_call_duration_ms body number no Max Call Duration — hard limit before the call ends, in milliseconds. Default 359000 (about six minutes).
post_call_analysis_data body array no The structured fields extracted from the transcript after each call. Array of { type, name, description, choices? }. Every agent ships with the standard four (call_outcome, and the fields the Post-Call Briefing leads with); add bespoke fields on top rather than replacing them, because the briefing renderer reads the standard ones by name.
post_call_analysis_model body string no Model that runs the post-call extraction. Internal config, left at the default unless you have measured a reason to change it.
reminder_max_count body number no How many times the agent re-prompts a silent caller before giving up. Pass null for no limit.
reminder_trigger_ms body number no Reminder After — prompt the caller after this much silence, in milliseconds. Shorter than end_call_after_silence_ms, or it never fires.
response_engine body object no ADVANCED. Binds the agent to its conversation flow on the provider. Set for you at provision time; overwriting it by hand can leave the agent unable to answer.
responsiveness body number no Responsiveness — how quickly the agent starts replying. 0 to 1, higher is faster. Default 1.
normalize_for_speech body boolean no Whether numbers, dates and abbreviations are rewritten so they are read aloud naturally ("$1,200" as "twelve hundred dollars"). Default false.
voicemail_option body object no Native voicemail handling. On answering-machine detection the provider runs this action deterministically (no AI free-wheeling). Pass null to disable. Shape: { action: { type: "static_text"|"prompt"|"hangup", text?: string }, detection_prompt?: string }.
begin_message_delay_ms body number no Milliseconds (0-5000) to wait before the agent speaks its first message on a call it initiates, so it does not talk over a voicemail greeting or beep.
briefing_enabled body boolean no Send post-call briefings. When on, the team gets a next-action-first summary email after every real conversation with this agent.
briefing_email body string no Briefing goes to — the address that receives the post-call briefing. This is the person who should action calls, not the caller.
briefing_sms_number body string no Text hot leads to — where the escalation text goes, in international format (e.g. +61431377068). Leave blank to create a follow-up task only, with no text. Only used when briefing_escalate_on_positive is true.
briefing_escalate_on_positive body boolean no Escalate hot leads. When a caller is ready to proceed or asks for a callback, also create a follow-up task, and text briefing_sms_number if one is set.
create_contact_for_unknown_callers body boolean no Create contacts for new callers. When on, an inbound call from a number that matches no contact creates one (phone = caller ID, name from the post-call field named in unknown_caller_name_field), so the call, its transcript and call automations all land on that contact. Only for real conversations: inbound, caller ID present, not voicemail, 10 seconds or longer. Default false.
unknown_caller_name_field body string no The post-call analysis field (a post_call_analysis_data name) that holds the name the caller gave, used when create_contact_for_unknown_callers creates a contact. Default "caller_name". A contact with no name found is saved as "Unknown caller".
max_concurrent_calls body number no Maximum calls this agent may have in progress at the same time (1-100, default 5). A hard ceiling on simultaneous outward calls: calls beyond it are refused, and calls queued by an Auto Queue wait and dial later instead. Raise it deliberately, it is the control that stops one bulk enrolment placing hundreds of paid calls at once. Does not limit inbound calls.
agent_role body string no Role: an optional job for this agent that adds a check before every outbound call it places (the first call and every follow-up). "appointment_setter": if the person already has an upcoming booking, the call is skipped. "review_requester": if the person has already left a review or was sent a review request in the last 30 days, the call is skipped. "feedback_agent": if the person has unhappy feedback not yet resolved, or gave feedback within the workspace cooldown (feedback settings, default 365 days), the call is skipped. In every case their remaining queued calls from this agent are cancelled and a note is added to their timeline. A role never adds anyone to a call list; your automations still decide who is called. Pass "" (or null) for no role, the default, which behaves exactly as before.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/post-voice-agents.md

POST /voice-agents/:agent_id/outbound-config Scopes: voice-agents:write

Enable OUTBOUND dialling for a voice agent. Set default_phone_number_id to an imported number: that number becomes the caller ID and is all outbound needs (each call passes the agent itself, so no number-level binding is required and ONE number can be the default for many agents). Do NOT use update_phone_number to bind the number to the agent for outbound; that binding is inbound-only. Per-agent, no website required.

13 parameters
Name In Type Required Description
agent_id path string yes
default_phone_number_id body string no
website_id body string no
allowed_country_codes body array no Country codes this agent may dial, e.g. ["AU"]. Empty or omitted allows all.
rate_limit_max_requests body number no Max calls ONE visitor (per IP / browser fingerprint / email / phone) may start per window. Default 2.
rate_limit_window_hours body number no Length of the per-visitor rate-limit window, in hours. Default 24.
global_rate_limit_max_requests body number no Max calls ALL visitors combined may start per window, i.e. the spend ceiling. Default 50.
global_rate_limit_window_hours body number no Length of the global rate-limit window, in hours. Default 24.
crm_auto_create_deal body boolean no Whether a caller who identifies themselves also gets an opportunity created, not just a contact.
crm_pipeline_id body string no Pipeline the auto-created opportunity lands in. Required for crm_auto_create_deal to do anything.
crm_initial_stage_id body string no Stage within that pipeline the opportunity starts at.
crm_default_products body array no Product ids attached to the auto-created opportunity.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/post-voice-agents-agent-id-outbound-config.md

POST /voice-agents/:agent_id/tests Scopes: voice-agents:write

Save a chat test for a voice agent: a pretend caller and the checks the conversation must pass. Tools that save or send are replaced with stand-ins during tests unless use_real_tools is true.

9 parameters
Name In Type Required Description
agent_id path string yes
name body string yes Short label, e.g. "Unhappy customer is not asked for a review".
caller_prompt body string yes Who the pretend caller is, what they want and how they behave, written to them in the second person. E.g. "You are Jordan, a customer whose leaking tap was fixed last week. It went well and you are happy to leave a review. Give your email as demo+review@mail.trustpager.net if asked."
success_criteria body array yes Plain-English checks the conversation must meet to pass, e.g. ["The agent asks how the job went", "The agent offers to email a review link"]. 1 to 10.
dynamic_variables body object no Values the agent sees for its {{variables}} during the test, e.g. {"first_name": "Jordan"}. Merged over the defaults set on the agent.
use_real_tools body boolean no Default false: every tool that saves or sends is replaced with a "done, nothing saved" stand-in so the test touches nothing real. True lets the tools run for real (bookings, emails, CRM writes happen).
tool_mocks body array no Optional: what a named tool should answer during the test (JSON text), e.g. to test what the agent does when no times are free.
sort_order body number no Position in the list, lowest first.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/post-voice-agents-agent-id-tests.md

POST /voice-agents/:agent_id/website-config Scopes: voice-agents:write

Bind a voice agent to a website, so it can take in-browser "talk to the AI" calls from that site. Optionally set the rate limits and CRM routing at the same time.

11 parameters
Name In Type Required Description
agent_id path string yes
website_id body string yes
rate_limit_max_requests body number no Max calls ONE visitor (per IP / browser fingerprint / email / phone) may start per window. Default 2.
rate_limit_window_hours body number no Length of the per-visitor rate-limit window, in hours. Default 24.
global_rate_limit_max_requests body number no Max calls ALL visitors combined may start per window, i.e. the spend ceiling. Default 50.
global_rate_limit_window_hours body number no Length of the global rate-limit window, in hours. Default 24.
crm_auto_create_deal body boolean no Whether a caller who identifies themselves also gets an opportunity created, not just a contact.
crm_pipeline_id body string no Pipeline the auto-created opportunity lands in. Required for crm_auto_create_deal to do anything.
crm_initial_stage_id body string no Stage within that pipeline the opportunity starts at.
crm_default_products body array no Product ids attached to the auto-created opportunity.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/post-voice-agents-agent-id-website-config.md

DELETE /voice-agents/:agent_id Scopes: voice-agents:delete

Delete a voice agent.

2 parameters
Name In Type Required Description
agent_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/delete-voice-agents-agent-id.md

DELETE /voice-agents/:agent_id/outbound-config/:config_id Scopes: voice-agents:delete

Delete an outbound-call config.

3 parameters
Name In Type Required Description
agent_id path string yes
config_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/delete-voice-agents-agent-id-outbound-config-config-id.md

DELETE /voice-agents/:agent_id/tests/:test_id Scopes: voice-agents:delete

Delete a saved voice agent chat test. Past run results keep their copy.

3 parameters
Name In Type Required Description
agent_id path string yes
test_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/delete-voice-agents-agent-id-tests-test-id.md

DELETE /voice-agents/:agent_id/website-config/:config_id Scopes: voice-agents:delete

Delete a voice-agent website config.

3 parameters
Name In Type Required Description
agent_id path string yes
config_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/delete-voice-agents-agent-id-website-config-config-id.md

DELETE /voice-agents/:agent_id/toolkit-access Scopes: voice-agents:write

Disable agent toolkit access: deactivate the agent dedicated toolkit key. In-call booking stops working until re-enabled.

2 parameters
Name In Type Required Description
agent_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/delete-voice-agents-agent-id-toolkit-access.md

POST /voice-agents/:agent_id/toolkit-access Scopes: voice-agents:write

Enable agent toolkit access for a voice agent: mint a non-expiring agent_toolkit:use key named after the agent, attach it, migrate the agent in-call booking tools (check_calendar_availability / book_appointment) off the legacy scheduling endpoints onto the session-bound native endpoints carrying that key, and publish. Fixes silent in-call booking failures caused by hand-pasted expiring keys. Idempotent.

2 parameters
Name In Type Required Description
agent_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/post-voice-agents-agent-id-toolkit-access.md

GET /voice-agents/calls/:call_id Scopes: calls:read

Fetch a voice-agent call by id (the TrustPager UUID, or the provider call id).

1 parameter
Name In Type Required Description
call_id path string yes

📄 /api/voice-agents/get-voice-agents-calls-call-id.md

GET /voice-agents/:agent_id/test-runs/:run_id Scopes: voice-agents:read

Get one chat test run: status (running or complete), counts, and for every conversation its result (pass, fail or error), the reason, and the transcript as Agent, Caller and Tool lines. While running, each read fetches the latest results.

2 parameters
Name In Type Required Description
agent_id path string yes
run_id path string yes

📄 /api/voice-agents/get-voice-agents-agent-id-test-runs-run-id.md

GET /voice-agents/:agent_id/toolkit-access Scopes: voice-agents:read

Check whether a voice agent has toolkit access enabled (a non-expiring agent_toolkit:use key) and whether its in-call booking tools are on the native session-bound endpoints.

1 parameter
Name In Type Required Description
agent_id path string yes

📄 /api/voice-agents/get-voice-agents-agent-id-toolkit-access.md

POST /voice-agents/:agent_id/call Scopes: calls:initiate

Initiate an outbound voice call via this agent.

5 parameters
Name In Type Required Description
agent_id path string yes
to_number body string yes
from_number body string no Caller-ID number in E.164 (e.g. +61485033555). Must be an active number of this workspace. Omit to use the agent default outbound number.
dynamic_variables body object no Values for the agent's dynamic variables, e.g. { "caller_context": "..." }. Missing ones are listed in the response warnings.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/post-voice-agents-agent-id-call.md

GET /voice-agents/:agent_id/test-runs Scopes: voice-agents:read

List recent chat test runs for a voice agent, newest first, with pass, fail and error counts.

2 parameters
Name In Type Required Description
agent_id path string yes
limit query number no 1 to 100, default 20.

📄 /api/voice-agents/get-voice-agents-agent-id-test-runs.md

GET /voice-agents/:agent_id/tests Scopes: voice-agents:read

List a voice agent's saved chat tests. A chat test is a pretend caller who talks to the agent in text, graded against plain-English pass criteria. Test this way first: it costs a fraction of a call and takes seconds. meta.credits_per_test is the price of one test conversation.

1 parameter
Name In Type Required Description
agent_id path string yes

📄 /api/voice-agents/get-voice-agents-agent-id-tests.md

GET /voice-agents Scopes: voice-agents:read

List voice agents in the workspace.

1 parameter
Name In Type Required Description
limit query number no

📄 /api/voice-agents/get-voice-agents.md

GET /voice-agents/blocklist Scopes: voice-agents:read

List the phone numbers this workspace will not place AI voice calls to. Covers numbers with no contact record, which contact-level unsubscribe cannot. Entries added during a call carry the call id in source.

1 parameter
Name In Type Required Description
limit query number no

📄 /api/voice-agents/get-voice-agents-blocklist.md

POST /voice-agents/:agent_id/patch-flow Scopes: voice-agents:write

Partial update to the voice-agent flow. Provide nodes_patch and/or edges_patch to merge into specific nodes/edges by id, patches for top-level flow fields, and/or refresh_briefing=true.

6 parameters
Name In Type Required Description
agent_id path string yes
nodes_patch body object no Partial nodes object to merge into existing flow nodes. Keys are node IDs.
edges_patch body object no Partial edges object to merge into existing flow edges. Keys are edge IDs.
patches body object no Partial top-level conversation flow fields to shallow-merge (e.g. global_prompt, or flex_mode). A node's instruction can be set to { type: "static_text", text } for verbatim delivery (same mechanism as voicemail_option), but this ONLY holds if the flow-level flex_mode is false - with flex_mode true (the default from provisioning) the whole flow runs as one single prompt and static_text is not guaranteed to be spoken as written. flex_mode has no per-node override: forcing one line verbatim means setting flex_mode:false for the whole agent, which also makes every other node run strict instead of free-flowing. Confirmed by live test call, SR 58972773.
refresh_briefing body boolean no Regenerate and inject the AI briefing into global_prompt.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/post-voice-agents-agent-id-patch-flow.md

POST /voice-agents/:agent_id/provision Scopes: voice-agents:write

Provision a voice agent that was created as an orphan row.

2 parameters
Name In Type Required Description
agent_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/post-voice-agents-agent-id-provision.md

POST /voice-agents/:agent_id/publish Scopes: voice-agents:write

Publish a voice agent draft to live.

2 parameters
Name In Type Required Description
agent_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/post-voice-agents-agent-id-publish.md

POST /voice-agents/calls/:call_id/recording/rehost Scopes: voice-agents:write

Re-host a voice-agent call recording into private storage.

2 parameters
Name In Type Required Description
call_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/post-voice-agents-calls-call-id-recording-rehost.md

DELETE /voice-agents/blocklist/:entry_id Scopes: voice-agents:delete

Take a number off the voice do-not-call list, so AI voice calls to it are allowed again. Use when a number was blocked in error.

2 parameters
Name In Type Required Description
entry_id path string yes UUID of the blocklist entry. Get ids from list_voice_call_blocklist.
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/delete-voice-agents-blocklist-entry-id.md

POST /voice-agents/:agent_id/tests/run Scopes: voice-agents:write

Run a voice agent's chat tests against its latest saved script (published or not). Runs every test unless test_ids is given; repeat (1 to 5) runs each one several times, since AI conversations vary and a pass rate is more telling than one pass. Charged per test conversation (credits_per_test from list_voice_agent_tests). Returns the run straight away; read results with get_voice_agent_test_run after 30 to 90 seconds.

4 parameters
Name In Type Required Description
agent_id path string yes
test_ids body array no Only these tests. Omit to run them all.
repeat body number no Times to run each test, 1 to 5. Default 1.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/post-voice-agents-agent-id-tests-run.md

POST /voice-agents/:agent_id/sync Scopes: voice-agents:write

Sync a voice agent config with the upstream provider.

2 parameters
Name In Type Required Description
agent_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/post-voice-agents-agent-id-sync.md

PATCH /voice-agents/:agent_id Scopes: voice-agents:write

Update a voice agent. Provider-native settings (voice, language, timeouts, voicemail_option, begin_message_delay_ms, etc.) are pushed to the live agent; call publish_voice_agent to make them live.

38 parameters
Name In Type Required Description
agent_id path string yes
agent_name body string no The agent's display name. Also the name stamped on every call it reports back, which is what call logs and transcript titles show, so renaming needs a republish.
language body string no Language — primary spoken language and accent, as a BCP-47 tag (e.g. "en-AU", "en-US"). Defaults to en-AU.
voice_id body string no The voice this agent speaks in. Accepts a TrustPager voice UUID from list_voices, or a catalogue voice id. New agents come up on the standard TrustPager receptionist voice, so leave it unset unless you want a different one.
voice_model body string no Speech model used to render the voice. Leave unset to take the default that pairs with the chosen voice; pin it only if you have a reason to.
voice_speed body number no Speed — how fast the agent speaks. 0.5 (slow) to 2.0 (fast), default 1.
voice_temperature body number no Stability — voice consistency. 0 to 2; lower is more varied, higher is more stable. Default 1.32.
volume body number no Volume — output level. 0 to 2, default 1.44.
ambient_sound body string no Background Sound played under the call. One of coffee-shop, convention-hall, summer-outdoor, mountain-outdoor, static-noise, call-center. Pass null or "" for none (the default).
ambient_sound_volume body number no Ambient Volume — loudness of the Background Sound. 0 to 2, default 0.48. Only matters when ambient_sound is set.
backchannel_frequency body number no Backchannel Frequency — how often the agent interjects with "mm-hm", "right" and similar. 0 to 1. Only applies when enable_backchannel is true.
backchannel_words body array no The specific words the agent uses to backchannel, e.g. ["mm-hm", "right", "I see"]. Replaces the whole list. Only applies when enable_backchannel is true.
enable_backchannel body boolean no Whether the agent makes short acknowledgement noises while the caller is talking. Default false.
conversation_flow_config body object no ADVANCED. The full conversation flow (nodes, edges, prompts and model choice) that drives what the agent says. Normally built in the Voice Agent Builder rather than written here; replacing it wholesale replaces the agent's entire script.
data_storage_setting body string no What the call recording and transcript retain. "everything" stores audio and transcripts (the default), "no_pii" masks personal data, "nothing" keeps no recording or transcript. Pass "" to fall back to the workspace default.
default_dynamic_variables body object no Fallback values for the {{variables}} used in the agent's prompts, applied when a call does not supply them. A flat object of string keys to string values.
end_call_after_silence_ms body number no Silence Timeout — end the call after this much silence, in milliseconds. Default 61000 (about one minute).
fallback_voice_ids body array no Voices to fall back to if the primary voice_id is unavailable mid-call. Accepts TrustPager voice UUIDs or catalogue ids from list_voices. Replaces the whole list. New agents come up with one standard fallback already set.
interruption_sensitivity body number no Interruption Sensitivity — how easily the caller can talk over the agent. 0 to 1, higher is easier. Default 0.85.
max_call_duration_ms body number no Max Call Duration — hard limit before the call ends, in milliseconds. Default 359000 (about six minutes).
post_call_analysis_data body array no The structured fields extracted from the transcript after each call. Array of { type, name, description, choices? }. Every agent ships with the standard four (call_outcome, and the fields the Post-Call Briefing leads with); add bespoke fields on top rather than replacing them, because the briefing renderer reads the standard ones by name.
post_call_analysis_model body string no Model that runs the post-call extraction. Internal config, left at the default unless you have measured a reason to change it.
reminder_max_count body number no How many times the agent re-prompts a silent caller before giving up. Pass null for no limit.
reminder_trigger_ms body number no Reminder After — prompt the caller after this much silence, in milliseconds. Shorter than end_call_after_silence_ms, or it never fires.
response_engine body object no ADVANCED. Binds the agent to its conversation flow on the provider. Set for you at provision time; overwriting it by hand can leave the agent unable to answer.
responsiveness body number no Responsiveness — how quickly the agent starts replying. 0 to 1, higher is faster. Default 1.
normalize_for_speech body boolean no Whether numbers, dates and abbreviations are rewritten so they are read aloud naturally ("$1,200" as "twelve hundred dollars"). Default false.
voicemail_option body object no Native voicemail handling. On answering-machine detection the provider runs this action deterministically (no AI free-wheeling). Pass null to disable. Shape: { action: { type: "static_text"|"prompt"|"hangup", text?: string }, detection_prompt?: string }.
begin_message_delay_ms body number no Milliseconds (0-5000) to wait before the agent speaks its first message on a call it initiates, so it does not talk over a voicemail greeting or beep.
briefing_enabled body boolean no Send post-call briefings. When on, the team gets a next-action-first summary email after every real conversation with this agent.
briefing_email body string no Briefing goes to — the address that receives the post-call briefing. This is the person who should action calls, not the caller.
briefing_sms_number body string no Text hot leads to — where the escalation text goes, in international format (e.g. +61431377068). Leave blank to create a follow-up task only, with no text. Only used when briefing_escalate_on_positive is true.
briefing_escalate_on_positive body boolean no Escalate hot leads. When a caller is ready to proceed or asks for a callback, also create a follow-up task, and text briefing_sms_number if one is set.
create_contact_for_unknown_callers body boolean no Create contacts for new callers. When on, an inbound call from a number that matches no contact creates one (phone = caller ID, name from the post-call field named in unknown_caller_name_field), so the call, its transcript and call automations all land on that contact. Only for real conversations: inbound, caller ID present, not voicemail, 10 seconds or longer. Default false.
unknown_caller_name_field body string no The post-call analysis field (a post_call_analysis_data name) that holds the name the caller gave, used when create_contact_for_unknown_callers creates a contact. Default "caller_name". A contact with no name found is saved as "Unknown caller".
max_concurrent_calls body number no Maximum calls this agent may have in progress at the same time (1-100, default 5). A hard ceiling on simultaneous outward calls: calls beyond it are refused, and calls queued by an Auto Queue wait and dial later instead. Raise it deliberately, it is the control that stops one bulk enrolment placing hundreds of paid calls at once. Does not limit inbound calls.
agent_role body string no Role: an optional job for this agent that adds a check before every outbound call it places (the first call and every follow-up). "appointment_setter": if the person already has an upcoming booking, the call is skipped. "review_requester": if the person has already left a review or was sent a review request in the last 30 days, the call is skipped. "feedback_agent": if the person has unhappy feedback not yet resolved, or gave feedback within the workspace cooldown (feedback settings, default 365 days), the call is skipped. In every case their remaining queued calls from this agent are cancelled and a note is added to their timeline. A role never adds anyone to a call list; your automations still decide who is called. Pass "" (or null) for no role, the default, which behaves exactly as before.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/patch-voice-agents-agent-id.md

POST /voice-agents/:agent_id/update-flow Scopes: voice-agents:write

Replace the entire voice-agent conversation flow. Use patch_voice_agent_flow for a partial update.

3 parameters
Name In Type Required Description
agent_id path string yes
conversation_flow_config body object yes Full conversation flow config object with nodes and edges (required). A node's instruction can be { type: "static_text", text }, spoken verbatim like voicemail_option, instead of { type: "prompt" } which the model paraphrases. VERBATIM ONLY WORKS IF THE FLOW-LEVEL flex_mode FIELD IS FALSE - when flex_mode is true (the platform default from provisioning) the voice provider treats the whole flow as one single prompt and a node's static_text instruction is not guaranteed to be spoken as written. There is no per-node override, so making one line verbatim requires setting flex_mode:false for the entire agent, which also switches the rest of the conversation from free-flowing to strict node-by-node. Confirmed by live test call, SR 58972773.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/post-voice-agents-agent-id-update-flow.md

PATCH /voice-agents/:agent_id/outbound-config/:config_id Scopes: voice-agents:write

Update an outbound-call config: caller-ID number, which countries it may dial, the call rate limits, and CRM routing.

14 parameters
Name In Type Required Description
agent_id path string yes
config_id path string yes
default_phone_number_id body string no
website_id body string no
allowed_country_codes body array no Country codes this agent may dial, e.g. ["AU"]. Empty or omitted allows all.
rate_limit_max_requests body number no Max calls ONE visitor (per IP / browser fingerprint / email / phone) may start per window. Default 2.
rate_limit_window_hours body number no Length of the per-visitor rate-limit window, in hours. Default 24.
global_rate_limit_max_requests body number no Max calls ALL visitors combined may start per window, i.e. the spend ceiling. Default 50.
global_rate_limit_window_hours body number no Length of the global rate-limit window, in hours. Default 24.
crm_auto_create_deal body boolean no Whether a caller who identifies themselves also gets an opportunity created, not just a contact.
crm_pipeline_id body string no Pipeline the auto-created opportunity lands in. Required for crm_auto_create_deal to do anything.
crm_initial_stage_id body string no Stage within that pipeline the opportunity starts at.
crm_default_products body array no Product ids attached to the auto-created opportunity.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/patch-voice-agents-agent-id-outbound-config-config-id.md

POST /voice-agents/:agent_id/update-settings Scopes: voice-agents:write

Update provider-native voice-agent settings (pushed to the live agent). Pass the fields to change inside a "settings" object. Then call publish_voice_agent to make them live.

3 parameters
Name In Type Required Description
agent_id path string yes
settings body object yes Provider-native settings to change. Accepts any of: voice_id, voice_speed, voice_temperature, volume, responsiveness, interruption_sensitivity, max_call_duration_ms, end_call_after_silence_ms, voicemail_option, begin_message_delay_ms, and other provider fields.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/post-voice-agents-agent-id-update-settings.md

PATCH /voice-agents/:agent_id/tests/:test_id Scopes: voice-agents:write

Edit a saved voice agent chat test. Send only the fields to change.

10 parameters
Name In Type Required Description
agent_id path string yes
test_id path string yes
name body string no Short label, e.g. "Unhappy customer is not asked for a review".
caller_prompt body string no Who the pretend caller is, what they want and how they behave, written to them in the second person. E.g. "You are Jordan, a customer whose leaking tap was fixed last week. It went well and you are happy to leave a review. Give your email as demo+review@mail.trustpager.net if asked."
success_criteria body array no Plain-English checks the conversation must meet to pass, e.g. ["The agent asks how the job went", "The agent offers to email a review link"]. 1 to 10.
dynamic_variables body object no Values the agent sees for its {{variables}} during the test, e.g. {"first_name": "Jordan"}. Merged over the defaults set on the agent.
use_real_tools body boolean no Default false: every tool that saves or sends is replaced with a "done, nothing saved" stand-in so the test touches nothing real. True lets the tools run for real (bookings, emails, CRM writes happen).
tool_mocks body array no Optional: what a named tool should answer during the test (JSON text), e.g. to test what the agent does when no times are free.
sort_order body number no Position in the list, lowest first.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/patch-voice-agents-agent-id-tests-test-id.md

PATCH /voice-agents/:agent_id/website-config/:config_id Scopes: voice-agents:write

Update a voice-agent website config: the per-visitor and global call rate limits, and the CRM routing for callers who identify themselves. Use this to loosen the limit for testing (then put it back) or to change which pipeline web-call leads land in.

12 parameters
Name In Type Required Description
agent_id path string yes
config_id path string yes
website_id body string no
rate_limit_max_requests body number no Max calls ONE visitor (per IP / browser fingerprint / email / phone) may start per window. Default 2.
rate_limit_window_hours body number no Length of the per-visitor rate-limit window, in hours. Default 24.
global_rate_limit_max_requests body number no Max calls ALL visitors combined may start per window, i.e. the spend ceiling. Default 50.
global_rate_limit_window_hours body number no Length of the global rate-limit window, in hours. Default 24.
crm_auto_create_deal body boolean no Whether a caller who identifies themselves also gets an opportunity created, not just a contact.
crm_pipeline_id body string no Pipeline the auto-created opportunity lands in. Required for crm_auto_create_deal to do anything.
crm_initial_stage_id body string no Stage within that pipeline the opportunity starts at.
crm_default_products body array no Product ids attached to the auto-created opportunity.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agents/patch-voice-agents-agent-id-website-config-config-id.md

Agent Knowledge Bases (8 endpoints)

Manage knowledge bases for voice agents. Each KB stores documents that agents can retrieve during calls. KBs can be seeded from help center articles or populated with custom content. Required scopes: voice-kbs:read, voice-kbs:write, voice-kbs:delete. NOTE: knowledge base writes (create and add-sources) are occasionally unavailable for a short time. If create or add_doc returns a 500, retry shortly.

📄 Full markdown: /api/voice-agent-kbs.md

POST /voice-agent-kbs/:kb_id/documents Scopes: voice-kbs:write

Add a document (title + text) to a voice-agent KB.

4 parameters
Name In Type Required Description
kb_id path string yes
title body string yes
text body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agent-kbs/post-voice-agent-kbs-kb-id-documents.md

POST /voice-agent-kbs Scopes: voice-kbs:write

Create a new voice-agent knowledge base, held in the voice provider native store so agents can recall from it mid-call.

3 parameters
Name In Type Required Description
name body string yes
description body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agent-kbs/post-voice-agent-kbs.md

DELETE /voice-agent-kbs/:kb_id Scopes: voice-kbs:delete

Delete a voice-agent KB, both in the voice provider native store and in TrustPager.

2 parameters
Name In Type Required Description
kb_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agent-kbs/delete-voice-agent-kbs-kb-id.md

DELETE /voice-agent-kbs/:kb_id/documents/:doc_id Scopes: voice-kbs:delete

Remove a document from a voice-agent KB.

3 parameters
Name In Type Required Description
kb_id path string yes
doc_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agent-kbs/delete-voice-agent-kbs-kb-id-documents-doc-id.md

POST /voice-agent-kbs/:kb_id/sync-help-center Scopes: voice-kbs:write

Sync help-center articles into a voice-agent KB.

2 parameters
Name In Type Required Description
kb_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agent-kbs/post-voice-agent-kbs-kb-id-sync-help-center.md

VOICE_AGENT_TEMPLATES (3 endpoints)

voice-agent-templates endpoints.

📄 Full markdown: /api/voice-agent-templates.md

POST /voice-agent-templates/:slug/create-agent Scopes: voice-agents:write

Create a working voice agent in this workspace from a template: the script, voice, safety role and tools are set up from the answers, and the agent is published. It answers no phone number and calls nobody until you connect a number or an automation calls it. Answer the template's questions (get_voice_agent_template); blanks take their defaults, and optional parts like booking or transfers are left out when their answer is blank. Returns the new agent, any warnings, and next steps.

4 parameters
Name In Type Required Description
slug path string yes Template slug, e.g. "appointment-setter".
agent_name body string no Optional name for the new agent. Defaults to "<business name> <template name>".
answers body object no Question key to answer, e.g. { "what_you_do": "We install solar panels across Perth.", "booking_calendar": "<event type id>", "caller_id": "<phone number id>" }.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-agent-templates/post-voice-agent-templates-slug-create-agent.md

GET /voice-agent-templates/:slug Scopes: voice-agents:read

Get one voice agent template by slug (e.g. "appointment-setter"), with its setup questions: key, label, type (text, long_text, event_type = a booking calendar id from list_scheduled_event_types, phone_number = a phone number id from list_phone_numbers, transfer_number = a phone number to put callers through to, email), whether it is required, and any default.

1 parameter
Name In Type Required Description
slug path string yes Template slug from list_voice_agent_templates, e.g. "appointment-setter".

📄 /api/voice-agent-templates/get-voice-agent-templates-slug.md

GET /voice-agent-templates Scopes: voice-agents:read

List the ready-made voice agent templates any workspace can create an agent from (Appointment Setter, Receptionist and more). Each has a category (sales, service, reputation, feedback), a direction (inbound answers calls, outbound makes them), plain-English "includes", and the setup questions create_voice_agent_from_template needs answered.

📄 /api/voice-agent-templates/get-voice-agent-templates.md

VOICE_CALL_DIRECTIVES (5 endpoints)

voice-call-directives endpoints.

📄 Full markdown: /api/voice-call-directives.md

POST /voice-directives Scopes: voice-directives:write

Create a call directive. directive_type is one of: business_hours_forward (config needs forward_to in E.164, plus windows, timezone and mode), known_caller_context (config optional: include_booking), unknown_caller_booking (no config; set voice_agent_config_id and enabled to let that agent book callers it cannot identify).

6 parameters
Name In Type Required Description
directive_type body string yes
voice_agent_config_id body string no
enabled body boolean no
priority body number no
config body object no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-call-directives/post-voice-directives.md

DELETE /voice-directives/:directive_id Scopes: voice-directives:delete

Delete a call directive.

2 parameters
Name In Type Required Description
directive_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-call-directives/delete-voice-directives-directive-id.md

GET /voice-directives Scopes: voice-directives:read

List call directives (e.g. business-hours forwarding) for the workspace.

4 parameters
Name In Type Required Description
directive_type query string no
voice_agent_config_id query string no
enabled query string no
limit query number no

📄 /api/voice-call-directives/get-voice-directives.md

PATCH /voice-directives/:directive_id Scopes: voice-directives:write

Update a call directive (enable/disable, priority, config).

6 parameters
Name In Type Required Description
directive_id path string yes
enabled body boolean no
priority body number no
config body object no
voice_agent_config_id body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voice-call-directives/patch-voice-directives-directive-id.md

Voice Agent Toolkit (16 endpoints)

Endpoints your voice agents call mid-conversation to check availability and book, reschedule or cancel an appointment for the caller.

📄 Full markdown: /api/agent-toolkit-voice.md

POST /agent/v1/voice/scheduling/:event_type_id/book Scopes: agent_toolkit:use

Voice agent toolkit — book an appointment for the caller.

3 parameters
Name In Type Required Description
event_type_id path string yes
slot body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-toolkit-voice/post-agent-v1-voice-scheduling-event-type-id-book.md

POST /agent/v1/voice/add-referral Scopes: agent_toolkit:use

Voice agent toolkit: save someone the customer recommended, as a contact and a referral credited to the customer. Only when the customer said it is OK to get in touch. Up to three per call; nobody is called automatically.

6 parameters
Name In Type Required Description
name body string yes The full name of the person they recommended.
phone body string yes Their phone number, as the customer read it out.
email body string no Their email, only if the customer offered it.
ok_to_contact body boolean yes True only if the customer said it is fine for us to get in touch with this person.
note body string no Anything useful the customer said about them, e.g. "neighbour, needs a new hot water system".
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-toolkit-voice/post-agent-v1-voice-add-referral.md

POST /agent/v1/voice/cancel-booking Scopes: agent_toolkit:use

Voice agent toolkit — cancel a booking keyed by caller phone.

1 parameter
Name In Type Required Description
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-toolkit-voice/post-agent-v1-voice-cancel-booking.md

POST /agent/v1/voice/pricing Scopes: agent_toolkit:use

Voice agent toolkit (optional, opt-in per agent) — read the workspace live product pricing from the catalogue so the agent never quotes prices from memory. Wire this tool only into agents whose workspace wants pricing shared by voice.

📄 /api/agent-toolkit-voice/post-agent-v1-voice-pricing.md

POST /agent/v1/voice/record-feedback Scopes: agent_toolkit:use

Voice agent toolkit: save the feedback of the customer on the call. Unhappy feedback (negative, or a rating at or below the workspace setting) is sent to the team as a high-priority task and email. Saves once per call.

5 parameters
Name In Type Required Description
summary body string yes What the customer thinks, in one plain sentence.
details body string no What they said, as close to their own words as you can.
rating body number no The rating out of 5 they gave, if they gave one.
sentiment body string no positive if they are happy, mixed if mostly fine with a gripe, negative if they are unhappy or describe a real problem.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-toolkit-voice/post-agent-v1-voice-record-feedback.md

POST /agent/v1/voice/request-callback Scopes: agent_toolkit:use

Voice agent toolkit — record a callback request.

2 parameters
Name In Type Required Description
reason body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-toolkit-voice/post-agent-v1-voice-request-callback.md

POST /agent/v1/voice/reschedule-booking Scopes: agent_toolkit:use

Voice agent toolkit — reschedule a booking keyed by caller phone.

2 parameters
Name In Type Required Description
slot body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-toolkit-voice/post-agent-v1-voice-reschedule-booking.md

POST /agent/v1/voice/save-testimonial Scopes: agent_toolkit:use

Voice agent toolkit: save the customer's own words as a testimonial, only with their spoken consent. It waits for the business to approve it in Reputation before anything is shared.

4 parameters
Name In Type Required Description
testimonial body string yes The customer's own words, as close to exactly what they said as you can, in the first person.
consent body boolean yes True only if the customer clearly said yes to sharing their words.
rating body number no Their rating out of 5, if they gave one and it was not already recorded.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-toolkit-voice/post-agent-v1-voice-save-testimonial.md

POST /agent/v1/voice/schedule-callback Scopes: agent_toolkit:use

Voice agent toolkit — schedule an automatic AI callback at the time the caller requested (rings them back from the agent's outbound number). Pass callback_day + callback_time + state; the server resolves the absolute date in the caller's timezone, so never compute a date yourself.

4 parameters
Name In Type Required Description
callback_day body string no Day to call back: "today", "tomorrow", a weekday, or YYYY-MM-DD. Do NOT compute a date.
callback_time body string yes Clock time the caller wants, e.g. "3pm", "2:30pm", "14:00".
state body string no Caller state/territory for timezone (e.g. QLD, NSW, VIC).
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-toolkit-voice/post-agent-v1-voice-schedule-callback.md

POST /agent/v1/voice/send-review-link Scopes: agent_toolkit:use

Voice agent toolkit: email the customer on the call the workspace review link. Uses their email on file; pass email only when the response says none is on file.

2 parameters
Name In Type Required Description
email body string no Only when the response said no email is on file: the email the customer gave, spelled back to confirm.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-toolkit-voice/post-agent-v1-voice-send-review-link.md

POST /agent/v1/voice/take-message Scopes: agent_toolkit:use

Voice agent toolkit — log an inbound message from the caller.

2 parameters
Name In Type Required Description
message body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-toolkit-voice/post-agent-v1-voice-take-message.md

POST /agent/v1/voice/unsubscribe Scopes: agent_toolkit:use

Voice agent toolkit: stop contacting the caller. A caller we recognise is unsubscribed across their contact records; a caller we do not recognise has their number added to the voice do-not-call list, so an opt-out is always honoured.

2 parameters
Name In Type Required Description
channel body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-toolkit-voice/post-agent-v1-voice-unsubscribe.md

POST /agent/v1/voice/verify-client Scopes: agent_toolkit:use

Voice agent toolkit: confirm a service caller is an existing client. Their full name, phone and email must all match one CRM contact. Answers yes or no only, never record details or which detail failed. A match unlocks calendar tools whose URL carries require_verified_client=1 for the rest of the call; after two failed checks the hosts of escalate_event_type_id (a tool URL param) get a follow-up task and email.

6 parameters
Name In Type Required Description
full_name body string yes The caller first and last name, as they said it.
email body string yes The caller email address, as they said it.
phone body string no The phone number the caller gave. Omit only when they said to use the number they are calling from, and set use_calling_number instead.
use_calling_number body boolean no True when the caller said the number they are calling from is the one on file.
reason body string no In a few words, what the caller needs help with.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-toolkit-voice/post-agent-v1-voice-verify-client.md

Knowledge Bases (14 endpoints)

Knowledge bases that ground your voice and workflow agents, plus the entries and sources inside them.

📄 Full markdown: /api/knowledge-bases.md

POST /knowledge-bases/adopt Scopes: knowledge:write

Adopt an existing voice-provider knowledge base into the unified system: links it in place (no duplicate), imports its web-page sources into search, and attaches any voice agents already using it. Pass retell_kb_id.

3 parameters
Name In Type Required Description
retell_kb_id body string yes
name body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/knowledge-bases/post-knowledge-bases-adopt.md

POST /knowledge-bases/:kb_id/attach Scopes: knowledge:write

Attach a knowledge base to an agent so the agent's searches are grounded in it. agent_type is 'voice' or 'workflow'; agent_id is the voice agent or workflow agent UUID.

4 parameters
Name In Type Required Description
kb_id path string yes
agent_type body string yes
agent_id body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/knowledge-bases/post-knowledge-bases-kb-id-attach.md

POST /knowledge-bases Scopes: knowledge:write

Create a named knowledge base. Populate it via sync-help-center, sync-products, or by creating entries with this kb_id, then attach it to agents.

6 parameters
Name In Type Required Description
name body string yes
description body string no
auto_resync_enabled body boolean no
resync_frequency body string no
voice_ambient_recall_enabled body boolean no Voice ambient recall: also mirror this knowledge base into the voice provider native store so voice agents can recall from it mid-call.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/knowledge-bases/post-knowledge-bases.md

DELETE /knowledge-bases/:kb_id Scopes: knowledge:delete

Delete a knowledge base. Its entries are kept (unlinked); agent attachments are removed.

2 parameters
Name In Type Required Description
kb_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/knowledge-bases/delete-knowledge-bases-kb-id.md

POST /knowledge-bases/:kb_id/detach Scopes: knowledge:write

Detach a knowledge base from an agent. The KB and its entries are kept.

4 parameters
Name In Type Required Description
kb_id path string yes
agent_type body string yes
agent_id body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/knowledge-bases/post-knowledge-bases-kb-id-detach.md

GET /knowledge-bases/:kb_id Scopes: knowledge:read

Fetch a knowledge base by UUID, with entry and attached-agent counts.

1 parameter
Name In Type Required Description
kb_id path string yes

📄 /api/knowledge-bases/get-knowledge-bases-kb-id.md

POST /knowledge-bases/:kb_id/ingest-text Scopes: knowledge:write

Add a block of text (e.g. an uploaded document's content) to a knowledge base as an embedded entry.

5 parameters
Name In Type Required Description
kb_id path string yes
title body string yes
text body string yes
source_ref body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/knowledge-bases/post-knowledge-bases-kb-id-ingest-text.md

POST /knowledge-bases/:kb_id/ingest-url Scopes: knowledge:write

Fetch a web page, extract its text, and add it to a knowledge base as an embedded entry. Idempotent per URL.

3 parameters
Name In Type Required Description
kb_id path string yes
url body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/knowledge-bases/post-knowledge-bases-kb-id-ingest-url.md

GET /knowledge-bases/:kb_id/entries Scopes: knowledge:read

List the entries (documents) inside a knowledge base.

3 parameters
Name In Type Required Description
kb_id path string yes
limit query number no
after query string no

📄 /api/knowledge-bases/get-knowledge-bases-kb-id-entries.md

GET /knowledge-bases Scopes: knowledge:read

List knowledge bases in the workspace. Pass agent_type + agent_id to list only the KBs attached to a given voice or workflow agent.

2 parameters
Name In Type Required Description
agent_type query string no
agent_id query string no

📄 /api/knowledge-bases/get-knowledge-bases.md

POST /knowledge-bases/:kb_id/sync-help-center Scopes: knowledge:write

Ingest help-center articles into a knowledge base as embedded entries. Pass article_ids to select specific articles, or omit to sync all. Idempotent.

3 parameters
Name In Type Required Description
kb_id path string yes
article_ids body array no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/knowledge-bases/post-knowledge-bases-kb-id-sync-help-center.md

POST /knowledge-bases/:kb_id/sync-products Scopes: knowledge:write

Ingest the active CRM product catalog into a knowledge base as embedded entries. Idempotent.

2 parameters
Name In Type Required Description
kb_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/knowledge-bases/post-knowledge-bases-kb-id-sync-products.md

PATCH /knowledge-bases/:kb_id Scopes: knowledge:write

Update a knowledge base (name, description, auto-resync settings).

7 parameters
Name In Type Required Description
kb_id path string yes
name body string no
description body string no
auto_resync_enabled body boolean no
resync_frequency body string no
voice_ambient_recall_enabled body boolean no Voice ambient recall: also mirror this knowledge base into the voice provider native store so voice agents can recall from it mid-call.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/knowledge-bases/patch-knowledge-bases-kb-id.md

Text Agents (6 endpoints)

Text agents are now part of the unified /agents endpoint. Use GET /agents?type=text to list text agents. See the Agents resource group for full documentation.

📄 Full markdown: /api/text-agents.md

GET /agents/:id Scopes: voice-agents:read

Retrieve a text agent. Auto-detects type.

1 parameter
Name In Type Required Description
id path uuid yes Text agent ID

📄 /api/text-agents/get-agents-id.md

POST /agents Scopes: voice-agents:write

Create a text agent. Include "type": "text" and "name" in the request body.

2 parameters
Name In Type Required Description
type body string yes Must be "text"
name body string yes Agent name

📄 /api/text-agents/post-agents.md

PATCH /agents/:id Scopes: voice-agents:write

Update a text agent. Auto-detects type.

1 parameter
Name In Type Required Description
id path uuid yes Text agent ID

📄 /api/text-agents/patch-agents-id.md

DELETE /agents/:id Scopes: voice-agents:write

Delete a text agent. Auto-detects type.

1 parameter
Name In Type Required Description
id path uuid yes Text agent ID

📄 /api/text-agents/delete-agents-id.md

GET /agents/:id/responses Scopes: voice-agents:read

List responses generated by a text agent.

1 parameter
Name In Type Required Description
id path uuid yes Text agent ID

📄 /api/text-agents/get-agents-id-responses.md

Transcripts (10 endpoints)

Transcribe call recordings and meetings, view results, and access AI coaching. Use POST /transcripts/transcribe to kick off transcription from any audio URL. Coaching results are under /transcripts/coaching.

📄 Full markdown: /api/transcripts.md

DELETE /transcripts/:transcript_id Scopes: transcripts:delete

Delete a transcript by UUID. Destructive — also removes associated recording from storage.

2 parameters
Name In Type Required Description
transcript_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/transcripts/delete-transcripts-transcript-id.md

GET /transcripts/:transcript_id Scopes: transcripts:read

Fetch a single transcript by UUID — full text + speaker segments. Use with ai_transcript_summary for highlights.

1 parameter
Name In Type Required Description
transcript_id path string yes

📄 /api/transcripts/get-transcripts-transcript-id.md

GET /transcripts/coaching Scopes: transcripts:read

List AI call-coaching results across all transcripts.

1 parameter
Name In Type Required Description
limit query number no

📄 /api/transcripts/get-transcripts-coaching.md

GET /transcripts Scopes: transcripts:read

List call and meeting transcripts. Filter by participant (email or phone), type, source, status, or date. Use before ai_transcript_summary.

12 parameters
Name In Type Required Description
limit query number no
after query string no
type query string no phone_call -> /inbox/phone-calls, meeting -> /inbox/meetings.
source query string no Where the transcript came from, matching the source field on each transcript (e.g. "api" for recordings sent through transcribe_recording).
transcription_status query string no Only "complete" rows have transcript_text.
occurred_after query string no ISO timestamp lower bound on occurred_at.
occurred_before query string no ISO timestamp upper bound on occurred_at.
participant_email query string no Find transcripts where a participant has this email (exact match).
participant_phone query string no Find transcripts where a participant has this phone. Digit-normalised last-9 match, so 0408119890, +61408119890 and 61408119890 all resolve to the same calls.
booking_id query string no Transcript recorded during a specific scheduling booking.
query query string no Partial, case-insensitive search on the transcript title.
fields query string no Comma-separated field list to return instead of the default set. The default omits transcript_text and metadata to keep the payload small, so pass "all" (or "transcript_text,metadata") when you need them.

📄 /api/transcripts/get-transcripts.md

POST /transcripts/:transcript_id/recording/rehost Scopes: transcripts:write

Re-host a transcript recording from external storage into the workspace bucket.

2 parameters
Name In Type Required Description
transcript_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/transcripts/post-transcripts-transcript-id-recording-rehost.md

POST /transcripts/transcribe Scopes: transcripts:write

Kick off AI transcription of an audio/video recording. Lands as a transcript in /inbox/phone-calls or /inbox/meetings and creates a single linked crm_activities row. Pass deal_id/contact_id when known at recording time to authoritatively link the activity (skips post-hoc phone/email matching). Set participants[].role to "caller" for the workspace user side so their email/phone is excluded from contact-matching.

13 parameters
Name In Type Required Description
recording_url body string yes Publicly fetchable HTTPS URL, or a TrustPager-rehosted private file URL. For a raw recording URL straight from a telephony provider, which is usually behind basic auth, also pass recording_auth.
recording_auth body string no Optional basic-auth credentials for recording_url, as "USER:PASSWORD". For a telephony provider this is normally your account id and auth token with that provider, in that order.
title body string yes Display title shown on the transcript and on the auto-created activity.
type body string yes phone_call (or alias "call") -> /inbox/phone-calls. meeting -> /inbox/meetings. voicemail -> /inbox.
source body string no Producer label for filtering later. Defaults to "api".
source_id body string no Optional foreign key in the producer system (call_sid, upload_id). Stored on metadata.
occurred_at body string no ISO timestamp the audio was captured. Defaults to now.
participants body array no Speakers on the recording. Used for diarisation AND for auto-linking to contacts/deals (by email and phone-last-9). Set role="caller" on the workspace-user side to exclude them from contact-matching.
metadata body object no Free-form metadata to attach to the transcript row.
contact_id body string no Authoritative callee contact known at recording time (e.g. softphone dialed from a contact card). Overrides the matcher's best guess for the activity's contact_id.
deal_id body string no Authoritative opportunity known at recording time (e.g. softphone dialed from an opportunity page). The activity is linked directly to this deal instead of being inferred by phone/email matching.
existing_transcript_id body string no UUID of an existing transcript to overwrite. Used for fallback/retry flows.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/transcripts/post-transcripts-transcribe.md

PATCH /transcripts/:transcript_id Scopes: transcripts:write

Update a transcript (e.g. rename, re-link to a contact/deal).

12 parameters
Name In Type Required Description
transcript_id path string yes
title body string no
contact_id body string no Re-link this transcript to a contact. Replaces the existing contact link; pass null to unlink.
deal_id body string no Re-link this transcript to an opportunity. Replaces the existing opportunity link; pass null to unlink.
transcript_type body string no What kind of conversation this was, e.g. a call or a meeting.
transcript_text body string no The transcript itself. Replaces the whole text, so read it and merge if you are correcting part of it.
occurred_at body string no When the conversation actually happened, as an ISO timestamp. Set this on anything imported: activity recency and staleness read this, not when the row was created.
duration_seconds body number no How long the conversation ran, in seconds.
participants body array no Who was on the call. Replaces the whole list.
recording_url body string no Link to the audio or video recording this transcript came from.
metadata body object no Custom field values on the transcript, keyed by field name. Replaces the whole object, so read the transcript and merge first.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/transcripts/patch-transcripts-transcript-id.md

Document Templates (14 endpoints)

Manage document templates with sections for proposals, contracts, and more.

📄 Full markdown: /api/document-templates.md

POST /document-templates/:template_id/sections Scopes: documents:write

Add a section to a document template.

7 parameters
Name In Type Required Description
template_id path string yes
type body string yes Section type in kebab-case. One of: cover-page, text-block, two-column, table, product-table, product-showcase, image-block, signature-block, signer-input, divider, page-break, quote-callout, terms-conditions, guarantee, doc-header, doc-footer, dynamic-pricing, dynamic-products, dynamic-images, dynamic-timeline, dynamic-executive-summary, dynamic-needs-solutions, dynamic-recommendations, dynamic-callout, dynamic-guarantee, rich-content, raw-html, product-brochure, referrer-statement-info, referrer-commission-items.
content body object no Section body. Shape depends on type, e.g. rich-content: { html, tiptapJson }; text-block: { heading, text }; dynamic-images: { heading, source: "opportunity_images" (the only supported source), columns: 1|2|3, startIndex, limit, aspect: "original"|"square"|"4:3"|"16:9", captionFrom: "none"|"filename"|"label" (label = the photo tags), label }. On dynamic-images, `label` is the "Only Photos Tagged" filter: a photo is included when any of its file tags matches it, case-insensitively (e.g. "Before"). Leave it blank for all photos. signer-input (a field the signer fills in at signing time): { label, fieldType: "text"|"textarea"|"date"|"number"|"checkbox_list", required: true|false, placeholder, signerEmail (blank = primary signer), options: ["label", ...] (checkbox_list only: the options the signer can tick; required means at least one) }. Answers are returned on get_signing_envelope (crm_signing_recipients[].input_values, keyed by section id; a checkbox list is the ticked labels joined with "||") and on the signature_completed trigger as signers[].inputs, keyed by field label.
styling body object no Styling overrides, e.g. backgroundColor, paddingTop, paddingBottom.
order_index body number no Display order (1-based). Sections render ascending.
is_visible body boolean no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/document-templates/post-document-templates-template-id-sections.md

POST /document-templates Scopes: documents:write

Create a document template.

15 parameters
Name In Type Required Description
name body string yes Template name, shown in the template list.
description body string no What this template is for, shown under its name in the list.
category body string no Grouping for the template list, e.g. "Proposals", "Contracts".
page_size body string no Paper size the document renders at, e.g. "A4" or "Letter".
page_margins body object no Page margins, as { top, right, bottom, left }.
brand_colors body object no Colour overrides for this template. Omit to inherit the workspace brand.
brand_typography body object no Font overrides for this template. Omit to inherit the workspace brand.
header body object no What prints at the top of every page except a cover page (and page 1 when hideOnFirstPage is true). Text fields take {{variables}} and {page_number} / {total_pages}.
footer body object no What prints at the bottom of every page except a cover page (and page 1 when hideOnFirstPage is true). Text fields take {{variables}} and {page_number} / {total_pages}.
filename_template body string no Pattern for the generated file name, with variables such as {{company_name}}, so every document off this template is named consistently.
is_archived body boolean no Whether the template is archived. Archived templates stay readable but drop out of the picker.
proper_case_names body boolean no Show names stored in capitals in normal capitalisation on this document ("AARON SMITH" prints as "Aaron Smith"). Applies to the contact first, last and full name, the account name and the opportunity name. Names typed in mixed case are left as they are, and the stored records are never changed. Default false.
email_template_settings body object no Per-template overrides for the email this document is sent with. Omit to use the workspace email chrome.
signing_settings body object no Signing delivery: { deliver: "email"|"portal", portalTemplateId, sendEmail }.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/document-templates/post-document-templates.md

DELETE /document-templates/:template_id/sections/:section_id Scopes: documents:delete

Delete a section.

3 parameters
Name In Type Required Description
template_id path string yes
section_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/document-templates/delete-document-templates-template-id-sections-section-id.md

DELETE /document-templates/:template_id Scopes: documents:delete

Delete a document template.

2 parameters
Name In Type Required Description
template_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/document-templates/delete-document-templates-template-id.md

POST /document-templates/:template_id/duplicate Scopes: documents:write

Duplicate a document template.

2 parameters
Name In Type Required Description
template_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/document-templates/post-document-templates-template-id-duplicate.md

POST /document-templates/import Scopes: documents:write

Turn a PDF into an imported document template that can be sent for e-signature (the Document Builder's Import PDF). Pass the PDF as file_id (upload it first with upload_file, type "document") or as file_base64. Optionally place the signing boxes in the same call with signers + field_overlays, then send it with send_for_signing using the returned id as template_id.

8 parameters
Name In Type Required Description
name body string yes Template name, shown in the template list.
file_id body string no A document-type file from upload_file or list_files. Pass this or file_base64.
file_base64 body string no The PDF itself, base64 encoded (up to about 23 MB). Pass this or file_id.
description body string no What this template is for, shown under its name in the list.
signing_settings body object no Signing delivery: { deliver: "email"|"portal", portalTemplateId, sendEmail }.
signers body array no Imported-PDF templates only (category "imported"): the signing roles, in order. Each is { id, name?, email? }, e.g. [{ "id": "signer_1", "name": "Director" }]. send_for_signing maps its signers onto these by position: its first signer fills every box owned by signers[0], and so on. Sending the array replaces the roster.
field_overlays body array no Imported-PDF templates only (category "imported"): the boxes placed on the PDF. Positions are fractions of the page (0 to 1) measured from the TOP-LEFT corner, so x 0.1, y 0.8 sits near the bottom-left. The builder's default sizes: signature w 0.28 h 0.06; name and text w 0.26 h 0.032; date w 0.16 h 0.032. A box with signer_id is filled by that signer; a box with no signer_id is a document field whose value (static text or a {{variable}}) is stamped on. Sending the array replaces every box.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/document-templates/post-document-templates-import.md

POST /document-templates/:template_id/render Scopes: documents:write

Render a document template to PDF and save to the file library (costs credits). Pass deal_id to auto-fill {{tokens}} from that opportunity. Set preview:true for a FREE dry-run that returns the rendered HTML + any unresolved_tokens WITHOUT saving or charging — use it to validate merge fields before send_for_signing (which resolves strictly and blocks on any gap).

9 parameters
Name In Type Required Description
template_id path string yes
deal_id body string no Opportunity to resolve merge tokens from.
preview body boolean no true = free dry-run: return html + unresolved_tokens + would_send, no PDF saved, no credits charged.
variables body object no Extra {{token}} values merged on top of the opportunity data.
strict_tokens body boolean no true = fail if any token is unresolved (ignored when preview:true).
folder body string no
filename body string no
document_type body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/document-templates/post-document-templates-template-id-render.md

POST /document-templates/:template_id/sections/reorder Scopes: documents:write

Reorder sections in a document template.

3 parameters
Name In Type Required Description
template_id path string yes
section_ids body array yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/document-templates/post-document-templates-template-id-sections-reorder.md

POST /document-templates/:template_id/send Scopes: documents:write

Render a document template to PDF and email it to a recipient in one call. Costs credits (PDF render). Use render_document_template with preview:true first to validate merge fields — this call resolves tokens strictly and fails on any unresolved {{token}} in the document body or the email subject/message.

13 parameters
Name In Type Required Description
template_id path string yes
deal_id body string no Opportunity to resolve {{tokens}} from, and (with recipient_source contact/account) the recipient. Required unless recipient_source is "custom".
recipient_source body string yes "contact"/"account" resolve the recipient from deal_id's linked contact/company. "custom" uses recipient_email/recipient_name directly.
recipient_email body string no Required when recipient_source is "custom"; used as a fallback otherwise.
recipient_name body string no
email_subject body string yes May contain {{tokens}} (e.g. {{deal.name}}). Must fully resolve.
email_message body string no Optional plain-text record of the message, kept on the send for reference. It is NOT shown in the email: write the email body in intro_html. May contain {{tokens}}; must fully resolve.
intro_html body string no The email body (HTML, may contain {{tokens}}). Wins over the workspace/per-template default for this send only. Omit to use the workspace default ("Your <document> is attached to this email.").
email_format body string no "branded" (default) wraps intro_html in the branded document email frame with the secure-delivery footer. "raw" sends intro_html as the entire email, exactly as written, with no frame and no footer; the PDF is still attached. Use raw when the workspace sends its own designed HTML. Requires intro_html.
subject_override body string no Per-call subject override (plain text, may contain {{tokens}}).
variables body object no Extra {{token}} values merged on top of the opportunity data.
document_type body string no crm_documents.document_type. Defaults to "Other".
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/document-templates/post-document-templates-template-id-send.md

PATCH /document-templates/:template_id/sections/:section_id Scopes: documents:write

Update a section. Partial: only the fields you pass are changed. Note that content is replaced wholesale, not deep-merged, so send the whole body.

8 parameters
Name In Type Required Description
template_id path string yes
section_id path string yes
type body string no Section type in kebab-case. One of: cover-page, text-block, two-column, table, product-table, product-showcase, image-block, signature-block, signer-input, divider, page-break, quote-callout, terms-conditions, guarantee, doc-header, doc-footer, dynamic-pricing, dynamic-products, dynamic-images, dynamic-timeline, dynamic-executive-summary, dynamic-needs-solutions, dynamic-recommendations, dynamic-callout, dynamic-guarantee, rich-content, raw-html, product-brochure, referrer-statement-info, referrer-commission-items.
content body object no Section body, replaced wholesale. Shape depends on type, e.g. rich-content: { html, tiptapJson } (html is what the PDF renderer reads, tiptapJson is what the document builder editor loads, so keep both in step).
styling body object no Styling overrides, e.g. backgroundColor, paddingTop, paddingBottom.
order_index body number no Display order (1-based). Sections render ascending.
is_visible body boolean no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/document-templates/patch-document-templates-template-id-sections-section-id.md

PATCH /document-templates/:template_id Scopes: documents:write

Update a document template. On an imported-PDF template (category "imported", made with import_document_template) this is also where the signing layout lives: signers + field_overlays.

18 parameters
Name In Type Required Description
template_id path string yes
name body string no Template name, shown in the template list.
description body string no What this template is for, shown under its name in the list.
category body string no Grouping for the template list, e.g. "Proposals", "Contracts".
page_size body string no Paper size the document renders at, e.g. "A4" or "Letter".
page_margins body object no Page margins, as { top, right, bottom, left }.
brand_colors body object no Colour overrides for this template. Omit to inherit the workspace brand.
brand_typography body object no Font overrides for this template. Omit to inherit the workspace brand.
header body object no What prints at the top of every page except a cover page (and page 1 when hideOnFirstPage is true). Text fields take {{variables}} and {page_number} / {total_pages}.
footer body object no What prints at the bottom of every page except a cover page (and page 1 when hideOnFirstPage is true). Text fields take {{variables}} and {page_number} / {total_pages}.
filename_template body string no Pattern for the generated file name, with variables such as {{company_name}}, so every document off this template is named consistently.
is_archived body boolean no Whether the template is archived. Archived templates stay readable but drop out of the picker.
proper_case_names body boolean no Show names stored in capitals in normal capitalisation on this document ("AARON SMITH" prints as "Aaron Smith"). Applies to the contact first, last and full name, the account name and the opportunity name. Names typed in mixed case are left as they are, and the stored records are never changed. Default false.
email_template_settings body object no Per-template overrides for the email this document is sent with. Omit to use the workspace email chrome.
signing_settings body object no Signing delivery: { deliver: "email"|"portal", portalTemplateId, sendEmail }.
signers body array no Imported-PDF templates only (category "imported"): the signing roles, in order. Each is { id, name?, email? }, e.g. [{ "id": "signer_1", "name": "Director" }]. send_for_signing maps its signers onto these by position: its first signer fills every box owned by signers[0], and so on. Sending the array replaces the roster.
field_overlays body array no Imported-PDF templates only (category "imported"): the boxes placed on the PDF. Positions are fractions of the page (0 to 1) measured from the TOP-LEFT corner, so x 0.1, y 0.8 sits near the bottom-left. The builder's default sizes: signature w 0.28 h 0.06; name and text w 0.26 h 0.032; date w 0.16 h 0.032. A box with signer_id is filled by that signer; a box with no signer_id is a document field whose value (static text or a {{variable}}) is stamped on. Sending the array replaces every box.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/document-templates/patch-document-templates-template-id.md

Signing (5 endpoints)

Manage e-signature envelopes. Send documents for signing and track completion status.

📄 Full markdown: /api/signing.md

GET /signing/envelopes/:envelope_id Scopes: documents:read

Fetch a signing envelope by UUID: signer status, document state, shared_link_url + embed_url (the one signing link every signer can use, each entering the PIN from their own signing email; embed_url is the iframe form), and the full audit trail (crm_signing_audit_log: sent/viewed/signed/split_generated/certificate_delivered/sealed_pdf_generated events, each with a metadata payload, e.g. emailed_to / shared_portal_ids for delivery events).

1 parameter
Name In Type Required Description
envelope_id path string yes

📄 /api/signing/get-signing-envelopes-envelope-id.md

GET /signing/envelopes Scopes: documents:read

List e-signing envelopes (documents sent for signature). Each envelope carries shared_link_url (the one signing link every signer can use; each enters the PIN from their own signing email) and embed_url (the same link for an iframe on a website or portal page).

4 parameters
Name In Type Required Description
limit query number no
after query string no
status query string no
deal_id query string no

📄 /api/signing/get-signing-envelopes.md

POST /signing/envelopes/:envelope_id/resend Scopes: signing:send

Resend a signing envelope (re-deliver the request to a specific pending signer). Use get_signing_envelope first to find the recipient_id (crm_signing_recipients[].id) for the signer to resend to.

3 parameters
Name In Type Required Description
envelope_id path string yes
recipient_id body string yes The pending signer to resend to (crm_signing_recipients.id, from get_signing_envelope).
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/signing/post-signing-envelopes-envelope-id-resend.md

POST /signing/send Scopes: signing:send

Send a document for e-signature. Requires a template and signers. An anchor (deal_id for an opportunity, or referrer_contact_id for a referrer contact) links it to a record, and is required only for the portal modes below; a plain email send can go without one. Works with section templates and imported-PDF templates (import_document_template). Costs credits. Two delivery modes: default emails the signer a standalone e-sign link; deliver:"portal" instead surfaces the signing item inside the anchored client portal (find-or-created from portal_template_id) and SUPPRESSES the standalone email, so the signer signs in the portal reached via the portal invite. Set show_in_portal:true (additive) to surface the item in the portal AND still email the link.

12 parameters
Name In Type Required Description
template_id body string yes
signers body array yes
deal_id body string no Opportunity anchor. Provide this OR referrer_contact_id. (opportunity_id is accepted as an alias.)
opportunity_id body string no Alias for deal_id (the opportunity anchor).
referrer_contact_id body string no Referrer anchor (a crm_contacts id). Provide this OR deal_id. The portal is anchored to this contact.
subject body string no
message body string no
deliver body string no Delivery mode. "email" (default) sends the standalone e-sign link. "portal" surfaces the item in the anchored portal and suppresses the email.
portal_template_id body string no Portal template to stamp a fresh portal from when none exists for the anchor (used with deliver:"portal").
portal_members body array no Extra people to invite to the portal as members/viewers beyond the signers (deliver:"portal"). Each { email, name? }. Signers are invited automatically.
show_in_portal body boolean no Additive: surface the signing item inside the anchored client portal AND still email the link. Default false.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/signing/post-signing-send.md

POST /signing/envelopes/:envelope_id/void Scopes: signing:send

Void a sent envelope (cancel the signing request).

2 parameters
Name In Type Required Description
envelope_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/signing/post-signing-envelopes-envelope-id-void.md

Forms (25 endpoints)

Create form templates, manage fields, send forms to contacts, and view submissions. Field types: text, textarea, number, date, select, checkbox, radio, file, spreadsheet, email, phone, url, currency, consent, rating, abn. Fields support CRM Variable Injection - map a field to a CRM variable so submitted values are written back to the linked deal, contact, or account before automations run. Set settings.public_mode=true to make a form publicly submittable (auto-creates contacts and opportunities on each submission).

📄 Full markdown: /api/forms.md

POST /forms/templates/:template_id/fields Scopes: forms:write

Add a field to a form template. The content JSON carries per-field config: static min/max on number fields (content.min/max), conditional visibility (content.visibility), and CROSS-FIELD VALIDATION on number/date fields via content.validation = { match: 'all'|'any', rules: [{ operator: 'lte'|'gte'|'lt'|'gt'|'date_after'|'date_before', target: { kind: 'literal', value: <number> } OR { kind: 'field', fieldId: '<other field id>', factor?: <number>, divideBy?: <number> }, message: '<shown when it fails>' }] }. Blocks submission when the value breaks the rule (e.g. IP benefit <= 70% of annual income / 12 => operator 'lte', target { kind:'field', fieldId:'<income field>', factor:0.7, divideBy:12 }); the target can be any other same-type field.

12 parameters
Name In Type Required Description
template_id path string yes UUID of the form template to add the field to.
type body string yes Field type, e.g. text, textarea, email, phone, number, date, select, checkbox, radio, file, signature, content. Drives how the field renders and validates on the public form. signature shows a draw-or-type signature pad; when submitting through the API, send its value as a PNG data URL (data:image/png;base64,...). It is stored as an image file and the answer reads "Signed".
label body string yes Visible label for the field on the public form.
placeholder body string no Placeholder text shown inside the empty input.
description body string no Helper text shown under the field to explain what to enter.
is_required body boolean no Whether the form cannot be submitted without this field.
is_visible body boolean no Whether the field renders at all. False hides it without deleting it or losing existing answers.
order_index body number no Position of the field within its step. Fields render in ascending order.
step_number body number no Which step of a multi-step form this field belongs to. Starts at 1.
content body object no Per-field JSON config: options, crm_variable/crm_variable_mode, min/max, visibility, and validation (cross-field rule set; see the tool description). crm_variable_mode is "write" (fill only when the CRM value is empty) or "overwrite"; leaving it out with a crm_variable set stores "overwrite". sensitive (bool) marks a text answer as Sensitive, e.g. a tax file number: it is stored encrypted, every read returns a masked copy like "••••••782" (read the real value with reveal_sensitive_field), and once true it cannot be set back. A field wired to a Sensitive custom field is Sensitive automatically. Wired answers are written to the CRM when the recipient submits, not while they type. contact.* is always the opportunity's main contact. To capture someone else on the same form (a director, a joint applicant), wire their fields to person.<role>.<field>, e.g. person.director.first_name or person.director.metadata.<custom_field_id>, where <role> is lowercase letters, digits and underscores and <field> is any contact field; on submit that person is found by email, then phone, or created, linked to the opportunity with the role, and their answers go to their own contact. If the opportunity has no company and the form answers account.* fields, submitting finds the company by account.tax_number, then by account.name, or creates it from account.name, and links it to the opportunity and its contact.
styling body object no Per-field presentation overrides (width, alignment and similar). Full-replace on write.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/forms/post-forms-templates-template-id-fields.md

POST /forms/templates/:template_id/archive Scopes: forms:write

Archive a form template.

2 parameters
Name In Type Required Description
template_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/forms/post-forms-templates-template-id-archive.md

POST /forms/templates/:template_id/wiring Scopes: forms:write

Bulk-set CRM-variable wiring on form fields. Wired answers are written to the CRM when the recipient submits, not while they type. contact.* is always the opportunity's main contact. To capture someone else on the same form (a director, a joint applicant), wire their fields to person.<role>.<field>, e.g. person.director.first_name or person.director.metadata.<custom_field_id>, where <role> is lowercase letters, digits and underscores and <field> is any contact field; on submit that person is found by email, then phone, or created, linked to the opportunity with the role, and their answers go to their own contact. If the opportunity has no company and the form answers account.* fields, submitting finds the company by account.tax_number, then by account.name, or creates it from account.name, and links it to the opportunity and its contact.

4 parameters
Name In Type Required Description
template_id path string yes
mappings body array no Each entry is { field_id, crm_variable, crm_variable_mode }. crm_variable may be person.<role>.<field> for a second person on the opportunity.
create_custom_fields body array no Custom fields to persist on company_settings before mapping. Each is { label, type, entity, options? }.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/forms/post-forms-templates-template-id-wiring.md

POST /forms/folders Scopes: forms:write

Create a form folder.

2 parameters
Name In Type Required Description
name body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/forms/post-forms-folders.md

POST /forms/templates Scopes: forms:write

Create a form template.

7 parameters
Name In Type Required Description
name body string yes Form name shown in the Forms list and as the default heading on the public form.
description body string no Short description of the form, shown under the heading on the public form.
settings body object no Free-form per-template config. On update_form_template the top-level keys you send are MERGED into the stored settings - unrelated keys (e.g. public_mode, completionMessage) are preserved, so you can PATCH a single key without fetching first. To clear a key, send it explicitly as null (omitting it keeps the stored value). Note create_form_template writes settings as given (there is nothing to merge into). Known keys: public_mode (bool) - form must have this true to be publicly submittable via submit_public_form / the hosted form page. public_pipeline_id / public_stage_id (uuid) - where a newly-created opportunity lands; falls back to the workspace default pipeline then its first stage. That fallback places NEW opportunities only: when a submission matches an opportunity that already exists, its card is left exactly where it is unless this form names a pipeline explicitly. A matched opportunity that is already won or lost is never moved either way (SR ea00e210). public_lead_source (string) - lead_source recorded on the new contact/deal, defaults to "Public form". public_deal_behavior ("create" default | "update_or_create" | "never_create" | "update_by_reference") - controls whether a submission from a contact who already has a matching deal updates that deal instead of always creating a new one. "never_create" only ever attaches to an existing match and never creates a deal. Mirrors booking_deal_behavior on scheduled event types. "update_by_reference" (SR 912d70fd) is for staff forms that update ONE existing opportunity found by a number typed into the form (e.g. a job number): it never creates a contact, account or opportunity, needs no email field, and refuses the submission with a message shown on the form (writing nothing) when the number is not found, matches more than one card, or the card is not in an allowed stage. On a match it links the submission to that opportunity, writes the other wired fields onto it, and fires form_completed automations (use those to move the card or send emails). update_by_reference keys: public_match_deal_field (string, required) - the deal custom field key to look up by, e.g. "job_number"; the form field wired to deal.metadata.<key> holds the typed number and is never written back. public_match_stage_ids (uuid[]) - the card must be in one of these stages. public_match_pipeline_id (uuid) - the card must sit on this pipeline. public_match_strip_prefix (string, e.g. "NSJ-") - the number is found with or without this prefix. public_match_not_found_message / public_match_wrong_stage_message (string) - the messages shown on the form. public_match_fields (string[], default ["email"]) - which identity fields to match an existing contact on. "name" is never honoured (fuzzy-name-collision risk). public_match_pipeline_id (uuid) - restrict deal matching to one pipeline; unset searches all pipelines. public_match_status_filter ("all" | "open" | "won" | "lost", default "open") - which deal statuses are eligible to match. public_match_strategy ("most_recent" | "highest_value" | "oldest", default "most_recent") - tie-breaker when multiple deals match. archivePdfOnSubmit (bool) - renders the completed responses to PDF and files it to the linked record. Costs 1 credit per page. attachPdfToNotifications (bool, default false) - also attaches that PDF to the completion notification emails, so a recipient with no CRM login gets the completed form as a file they can print or forward. Requires archivePdfOnSubmit (nothing is rendered without it) and costs no extra credits, since it attaches the PDF already rendered. start_fresh_on_resend (bool, default false) - when this form is sent again on the same opportunity, the previous answers are recorded on the opportunity timeline and cleared, so the recipient starts blank (for quote approvals and other decisions). When false, a re-send keeps the answers so a draft can be resumed. The resend_form_submission action (same request, new email) never clears answers. completionMessage (string), notifyEmails (string[]), submissionFolder (string), stepCount (number), stepHeaders (object) are also recognised builder-managed keys.
folder body string no Folder name this form is filed under in the Forms list. Pass null to move it back to the root.
slug body string no URL-safe segment for the public form link. Must be unique among the workspace's live forms: a slug another form already has is refused. Leave it out when creating and one is made from the name (with -2, -3 added if needed). Changing it changes the public URL and breaks any link already shared.
email_template_settings body object no Per-form override of the email chrome used for this form's invite and completion emails. Takes precedence over the workspace-wide email_template_settings. Shape: { intro_html, subject_override }. Full-replace on write; pass null to fall back to the workspace default.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/forms/post-forms-templates.md

POST /forms/submissions/internal-fill Scopes: forms:write

Create an internal-fill submission for staff entry. No email, PIN pre-cleared.

3 parameters
Name In Type Required Description
template_id body string yes
deal_id body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/forms/post-forms-submissions-internal-fill.md

DELETE /forms/templates/:template_id/fields/:field_id Scopes: forms:delete

Delete a form field.

3 parameters
Name In Type Required Description
template_id path string yes
field_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/forms/delete-forms-templates-template-id-fields-field-id.md

DELETE /forms/folders/:folder_id Scopes: forms:delete

Delete a form folder.

2 parameters
Name In Type Required Description
folder_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/forms/delete-forms-folders-folder-id.md

DELETE /forms/submissions/:submission_id Scopes: forms:delete

Permanently delete a form submission (and any uploaded files). Optional ?delete_archived_pdf=true also removes the auto-archived PDF in the Documents library. Cannot be undone.

3 parameters
Name In Type Required Description
submission_id path string yes
delete_archived_pdf query boolean no When true, also deletes the auto-archived PDF stored in crm_documents from this submission.
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/forms/delete-forms-submissions-submission-id.md

DELETE /forms/templates/:template_id Scopes: forms:delete

Delete a form template.

2 parameters
Name In Type Required Description
template_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/forms/delete-forms-templates-template-id.md

POST /forms/templates/:template_id/duplicate Scopes: forms:write

Duplicate a form template.

2 parameters
Name In Type Required Description
template_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/forms/post-forms-templates-template-id-duplicate.md

GET /forms/submissions Scopes: forms:read

List form submissions. Pass include_responses: true to embed every answer inline (same shape as get_form_submission) instead of one call per submission, with signed URLs on uploaded files. Filter on answers with response_field plus response_value / response_value_from / response_value_to. To pull one account's submissions where the form is opportunity-scoped, use deal_customer_id (the account on the opportunity), not customer_id.

13 parameters
Name In Type Required Description
template_id query string no
include_responses query boolean no Embed form_submission_responses inline on each submission, with a files[] array carrying short-lived signed_url values for uploads. Avoids an N+1 of get_form_submission calls.
deal_id query string no Opportunity id, or a comma separated list of them. Alias: opportunity_id.
opportunity_id query string no Alias for deal_id. Accepts a comma separated list.
deal_customer_id query string no Account (customer) id on the OPPORTUNITY, comma separated list allowed. Use this to pull every submission for one account when the form is opportunity-scoped, where each opportunity is a single event and deal_id therefore cannot group them.
contact_id query string no Contact id, or a comma separated list of them.
customer_id query string no Account id set directly on the submission. Usually null on opportunity-scoped forms; prefer deal_customer_id there.
status query string no Submission status, e.g. completed, sent, expired.
response_field query string no Field to filter answers on, by field label or field id. Required to use any response_value filter.
response_value query string no Exact answer match on response_field.
response_value_from query string no Lower bound on response_field. TEXT comparison: correct for ISO dates (2026-05-04), not valid for numbers.
response_value_to query string no Upper bound on response_field. TEXT comparison: correct for ISO dates, not valid for numbers.
limit query number no

📄 /api/forms/get-forms-submissions.md

GET /forms/templates Scopes: forms:read

List form templates.

1 parameter
Name In Type Required Description
limit query number no

📄 /api/forms/get-forms-templates.md

POST /forms/templates/:template_id/fields/reorder Scopes: forms:write

Reorder fields on a form template.

3 parameters
Name In Type Required Description
template_id path string yes
field_ids body array yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/forms/post-forms-templates-template-id-fields-reorder.md

POST /forms/submissions/:submission_id/resend Scopes: forms:write

Resend a form submission invitation.

2 parameters
Name In Type Required Description
submission_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/forms/post-forms-submissions-submission-id-resend.md

POST /forms/send Scopes: forms:send

Send a form to a recipient. Costs credits. Requires at least one of deal_id, contact_id or customer_id so the submission links to a CRM record. To send to co-applicants (e.g. joint mortgage applicants) pass additional_recipients: they SHARE one submission with the primary recipient, each gets their own link + PIN, all write to the same answers, and any of them can finalize.

14 parameters
Name In Type Required Description
template_id body string yes UUID of the form template to send. Get ids from list_form_templates.
recipient_email body string yes Primary recipient email. Owns the shared submission.
recipient_name body string yes Primary recipient name, used to address the invite email.
deal_id body string no Opportunity to link the submission to. Preferred when the form is opportunity-scoped. At least one of deal_id, contact_id or customer_id is required.
contact_id body string no Contact to link the submission to. Sufficient on its own for a standalone client-profile form. At least one of deal_id, contact_id or customer_id is required.
customer_id body string no Company/account to link the submission to. At least one of deal_id, contact_id or customer_id is required.
personal_message body string no Short personal note from the sender, rendered inside the invite email above the form link.
intro_html body string no HTML body for this send only, overriding the per-template and workspace email chrome. Wins over both.
subject_override body string no Subject line for this send only, overriding the per-template and workspace default.
expires_in_days body number no How many days the form link stays valid. Omit to use the platform default.
notify_emails body array no Addresses to notify when this submission is completed, for this send only. Merged with the per-template and workspace-level notify lists.
additional_recipients body array no Co-applicants who share ONE submission with the primary recipient. Each gets their own secure link + PIN; all write to the same responses; any can submit to finalize.
prefill body object no Prefill values keyed by each field's prefill key (set in the form builder under "Prefill via link"). Seeded onto the submission so the recipient opens the form pre-populated; fields set to hide-when-prefilled are filled silently. Example: { "broker_last_name": "Smith" }.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/forms/post-forms-send.md

POST /forms/templates/:template_id/submit Scopes: forms:write

Submit a public form. Find-or-creates a contact + opportunity from the submitted answers (matched by the fields wired to contact.email / first_name / last_name / phone / account.name), then writes every wired field onto the records. The template must have public mode enabled. Credit-free.

4 parameters
Name In Type Required Description
template_id path string yes Form template id (must have public mode enabled).
responses body array no Submitted answers.
fields body object no Convenience alternative to responses — an object keyed by form field id with the answer as the value.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/forms/post-forms-templates-template-id-submit.md

PATCH /forms/templates/:template_id/fields/:field_id Scopes: forms:write

Update a form field. Pass content to change per-field config: min/max, visibility, CRM wiring, or cross-field validation. content.validation = { match: 'all'|'any', rules: [{ operator: 'lte'|'gte'|'lt'|'gt'|'date_after'|'date_before', target: { kind: 'literal', value: <number> } OR { kind: 'field', fieldId: '<other field id>', factor?: <number>, divideBy?: <number> }, message: '<shown when it fails>' }] } (number/date fields; blocks submission on a failing value). Pass { validation: null } inside content to clear it.

13 parameters
Name In Type Required Description
template_id path string yes UUID of the form template the field belongs to.
field_id path string yes UUID of the field to update. Get ids from get_form_template.
type body string no Field type, e.g. text, textarea, email, phone, number, date, select, checkbox, radio, file, signature, content. Drives how the field renders and validates on the public form. signature shows a draw-or-type signature pad; when submitting through the API, send its value as a PNG data URL (data:image/png;base64,...). It is stored as an image file and the answer reads "Signed".
label body string no Visible label for the field on the public form.
placeholder body string no Placeholder text shown inside the empty input.
description body string no Helper text shown under the field to explain what to enter.
is_required body boolean no Whether the form cannot be submitted without this field.
is_visible body boolean no Whether the field renders at all. False hides it without deleting it or losing existing answers.
order_index body number no Position of the field within its step. Fields render in ascending order.
step_number body number no Which step of a multi-step form this field belongs to. Starts at 1.
content body object no Per-field JSON config: options, crm_variable/crm_variable_mode, min/max, visibility, and validation (cross-field rule set; see the tool description). crm_variable_mode is "write" (fill only when the CRM value is empty) or "overwrite"; leaving it out with a crm_variable set stores "overwrite". sensitive (bool) marks a text answer as Sensitive, e.g. a tax file number: it is stored encrypted, every read returns a masked copy like "••••••782" (read the real value with reveal_sensitive_field), and once true it cannot be set back. A field wired to a Sensitive custom field is Sensitive automatically. Wired answers are written to the CRM when the recipient submits, not while they type. contact.* is always the opportunity's main contact. To capture someone else on the same form (a director, a joint applicant), wire their fields to person.<role>.<field>, e.g. person.director.first_name or person.director.metadata.<custom_field_id>, where <role> is lowercase letters, digits and underscores and <field> is any contact field; on submit that person is found by email, then phone, or created, linked to the opportunity with the role, and their answers go to their own contact. If the opportunity has no company and the form answers account.* fields, submitting finds the company by account.tax_number, then by account.name, or creates it from account.name, and links it to the opportunity and its contact.
styling body object no Per-field presentation overrides (width, alignment and similar). Full-replace on write.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/forms/patch-forms-templates-template-id-fields-field-id.md

PATCH /forms/folders/:folder_id Scopes: forms:write

Update a form folder.

3 parameters
Name In Type Required Description
folder_id path string yes UUID of the form folder to update. Get ids from list_form_folders.
name body string no Folder name shown in the Forms list.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/forms/patch-forms-folders-folder-id.md

PATCH /forms/templates/:template_id Scopes: forms:write

Update a form template.

8 parameters
Name In Type Required Description
template_id path string yes UUID of the form template to update. Get ids from list_form_templates.
name body string no Form name shown in the Forms list and as the default heading on the public form.
description body string no Short description of the form, shown under the heading on the public form.
settings body object no Free-form per-template config. On update_form_template the top-level keys you send are MERGED into the stored settings - unrelated keys (e.g. public_mode, completionMessage) are preserved, so you can PATCH a single key without fetching first. To clear a key, send it explicitly as null (omitting it keeps the stored value). Note create_form_template writes settings as given (there is nothing to merge into). Known keys: public_mode (bool) - form must have this true to be publicly submittable via submit_public_form / the hosted form page. public_pipeline_id / public_stage_id (uuid) - where a newly-created opportunity lands; falls back to the workspace default pipeline then its first stage. That fallback places NEW opportunities only: when a submission matches an opportunity that already exists, its card is left exactly where it is unless this form names a pipeline explicitly. A matched opportunity that is already won or lost is never moved either way (SR ea00e210). public_lead_source (string) - lead_source recorded on the new contact/deal, defaults to "Public form". public_deal_behavior ("create" default | "update_or_create" | "never_create" | "update_by_reference") - controls whether a submission from a contact who already has a matching deal updates that deal instead of always creating a new one. "never_create" only ever attaches to an existing match and never creates a deal. Mirrors booking_deal_behavior on scheduled event types. "update_by_reference" (SR 912d70fd) is for staff forms that update ONE existing opportunity found by a number typed into the form (e.g. a job number): it never creates a contact, account or opportunity, needs no email field, and refuses the submission with a message shown on the form (writing nothing) when the number is not found, matches more than one card, or the card is not in an allowed stage. On a match it links the submission to that opportunity, writes the other wired fields onto it, and fires form_completed automations (use those to move the card or send emails). update_by_reference keys: public_match_deal_field (string, required) - the deal custom field key to look up by, e.g. "job_number"; the form field wired to deal.metadata.<key> holds the typed number and is never written back. public_match_stage_ids (uuid[]) - the card must be in one of these stages. public_match_pipeline_id (uuid) - the card must sit on this pipeline. public_match_strip_prefix (string, e.g. "NSJ-") - the number is found with or without this prefix. public_match_not_found_message / public_match_wrong_stage_message (string) - the messages shown on the form. public_match_fields (string[], default ["email"]) - which identity fields to match an existing contact on. "name" is never honoured (fuzzy-name-collision risk). public_match_pipeline_id (uuid) - restrict deal matching to one pipeline; unset searches all pipelines. public_match_status_filter ("all" | "open" | "won" | "lost", default "open") - which deal statuses are eligible to match. public_match_strategy ("most_recent" | "highest_value" | "oldest", default "most_recent") - tie-breaker when multiple deals match. archivePdfOnSubmit (bool) - renders the completed responses to PDF and files it to the linked record. Costs 1 credit per page. attachPdfToNotifications (bool, default false) - also attaches that PDF to the completion notification emails, so a recipient with no CRM login gets the completed form as a file they can print or forward. Requires archivePdfOnSubmit (nothing is rendered without it) and costs no extra credits, since it attaches the PDF already rendered. start_fresh_on_resend (bool, default false) - when this form is sent again on the same opportunity, the previous answers are recorded on the opportunity timeline and cleared, so the recipient starts blank (for quote approvals and other decisions). When false, a re-send keeps the answers so a draft can be resumed. The resend_form_submission action (same request, new email) never clears answers. completionMessage (string), notifyEmails (string[]), submissionFolder (string), stepCount (number), stepHeaders (object) are also recognised builder-managed keys.
folder body string no Folder name this form is filed under in the Forms list. Pass null to move it back to the root.
slug body string no URL-safe segment for the public form link. Must be unique among the workspace's live forms: a slug another form already has is refused. Leave it out when creating and one is made from the name (with -2, -3 added if needed). Changing it changes the public URL and breaks any link already shared.
email_template_settings body object no Per-form override of the email chrome used for this form's invite and completion emails. Takes precedence over the workspace-wide email_template_settings. Shape: { intro_html, subject_override }. Full-replace on write; pass null to fall back to the workspace default.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/forms/patch-forms-templates-template-id.md

POST /forms/submissions/:submission_id/void Scopes: forms:write

Void a form submission.

2 parameters
Name In Type Required Description
submission_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/forms/post-forms-submissions-submission-id-void.md

Websites (34 endpoints)

Manage TrustPager-hosted websites, pages, and page sections.

📄 Full markdown: /api/websites.md

POST /websites/:website_id/deploy/domain Scopes: websites:write

Attach a custom domain to a website's deployment and return the exact DNS record(s) the owner must add at their registrar to point the domain at the site. Attaches both the bare domain and its "www" (or vice-versa) and sets one canonical host, 308-redirecting the other to it. Also stores the records on the website so the UI can show them.

4 parameters
Name In Type Required Description
website_id path string yes
domain body string yes The custom domain to point at the site, e.g. "www.example-client.com".
canonical body string no Which host is canonical: "www" (default) redirects the bare domain to www; "apex" redirects www to the bare domain.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/post-websites-website-id-deploy-domain.md

POST /websites/:website_id/redirect-domains Scopes: websites:write

Point a legacy or secondary domain at a live website as a redirect-only domain: it 301-redirects every visitor (and their path + query) cross-domain to the site, so retired URLs keep their SEO value instead of 404ing. Does NOT serve the site or touch its hosting. website_id is the TARGET site to redirect to. Returns the A/AAAA DNS records the domain owner adds at their registrar to point it at our redirect service. Use for domain consolidations, rebrands, or capturing traffic from an old domain. Optional landing_path sends EVERY visitor to one page of the site instead of keeping their path, with {domain} replaced by the name they typed: use it for parked domains that should land on a "this domain may be for sale" page (a temporary 302, so a sold domain is not remembered by browsers).

4 parameters
Name In Type Required Description
website_id path string yes The TARGET website the legacy domain should redirect to.
redirect_domain body string yes The legacy/secondary domain to redirect FROM, e.g. "oldbrand.com".
landing_path body string no Optional. A path on the target site that every request lands on, instead of the visitor's own path. {domain} is replaced with the name they typed, e.g. "/available/?domain={domain}". Must start with "/". Leave out to keep the visitor's path.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/post-websites-website-id-redirect-domains.md

POST /websites Scopes: websites:write

Create a new website. Registers the site record (site_key, domain, branding) used by public-form embeds and the website lead gateway.

19 parameters
Name In Type Required Description
name body string yes Site name shown in the Websites list. Internal label, not rendered on the site.
domain body string no Live custom domain as a BARE HOSTNAME, e.g. "www.example.com". A pasted full URL is normalized to the bare host on write. Pass null to unset.
staging_domain body string no Preview/staging hostname, bare like `domain`. A pasted full URL is normalized on write. Pass null to unset.
additional_domains body array no Any other hostnames this site is served from, as bare hostnames, e.g. ["shop.example.com"]. These are allowed through the website lead gateway AND registered on the site's bot-protection widget, so a host missing here is one that gets rejected. `domain` and its www/apex counterpart, the staging hostname and the deployed URL are already included and do not need repeating. Full-replace on write; pass [] or null to clear.
is_public body boolean no Whether the published site is reachable. False keeps it deployed but private.
brand_colors body object no Site colour palette, e.g. { "primary": "#0f172a", "secondary": "#38bdf8", "accent": "#f97316" }. Full-replace on write.
brand_typography body object no Font choices for the site, e.g. { "heading": "Inter", "body": "Inter" }. Full-replace on write.
navbar body object no Navigation bar configuration: links, layout and CTA. Full-replace on write — read the current value first and send it back modified.
footer body object no Footer configuration: columns, links, social and legal text. Full-replace on write.
lead_gen_form body object no Lead-capture form config for the site. Submissions post to the public website trigger endpoint using the site_key and land in the CRM. Full-replace on write.
default_meta_title body string no Fallback <title> for pages that do not set their own. Per-page values win.
default_meta_description body string no Fallback meta description for pages that do not set their own. Per-page values win.
default_og_image body string no Absolute URL of the fallback Open Graph / social share image (1200x630).
google_tag_manager_id body string no Google Tag Manager container id, e.g. "GTM-XXXXXXX". Injected into every page.
microsoft_clarity_id body string no Microsoft Clarity project id. Injected into every page.
header_scripts body string no Raw HTML injected into <head> on every page. Runs on the live site — only add markup you trust.
footer_scripts body string no Raw HTML injected before </body> on every page. Runs on the live site — only add markup you trust.
github_repo body string no Repository backing this site, as "owner/repo". Connects the site to its source; the deploy fields themselves are managed and not writable here.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/post-websites.md

POST /websites/:website_id/repo Scopes: websites:write

Create a GitHub repository for this website in our org (from the website starter template) and store it on the website record. Use this to spin up a brand-new site before provisioning its deployment. Optional: repo_name, private (default true), description.

5 parameters
Name In Type Required Description
website_id path string yes
repo_name body string no Repository name (defaults to a slug of the website name).
private body boolean no Whether the repo is private. Defaults to true.
description body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/post-websites-website-id-repo.md

POST /websites/:website_id/turnstile Scopes: websites:write

Set up bot protection (Turnstile) for a website: creates the bot-protection widget covering every hostname the site is served from, and returns the Turnstile Site Key plus a copy-paste guide for adding the widget to the site's forms. Protection is NOT switched on by this call, so nothing is rejected yet. Add the widget to the site first, load a page that has one, then switch it on with update_website_turnstile (enabled: true) - switching it on before the site can pass a bot check is refused, because it would reject every form, booking and lead submission. The website needs a domain or a deployed URL first. Optional mode: managed (default, shows a checkbox when the visitor looks suspicious), non-interactive (near-invisible, no checkbox), invisible (fully invisible).

5 parameters
Name In Type Required Description
website_id path string yes
mode body string no Widget interaction mode. Defaults to managed.
enabled body boolean no Switch protection on immediately. Defaults to false, and is refused unless the site has already been seen passing a bot check (or force is true), because arming a site whose forms have no widget rejects every submission.
force body boolean no Switch protection on even though the site has never been seen passing a bot check. Only use when you have confirmed the widget is already live on the site's forms.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/post-websites-website-id-turnstile.md

DELETE /websites/:website_id Scopes: websites:delete

Delete a website and its associated content. This action cannot be undone.

2 parameters
Name In Type Required Description
website_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/delete-websites-website-id.md

DELETE /websites/:website_id/analytics Scopes: websites:write

Turn traffic reporting off for a website and stop collecting its page views. Historical data is discarded. The tracking snippet can be left in the site; it reports nowhere once this is done.

2 parameters
Name In Type Required Description
website_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/delete-websites-website-id-analytics.md

POST /websites/:website_id/analytics Scopes: websites:write

Connect a website to traffic reporting, so its page views and broken URLs can be read straight from the website record instead of the client's own analytics. Requires that Web Analytics has already been switched on for this site in the hosting dashboard (a one-time manual step); this finds that and links it to the website record. Returns a tracking snippet that must be added to the site's <head> before any data arrives; nothing is reported until it is. Safe to call twice: an already-connected site returns its existing snippet.

2 parameters
Name In Type Required Description
website_id path string yes UUID of the website. Get ids from list_websites.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/post-websites-website-id-analytics.md

GET /websites/:website_id Scopes: websites:read

Fetch a website by UUID, including site_key, domain, branding, navbar, footer, and SEO configuration.

1 parameter
Name In Type Required Description
website_id path string yes

📄 /api/websites/get-websites-website-id.md

GET /websites/:website_id/broken-urls Scopes: websites:read

Find the URLs on a website that visitors are hitting but that are not real pages, worst first. For each one: the actual HTTP status (checked live, so 404 means 404), how many page views it took, and the referrer sending people there, which is what tells you whether to fix a link or add a redirect. Answers "which page is broken and how badly". Compares real traffic against the site's own sitemap, then status-checks the top 25 suspects. Requires traffic reporting to be on (enable_website_traffic_reporting).

3 parameters
Name In Type Required Description
website_id path string yes UUID of the website. Get ids from list_websites.
from query string no Start of the window, YYYY-MM-DD or an ISO timestamp. Defaults to 7 days ago.
to query string no End of the window, YYYY-MM-DD or an ISO timestamp. Defaults to now.

📄 /api/websites/get-websites-website-id-broken-urls.md

GET /websites/:website_id/deploy/status Scopes: websites:read

Get the current deployment status for a website (building, live, or error), including the latest build stage, deployment URL, and when it last deployed.

1 parameter
Name In Type Required Description
website_id path string yes

📄 /api/websites/get-websites-website-id-deploy-status.md

GET /websites/:website_id/deploy/domain Scopes: websites:read

Check whether a website's attached custom domain has verified (DNS propagated) and return the stored DNS records. Each host also reports "recoverable": true when the domain provider has stopped trying to verify it, which means the owner's DNS is NOT the problem and retry_website_domain is needed - do not tell the owner to re-check their DNS in that case.

1 parameter
Name In Type Required Description
website_id path string yes

📄 /api/websites/get-websites-website-id-deploy-domain.md

GET /websites/:website_id/integration-guides/:system Scopes: websites:read

Get the full copy-paste guide for wiring one TrustPager system into a website, pre-filled with this site's real site_key + slug (and its Turnstile Site Key, if Bot Protection is on). Hand the returned "guide" text straight to a web designer or their AI. system: notepads, email, documents, forms, scheduling, order-forms, stripe, ecommerce, docusign, voice-agent, voice-agent-outbound, outgoing-webhook, native-integrations, turnstile. Call list_website_integration_guides to see them all.

2 parameters
Name In Type Required Description
website_id path string yes
system path string yes Which system to integrate: notepads | email | documents | forms | scheduling | order-forms | stripe | ecommerce | docusign | voice-agent | voice-agent-outbound | outgoing-webhook | native-integrations | turnstile.

📄 /api/websites/get-websites-website-id-integration-guides-system.md

GET /websites/:website_id/traffic Scopes: websites:read

Get a website's traffic: page views and visits for a date range, grouped by page path (default), referrer, country, device, browser, or date. Answers "is traffic up or down since we changed X" and "which pages do people actually read". Bot traffic is excluded. Requires traffic reporting to be on (enable_website_traffic_reporting). For finding broken URLs specifically, use get_website_broken_urls instead.

5 parameters
Name In Type Required Description
website_id path string yes UUID of the website. Get ids from list_websites.
from query string no Start of the window, YYYY-MM-DD or an ISO timestamp. Defaults to 7 days ago. Windows wider than 92 days are rejected, and data older than 7 days is sampled by the hosting layer.
to query string no End of the window, YYYY-MM-DD or an ISO timestamp. Defaults to now.
group_by query string no What to break the numbers down by. Defaults to path.
limit query number no Maximum rows to return, 1 to 500. Defaults to 50.

📄 /api/websites/get-websites-website-id-traffic.md

GET /websites/:website_id/analytics Scopes: websites:read

Check whether traffic reporting is on for a website, whether its tracking snippet is installed on the live site, and get the snippet to install. Call this first if get_website_traffic or get_website_broken_urls returns no data.

1 parameter
Name In Type Required Description
website_id path string yes

📄 /api/websites/get-websites-website-id-analytics.md

GET /websites/:website_id/turnstile Scopes: websites:read

Get a website's bot protection status: whether it's configured, enabled, its widget mode, and its Turnstile Site Key (the public key custom-built forms render the Turnstile widget with). Also returns `hostnames`, the exact list of hosts the widget accepts and the website lead gateway allows (a visitor on a host missing from this list is rejected), and `wiring_seen_at` / `wiring_seen_via` / `wiring_seen_host` - when we last saw this site actually pass a bot check, and from which host. A null wiring_seen_at means the site has never sent one, and switching protection on would reject every submission. Never returns the widget secret.

1 parameter
Name In Type Required Description
website_id path string yes

📄 /api/websites/get-websites-website-id-turnstile.md

GET /websites/:website_id/integration-guides Scopes: websites:read

List the copy-paste integration guides available for a website (e.g. blog). Each entry says whether anything is published yet. Use get_website_integration_guide to fetch the full instructions.

1 parameter
Name In Type Required Description
website_id path string yes

📄 /api/websites/get-websites-website-id-integration-guides.md

GET /websites/:website_id/redirect-domains Scopes: websites:read

List the redirect-only domains pointing at a website, each with its DNS records, its landing_path (null when it keeps the visitor's path) and whether it is live (actually redirecting to the site yet). Domains not yet live are checked live, up to 20 per call; "checked": false on a pending domain means this call did not check it, so list again later rather than treating it as broken.

1 parameter
Name In Type Required Description
website_id path string yes

📄 /api/websites/get-websites-website-id-redirect-domains.md

GET /websites/:website_id/redirects Scopes: websites:read

List the path redirects on a website: each source path, where it sends visitors, and its status code. Also returns how many redirect slots are used against the limit. These are paths on this site (e.g. "/video"), NOT whole legacy domains - for those use list_website_redirect_domains.

1 parameter
Name In Type Required Description
website_id path string yes UUID of the website. Get ids from list_websites.

📄 /api/websites/get-websites-website-id-redirects.md

POST /websites/:website_id/deploy/provision Scopes: websites:write

Provision a git-connected TrustPager hosting deployment for this website's connected GitHub repo. Creates the hosting project so every push auto-builds and deploys. The repo must already be connected (via create_website_repo). Optional: project_name, branch (default main), build_command (default "npm run build"), output_dir (default "dist"), root_dir. Returns the production and staging URLs.

7 parameters
Name In Type Required Description
website_id path string yes
project_name body string no
branch body string no
build_command body string no
output_dir body string no
root_dir body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/post-websites-website-id-deploy-provision.md

POST /websites/:website_id/redirects/publish Scopes: websites:write

Publish a website's redirects to the live site in one rebuild. Only needed after adding several redirects with publish:false, or to retry a publish that failed. A single set_website_redirect already publishes itself.

2 parameters
Name In Type Required Description
website_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/post-websites-website-id-redirects-publish.md

POST /websites/:website_id/deploy Scopes: websites:write

Trigger a fresh deployment of the website from its connected git branch (default main). Use to rebuild without pushing new code.

3 parameters
Name In Type Required Description
website_id path string yes
branch body string no Branch to deploy. Defaults to main.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/post-websites-website-id-deploy.md

DELETE /websites/:website_id/deploy Scopes: websites:delete

Take a website offline: delete its TrustPager hosting deployment (the hosting project). The GitHub repo is kept. Acts only on this website's own deployment.

2 parameters
Name In Type Required Description
website_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/delete-websites-website-id-deploy.md

DELETE /websites/:website_id/redirects/:redirect_id Scopes: websites:delete

Remove one path redirect from a website and republish the live site without it. Identify it by its id from list_website_redirects. To switch a redirect off without losing it, call set_website_redirect with is_active false instead.

3 parameters
Name In Type Required Description
website_id path string yes
redirect_id path string yes UUID of the redirect to remove. Get ids from list_website_redirects.
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/delete-websites-website-id-redirects-redirect-id.md

DELETE /websites/:website_id/redirect-domains/:host Scopes: websites:delete

Remove a redirect-only domain from a website. The domain stops redirecting once its DNS is repointed or removed.

3 parameters
Name In Type Required Description
website_id path string yes
host path string yes The redirect domain to remove, e.g. "oldbrand.com".
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/delete-websites-website-id-redirect-domains-host.md

DELETE /websites/:website_id/turnstile Scopes: websites:write

Remove bot protection from a website: deletes the Turnstile widget and its configuration. Public forms on this site will no longer be verified.

2 parameters
Name In Type Required Description
website_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/delete-websites-website-id-turnstile.md

POST /websites/:website_id/deploy/domain/retry Scopes: websites:write

Revive a custom domain that the hosting provider has stopped serving, when get_website_domain_status reports "recoverable": true for a host. Restarts verification in place where possible, and only as a last resort briefly detaches and re-attaches the domain (a short outage on an otherwise-working site). Rate limited to once per 24 hours per website, because each attempt consumes part of the certificate allowance for that domain. Do NOT call this on a domain that is merely pending - pending resolves on its own once DNS propagates.

2 parameters
Name In Type Required Description
website_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/post-websites-website-id-deploy-domain-retry.md

POST /websites/:website_id/deploy/domain/revalidate Scopes: websites:write

Call this right after the owner points their www CNAME at the website. It asks the hosting provider to check the custom domain again immediately, so the security certificate is issued within a minute or two instead of waiting up to 15 minutes for the next scheduled check (visitors get a security error until it exists). Only acts when public DNS already points at the website and the domain is still pending; never detaches anything. Returns 409 if DNS has not changed yet, if the domain was re-checked in the last 60 seconds, or if the domain is "recoverable" (use retry_website_domain for that). Returns status "active" once live.

2 parameters
Name In Type Required Description
website_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/post-websites-website-id-deploy-domain-revalidate.md

POST /websites/:website_id/redirects Scopes: websites:write

Add or update one path redirect on a website, then publish it to the live site. Use for short branded links (send "/video" to a long booking URL so an SMS carries the client's own domain) and for fixing broken URLs found by get_website_broken_urls. Re-calling with the same source_path re-points it, so this both creates and edits. Publishing commits to the site's repo and rebuilds it, so the redirect is normally live within a couple of minutes. The website must have a connected repository.

7 parameters
Name In Type Required Description
website_id path string yes UUID of the website. Get ids from list_websites.
source_path body string yes The path on this site to redirect FROM, starting with "/", e.g. "/video". A single trailing "*" redirects a whole section, e.g. "/old-blog/*".
target_url body string yes Where to send the visitor: a full https:// address, or a path on the same site starting with "/".
status_code body number no HTTP redirect status: 301 (permanent), 302 (temporary, the default), 307, or 308. Use 302 unless the destination is settled, because browsers cache a 301 hard.
is_active body boolean no Whether the redirect is live. Defaults to true. Set false to switch one off without deleting it.
publish body boolean no Whether to rebuild the live site now. Defaults to true. Pass false for each of a batch of redirects, then call publish_website_redirects once, so the site rebuilds a single time instead of once per redirect. Note this only defers the rebuild: publishing always writes every active redirect, so the next publish from any redirect takes this one live too. To keep a redirect off the live site regardless, set is_active false instead.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/post-websites-website-id-redirects.md

PATCH /websites/:website_id Scopes: websites:write

Update a website: name, domain, branding, navbar, footer, SEO meta tags, lead gen form, or custom scripts.

20 parameters
Name In Type Required Description
website_id path string yes UUID of the website to update. Get ids from list_websites.
name body string no Site name shown in the Websites list. Internal label, not rendered on the site.
domain body string no Live custom domain as a BARE HOSTNAME, e.g. "www.example.com". A pasted full URL is normalized to the bare host on write. Pass null to unset.
staging_domain body string no Preview/staging hostname, bare like `domain`. A pasted full URL is normalized on write. Pass null to unset.
additional_domains body array no Any other hostnames this site is served from, as bare hostnames, e.g. ["shop.example.com"]. These are allowed through the website lead gateway AND registered on the site's bot-protection widget, so a host missing here is one that gets rejected. `domain` and its www/apex counterpart, the staging hostname and the deployed URL are already included and do not need repeating. Full-replace on write; pass [] or null to clear.
is_public body boolean no Whether the published site is reachable. False keeps it deployed but private.
brand_colors body object no Site colour palette, e.g. { "primary": "#0f172a", "secondary": "#38bdf8", "accent": "#f97316" }. Full-replace on write.
brand_typography body object no Font choices for the site, e.g. { "heading": "Inter", "body": "Inter" }. Full-replace on write.
navbar body object no Navigation bar configuration: links, layout and CTA. Full-replace on write — read the current value first and send it back modified.
footer body object no Footer configuration: columns, links, social and legal text. Full-replace on write.
lead_gen_form body object no Lead-capture form config for the site. Submissions post to the public website trigger endpoint using the site_key and land in the CRM. Full-replace on write.
default_meta_title body string no Fallback <title> for pages that do not set their own. Per-page values win.
default_meta_description body string no Fallback meta description for pages that do not set their own. Per-page values win.
default_og_image body string no Absolute URL of the fallback Open Graph / social share image (1200x630).
google_tag_manager_id body string no Google Tag Manager container id, e.g. "GTM-XXXXXXX". Injected into every page.
microsoft_clarity_id body string no Microsoft Clarity project id. Injected into every page.
header_scripts body string no Raw HTML injected into <head> on every page. Runs on the live site — only add markup you trust.
footer_scripts body string no Raw HTML injected before </body> on every page. Runs on the live site — only add markup you trust.
github_repo body string no Repository backing this site, as "owner/repo". Connects the site to its source; the deploy fields themselves are managed and not writable here.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/patch-websites-website-id.md

PATCH /websites/:website_id/redirect-domains/:host Scopes: websites:write

Set or clear the landing page of an existing redirect-only domain. With landing_path set, every request for the domain lands on that one page of the website (a temporary 302), with {domain} replaced by the name the visitor typed, e.g. "/available/?domain={domain}". Send landing_path: null (or "") to clear it, so the domain goes back to keeping the visitor's own path (a permanent 301).

4 parameters
Name In Type Required Description
website_id path string yes The TARGET website the redirect domain points at.
host path string yes The redirect domain to change, e.g. "buycar.com.au".
landing_path body string no A path on the website starting with "/" that every request for the domain lands on, {domain} filled with the name the visitor typed, e.g. "/available/?domain={domain}". Send "" (or null) to clear it, so the domain keeps the visitor's own path again. This is the only field the call changes.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/patch-websites-website-id-redirect-domains-host.md

PATCH /websites/:website_id/analytics Scopes: websites:write

Record whether the tracking snippet is present on the live site. Set beacon_installed true once the snippet has been added and the site redeployed, so the traffic reports stop warning that data may be missing.

3 parameters
Name In Type Required Description
website_id path string yes
beacon_installed body boolean no Whether the tracking snippet is now present in the live site. Set true after adding it to the site and redeploying; the traffic reports stop warning about missing data once this is true.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/patch-websites-website-id-analytics.md

PATCH /websites/:website_id/turnstile Scopes: websites:write

Update a website's bot protection: toggle the master switch on/off (enabled), change the widget mode (managed, non-interactive, invisible), or set per-surface overrides (surface_policy). The master switch protects every widget-capable surface at once; surface_policy overrides individual surfaces while the master is on, e.g. {"lead_form":"rate_limited"} keeps forms/bookings challenged but drops the Lead Form to rate limiting. Pass surface_policy: null to clear all overrides. Switching it ON is refused with a 409 when the site has never been seen passing a bot check, or when visitors are on a hostname the widget does not accept - both of those states would make the site reject every form, booking and lead submission. Check get_website_turnstile for wiring_seen_at and hostnames first; pass force: true only when you have confirmed the widget is live on the site's forms.

6 parameters
Name In Type Required Description
website_id path string yes
enabled body boolean no
mode body string no
force body boolean no Switch protection on even though the site has never been seen passing a bot check, or is serving from a hostname the widget does not accept. Only use when you have confirmed the widget is already live on the site's forms.
surface_policy body object,null no Per-surface overrides while the master is on. Keys: forms, bookings, lead_form, voice. Values: widget | rate_limited | off. A surface with no entry inherits widget while the master is on. "voice" governs in-browser AI voice calls ("Talk to the AI"): set it to widget only once the site actually renders a Turnstile widget before the call starts, otherwise every call is rejected; rate_limited leaves it guarded by the per-visitor and global call limits instead. null clears all overrides.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/websites/patch-websites-website-id-turnstile.md

SEO Research (16 endpoints)

Keyword research, competitor gap analysis and live search result snapshots.

📄 Full markdown: /api/seo.md

POST /seo-research/ai-visibility Scopes: seo:write

Check how visible a brand is in AI-generated search answers via TrustPager SEO. Costs credits.

5 parameters
Name In Type Required Description
brand body string no Brand or business name.
topics body string no Comma or newline-separated queries to check.
website_id body string yes UUID of the website this research is for.
location body string no Country code: AU (default), US, GB, NZ, CA.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/seo/post-seo-research-ai-visibility.md

POST /seo-research/backlink-links Scopes: seo:write

Drill into the INDIVIDUAL backlinks for a domain via TrustPager SEO — the linking page, where it points (url_to), and whether it is broken. Optionally filter to one referring_domain. Use after seo_backlinks to inspect a specific domain or find broken inbound links. Costs credits.

4 parameters
Name In Type Required Description
domain body string yes Your domain whose inbound links to inspect, e.g. yoursite.com.
referring_domain body string no Optional — limit to links coming from this one referring domain.
website_id body string yes UUID of the website this research is for.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/seo/post-seo-research-backlink-links.md

POST /seo-research/backlinks Scopes: seo:write

Analyse backlinks for a domain via TrustPager SEO. Returns referring domains with rank, backlink count, and dofollow status. Costs credits.

3 parameters
Name In Type Required Description
domain body string yes Domain to analyse, e.g. yoursite.com.
website_id body string yes UUID of the website this research is for.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/seo/post-seo-research-backlinks.md

POST /seo-research/brand-mentions Scopes: seo:write

Find web mentions of a brand or business with sentiment via TrustPager SEO. Costs credits.

3 parameters
Name In Type Required Description
brand body string yes Brand or business name to search for.
website_id body string yes UUID of the website this research is for.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/seo/post-seo-research-brand-mentions.md

POST /seo-research/check-rankings Scopes: seo:write

Re-check every tracked keyword on a website now, each in its tracked location (TrustPager SEO). Runs in the background: read results with seo_list_rankings a few minutes later. Costs credits per keyword, charged up front; failed lookups are refunded. 409 if a check is already running. Nothing re-checks automatically, so call this whenever fresh positions are needed.

2 parameters
Name In Type Required Description
website_id body string yes UUID of the website whose tracked keywords to re-check.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/seo/post-seo-research-check-rankings.md

POST /seo-research/competitor-gap Scopes: seo:write

Find the keywords a competitor domain ranks for via TrustPager SEO, with volume/CPC/difficulty. Optionally pass your_domain to focus on the gap. Costs credits. Saved as a run.

6 parameters
Name In Type Required Description
competitor_domain body string yes Competitor domain, e.g. competitor.com.
website_id body string yes UUID of the website this research is for. Always required — every SEO run must belong to a website.
your_domain body string no Optional — your own domain to compare against.
location body string no Country code: AU (default), US, GB, NZ, CA.
language body string no Language code, default en.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/seo/post-seo-research-competitor-gap.md

GET /seo-research/runs/:run_id Scopes: seo:read

Fetch a TrustPager SEO research run by UUID with its full result rows.

1 parameter
Name In Type Required Description
run_id path string yes

📄 /api/seo/get-seo-research-runs-run-id.md

POST /seo-research/keyword-research Scopes: seo:write

Run keyword research via TrustPager SEO. Returns each keyword with monthly search volume, CPC, competition, and keyword difficulty. Costs credits. Results are saved as a run you can re-open without paying again.

5 parameters
Name In Type Required Description
seed_keywords body array yes One or more seed keywords to expand from.
website_id body string yes UUID of the website this research is for. Always required — every SEO run must belong to a website.
location body string no Country code: AU (default), US, GB, NZ, CA.
language body string no Language code, default en.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/seo/post-seo-research-keyword-research.md

GET /seo-research/rank-snapshots Scopes: seo:read

List rank position snapshots for tracked keywords via TrustPager SEO, newest first. Paginated: pagination.total is the real number of snapshots matching the filter, and pagination.next_cursor pages back through older history.

4 parameters
Name In Type Required Description
website_id query string no
tracked_keyword_id query string no
limit query number no Page size, 1 to 100. Defaults to 25.
after query string no Snapshot id from a previous response's pagination.next_cursor, to fetch the next page of older snapshots.

📄 /api/seo/get-seo-research-rank-snapshots.md

GET /seo-research/runs Scopes: seo:read

List past TrustPager SEO research runs (newest first). Filter by website_id or tool.

3 parameters
Name In Type Required Description
website_id query string no
tool query string no keyword_research | competitor_gap | serp_snapshot | backlinks | brand_mentions | ai_visibility | local_seo | site_audit
limit query number no

📄 /api/seo/get-seo-research-runs.md

GET /seo-research/tracked-keywords Scopes: seo:read

List tracked keywords for a website via TrustPager SEO. last_checked_at is when each keyword position was last looked up (null = never checked).

1 parameter
Name In Type Required Description
website_id query string no

📄 /api/seo/get-seo-research-tracked-keywords.md

POST /seo-research/llm-visibility Scopes: seo:write

Ask a real LLM (ChatGPT) your prompts and check whether the brand is mentioned or cited in the AI answer via TrustPager SEO. The new frontier beyond Google rankings. Costs credits.

4 parameters
Name In Type Required Description
brand body string no Brand or business name to look for in the AI answer.
topics body string no Newline-separated prompts/questions to ask the LLM.
website_id body string yes UUID of the website this research is for.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/seo/post-seo-research-llm-visibility.md

POST /seo-research/local-seo Scopes: seo:write

Audit local search presence for a business via TrustPager SEO. Returns map-pack listings with ratings, addresses, and reviews. Costs credits.

4 parameters
Name In Type Required Description
business_name body string yes Business name to search for.
website_id body string yes UUID of the website this research is for.
location body string no Country code: AU (default), US, GB, NZ, CA.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/seo/post-seo-research-local-seo.md

POST /seo-research/site-audit Scopes: seo:write

Start a technical site audit via TrustPager SEO. Crawls up to 100 pages and returns issues (missing titles, broken links, slow pages). Async — poll seo_get_run for results. Costs credits.

3 parameters
Name In Type Required Description
start_url body string yes URL to start crawling from, e.g. https://yoursite.com.
website_id body string yes UUID of the website this audit is for.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/seo/post-seo-research-site-audit.md

POST /seo-research/serp-snapshot Scopes: seo:write

Capture the live top organic search results for a keyword in a location via TrustPager SEO. Costs credits. Saved as a run.

5 parameters
Name In Type Required Description
keyword body string yes The search query to snapshot.
website_id body string yes UUID of the website this research is for. Always required — every SEO run must belong to a website.
location body string no Country code: AU (default), US, GB, NZ, CA.
language body string no Language code, default en.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/seo/post-seo-research-serp-snapshot.md

POST /seo-research/track-keywords Scopes: seo:write

Add keywords to the Rank Tracker for a website via TrustPager SEO and check their positions once, in the background. Costs credits per NEW keyword (same price as seo_check_rankings), charged up front; keywords already tracked in that location are not re-checked or charged, and failed lookups are refunded. 402 with nothing tracked if the workspace lacks credits; 409 if a check is already running for the website. Positions are NOT re-checked automatically; use seo_check_rankings to re-check them.

4 parameters
Name In Type Required Description
keywords body array yes Keywords to track.
website_id body string yes UUID of the website to track rankings for.
location body string no Country code: AU (default), US, GB, NZ, CA.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/seo/post-seo-research-track-keywords.md

Portals (25 endpoints)

Client portal templates and the live portal instances created from them.

📄 Full markdown: /api/portals.md

POST /portals/:portal_id/members Scopes: portals:write

Add a member to a portal by email and (by default) send them the workspace-branded invite. Adding IS inviting: the backend creates or links a company-less external login (broker, client, site staff who NEVER join your company), sends the invite/access email, and writes a log row. The path is chosen automatically from the person's identity state: a brand-new person gets a set-password link; an un-activated account gets a fresh set-password link re-issued; an already-active platform user gets an 'access granted' notice. Set `send_email: false` to add them silently, with the same access and portal chat but no email. Idempotent per email. Returns `state` (new | pending | active) and `email_sent`. Identical to invite_portal_member.

5 parameters
Name In Type Required Description
portal_id path string yes
email body string yes The person to add / invite. Required.
full_name body string no Optional display name shown in the portal.
send_email body boolean no Send the invite email. Defaults to true. False adds them silently with identical access and portal chat, but someone who has no TrustPager login yet then has no way in until you resend their access link.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/portals/post-portals-portal-id-members.md

POST /portals/:portal_id/referrer Scopes: portals:write

Connect a referral partner (a contact) to a partner portal as its bound referrer. The referral dashboard and the portal's "Submit a referral now" form both attribute referrals to this contact, so a portal whose referrer is missing cannot accept referrals until this is set. The contact must already exist in the workspace; connecting it also flags it as a referral partner so it appears in the referrer picker. Use this to fix a portal reported as "not fully set up" or to re-point a portal at a different partner.

3 parameters
Name In Type Required Description
portal_id path string yes
contact_id body string yes The contact to bind as this portal's referral partner. Must exist in the workspace.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/portals/post-portals-portal-id-referrer.md

POST /portals Scopes: portals:write

Create a portal INSTANCE (a live portal). PREFER instantiate_portal_template to create a portal for an opportunity — it stamps a template onto the opportunity, links it, and auto-shares the right records. Use this lower-level call only when you already hold the template id and anchor. config.templateId is REQUIRED: every instance must derive from a Portal Template (the reusable design, in portal_templates; create one with create_portal_template). A portal is the unit of access: members see only records shared into it. Pass config.anchor to link it to the CRM opportunity, plus optional branding / welcome / contact overlay.

4 parameters
Name In Type Required Description
name body string yes Display name, e.g. the opportunity / client name.
type body string no partner | client | doc_collection | stakeholder. Defaults to client. client = a private login for a client to track their project. partner = a referrer/broker portal (adds Referrals & commissions). stakeholder = an external contractor/stakeholder portal (pairs with config.stakeholder.enabled). doc_collection = a document-collection portal.
config body object no Portal config (jsonb). templateId = <portal_templates uuid> is REQUIRED (every instance derives from a template; get ids from list_portal_templates). Prefer instantiate_portal_template (opportunity) or instantiate_partner_portal (contact/referrer) over building an instance by hand. Portal design config (jsonb). REPLACE-not-merge: get the current config, merge your change, PATCH the whole object back — a partial config wipes the omitted keys. Keys: `branding` {logoUrl,primaryColor,secondaryColor,tagline,icon}; `welcome` {headline,body}; `contact` {name,role,email,phone}; `description` (invite-email blurb); `menus` [{id,label,icon,content?,sidebar:[{type,ref?,slug?,label?,icon?,memberCanEdit?}]}] — the top-nav tabs + sidebar entries (entry types: notepad, notepad_folder, scheduler, form, work_orders, signing, documents, invoices, files, referrals, booking, notepads, transcripts, chat); `dataSharing` {<content_type>: {enabled, mode:"all"|"selected", resourceIds?, statusIds?, memberCanEdit?, memberEditableFields?}} — Container 1, auto-share of the ANCHORED OPPORTUNITY's content (content types: work_orders, transcripts, invoices, signing, documents, files, bookings, images, notepads). enabled=false fully protects a type; mode "selected" limits to resourceIds; memberEditableFields (work_orders only) is the allowlist of field SLUGS a member may write — omit it for the original "every field editable" behaviour, or pass [] for status-only so a contractor can accept/decline a job offer without being able to retype the offer amount; memberSelectableStatuses (work_orders only) is the same idea for status — the allowlist of status IDS a member may move a job INTO, omitted meaning every workspace status is offered; requiredFieldsByStatus (work_orders only) {"<status id>":["slug"]} gates a transition until those fields are filled ("_attachments" = at least one photo), so a subcontractor cannot mark a job finished before the work is confirmed; `templateContent` {signing?:[document_template_id], documents?:[document_template_id], files?:[secure_file_id], images?:[company_file_id], notepads?:[notepad_id]} — Container 2, TEMPLATE content proliferated into EVERY instance (signing/documents are rendered per instance with the referrer/opportunity variables). Set this on a TEMPLATE; a create/edit trigger proliferates it. `referral` {enabled,commissions,referrerContactId} — PARTNER portals only (commission dashboard); referrerContactId is pinned at instantiation. `stakeholder` {enabled,contractorContactId}: STAKEHOLDER portals only (type "stakeholder"), the external contractor/subcontractor design. enabled=true adds the "My Jobs" (contractor_jobs) + member-editable work_orders tabs; contractorContactId is pinned at instantiation and scopes the jobs dashboard to that contractor across every opportunity they service. `defaultMembers` [{email,name?}] (TEMPLATE only): the internal team seeded as members onto EVERY portal instantiated from this template, so they get in-portal access without an invite email. `documentsOpportunityId` — PARTNER portals only: the referrer's own opportunity that scopes templateContent signing/document/file candidates + variables. `anchor` {type:"opportunity"|"contact", id} — set by the instantiate endpoints, not by hand. `templateId` — the source template (set at instantiation). `keepInstancesAligned` (TEMPLATE only, boolean, default false) — when true, every instance of this template (existing AND future) keeps its DESIGN (menus, home, branding, welcome, contact, nav, description) matched to the template: any template design edit propagates to all instances automatically, and switching it on aligns the existing instances immediately. Each instance's own opportunity anchor, referrer, members, shared records, and data-sharing rules are always preserved. Set it with set_portal_template_alignment. `lastSyncedAt` (instance only) — stamped by the alignment job when an instance was last matched to its template.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/portals/post-portals.md

POST /portals/templates Scopes: portals:write

Create a reusable portal template (design only). Put branding / welcome / contact / pages / tools / nav into config; instantiate_portal_template later stamps it onto an opportunity.

4 parameters
Name In Type Required Description
name body string yes Template name, e.g. "Client Project Portal".
type body string no partner | client | doc_collection | stakeholder. Defaults to client. client = a private login for a client to track their project. partner = a referrer/broker portal (adds Referrals & commissions). stakeholder = an external contractor/stakeholder portal (pairs with config.stakeholder.enabled). doc_collection = a document-collection portal.
config body object no Portal design config (jsonb). REPLACE-not-merge: get the current config, merge your change, PATCH the whole object back — a partial config wipes the omitted keys. Keys: `branding` {logoUrl,primaryColor,secondaryColor,tagline,icon}; `welcome` {headline,body}; `contact` {name,role,email,phone}; `description` (invite-email blurb); `menus` [{id,label,icon,content?,sidebar:[{type,ref?,slug?,label?,icon?,memberCanEdit?}]}] — the top-nav tabs + sidebar entries (entry types: notepad, notepad_folder, scheduler, form, work_orders, signing, documents, invoices, files, referrals, booking, notepads, transcripts, chat); `dataSharing` {<content_type>: {enabled, mode:"all"|"selected", resourceIds?, statusIds?, memberCanEdit?, memberEditableFields?}} — Container 1, auto-share of the ANCHORED OPPORTUNITY's content (content types: work_orders, transcripts, invoices, signing, documents, files, bookings, images, notepads). enabled=false fully protects a type; mode "selected" limits to resourceIds; memberEditableFields (work_orders only) is the allowlist of field SLUGS a member may write — omit it for the original "every field editable" behaviour, or pass [] for status-only so a contractor can accept/decline a job offer without being able to retype the offer amount; memberSelectableStatuses (work_orders only) is the same idea for status — the allowlist of status IDS a member may move a job INTO, omitted meaning every workspace status is offered; requiredFieldsByStatus (work_orders only) {"<status id>":["slug"]} gates a transition until those fields are filled ("_attachments" = at least one photo), so a subcontractor cannot mark a job finished before the work is confirmed; `templateContent` {signing?:[document_template_id], documents?:[document_template_id], files?:[secure_file_id], images?:[company_file_id], notepads?:[notepad_id]} — Container 2, TEMPLATE content proliferated into EVERY instance (signing/documents are rendered per instance with the referrer/opportunity variables). Set this on a TEMPLATE; a create/edit trigger proliferates it. `referral` {enabled,commissions,referrerContactId} — PARTNER portals only (commission dashboard); referrerContactId is pinned at instantiation. `stakeholder` {enabled,contractorContactId}: STAKEHOLDER portals only (type "stakeholder"), the external contractor/subcontractor design. enabled=true adds the "My Jobs" (contractor_jobs) + member-editable work_orders tabs; contractorContactId is pinned at instantiation and scopes the jobs dashboard to that contractor across every opportunity they service. `defaultMembers` [{email,name?}] (TEMPLATE only): the internal team seeded as members onto EVERY portal instantiated from this template, so they get in-portal access without an invite email. `documentsOpportunityId` — PARTNER portals only: the referrer's own opportunity that scopes templateContent signing/document/file candidates + variables. `anchor` {type:"opportunity"|"contact", id} — set by the instantiate endpoints, not by hand. `templateId` — the source template (set at instantiation). `keepInstancesAligned` (TEMPLATE only, boolean, default false) — when true, every instance of this template (existing AND future) keeps its DESIGN (menus, home, branding, welcome, contact, nav, description) matched to the template: any template design edit propagates to all instances automatically, and switching it on aligns the existing instances immediately. Each instance's own opportunity anchor, referrer, members, shared records, and data-sharing rules are always preserved. Set it with set_portal_template_alignment. `lastSyncedAt` (instance only) — stamped by the alignment job when an instance was last matched to its template.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/portals/post-portals-templates.md

DELETE /portals/:portal_id Scopes: portals:delete

Delete a portal. Cascades to its members and shares (the underlying shared records are NOT deleted).

2 parameters
Name In Type Required Description
portal_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/portals/delete-portals-portal-id.md

DELETE /portals/templates/:template_id Scopes: portals:delete

Delete a portal template. Portals already instantiated from it are unaffected.

2 parameters
Name In Type Required Description
template_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/portals/delete-portals-templates-template-id.md

GET /portals/:portal_id Scopes: portals:read

Fetch a portal by UUID.

1 parameter
Name In Type Required Description
portal_id path string yes

📄 /api/portals/get-portals-portal-id.md

GET /portals/templates/:template_id Scopes: portals:read

Fetch a portal template by UUID (its full design config).

1 parameter
Name In Type Required Description
template_id path string yes

📄 /api/portals/get-portals-templates-template-id.md

POST /portals/templates/:template_id/instantiate-for-contact Scopes: portals:write

Create a NEW portal from a template anchored to a CONTACT (a referrer) — the partner-portal path. Pins referral.referrerContactId (commission dashboard scopes to them) and optionally documents_opportunity_id (the referrer's own opportunity that supplies the templateContent signing/document/file candidates + per-instance signing variables). The template's templateContent proliferates into the instance automatically. Use this instead of instantiate_portal_template for partner portals.

6 parameters
Name In Type Required Description
template_id path string yes
contact_id body string yes The referrer contact to anchor the partner portal to (required).
documents_opportunity_id body string no Optional: the referrer's own opportunity that scopes signing/document/file template content + fills per-instance signing variables.
name body string no Portal name. Defaults to the template name.
invite_contact body boolean no Invite the contact as a portal member (branded email). Default true.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/portals/post-portals-templates-template-id-instantiate-for-contact.md

POST /portals/templates/:template_id/instantiate Scopes: portals:write

Create a NEW portal from a template, anchored to an opportunity. Copies the template design and sets config.anchor to the opportunity; optionally seeds members. Returns the new portal. Share records into it afterwards with share_to_portal.

5 parameters
Name In Type Required Description
template_id path string yes
opportunity_id body string yes The CRM opportunity to anchor the new portal to (required).
name body string no Portal name. Defaults to the template name.
members body array no Optional members to seed: [{ email, user_id?, status? }].
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/portals/post-portals-templates-template-id-instantiate.md

POST /portals/:portal_id/invite Scopes: portals:write

Invite an external person to a portal by email and send them a workspace-branded invite email. Records them as a company-less member (broker / client / site staff) who NEVER joins your company. Handles all three identity states automatically: a brand-new person gets a set-password link; a person with an un-activated account gets a fresh set-password link re-issued; a person already active on the platform gets an 'access granted' notice and signs in with their existing password. Set `send_email: false` to grant access silently instead. Idempotent per email. Returns `state` (new | pending | active) and `email_sent`.

5 parameters
Name In Type Required Description
portal_id path string yes
email body string yes The person to invite. Required.
full_name body string no Optional display name shown in the portal.
send_email body boolean no Send the invite email. Defaults to true. False grants access and portal chat silently, but someone who has no TrustPager login yet then has no way in until you resend their access link.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/portals/post-portals-portal-id-invite.md

GET /portals/:portal_id/shares Scopes: portals:read

List the records shared into a portal.

2 parameters
Name In Type Required Description
portal_id path string yes
resource_type query string no

📄 /api/portals/get-portals-portal-id-shares.md

GET /portals/template-content-candidates Scopes: portals:read

List the workspace resources valid to attach as template content of a given type (so you attach real ids, not guesses). signing/documents → document templates; files → secure files; images → company images; notepads → notepads.

1 parameter
Name In Type Required Description
type query string yes One of: signing, documents, files, images, notepads (query param).

📄 /api/portals/get-portals-template-content-candidates.md

GET /portals/templates Scopes: portals:read

List reusable portal templates (designs). A template carries branding / welcome / contact / pages / tools / nav order in config, with no anchor, members, or shares.

📄 /api/portals/get-portals-templates.md

GET /portals Scopes: portals:read

List client/partner portals in the workspace.

2 parameters
Name In Type Required Description
type query string no Filter by portal type: partner | client | doc_collection | stakeholder. Omit for all types.
status query string no Filter by status: active | disabled. Omit for all statuses.

📄 /api/portals/get-portals.md

POST /portals/:portal_id/reconcile-autoshare Scopes: portals:write

Back-fill a live portal: share the anchored opportunity's EXISTING records for every dataSharing type set to enabled+all. New records auto-share via triggers; call this once after enabling a type so content that predates the rule also appears.

2 parameters
Name In Type Required Description
portal_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/portals/post-portals-portal-id-reconcile-autoshare.md

DELETE /portals/:portal_id/members/:member_id Scopes: portals:write

Remove a member from a portal (revokes their access to it).

3 parameters
Name In Type Required Description
portal_id path string yes
member_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/portals/delete-portals-portal-id-members-member-id.md

DELETE /portals/:portal_id/shares/:share_id Scopes: portals:write

Stop sharing a record into a portal (members lose access to it). Does not delete the underlying record.

3 parameters
Name In Type Required Description
portal_id path string yes
share_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/portals/delete-portals-portal-id-shares-share-id.md

POST /portals/data-sharing Scopes: portals:write

Safely set ONE opportunity-auto-share rule (Container 1) on a portal instance OR a template — server-side merge, so you never replace the whole config. enabled=false fully protects the type; mode "all" auto-shares all of the anchored opportunity's records of that type, "selected" limits to resource_ids.

12 parameters
Name In Type Required Description
portal_id body string no Target a live instance (one of portal_id/template_id).
template_id body string no Target a template (one of portal_id/template_id).
content_type body string yes One of: work_orders, transcripts, invoices, signing, documents, files, bookings, images, notepads.
enabled body boolean no Master switch. false = fully protected (never shared/seen). Defaults true when first set.
mode body string no "all" (auto-share every record) or "selected" (only resource_ids).
resource_ids body array no For mode "selected": the exact record ids.
status_ids body array no work_orders only: filter by status ids.
member_can_edit body boolean no work_orders/notepads: let members edit, not just view.
member_editable_fields body array no work_orders only, alongside member_can_edit: the field SLUGS a member may write. Status is always editable when member_can_edit is true, so this covers the data fields. Omit the key entirely for the original behaviour (every field editable); pass [] for status-only, so a contractor can accept or decline a job but cannot change the offer amount; pass null to clear the allowlist.
member_selectable_statuses body array no work_orders only: the status IDS a member may move a work order INTO — the status equivalent of member_editable_fields. Omit for the original behaviour (every workspace status offered); pass the short list a contractor should see instead of your internal quoting and payment stages; pass null to clear it. NOT the same as status_ids, which decides which work orders are shared into the portal at all.
required_fields_by_status body object no work_orders only: a completion gate, as {"<status id>": ["field_slug", …]}. Every listed slug must be filled before a member may move a work order INTO that status, enforced server-side — so a subcontractor cannot mark a job finished before the work is confirmed. Use the slug "_attachments" to require at least one photo. Pass {} or null to clear the gate.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/portals/post-portals-data-sharing.md

POST /portals/template-alignment Scopes: portals:write

Turn "keep all instances aligned with this template" on or off for a portal TEMPLATE. When enabled=true, every instance of this template — existing and future — keeps its DESIGN (tabs/pages, page content, branding, welcome, contact) matched to the template: switching it on aligns the existing instances immediately, and every later template design edit propagates to all of them automatically. enabled=false re-freezes them as independent. Each instance's own opportunity anchor, referrer, members, shared records, and data-sharing rules are always preserved.

3 parameters
Name In Type Required Description
template_id body string yes
enabled body boolean yes true = keep all instances matched to this template; false = each instance is independent (default).
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/portals/post-portals-template-alignment.md

POST /portals/template-content Scopes: portals:write

Safely set ONE template-content type (Container 2) on a portal TEMPLATE — server-side merge. This content proliferates into every instance (signing/documents rendered per instance with the referrer/opportunity variables). Pass the full desired id list for the type (replaces just that type's list). Use list_portal_template_content_candidates to find valid ids.

4 parameters
Name In Type Required Description
template_id body string yes
content_type body string yes One of: signing, documents, files, images, notepads. signing/documents take document_template ids; files/images/notepads take the workspace resource ids.
resource_ids body array yes The full id list for this type (empty array clears it).
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/portals/post-portals-template-content.md

POST /portals/:portal_id/shares Scopes: portals:write

Share a workspace record into a portal so its members can see it. This is the explicit access grant - nothing appears in a portal until shared. Idempotent. resource_type is one of: document, file, image, invoice, signing_envelope, booking, scheduled_event_type, form_template, form_submission, work_order, work_order_portal, referral, commission_payment, notepad, notepad_folder, transcript, opportunity_notepad. Scheduler (scheduled_event_type) and form_template surface as Tools the member can act on; documents, invoices and signing surface as Details; opportunity_notepad surfaces as a member-editable notepad in the portal.

4 parameters
Name In Type Required Description
portal_id path string yes
resource_type body string yes The kind of record being shared (e.g. document, invoice, scheduled_event_type, form_template, work_order).
resource_id body string yes UUID of the record to share.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/portals/post-portals-portal-id-shares.md

PATCH /portals/:portal_id Scopes: portals:write

Update a portal. Every field is optional and independent: whatever you omit is left exactly as stored. Set status to disabled to suspend member access. Passing config REPLACES the whole object (get → merge → patch), but omitting name, type, or status does NOT reset them, so do not echo them back unless you mean to change them.

6 parameters
Name In Type Required Description
portal_id path string yes
name body string no New portal name. Omit to leave it unchanged.
type body string no partner | client | doc_collection | stakeholder. OMIT this field to leave the current type unchanged: only supply it when you deliberately want to change the type. client = a private login for a client to track their project. partner = a referrer/broker portal (adds Referrals & commissions). stakeholder = an external contractor/stakeholder portal (pairs with config.stakeholder.enabled). doc_collection = a document-collection portal.
status body string no active | disabled. Omit to leave it unchanged.
config body object no Portal design config (jsonb). REPLACE-not-merge: get the current config, merge your change, PATCH the whole object back — a partial config wipes the omitted keys. Keys: `branding` {logoUrl,primaryColor,secondaryColor,tagline,icon}; `welcome` {headline,body}; `contact` {name,role,email,phone}; `description` (invite-email blurb); `menus` [{id,label,icon,content?,sidebar:[{type,ref?,slug?,label?,icon?,memberCanEdit?}]}] — the top-nav tabs + sidebar entries (entry types: notepad, notepad_folder, scheduler, form, work_orders, signing, documents, invoices, files, referrals, booking, notepads, transcripts, chat); `dataSharing` {<content_type>: {enabled, mode:"all"|"selected", resourceIds?, statusIds?, memberCanEdit?, memberEditableFields?}} — Container 1, auto-share of the ANCHORED OPPORTUNITY's content (content types: work_orders, transcripts, invoices, signing, documents, files, bookings, images, notepads). enabled=false fully protects a type; mode "selected" limits to resourceIds; memberEditableFields (work_orders only) is the allowlist of field SLUGS a member may write — omit it for the original "every field editable" behaviour, or pass [] for status-only so a contractor can accept/decline a job offer without being able to retype the offer amount; memberSelectableStatuses (work_orders only) is the same idea for status — the allowlist of status IDS a member may move a job INTO, omitted meaning every workspace status is offered; requiredFieldsByStatus (work_orders only) {"<status id>":["slug"]} gates a transition until those fields are filled ("_attachments" = at least one photo), so a subcontractor cannot mark a job finished before the work is confirmed; `templateContent` {signing?:[document_template_id], documents?:[document_template_id], files?:[secure_file_id], images?:[company_file_id], notepads?:[notepad_id]} — Container 2, TEMPLATE content proliferated into EVERY instance (signing/documents are rendered per instance with the referrer/opportunity variables). Set this on a TEMPLATE; a create/edit trigger proliferates it. `referral` {enabled,commissions,referrerContactId} — PARTNER portals only (commission dashboard); referrerContactId is pinned at instantiation. `stakeholder` {enabled,contractorContactId}: STAKEHOLDER portals only (type "stakeholder"), the external contractor/subcontractor design. enabled=true adds the "My Jobs" (contractor_jobs) + member-editable work_orders tabs; contractorContactId is pinned at instantiation and scopes the jobs dashboard to that contractor across every opportunity they service. `defaultMembers` [{email,name?}] (TEMPLATE only): the internal team seeded as members onto EVERY portal instantiated from this template, so they get in-portal access without an invite email. `documentsOpportunityId` — PARTNER portals only: the referrer's own opportunity that scopes templateContent signing/document/file candidates + variables. `anchor` {type:"opportunity"|"contact", id} — set by the instantiate endpoints, not by hand. `templateId` — the source template (set at instantiation). `keepInstancesAligned` (TEMPLATE only, boolean, default false) — when true, every instance of this template (existing AND future) keeps its DESIGN (menus, home, branding, welcome, contact, nav, description) matched to the template: any template design edit propagates to all instances automatically, and switching it on aligns the existing instances immediately. Each instance's own opportunity anchor, referrer, members, shared records, and data-sharing rules are always preserved. Set it with set_portal_template_alignment. `lastSyncedAt` (instance only) — stamped by the alignment job when an instance was last matched to its template.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/portals/patch-portals-portal-id.md

PATCH /portals/templates/:template_id Scopes: portals:write

Update a portal template. Every field is optional and independent: whatever you omit is left exactly as stored. Passing config REPLACES the stored design (get → merge → patch the whole object), but omitting name or type does NOT reset them, so do not echo them back unless you mean to change them.

5 parameters
Name In Type Required Description
template_id path string yes
name body string no New template name. Omit to leave it unchanged.
type body string no partner | client | doc_collection | stakeholder. OMIT this field to leave the current type unchanged: only supply it when you deliberately want to change the type. client = a private login for a client to track their project. partner = a referrer/broker portal (adds Referrals & commissions). stakeholder = an external contractor/stakeholder portal (pairs with config.stakeholder.enabled). doc_collection = a document-collection portal.
config body object no Portal design config (jsonb). REPLACE-not-merge: get the current config, merge your change, PATCH the whole object back — a partial config wipes the omitted keys. Keys: `branding` {logoUrl,primaryColor,secondaryColor,tagline,icon}; `welcome` {headline,body}; `contact` {name,role,email,phone}; `description` (invite-email blurb); `menus` [{id,label,icon,content?,sidebar:[{type,ref?,slug?,label?,icon?,memberCanEdit?}]}] — the top-nav tabs + sidebar entries (entry types: notepad, notepad_folder, scheduler, form, work_orders, signing, documents, invoices, files, referrals, booking, notepads, transcripts, chat); `dataSharing` {<content_type>: {enabled, mode:"all"|"selected", resourceIds?, statusIds?, memberCanEdit?, memberEditableFields?}} — Container 1, auto-share of the ANCHORED OPPORTUNITY's content (content types: work_orders, transcripts, invoices, signing, documents, files, bookings, images, notepads). enabled=false fully protects a type; mode "selected" limits to resourceIds; memberEditableFields (work_orders only) is the allowlist of field SLUGS a member may write — omit it for the original "every field editable" behaviour, or pass [] for status-only so a contractor can accept/decline a job offer without being able to retype the offer amount; memberSelectableStatuses (work_orders only) is the same idea for status — the allowlist of status IDS a member may move a job INTO, omitted meaning every workspace status is offered; requiredFieldsByStatus (work_orders only) {"<status id>":["slug"]} gates a transition until those fields are filled ("_attachments" = at least one photo), so a subcontractor cannot mark a job finished before the work is confirmed; `templateContent` {signing?:[document_template_id], documents?:[document_template_id], files?:[secure_file_id], images?:[company_file_id], notepads?:[notepad_id]} — Container 2, TEMPLATE content proliferated into EVERY instance (signing/documents are rendered per instance with the referrer/opportunity variables). Set this on a TEMPLATE; a create/edit trigger proliferates it. `referral` {enabled,commissions,referrerContactId} — PARTNER portals only (commission dashboard); referrerContactId is pinned at instantiation. `stakeholder` {enabled,contractorContactId}: STAKEHOLDER portals only (type "stakeholder"), the external contractor/subcontractor design. enabled=true adds the "My Jobs" (contractor_jobs) + member-editable work_orders tabs; contractorContactId is pinned at instantiation and scopes the jobs dashboard to that contractor across every opportunity they service. `defaultMembers` [{email,name?}] (TEMPLATE only): the internal team seeded as members onto EVERY portal instantiated from this template, so they get in-portal access without an invite email. `documentsOpportunityId` — PARTNER portals only: the referrer's own opportunity that scopes templateContent signing/document/file candidates + variables. `anchor` {type:"opportunity"|"contact", id} — set by the instantiate endpoints, not by hand. `templateId` — the source template (set at instantiation). `keepInstancesAligned` (TEMPLATE only, boolean, default false) — when true, every instance of this template (existing AND future) keeps its DESIGN (menus, home, branding, welcome, contact, nav, description) matched to the template: any template design edit propagates to all instances automatically, and switching it on aligns the existing instances immediately. Each instance's own opportunity anchor, referrer, members, shared records, and data-sharing rules are always preserved. Set it with set_portal_template_alignment. `lastSyncedAt` (instance only) — stamped by the alignment job when an instance was last matched to its template.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/portals/patch-portals-templates-template-id.md

Order Forms (6 endpoints)

Manage payment order forms connected to Stripe for product purchases.

📄 Full markdown: /api/order-forms.md

POST /order-forms Scopes: forms:write

Create an order form configuration: which product categories and products it offers, which contact details it demands, and what happens in the CRM when someone orders.

14 parameters
Name In Type Required Description
website_id body string yes The website this order form belongs to.
is_active body boolean no Whether the form accepts orders. Turn off to take it down without deleting it.
categories body array no Product category IDs to show, in display order. Omit to show every category.
products body array no Product IDs to offer. Omit to offer everything in the chosen categories.
require_email body boolean no Demand an email address before the order can be submitted.
require_phone body boolean no Demand a phone number before the order can be submitted.
require_address body boolean no Demand a delivery address before the order can be submitted.
success_message body string no What the customer reads after ordering.
crm_auto_create_deal body boolean no Create an opportunity in the CRM for every order.
crm_pipeline_id body string no Pipeline the created opportunity lands in. Required when crm_auto_create_deal is on.
crm_initial_stage_id body string no Stage the created opportunity starts at. Must belong to crm_pipeline_id.
crm_default_products body array no Product IDs attached to every created opportunity on top of what was ordered.
email_config_id body string no Sending identity for this form's emails. Omit to use the workspace default.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/order-forms/post-order-forms.md

DELETE /order-forms/:order_form_id Scopes: forms:delete

Delete an order form configuration.

2 parameters
Name In Type Required Description
order_form_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/order-forms/delete-order-forms-order-form-id.md

GET /order-forms Scopes: forms:read

List order form configurations.

1 parameter
Name In Type Required Description
limit query number no

📄 /api/order-forms/get-order-forms.md

PATCH /order-forms/:order_form_id Scopes: forms:write

Update an order form configuration: which product categories and products it offers, which contact details it demands, and what happens in the CRM when someone orders.

15 parameters
Name In Type Required Description
order_form_id path string yes
website_id body string no The website this order form belongs to.
is_active body boolean no Whether the form accepts orders. Turn off to take it down without deleting it.
categories body array no Product category IDs to show, in display order. Omit to show every category.
products body array no Product IDs to offer. Omit to offer everything in the chosen categories.
require_email body boolean no Demand an email address before the order can be submitted.
require_phone body boolean no Demand a phone number before the order can be submitted.
require_address body boolean no Demand a delivery address before the order can be submitted.
success_message body string no What the customer reads after ordering.
crm_auto_create_deal body boolean no Create an opportunity in the CRM for every order.
crm_pipeline_id body string no Pipeline the created opportunity lands in. Required when crm_auto_create_deal is on.
crm_initial_stage_id body string no Stage the created opportunity starts at. Must belong to crm_pipeline_id.
crm_default_products body array no Product IDs attached to every created opportunity on top of what was ordered.
email_config_id body string no Sending identity for this form's emails. Omit to use the workspace default.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/order-forms/patch-order-forms-order-form-id.md

Stores (9 endpoints)

Ecommerce storefronts and the products they sell.

📄 Full markdown: /api/stores.md

POST /stores/:store_id/products Scopes: ecommerce:write

Add an existing crm_product to a store, with optional storefront overrides (price_override, storefront_title, images, display_order) and an optional payment plan (max_installments, installment_interval).

13 parameters
Name In Type Required Description
store_id path string yes
product_id body string yes
price_override body number no
storefront_title body string no
storefront_description body string no
storefront_images body array no
seo body object no
display_order body number no
is_visible body boolean no
max_installments body number,null no Offer a payment plan on this listing: the most payments (2-24) a buyer may split the price into. null = pay in full only (the default). Payments after the first are charged automatically to the saved card and STOP after the last one. Not offered on a store that collects a delivery address.
installment_interval body string no Gap between plan payments. Default month.
listing_status body string no available (default) or under_offer. Under offer: the listing stays in the storefront feed with listing_status "under_offer" so the site can show an "Under offer" tag, and checkout refuses it (409, code "under_offer") until it is set back to available. Use it while a buyer's deposit is held on a one-of-a-kind item. Stock is not touched.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/stores/post-stores-store-id-products.md

POST /stores Scopes: ecommerce:write

Create an ecommerce storefront. Slug auto-derives from name if omitted. Sells crm_products via add_store_product.

17 parameters
Name In Type Required Description
name body string yes
slug body string no
website_id body string no
currency body string no
tax_inclusive body boolean no
tax_rate body number no
is_active body boolean no
branding body object no
shipping_settings body object no Delivery, postage and tax rules. Four optional keys, all set from the store's "Delivery and tax" card. allowed_countries: array of two-letter country codes the buyer may pick as a delivery destination (UI label "Countries you deliver to"); defaults to ["AU"]. domestic_country: two-letter code for where the store is based (UI label "Your home country"); defaults to the first allowed_countries entry, else "AU". export_tax_rate: percentage applied INSTEAD OF tax_rate when an order is delivered outside domestic_country (UI label "Overseas tax rate (%)"). Set export_tax_rate to 0 for a GST-free export, e.g. an Australian store posting goods overseas. Omit export_tax_rate entirely and every order keeps the store-wide tax_rate regardless of destination, which is the pre-existing behaviour. The destination is taken from the order's delivery address: from the `shipping` object your storefront posts on create-checkout when it collects the address itself, otherwise from the address the payment provider collects, in which case the tax is corrected when the payment reconciles. shipping_rates: postage charged as its own line, an object of two-letter country code to amount plus an optional "default" for anywhere without its own rate (UI label "Charge postage as a separate line"), e.g. {"AU": 12.50, "NZ": 24, "default": 30}. Omit shipping_rates and no postage is charged, shipping_total stays 0, and freight has to be built into the item prices, which is the pre-existing behaviour. Postage is taxed at the same destination-aware rate as the items, so a GST-free export pays no tax on its freight either, and it requires collect_shipping_address to be true. A per-country amount is only exact when your storefront posts the delivery address itself; on payment-provider-hosted checkout the destination is not known until after the amount is set, so "default" is charged, and unlike tax it cannot be corrected afterwards.
checkout_settings body object no Checkout behaviour. Two optional keys. stock_hold_minutes: how long an inventory-tracked cart holds its stock while the buyer pays. checkout_fields: UI card "Checkout questions". An array of questions the buyer must answer at checkout, on top of name, email, phone and delivery address. Use it for anything you are required to ask before you can sell: which basis of eligibility a .au domain buyer claims (auDA), an ABN or licence number, a trade-only declaration, age or ID confirmation, acceptance of per-product terms. Each entry is { id, type, label, description?, options?, is_required, order_index }. id is a lowercase slug (a letter, then letters, digits or underscores) and it is ALSO the opportunity custom-field id the answer is written to, so create an opportunity custom field with the same id and the answer renders on the order card with no other wiring. type is one of text, textarea, select, checkbox. options is required on a select and lists the exact answers offered. description is the help text under the label, which is where legal wording goes. At most 20 questions, 50 options each, answers up to 2000 characters. Your storefront reads these from the ecommerce-public "resolve" response, renders them, and posts the answers back as checkout_fields: { "<id>": "<answer>" }. The answers are validated server-side on every checkout, for card, PayPal and subscription alike: a missing required answer is refused with a 400 and no order is created, and an answer outside a select's options is refused too. What was asked and what was answered is stored on the order as metadata.checkout_fields, and the answer is copied onto the linked opportunity. Omit checkout_fields entirely and checkout asks nothing extra, which is what every store does today.
collect_shipping_address body boolean no UI label "Collect delivery address at checkout". Ask the buyer for a delivery address at checkout. Turn this on for a store that posts physical goods, so a paid-order automation can print a packing slip; leave it off for a digital store so checkout does not demand an address nobody reads.
fulfillment_pipeline_id body string no
fulfillment_stage_id body string no
auto_create_opportunity body boolean no
email_config_id body string no
opportunity_name_template body string no UI label "Opportunity name". What to call the opportunity created for a paid order. Set on the "Order opportunities" card of the store settings page (/growth/ecommerce/:id, Manage > Settings). Leave it empty and every order card is named "Order ORD-...", which is the pre-existing behaviour and what every store does today. Set it to name the card after something the workspace actually uses, e.g. "{{deal.metadata.job_number}} - {{customer_name}}" for a lab that keys its reports and invoices on a job number. Tokens: {{order_number}}, {{customer_name}}, {{customer_email}}, {{customer_phone}}, {{store_name}}, {{amount}}, {{currency}}, {{item_count}}, {{items_line}} (what was ordered, on one line), {{items_text}} (one item per line, avoid in a name), {{shipping_name}}, {{shipping_text}}, {{shipping_address.city}} and the other address parts, plus {{deal.metadata.<custom_field_id>}} for any opportunity custom field. The same token names the store_order_paid trigger publishes, so a template reads like the paid-order email a workspace has already written. The opportunity custom fields resolve because the name is applied AFTER the opportunity exists, so an Auto Number field with "Generate automatically" on has already minted its number. Applied only to a NEWLY created opportunity: an order matched to an existing opportunity never gets renamed. A token that resolves to nothing renders as empty, and a template that renders to nothing at all falls back to "Order <order_number>" rather than leaving a card unnamed.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/stores/post-stores.md

DELETE /stores/:store_id Scopes: ecommerce:delete

Delete an ecommerce store. Fails if it has orders (deactivate instead).

2 parameters
Name In Type Required Description
store_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/stores/delete-stores-store-id.md

GET /stores/:store_id Scopes: ecommerce:read

Fetch a single ecommerce store by UUID.

1 parameter
Name In Type Required Description
store_id path string yes

📄 /api/stores/get-stores-store-id.md

GET /stores/:store_id/products Scopes: ecommerce:read

List the catalog (products) for a store. Each row also reports track_inventory, on_hand, available_stock and in_stock, so you can tell a sold-out listing from a hidden one. available_stock is null when the product has no finite stock, which means unlimited, not zero.

1 parameter
Name In Type Required Description
store_id path string yes

📄 /api/stores/get-stores-store-id-products.md

GET /stores Scopes: ecommerce:read

List ecommerce storefronts in the workspace.

1 parameter
Name In Type Required Description
limit query number no

📄 /api/stores/get-stores.md

DELETE /stores/:store_id/products/:store_product_id Scopes: ecommerce:delete

Remove a product from a store (does not delete the underlying crm_product).

3 parameters
Name In Type Required Description
store_id path string yes
store_product_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/stores/delete-stores-store-id-products-store-product-id.md

PATCH /stores/:store_id Scopes: ecommerce:write

Update an ecommerce store (name, branding, currency, tax, fulfillment pipeline, etc.).

18 parameters
Name In Type Required Description
store_id path string yes
name body string no
slug body string no
is_active body boolean no
currency body string no
tax_inclusive body boolean no
tax_rate body number no
branding body object no
shipping_settings body object no Delivery, postage and tax rules. Four optional keys, all set from the store's "Delivery and tax" card. allowed_countries: array of two-letter country codes the buyer may pick as a delivery destination (UI label "Countries you deliver to"); defaults to ["AU"]. domestic_country: two-letter code for where the store is based (UI label "Your home country"); defaults to the first allowed_countries entry, else "AU". export_tax_rate: percentage applied INSTEAD OF tax_rate when an order is delivered outside domestic_country (UI label "Overseas tax rate (%)"). Set export_tax_rate to 0 for a GST-free export, e.g. an Australian store posting goods overseas. Omit export_tax_rate entirely and every order keeps the store-wide tax_rate regardless of destination, which is the pre-existing behaviour. The destination is taken from the order's delivery address: from the `shipping` object your storefront posts on create-checkout when it collects the address itself, otherwise from the address the payment provider collects, in which case the tax is corrected when the payment reconciles. shipping_rates: postage charged as its own line, an object of two-letter country code to amount plus an optional "default" for anywhere without its own rate (UI label "Charge postage as a separate line"), e.g. {"AU": 12.50, "NZ": 24, "default": 30}. Omit shipping_rates and no postage is charged, shipping_total stays 0, and freight has to be built into the item prices, which is the pre-existing behaviour. Postage is taxed at the same destination-aware rate as the items, so a GST-free export pays no tax on its freight either, and it requires collect_shipping_address to be true. A per-country amount is only exact when your storefront posts the delivery address itself; on payment-provider-hosted checkout the destination is not known until after the amount is set, so "default" is charged, and unlike tax it cannot be corrected afterwards.
checkout_settings body object no Checkout behaviour. Two optional keys. stock_hold_minutes: how long an inventory-tracked cart holds its stock while the buyer pays. checkout_fields: UI card "Checkout questions". An array of questions the buyer must answer at checkout, on top of name, email, phone and delivery address. Use it for anything you are required to ask before you can sell: which basis of eligibility a .au domain buyer claims (auDA), an ABN or licence number, a trade-only declaration, age or ID confirmation, acceptance of per-product terms. Each entry is { id, type, label, description?, options?, is_required, order_index }. id is a lowercase slug (a letter, then letters, digits or underscores) and it is ALSO the opportunity custom-field id the answer is written to, so create an opportunity custom field with the same id and the answer renders on the order card with no other wiring. type is one of text, textarea, select, checkbox. options is required on a select and lists the exact answers offered. description is the help text under the label, which is where legal wording goes. At most 20 questions, 50 options each, answers up to 2000 characters. Your storefront reads these from the ecommerce-public "resolve" response, renders them, and posts the answers back as checkout_fields: { "<id>": "<answer>" }. The answers are validated server-side on every checkout, for card, PayPal and subscription alike: a missing required answer is refused with a 400 and no order is created, and an answer outside a select's options is refused too. What was asked and what was answered is stored on the order as metadata.checkout_fields, and the answer is copied onto the linked opportunity. Omit checkout_fields entirely and checkout asks nothing extra, which is what every store does today.
collect_shipping_address body boolean no UI label "Collect delivery address at checkout". Ask the buyer for a delivery address at checkout. Turn this on for a store that posts physical goods, so a paid-order automation can print a packing slip; leave it off for a digital store so checkout does not demand an address nobody reads.
fulfillment_pipeline_id body string no
fulfillment_stage_id body string no
auto_create_opportunity body boolean no
email_config_id body string no
website_id body string no
opportunity_name_template body string no UI label "Opportunity name". What to call the opportunity created for a paid order. Set on the "Order opportunities" card of the store settings page (/growth/ecommerce/:id, Manage > Settings). Leave it empty and every order card is named "Order ORD-...", which is the pre-existing behaviour and what every store does today. Set it to name the card after something the workspace actually uses, e.g. "{{deal.metadata.job_number}} - {{customer_name}}" for a lab that keys its reports and invoices on a job number. Tokens: {{order_number}}, {{customer_name}}, {{customer_email}}, {{customer_phone}}, {{store_name}}, {{amount}}, {{currency}}, {{item_count}}, {{items_line}} (what was ordered, on one line), {{items_text}} (one item per line, avoid in a name), {{shipping_name}}, {{shipping_text}}, {{shipping_address.city}} and the other address parts, plus {{deal.metadata.<custom_field_id>}} for any opportunity custom field. The same token names the store_order_paid trigger publishes, so a template reads like the paid-order email a workspace has already written. The opportunity custom fields resolve because the name is applied AFTER the opportunity exists, so an Auto Number field with "Generate automatically" on has already minted its number. Applied only to a NEWLY created opportunity: an order matched to an existing opportunity never gets renamed. A token that resolves to nothing renders as empty, and a template that renders to nothing at all falls back to "Order <order_number>" rather than leaving a card unnamed.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/stores/patch-stores-store-id.md

PATCH /stores/:store_id/products/:store_product_id Scopes: ecommerce:write

Update a store product (price_override, visibility, storefront overrides, display order, payment plan: max_installments / installment_interval, listing_status: available / under_offer).

14 parameters
Name In Type Required Description
store_id path string yes
store_product_id path string yes
price_override body number no
product_id body string no UUID of the catalogue product this storefront listing sells. Set it to point the listing at a different product without losing its storefront copy, images and position.
storefront_title body string no
storefront_description body string no
storefront_images body array no
seo body object no
display_order body number no
is_visible body boolean no
max_installments body number,null no Offer a payment plan on this listing: the most payments (2-24) a buyer may split the price into. null = pay in full only (the default). Payments after the first are charged automatically to the saved card and STOP after the last one. Not offered on a store that collects a delivery address.
installment_interval body string no Gap between plan payments. Default month.
listing_status body string no available (default) or under_offer. Under offer: the listing stays in the storefront feed with listing_status "under_offer" so the site can show an "Under offer" tag, and checkout refuses it (409, code "under_offer") until it is set back to available. Use it while a buyer's deposit is held on a one-of-a-kind item. Stock is not touched.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/stores/patch-stores-store-id-products-store-product-id.md

Orders (5 endpoints)

Ecommerce orders and their line items, including status and fulfilment updates.

📄 Full markdown: /api/orders.md

GET /orders/:order_id Scopes: ecommerce:read

Fetch a single ecommerce order by UUID, including its line items.

1 parameter
Name In Type Required Description
order_id path string yes

📄 /api/orders/get-orders-order-id.md

GET /orders/:order_id/items Scopes: ecommerce:read

List the line items for an ecommerce order.

1 parameter
Name In Type Required Description
order_id path string yes

📄 /api/orders/get-orders-order-id-items.md

GET /orders Scopes: ecommerce:read

List ecommerce orders. Filter by status or store_id query params.

3 parameters
Name In Type Required Description
status query string no
store_id query string no
limit query number no

📄 /api/orders/get-orders.md

POST /orders/:order_id/reconcile Scopes: ecommerce:write

Reconcile an order against Stripe (safety net for missed webhooks): pulls the checkout session status with the store's Connect token and marks the order paid only if Stripe confirms payment. For a payment-plan order it also creates the schedule for the remaining payments if it is missing (never a second one).

2 parameters
Name In Type Required Description
order_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/orders/post-orders-order-id-reconcile.md

PATCH /orders/:order_id Scopes: ecommerce:write

Update an ecommerce order's status, fulfillment_status, notes, or shipping address.

8 parameters
Name In Type Required Description
order_id path string yes
status body string no
fulfillment_status body string no
payment_status body string no
notes body string no
shipping_address body object no
metadata body object no Custom field values on the order, keyed by field name. Replaces the whole object, so read the order and merge first.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/orders/patch-orders-order-id.md

Invoices (14 endpoints)

Native invoices and their line items, optionally linked to an opportunity or company.

📄 Full markdown: /api/invoices.md

POST /invoices/:invoice_id/line-items Scopes: invoices:write

Add one line item to a native invoice. Requires description; line_total and invoice totals are computed.

12 parameters
Name In Type Required Description
invoice_id path string yes
product_id body string no UUID of the catalogue product this line bills for. Optional: a line can be free text.
source_deal_product_id body string no UUID of the opportunity product this line was generated from, so an invoice line can be traced back to the quoted line it came from.
description body string yes What this line is for. Required on every line.
quantity body number no How many units. Defaults to 1.
unit body string no Unit of measure shown on the invoice, e.g. "hours", "each", "m2".
unit_price body number no Price per unit, before discount and tax.
discount_percent body number no Percentage discount on this line, 0 to 100. Defaults to 0.
tax_amount body number no Tax on this line, as an amount rather than a rate.
external_line_id body string no The line id in the system this invoice was imported from, so re-importing updates the line instead of duplicating it.
sort_order body integer no Position of this line on the invoice, ascending. Defaults to 0.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/invoices/post-invoices-invoice-id-line-items.md

POST /invoices/:invoice_id/line-items/bulk Scopes: invoices:write

Add up to 500 line items to a native invoice in one call. Body: { line_items: [...] }, each item the same shape as add_invoice_line_item. Used for invoice-data imports.

3 parameters
Name In Type Required Description
invoice_id path string yes
line_items body array yes Line items on the invoice. Header totals are computed from these.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/invoices/post-invoices-invoice-id-line-items-bulk.md

POST /invoices/bulk Scopes: invoices:write

Create up to 100 native invoices in one request, each with inline line_items (header totals computed from the lines). Per-row errors are isolated. Built for high-volume migrations and batch invoicing.

2 parameters
Name In Type Required Description
invoices body array yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/invoices/post-invoices-bulk.md

POST /invoices/:invoice_id/cancel-installments Scopes: invoices:write

Stop the instalments on a native invoice. If the client has started a plan, no further payments are taken; what was paid stays paid and the invoice keeps its remaining balance (installment_status becomes cancelled). If no plan has started yet, instalments are switched off and the pay link offers pay in full only.

2 parameters
Name In Type Required Description
invoice_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/invoices/post-invoices-invoice-id-cancel-installments.md

POST /invoices Scopes: invoices:write

Create a native invoice, optionally linked to an opportunity and with inline line_items (header totals are computed from the lines). Pass external_order_id to link an ecommerce order.

18 parameters
Name In Type Required Description
customer_id body string no UUID of the company being invoiced.
opportunity_id body string no UUID of the opportunity this invoice belongs to.
external_order_id body string no The order id in the system this invoice came from, so a re-import updates rather than duplicates.
invoice_number body string no Human-readable invoice number shown to the customer.
invoice_type body string no Whether this bills a customer or records a bill from a supplier. Defaults to the customer-facing type.
issue_date body string no Date the invoice was issued (YYYY-MM-DD).
due_date body string no Date the invoice falls due (YYYY-MM-DD). Set it equal to issue_date for due-on-receipt / same-day terms.
status body string no Invoice status. Payment status moves forward only, so a paid invoice cannot be walked back to unpaid by a later import.
currency_code body string no ISO currency code for the invoice, e.g. "AUD".
currency_rate body number no Exchange rate to the workspace currency at the time of issue. Carry this over when importing foreign-currency invoices, or converted reporting totals will be wrong.
reference body string no Free-text reference shown on the invoice, e.g. a purchase order number.
contact_name body string no Name of the person the invoice is addressed to, as it should print.
contact_external_id body string no The contact id in the system this invoice came from, for matching on re-import.
amount_paid body number no How much has already been paid against this invoice. Set it when importing an invoice that is part- or fully-paid, otherwise it imports as outstanding.
amount_credited body number no How much has been credited against this invoice, e.g. by a credit note.
source_platform body string no Which system this invoice was imported from. Set on create only; update_invoice ignores it so an invoice cannot change its origin.
line_items body array no Line items on the invoice. Header totals are computed from these.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/invoices/post-invoices.md

POST /invoices/:invoice_id/payment-link Scopes: invoices:write

Generate a Stripe payment link for a native invoice using the workspace's connected Stripe account, and store it on the invoice. The link then appears as the "Pay now" button everywhere the invoice is shown (client portal, invoice emails). Requires Stripe connected under Integrations. One-off charges the outstanding balance; pass recurring_interval (day|week|month|year) to make it a subscription charging the full total each period. Pass max_installments (2 up to the workspace cap, default 12) to let the client choose on the invoice's pay page between paying in full and paying in 2..max_installments payments that stop on their own after the last one; the client never types an amount. Each payment is recorded on the invoice (amount_paid, amount_due, installment_payments) and fires stripe_payment_succeeded with installment_number, installments_total and invoice_number; the invoice turns paid on the last payment. TrustPager (native) invoices only.

6 parameters
Name In Type Required Description
invoice_id path string yes
recurring_interval body string no
recurring_interval_count body number no
max_installments body number no Switch instalments on: the most payments the client may choose (2 up to the workspace cap). Omit for an ordinary pay-in-full link.
installment_interval body string no Gap between instalments when max_installments is set. Defaults to month.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/invoices/post-invoices-invoice-id-payment-link.md

DELETE /invoices/:invoice_id Scopes: invoices:write

Delete a native invoice by UUID (its line items are removed too). Destructive.

2 parameters
Name In Type Required Description
invoice_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/invoices/delete-invoices-invoice-id.md

DELETE /invoices/:invoice_id/line-items/:line_id Scopes: invoices:write

Delete one line item from a native invoice. Invoice totals recompute. Destructive.

3 parameters
Name In Type Required Description
invoice_id path string yes
line_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/invoices/delete-invoices-invoice-id-line-items-line-id.md

GET /invoices/:invoice_id Scopes: invoices:read

Fetch a single native invoice by UUID, including its line_items.

1 parameter
Name In Type Required Description
invoice_id path string yes

📄 /api/invoices/get-invoices-invoice-id.md

GET /invoices Scopes: invoices:read

List native invoices. Filter by opportunity_id, customer_id, status, source_platform, external_order_id, or search invoice number / reference.

8 parameters
Name In Type Required Description
limit query number no
after query string no
opportunity_id query string no
customer_id query string no
status query string no
source_platform query string no
external_order_id query string no
search query string no

📄 /api/invoices/get-invoices.md

POST /invoices/:invoice_id/resend Scopes: invoices:write

Send an invoice that already exists, again: a branded email with the invoice PDF attached and a "Pay now" link. Unlike send_invoice this also works on invoices held in a connected accounting platform, fetching the current invoice and PDF from it without altering it, so chasing an overdue invoice needs the invoices:write scope rather than a login to the accounting file. Requires the invoice to be approved/open, not draft or voided.

5 parameters
Name In Type Required Description
invoice_id path string yes
to body string no Optional. Send to this address instead of the contact the invoice is addressed to. Use it to chase an accounts-payable inbox without changing the invoice itself.
attach_pdf body boolean no Optional, defaults to true. Attach the invoice PDF to the email.
recipient_contact_id body string no Optional. UUID of another contact on this workspace to send to instead, e.g. a bookkeeper. Ignored when "to" is given.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/invoices/post-invoices-invoice-id-resend.md

POST /invoices/:invoice_id/send Scopes: invoices:write

Email a native invoice to its linked contact (or an explicit "to" address) via TrustPager Mail. The email includes the line items, amount due, and a "Pay now" button when the invoice has a payment link (run create_invoice_payment_link first for Stripe card payment); otherwise it shows the workspace bank/BPAY details.

3 parameters
Name In Type Required Description
invoice_id path string yes
to body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/invoices/post-invoices-invoice-id-send.md

PATCH /invoices/:invoice_id Scopes: invoices:write

Update a native invoice header (status, dates, reference, amount_paid, etc.). amount_due is recomputed. Pass only the fields you want to change.

7 parameters
Name In Type Required Description
invoice_id path string yes
status body string no
due_date body string no
reference body string no
amount_paid body number no
amount_credited body number no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/invoices/patch-invoices-invoice-id.md

PATCH /invoices/:invoice_id/line-items/:line_id Scopes: invoices:write

Update one line item on a native invoice. line_total and invoice totals recompute when pricing fields change.

13 parameters
Name In Type Required Description
invoice_id path string yes
line_id path string yes
product_id body string no UUID of the catalogue product this line bills for. Optional: a line can be free text.
source_deal_product_id body string no UUID of the opportunity product this line was generated from, so an invoice line can be traced back to the quoted line it came from.
description body string no What this line is for. Required on every line.
quantity body number no How many units. Defaults to 1.
unit body string no Unit of measure shown on the invoice, e.g. "hours", "each", "m2".
unit_price body number no Price per unit, before discount and tax.
discount_percent body number no Percentage discount on this line, 0 to 100. Defaults to 0.
tax_amount body number no Tax on this line, as an amount rather than a rate.
external_line_id body string no The line id in the system this invoice was imported from, so re-importing updates the line instead of duplicating it.
sort_order body integer no Position of this line on the invoice, ascending. Defaults to 0.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/invoices/patch-invoices-invoice-id-line-items-line-id.md

Recurring Invoices (5 endpoints)

Recurring invoice schedules that generate invoices automatically on a cadence.

📄 Full markdown: /api/recurring-invoices.md

POST /recurring-invoices Scopes: recurring_invoices:write

Set up a recurring invoice: pick a schedule (interval_unit week|month|year + interval_count) and line_items, optionally linked to an opportunity/contact. Each period TrustPager generates a fresh native invoice, attaches a Stripe 'Pay now' link (attach_payment_link, needs Stripe connected), and emails it (auto_send). Optional end_date or max_occurrences stop the series. Starts active by default.

19 parameters
Name In Type Required Description
name body string no
opportunity_id body string no
contact_id body string no
customer_id body string no
contact_name body string no
currency_code body string no
reference body string no
interval_unit body string no
interval_count body number no
start_date body string no
next_run_at body string no
end_date body string no
max_occurrences body number no
due_days body number no Whole days after the issue date that each generated invoice falls due. 0 = due on receipt (the due date is the issue date). Defaults to 7.
auto_send body boolean no
attach_payment_link body boolean no
status body string no
line_items body array yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/recurring-invoices/post-recurring-invoices.md

DELETE /recurring-invoices/:recurring_invoice_id Scopes: recurring_invoices:write

Stop a recurring invoice schedule permanently (no more invoices are generated). Already-generated invoices are kept. Destructive.

2 parameters
Name In Type Required Description
recurring_invoice_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/recurring-invoices/delete-recurring-invoices-recurring-invoice-id.md

GET /recurring-invoices/:recurring_invoice_id Scopes: recurring_invoices:read

Fetch one recurring invoice schedule by UUID, including its template line items, next run date, and how many invoices it has generated.

1 parameter
Name In Type Required Description
recurring_invoice_id path string yes

📄 /api/recurring-invoices/get-recurring-invoices-recurring-invoice-id.md

GET /recurring-invoices Scopes: recurring_invoices:read

List recurring invoice schedules. Filter by status (active|paused|stopped|completed) or opportunity_id. Each schedule auto-generates a native invoice every period.

3 parameters
Name In Type Required Description
status query string no
opportunity_id query string no
limit query number no

📄 /api/recurring-invoices/get-recurring-invoices.md

PATCH /recurring-invoices/:recurring_invoice_id Scopes: recurring_invoices:write

Update a recurring invoice schedule (line items, interval, dates, toggles) OR change its status to pause/resume/stop it. status='paused' stops generating, status='active' resumes, status='stopped' ends it. Pass only the fields you want to change.

17 parameters
Name In Type Required Description
recurring_invoice_id path string yes
name body string no
opportunity_id body string no
contact_id body string no
currency_code body string no
reference body string no
interval_unit body string no
interval_count body number no
next_run_at body string no
end_date body string no
max_occurrences body number no
due_days body number no Whole days after the issue date that each generated invoice falls due. 0 = due on receipt (the due date is the issue date). Defaults to 7.
auto_send body boolean no
attach_payment_link body boolean no
status body string no
line_items body array no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/recurring-invoices/patch-recurring-invoices-recurring-invoice-id.md

Files (15 endpoints)

Unified file storage endpoint for all file types. Use ?type=document (private PDFs), ?type=image (public CDN/R2 images), or ?type=secure (confidential private files). Single-resource routes (/files/:id) auto-detect the type.

📄 Full markdown: /api/files.md

POST /files/bundle Scopes: files:read

Bundle up to 50 files (documents, images, or secure files) into a single ZIP. Auto-detects each file's type. Two response modes: default returns binary application/zip with Content-Disposition: attachment (best for direct browser downloads). Pass ?response=url to instead stage the ZIP in private storage and receive a 10-minute signed download URL — the right mode for API/MCP agents that can't consume large binary bodies. Max 50 files / 200MB total. Skipped files (storage errors) are listed in _manifest.txt inside the ZIP.

3 parameters
Name In Type Required Description
ids body array yes File UUIDs to bundle (1-50). Mix of document / image / secure types is fine — each is auto-detected.
filename body string no Optional ZIP filename (without extension). Defaults to "attachments_<YYYY-MM-DD>.zip".
response body string no Response mode. "binary" (default): streams the ZIP back in the response body. "url": stages the ZIP and returns a JSON body with a signed download URL good for 10 minutes. Pass as a query string (?response=url) — body field is also accepted.

📄 /api/files/post-files-bundle.md

POST /files/folders Scopes: files:write

Create a new file folder.

2 parameters
Name In Type Required Description
name body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/files/post-files-folders.md

DELETE /files/:file_id Scopes: files:delete

Delete a file by UUID. Destructive.

2 parameters
Name In Type Required Description
file_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/files/delete-files-file-id.md

DELETE /files/folders/:folder_id Scopes: files:delete

Delete a file folder by UUID.

2 parameters
Name In Type Required Description
folder_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/files/delete-files-folders-folder-id.md

GET /files/:file_id/download Scopes: files:read

Download a workspace file by UUID.

1 parameter
Name In Type Required Description
file_id path string yes

📄 /api/files/get-files-file-id-download.md

GET /files/:file_id Scopes: files:read

Fetch a single file metadata record by UUID.

1 parameter
Name In Type Required Description
file_id path string yes

📄 /api/files/get-files-file-id.md

GET /files/:file_id/signed-url Scopes: files:read

Get a signed playback URL for a private file.

1 parameter
Name In Type Required Description
file_id path string yes

📄 /api/files/get-files-file-id-signed-url.md

GET /files Scopes: files:read

List files in workspace storage. `type` is REQUIRED and selects which store to read: document, image or secure. Then optionally filter by folder, tag, category, document_type, file_category, or free-text search. Every file carries a `tags` array of {name, color} — the same tags shown on the file in the Files page — so `tag` is how you pull back, say, only the "Before" photos.

9 parameters
Name In Type Required Description
type query string yes REQUIRED. Which file store to list: document (PDFs and docs), image (images/media), or secure (private files). Omitting it returns a 400.
limit query number no
after query string no
search query string no Free-text search across file names.
folder query string no
tag query string no Return only files carrying this tag, matched on the tag name (e.g. "Before"). Case-sensitive, one tag per request.
category query string no
document_type query string no
file_category query string no

📄 /api/files/get-files.md

POST /files/:file_id/make-private Scopes: files:write

Make a file private.

2 parameters
Name In Type Required Description
file_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/files/post-files-file-id-make-private.md

POST /files/:file_id/make-public Scopes: files:write

Make a file publicly accessible.

2 parameters
Name In Type Required Description
file_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/files/post-files-file-id-make-public.md

POST /files/:file_id/publish Scopes: files:write

Publish a document (make it generally accessible).

2 parameters
Name In Type Required Description
file_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/files/post-files-file-id-publish.md

POST /files/:file_id/unpublish Scopes: files:write

Unpublish a document.

2 parameters
Name In Type Required Description
file_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/files/post-files-file-id-unpublish.md

PUT /files/:file_id Scopes: files:write

Update a file metadata record: rename it, change its description, move it to another folder, or set its tags. Only the keys you send are changed.

8 parameters
Name In Type Required Description
file_id path string yes REQUIRED. The file id.
name body string no The file name as shown in the Files page, including its extension.
description body string no Free-text description stored with the file.
folder body string no The folder the file sits in on the Files page. This is the folder NAME, not an id.
tags body array no Full replace of the file's tags — the coloured chips shown on the file in the Files page (e.g. ["Before"] on a job photo). Accepts names (["Before"]) or objects ([{"name":"Before","color":"#f97316"}]). Send [] to clear every tag; omit the key to leave tags untouched. Max 10.
document_type body string no Document files only — the document category.
file_category body string no Secure files only — the file category.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/files/put-files-file-id.md

POST /files/upload Scopes: files:write

Upload a file to workspace storage (max 23MB per file, 10MB for images). Pass type=document (PDF), type=image, or type=secure (private files — the default for spreadsheets, CSVs, Office docs, and anything else). Non-PDF files sent as type=document are auto-filed as secure files instead of rejected (the response meta returns auto_filed_as:"file"); attach those to an opportunity with add_opportunity_file, not add_opportunity_document.

11 parameters
Name In Type Required Description
type body string yes REQUIRED. The kind of file: "document" (PDF), "image", or "secure" (private files — spreadsheets, CSVs, Office docs, and anything else). This is the top-level field that selects how the file is stored; it is NOT the same as "category" or "document_type".
base64 body string yes REQUIRED. The file content as base64. A leading "data:...;base64," prefix is accepted and stripped automatically.
name body string yes REQUIRED. The filename including its extension (e.g. "briefing.pdf").
mime_type body string no Optional MIME type (e.g. "application/pdf"). Defaults to a value derived from the filename extension.
folder body string no Optional folder/label to file the upload under.
description body string no Optional description stored with the file.
document_type body string no Optional. Only used when type="document" — categorises the document record.
category body string no Optional. Only used when type="image" — sets the storage path prefix and image category. Does NOT set the file type; use the "type" field for that.
file_category body string no Optional. Only used when type="secure" — categorises the secure file (defaults to a value derived from the MIME type).
tags body array no Optional tags to classify the file, shown as coloured chips on the file in the Files page (e.g. ["Before"] on a job photo). Accepts names (["Before"]) or objects ([{"name":"Before","color":"#f97316"}]); a name with no colour gets the workspace default. Max 10.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/files/post-files-upload.md

Images (1 endpoint)

Image optimisation: resize a stored image and generate fast WebP variants.

📄 Full markdown: /api/images.md

POST /images/optimize Scopes: files:write

Optimize a stored image — resize and generate WebP variants for fast delivery.

3 parameters
Name In Type Required Description
file_id body string yes
max_width body number no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/images/post-images-optimize.md

Notepads (18 endpoints)

Manage rich-text notepads organized in folders. Supports markdown input, iterative editing (append/prepend/section patches), per-record visibility, and ACL-based access control for restricted notes.

📄 Full markdown: /api/notepads.md

POST /notepads/:notepad_id/acl Scopes: notepads:write

Grant access to a notepad (user or role principal).

5 parameters
Name In Type Required Description
notepad_id path string yes
principal_type body string yes
principal_id body string yes
permission body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/notepads/post-notepads-notepad-id-acl.md

POST /notepads/folders/:folder_id/acl Scopes: notepads:write

Grant access to a notepad folder (user or role principal).

5 parameters
Name In Type Required Description
folder_id path string yes
principal_type body string yes
principal_id body string yes
permission body string no read or write
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/notepads/post-notepads-folders-folder-id-acl.md

POST /notepads Scopes: notepads:write

Create a new notepad. Supports markdown content. Set is_public=true to publish it to the public blog (a slug is derived from the title); notepads are private by default. Set meta_title / meta_description to control the published post's SEO tags.

13 parameters
Name In Type Required Description
title body string yes
content body string no
folder_id body string no UUID of the folder this notepad is filed in. Get real ids from list_notepad_folders. Pass null to move it to the top level.
folder body string no Free-text folder label, the older way of grouping notepads. Prefer folder_id, which points at a real folder record.
is_favorite body boolean no Whether this notepad is pinned to the top of the list for the workspace.
visibility body string no Who inside the workspace can see this notepad: "all_users" (default) or "restricted". Separate from is_public, which controls the public blog.
is_public body boolean no Publish to the public blog. Private by default. The post only appears publicly when it is also in a website's designated blog folder.
slug body string no Optional public URL slug. Derived from the title when omitted.
cover_image body string no Cover image URL — shown on the Notepads card grid and at the top of the public blog post.
meta_title body string no SEO title tag for the published post. Keep it to about 60 characters. When omitted the website falls back to the notepad title.
meta_description body string no SEO meta description for the published post. Keep it to about 160 characters. When omitted the website falls back to an auto-generated excerpt of the body.
og_image body string no Social share image URL (og:image) for the published post. Falls back to cover_image when omitted.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/notepads/post-notepads.md

POST /notepads/folders Scopes: notepads:write

Create a new notepad folder. Set is_public=true to mark it a public content collection that can be integrated as a section on a website (the public notepads inside it then appear there).

3 parameters
Name In Type Required Description
name body string yes
is_public body boolean no Mark the folder as a public content collection (integratable on websites).
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/notepads/post-notepads-folders.md

DELETE /notepads/:notepad_id Scopes: notepads:delete

Delete a notepad by UUID. Destructive.

2 parameters
Name In Type Required Description
notepad_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/notepads/delete-notepads-notepad-id.md

DELETE /notepads/folders/:folder_id Scopes: notepads:delete

Delete a notepad folder by UUID.

2 parameters
Name In Type Required Description
folder_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/notepads/delete-notepads-folders-folder-id.md

GET /notepads/:notepad_id Scopes: notepads:read

Fetch a notepad by UUID, including its full content.

1 parameter
Name In Type Required Description
notepad_id path string yes

📄 /api/notepads/get-notepads-notepad-id.md

POST /notepads/:notepad_id/deals Scopes: notepads:write

Link a notepad to an opportunity.

3 parameters
Name In Type Required Description
notepad_id path string yes
deal_id body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/notepads/post-notepads-notepad-id-deals.md

GET /notepads Scopes: notepads:read

List notepads in the workspace. Filter by folder, folder_id, visibility, is_favorite, is_public, or free-text search.

8 parameters
Name In Type Required Description
limit query number no
after query string no
search query string no Free-text search across notepad titles and content.
folder query string no
folder_id query string no
visibility query string no
is_favorite query boolean no
is_public query boolean no Filter to public (blog-published) notepads only.

📄 /api/notepads/get-notepads.md

DELETE /notepads/acl/:acl_id Scopes: notepads:write

Revoke a notepad ACL entry by UUID.

2 parameters
Name In Type Required Description
acl_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/notepads/delete-notepads-acl-acl-id.md

DELETE /notepads/:notepad_id/deals/:deal_id Scopes: notepads:write

Unlink a notepad from an opportunity.

3 parameters
Name In Type Required Description
notepad_id path string yes
deal_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/notepads/delete-notepads-notepad-id-deals-deal-id.md

PATCH /notepads/:notepad_id Scopes: notepads:write

Update the title and/or content of a notepad. To change one section without touching the rest (safer when other people edit the note), send patches instead of content: each patch finds a heading by its text and replaces or appends to that section; meta.patches_applied and meta.patches_unmatched say which headings were found. mode "append" or "prepend" adds content to the end or start instead of replacing it. Set is_public=true to publish it to the public blog, or false to unpublish; a slug is derived from the title on first publish. Set meta_title / meta_description to control the published post's SEO tags.

17 parameters
Name In Type Required Description
notepad_id path string yes
title body string no
content body string no The new body, as markdown or HTML. Replaces the whole note unless mode is "append" or "prepend". Not used together with patches.
mode body string no How content is applied: replace the whole note (default), add it to the end, or add it to the start.
patches body array no Change single sections instead of the whole note. Not used together with content.
return_content body boolean no true returns the full note, including its content, in the response.
folder_id body string no UUID of the folder this notepad is filed in. Get real ids from list_notepad_folders. Pass null to move it to the top level.
folder body string no Free-text folder label, the older way of grouping notepads. Prefer folder_id, which points at a real folder record.
is_favorite body boolean no Whether this notepad is pinned to the top of the list for the workspace.
visibility body string no Who inside the workspace can see this notepad: "all_users" (default) or "restricted". Separate from is_public, which controls the public blog.
is_public body boolean no Publish (true) or unpublish (false) to the public blog.
slug body string no Optional public URL slug. Derived from the title when omitted.
cover_image body string no Cover image URL (shown on the card grid + public post). Pass null to clear.
meta_title body string no SEO title tag for the published post. Keep it to about 60 characters. Pass null to clear and fall back to the notepad title.
meta_description body string no SEO meta description for the published post. Keep it to about 160 characters. Pass null to clear and fall back to an auto-generated excerpt of the body.
og_image body string no Social share image URL (og:image) for the published post. Pass null to clear and fall back to cover_image.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/notepads/patch-notepads-notepad-id.md

PATCH /notepads/folders/:folder_id Scopes: notepads:write

Rename or update a notepad folder. Set is_public to mark it (true) or unmark it (false) as a public content collection integratable on websites.

6 parameters
Name In Type Required Description
folder_id path string yes
name body string no
is_public body boolean no Public content collection flag.
parent_id body string no UUID of the folder this one sits inside. Pass null to move it to the top level.
visibility body string no Who inside the workspace can see this folder: "all_users" (default) or "restricted". Separate from is_public, which controls the public collection.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/notepads/patch-notepads-folders-folder-id.md

Whiteboards (8 endpoints)

Whiteboards and their canvas contents, optionally linked to an opportunity.

📄 Full markdown: /api/whiteboards.md

POST /whiteboards/:whiteboard_id/elements Scopes: whiteboards:write

Append elements to the current whiteboard canvas without wiping what is already there.

3 parameters
Name In Type Required Description
whiteboard_id path string yes
elements body array yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/whiteboards/post-whiteboards-whiteboard-id-elements.md

POST /whiteboards Scopes: whiteboards:write

Create a whiteboard. Starts empty; use set_whiteboard_content to add a diagram.

4 parameters
Name In Type Required Description
name body string yes
description body string no
opportunity_id body string no Optional opportunity to link the whiteboard to.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/whiteboards/post-whiteboards.md

DELETE /whiteboards/:whiteboard_id Scopes: whiteboards:delete

Delete a whiteboard and its canvas.

2 parameters
Name In Type Required Description
whiteboard_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/whiteboards/delete-whiteboards-whiteboard-id.md

POST /whiteboards/:whiteboard_id/duplicate Scopes: whiteboards:write

Clone a whiteboard and its current canvas into a new board.

3 parameters
Name In Type Required Description
whiteboard_id path string yes
name body string no Optional name for the copy.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/whiteboards/post-whiteboards-whiteboard-id-duplicate.md

GET /whiteboards/:whiteboard_id Scopes: whiteboards:read

Fetch a whiteboard with its current canvas elements, appState and files.

1 parameter
Name In Type Required Description
whiteboard_id path string yes

📄 /api/whiteboards/get-whiteboards-whiteboard-id.md

GET /whiteboards Scopes: whiteboards:read

List whiteboards in the workspace. Filter by opportunity_id or search by name.

2 parameters
Name In Type Required Description
opportunity_id query string no
search query string no

📄 /api/whiteboards/get-whiteboards.md

PUT /whiteboards/:whiteboard_id/content Scopes: whiteboards:write

Replace the entire whiteboard canvas with an array of Excalidraw elements (rectangle, ellipse, diamond, arrow, line, text). Minimal elements are auto-completed. NOTE: an open builder tab reads content on load and auto-saves over external changes — close/reopen the board to see the result.

5 parameters
Name In Type Required Description
whiteboard_id path string yes
elements body array yes Excalidraw elements. Each needs type + x + y + width + height; text/colors/points optional.
appState body object no
files body object no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/whiteboards/put-whiteboards-whiteboard-id-content.md

PATCH /whiteboards/:whiteboard_id Scopes: whiteboards:write

Update a whiteboard name, description, or linked opportunity.

6 parameters
Name In Type Required Description
whiteboard_id path string yes UUID of the whiteboard to update. Get ids from list_whiteboards.
opportunity_id body string no UUID of the opportunity this whiteboard hangs off. Pass null to make it standalone.
name body string no Whiteboard name shown in the Whiteboards list.
description body string no What this whiteboard is for. Internal note for your team.
settings body object no Whiteboard canvas settings (grid, snapping, background, default tool). Full-replace on write.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/whiteboards/patch-whiteboards-whiteboard-id.md

Spreadsheets (16 endpoints)

Manage spreadsheet templates (column definitions) and populated spreadsheets (row data). Spreadsheets can be linked to opportunities and are also created automatically when a form with a spreadsheet field is submitted. Scopes: spreadsheets:read, spreadsheets:write, spreadsheets:delete.

📄 Full markdown: /api/spreadsheets.md

POST /spreadsheets/:spreadsheet_id/rows Scopes: spreadsheets:write

Append a row to a spreadsheet.

3 parameters
Name In Type Required Description
spreadsheet_id path string yes
cells body object no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/spreadsheets/post-spreadsheets-spreadsheet-id-rows.md

POST /spreadsheets/:spreadsheet_id/rows/bulk Scopes: spreadsheets:write

Bulk-append rows.

3 parameters
Name In Type Required Description
spreadsheet_id path string yes
rows body array yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/spreadsheets/post-spreadsheets-spreadsheet-id-rows-bulk.md

POST /spreadsheets Scopes: spreadsheets:write

Create a spreadsheet from a template.

3 parameters
Name In Type Required Description
template_id body string yes
name body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/spreadsheets/post-spreadsheets.md

POST /spreadsheets/templates Scopes: spreadsheets:write

Create a spreadsheet template.

3 parameters
Name In Type Required Description
name body string yes
columns body array no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/spreadsheets/post-spreadsheets-templates.md

DELETE /spreadsheets/:spreadsheet_id Scopes: spreadsheets:delete

Delete a spreadsheet.

2 parameters
Name In Type Required Description
spreadsheet_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/spreadsheets/delete-spreadsheets-spreadsheet-id.md

DELETE /spreadsheets/:spreadsheet_id/rows/:row_id Scopes: spreadsheets:delete

Delete a row.

3 parameters
Name In Type Required Description
spreadsheet_id path string yes
row_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/spreadsheets/delete-spreadsheets-spreadsheet-id-rows-row-id.md

DELETE /spreadsheets/templates/:template_id Scopes: spreadsheets:delete

Delete a spreadsheet template.

2 parameters
Name In Type Required Description
template_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/spreadsheets/delete-spreadsheets-templates-template-id.md

POST /spreadsheets/:spreadsheet_id/import_from_document Scopes: spreadsheets:write

Extract rows from a PDF via a document mapper.

4 parameters
Name In Type Required Description
spreadsheet_id path string yes
mapper_id body string yes
file_id body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/spreadsheets/post-spreadsheets-spreadsheet-id-import-from-document.md

GET /spreadsheets Scopes: spreadsheets:read

List spreadsheets in the workspace.

1 parameter
Name In Type Required Description
limit query number no

📄 /api/spreadsheets/get-spreadsheets.md

PATCH /spreadsheets/:spreadsheet_id Scopes: spreadsheets:write

Update a spreadsheet.

5 parameters
Name In Type Required Description
spreadsheet_id path string yes UUID of the spreadsheet to update. Get ids from list_spreadsheets.
name body string no Spreadsheet name shown in the Spreadsheets list.
opportunity_id body string no UUID of the opportunity this spreadsheet hangs off. Pass null to make it standalone.
is_archived body boolean no Archive the spreadsheet. Archived spreadsheets stay readable but are hidden from the default list.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/spreadsheets/patch-spreadsheets-spreadsheet-id.md

PATCH /spreadsheets/:spreadsheet_id/rows/:row_id Scopes: spreadsheets:write

Update a row.

4 parameters
Name In Type Required Description
spreadsheet_id path string yes
row_id path string yes
cells body object no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/spreadsheets/patch-spreadsheets-spreadsheet-id-rows-row-id.md

PATCH /spreadsheets/templates/:template_id Scopes: spreadsheets:write

Update a spreadsheet template.

8 parameters
Name In Type Required Description
template_id path string yes UUID of the spreadsheet template to update. Get ids from list_spreadsheet_templates.
name body string no Template name shown in the Spreadsheets list.
description body string no What this template is for. Internal note for your team.
columns body array no Ordered column definitions. Each column: { id, label, type, options? }. Validated on write — an unknown column type is rejected. Full-replace array.
merge_rules body object no How rows from different sources are matched and merged into one row. Validated on write. Full-replace on write.
default_sort body object no Default sort applied when the spreadsheet opens, e.g. { "column_id": "...", "direction": "asc" }. Validated on write.
is_archived body boolean no Archive the template. Archived templates stay readable but are hidden from the picker.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/spreadsheets/patch-spreadsheets-templates-template-id.md

Reports (18 endpoints)

Query engine, dashboard CRUD, card CRUD, and funnel configuration for the workspace reporting system. Dashboards support per-record visibility (all_users or restricted) with ACL-based access grants.

📄 Full markdown: /api/reports.md

POST /report-dashboards/:dashboard_id/cards Scopes: opportunities:read

Add a card to a dashboard. Without a position it goes last. Emailed digests show the first 6 cards only, so a card that lands 7th or later returns a _warnings entry saying it will be named in the email rather than shown. Standard cards carry a query_spec (same shape as query_report). A funnel card (visualization_type "funnel") measures pipeline pass-through conversion from the stage-move log: omit query_spec and pass display_config.funnel = { pipeline_id, steps: [{ name, stage_ids }] } instead.

8 parameters
Name In Type Required Description
dashboard_id path string yes Dashboard UUID
title body string yes Card title
visualization_type body string no stat, bar, horizontal_bar, line, area, donut, pie, table, composed, or funnel
query_spec body object no Query spec (same shape as query_report. Note filters[]: in / not_in take a non-empty "values" array, every other operator takes a single "value", and a malformed filter is rejected rather than ignored). Omit for funnel cards. On a card grouped by one person (moved_by_user_name, assigned_user_name or assignee_name), always_show_user_ids: [user ids] lists those people with 0 when they have no rows. For a drilldown table card set mode:"drilldown" and list the columns you want in dimensions[] (in order, custom fields included as cf_<id>); the emailed digest table renders up to 8 of them.
display_config body object no Display overrides. Funnel cards: { funnel: { pipeline_id, steps: [{ name, stage_ids }] } }. Table cards: { columns, max_rows }. max_rows caps how many rows render in the emailed digest table (default 25, max 100) — raise it if rows are being dropped from the digest.
size body string no sm, md, lg
position body number no Order within the dashboard (lower comes first). Omit to add the card at the end.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reports/post-report-dashboards-dashboard-id-cards.md

POST /report-dashboards/:dashboard_id/acl Scopes: opportunities:read

Grant dashboard access.

4 parameters
Name In Type Required Description
dashboard_id path string yes
principal_type body string yes
principal_id body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reports/post-report-dashboards-dashboard-id-acl.md

PUT /report-funnels Scopes: opportunities:read

Configure a funnel for a pipeline.

3 parameters
Name In Type Required Description
pipeline_id body string yes
steps body array yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reports/put-report-funnels.md

POST /report-dashboards Scopes: opportunities:read

Create a report dashboard.

5 parameters
Name In Type Required Description
name body string no
description body string no
template body string no Dashboard template key to seed cards from.
pipeline_id body string no Scope the seeded cards to one pipeline.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reports/post-report-dashboards.md

DELETE /report-dashboards/:dashboard_id Scopes: opportunities:read

Delete a dashboard.

2 parameters
Name In Type Required Description
dashboard_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reports/delete-report-dashboards-dashboard-id.md

GET /report-funnels Scopes: opportunities:read

Get funnel config for a pipeline.

1 parameter
Name In Type Required Description
pipeline_id query string yes

📄 /api/reports/get-report-funnels.md

POST /reports/query Scopes: opportunities:read

Run a report query against the workspace. Free read. In aggregate mode (default) dimensions[] are the grouping keys. In drilldown mode (mode:"drilldown") dimensions[] instead names the columns to return and their order — any dimension or measure field from list_report_sources, including per-workspace custom fields (cf_<id>); omit it to get the source default column set. The "deals" source excludes pipelines flagged exclude_from_lead_reporting by default; pass include_excluded_pipelines:true to include them.

6 parameters
Name In Type Required Description
source body string yes
measures body array no
dimensions body array no
filters body array no Filters, all ANDed. Two shapes, and sending the wrong one is rejected rather than ignored: in / not_in take a non-empty LIST in "values" (plural), e.g. { field: "pipeline_name", operator: "in", values: ["Client Acquisitions", "Enquiry Funnel"] }; every other operator takes a single "value", e.g. { field: "status", operator: "eq", value: "open" }. Operators: eq, neq, gt, gte, lt, lte, in, not_in, like, contains, is_null, is_not_null (the last two take neither value nor values). Date values accept relative windows like {{today-28}} or {{now-24h}}.
include_excluded_pipelines body boolean no Deals source only. Include pipelines flagged exclude_from_lead_reporting (excluded by default).
always_show_user_ids body array no Opt-in "always show these people" for a by-person aggregate: each listed user (workspace members only, up to 50) with no rows gets a row of zeros, so someone who did nothing shows 0 instead of being left off. Only with exactly one dimension of moved_by_user_name, assigned_user_name or assignee_name and no time_dimension. Set it inside the query_spec of a card to keep it on the dashboard and the emailed digest.

📄 /api/reports/post-reports-query.md

DELETE /report-cards/:card_id Scopes: opportunities:read

Delete a card.

2 parameters
Name In Type Required Description
card_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reports/delete-report-cards-card-id.md

POST /reports/render-preview Scopes: opportunities:read

Server-side render a dashboard to HTML for preview, exactly as the emailed digest shows it. Emails show the first 6 cards in dashboard order: rendered_card_count never exceeds 6, so compare total_card_count and omitted_card_count to see whether cards were left out (they are named at the bottom with a link).

1 parameter
Name In Type Required Description
dashboard_id body string yes

📄 /api/reports/post-reports-render-preview.md

PUT /report-dashboards/:dashboard_id/reorder Scopes: opportunities:read

Reorder cards on a dashboard.

3 parameters
Name In Type Required Description
dashboard_id path string yes
card_ids body array yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reports/put-report-dashboards-dashboard-id-reorder.md

POST /reports/send Scopes: opportunities:read

Send a dashboard digest email. The dashboard renders inline as the email body; set attach_pdf to also send a printable A4 copy. Emails show the first 6 cards in dashboard order; any cards past that are named at the bottom of the email with a link to the dashboard, and the response reports them as total_card_count, omitted_card_count and omitted_cards.

9 parameters
Name In Type Required Description
dashboard_id body string yes
recipients body array no Who to send to: email strings, or { email, name?, user_id? } objects. Pass user_id to scope cards filtered to "current user" to that person.
to body string no Single-address shorthand for recipients.
subject body string no Defaults to the dashboard name.
intro_html body string no HTML block above the report.
outro_html body string no HTML block below the report.
suppress_if_empty body boolean no Default true: skip a recipient whose every card returned zero rows.
attach_pdf body boolean no Also attach the rendered dashboard as a printable A4 PDF. Off by default. Bills 1 credit per page per recipient. If the render fails the email still sends without it, and the response says why under recipients[].pdf_error.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reports/post-reports-send.md

PATCH /report-cards/:card_id Scopes: opportunities:read

Update a card: its title, chart type, query, display options, size or position. Only the fields you send change.

8 parameters
Name In Type Required Description
card_id path string yes Card UUID
title body string no Card title
visualization_type body string no stat, bar, horizontal_bar, line, area, donut, pie, table, composed, or funnel
query_spec body object no Query spec (same shape as query_report). On a card grouped by one person, always_show_user_ids: [user ids] lists those people with 0 when they have no rows. For a drilldown table card set mode:"drilldown" and list the columns you want in dimensions[] (in order, custom fields included as cf_<id>). A drilldown needs no measures[]: it lists raw rows and aggregates nothing.
display_config body object no Display overrides. Funnel cards: { funnel: { pipeline_id, steps: [{ name, stage_ids }] } }. Table cards: { columns, max_rows }.
position body number no Order within the dashboard.
size body string no sm, md, lg
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reports/patch-report-cards-card-id.md

PATCH /report-dashboards/:dashboard_id Scopes: opportunities:read

Update a dashboard: its name, description or visibility. Only the fields you send change.

5 parameters
Name In Type Required Description
dashboard_id path string yes Dashboard UUID
name body string no Dashboard name.
description body string no Dashboard description.
visibility body string no all_users or restricted.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reports/patch-report-dashboards-dashboard-id.md

AI Knowledge (7 endpoints)

Manage company AI Knowledge entries with AI-powered semantic search. Use for policies, FAQs, agent instructions, and product documentation.

📄 Full markdown: /api/knowledge.md

POST /knowledge Scopes: knowledge:write

Create a new knowledge base entry. Requires title and content.

9 parameters
Name In Type Required Description
title body string yes
content body string yes
category body string no
tags body array no
kb_id body string no UUID of the knowledge base this entry belongs to. Get real ids from list_knowledge_bases. Omit for the workspace default.
source_type body string no What kind of thing this entry was built from, e.g. a product, a document or a page.
source_id body string no UUID of the record this entry was built from, so it can be regenerated or kept in step when that record changes.
source_ref body string no A reference to the source when it has no UUID, e.g. a URL or a file name.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/knowledge/post-knowledge.md

DELETE /knowledge/:knowledge_id Scopes: knowledge:delete

Delete a knowledge base entry by UUID. Destructive.

2 parameters
Name In Type Required Description
knowledge_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/knowledge/delete-knowledge-knowledge-id.md

GET /knowledge/:knowledge_id Scopes: knowledge:read

Fetch a single knowledge base entry by UUID. Returns full content.

1 parameter
Name In Type Required Description
knowledge_id path string yes

📄 /api/knowledge/get-knowledge-knowledge-id.md

GET /knowledge Scopes: knowledge:read

List entries in the workspace knowledge base. Supports filtering by category, tag, and text search.

5 parameters
Name In Type Required Description
limit query number no
after query string no
category query string no
tag query string no Exact match against one of the entry's tags.
search query string no Free-text search on the entry title.

📄 /api/knowledge/get-knowledge.md

POST /knowledge/search Scopes: knowledge:read

Semantic search across the workspace knowledge base. Use to find relevant context before answering a customer question. Pass kb_ids to scope to specific knowledge bases, or agent_type + agent_id to auto-scope to the knowledge bases attached to that agent.

5 parameters
Name In Type Required Description
query body string yes Search query
limit body number no Max results (default 5)
kb_ids body array no Optional: restrict search to these knowledge base IDs
agent_type body string no Optional: with agent_id, auto-scope to the KBs attached to this agent
agent_id body string no Optional: the agent whose attached KBs scope the search

📄 /api/knowledge/post-knowledge-search.md

POST /knowledge/sync-from-products Scopes: knowledge:write

Refresh the knowledge base entries that auto-generate from the product catalog.

1 parameter
Name In Type Required Description
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/knowledge/post-knowledge-sync-from-products.md

PATCH /knowledge/:knowledge_id Scopes: knowledge:write

Update a knowledge base entry. Pass only the fields you want to change.

10 parameters
Name In Type Required Description
knowledge_id path string yes
title body string no
content body string no
category body string no
tags body array no
kb_id body string no UUID of the knowledge base this entry belongs to. Get real ids from list_knowledge_bases. Omit for the workspace default.
source_type body string no What kind of thing this entry was built from, e.g. a product, a document or a page.
source_id body string no UUID of the record this entry was built from, so it can be regenerated or kept in step when that record changes.
source_ref body string no A reference to the source when it has no UUID, e.g. a URL or a file name.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/knowledge/patch-knowledge-knowledge-id.md

Agent Memory (7 endpoints)

Cross-run persistent state for managed agents. Each row records what an agent learned, decided, or intends to revisit about a subject. Supports semantic search via vector embeddings. Required scopes: memory:read (GET/search), memory:write (POST/PATCH/DELETE).

📄 Full markdown: /api/memory.md

DELETE /memory/:memory_id Scopes: memory:write

Soft-delete a memory. Sets deleted_at; the row is preserved for audit but no longer surfaces in list / search / get calls. Reversible only by admin SQL.

2 parameters
Name In Type Required Description
memory_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/memory/delete-memory-memory-id.md

GET /memory/:memory_id Scopes: memory:read

Fetch a single agent memory by its uuid.

1 parameter
Name In Type Required Description
memory_id path string yes

📄 /api/memory/get-memory-memory-id.md

GET /memory Scopes: memory:read

List agent memories for this workspace, filterable by subject, kind, agent, tag, linked entity, and time. Cursor-paginated. Use this for keyed lookup before composing new actions (e.g. "what have I already said to this contact?"); use search_memory for fuzzy / semantic lookup.

11 parameters
Name In Type Required Description
subject_type query string no Filter by subject type — e.g. "contact", "opportunity", "company", "user", "self".
subject_id query string no Filter by subject id (use with subject_type).
kind query string no Filter by kind — e.g. "pitch", "objection", "preference", "next_steps".
agent_registry_id query string no Filter by owning agent.
visibility query string no Filter by visibility.
tag query array no Tag filter (matches any of the provided tags). Pass as repeated `tag` query params or array.
linked_entity_type query string no Find memories linked to an entity of this type.
linked_entity_id query string no Find memories linked to this entity id (must be combined with linked_entity_type).
since query string no ISO timestamp — return memories created after this time.
limit query number no
after query string no

📄 /api/memory/get-memory.md

POST /memory/purge Scopes: memory:delete

Bulk-retract agent memories matching a filter, in one call. Soft-deletes by default (reversible, audit-preserving); pass hard:true to physically remove rows for PII / right-to-be-forgotten. This is the "forget everything about this contact / opportunity / company" primitive — filter by subject, agent, kind, tag, or linked entity. Refuses to run with no filter unless you set all:true. Requires the memory:delete scope.

10 parameters
Name In Type Required Description
subject_type body string no Purge memories about this subject type — e.g. "contact", "opportunity", "company", "user", "self".
subject_id body string no Purge memories about this specific subject id (use with subject_type for "forget this contact").
agent_registry_id body string no Purge memories owned by this agent.
kind body string no Purge memories of this kind.
tag body string no Purge memories carrying this tag.
linked_entity_type body string no Purge memories linked to an entity of this type (combine with linked_entity_id).
linked_entity_id body string no Purge memories linked to this entity id (combine with linked_entity_type).
hard body boolean no Default false (soft-delete). true physically removes the rows — irreversible.
all body boolean no Set true to purge EVERY memory in the workspace. Required when no filter is provided; ignored otherwise.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/memory/post-memory-purge.md

POST /memory/search Scopes: memory:read

Semantic search over agent memories. Pass a natural-language query; returns the top-K most similar memories ranked by cosine similarity. Use when you need fuzzy / topic-based lookup ("have I discussed pricing with this contact?") rather than exact filter by subject_id. Use list_memory for keyed lookup ("what have I said to contact X about kind Y?").

8 parameters
Name In Type Required Description
query body string yes Natural language query.
subject_type body string no
subject_id body string no
kind body string no
agent_registry_id body string no
visibility body string no
limit body number no Default 10, max 50.
threshold body number no Cosine similarity floor (0.0–1.0). Default 0.3.

📄 /api/memory/post-memory-search.md

PATCH /memory/:memory_id Scopes: memory:write

Update a memory in place — change content, metadata, tags, links, confidence, expiry, or visibility. Re-embeds automatically if content changes. For upsert semantics, prefer write_memory with the same key.

9 parameters
Name In Type Required Description
memory_id path string yes
content body string no
metadata body object no
linked_entities body array no
tags body array no
confidence body number no
expires_at body string no
visibility body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/memory/patch-memory-memory-id.md

POST /memory Scopes: memory:write

Write a memory for an agent. If `key` is provided, behaves as upsert on (agent, subject, kind, key) — re-running the same key UPDATES the existing memory rather than creating a duplicate. Use this to record what you have learned, decided, or intend to revisit. Content is natural-language; metadata is structured. Embedding generated automatically for semantic search.

16 parameters
Name In Type Required Description
agent_registry_id body string no The owning agent id. For agent-private state.
visibility body string no Default "private". Set "shared" to make this memory readable by other agents in the workspace.
subject_type body string yes What this memory is about. Canonical values: "contact", "opportunity", "company", "user" (a workspace user — the person the agent is helping), "self". Agent-defined values also work.
subject_id body string no The id of the subject (the contact uuid, opportunity uuid, user uuid, etc.). Null for global/self memories.
kind body string yes The category of memory — e.g. "pitch", "objection", "preference", "observation", "next_steps". Free-form; agents define their own taxonomy.
key body string no Optional unique key within (agent, subject, kind) — enables upsert. Example: an article_slug for a pitch memory means re-pitching the same slug UPDATES rather than duplicates.
content body string yes The memory itself, in natural language.
metadata body object no Structured payload alongside the content.
linked_entities body array no Other entities this memory references. e.g. [{type:"opportunity", id:"..."}, {type:"article", id:"voice-agents-setup"}].
tags body array no Free-form semantic facets. e.g. ["competitor:acme-crm", "objection:pricing", "feature:voice_agents"].
confidence body number no 0.0–1.0. Default 1.0. Decreases as the memory ages or gets contradicted.
source_kind body string no Where this memory came from — e.g. "email_send", "transcript", "tool_observation", "manual".
source_id body string no Pointer back to the source event (e.g. email_logs.id).
parent_memory_id body string no Link to an earlier memory this one updates or continues.
expires_at body string no ISO timestamp. Memory becomes inactive after this time.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/memory/post-memory.md

Company (33 endpoints)

View and manage company settings, users, and CRM configuration.

📄 Full markdown: /api/company.md

GET /our-company/users/:user_id Scopes: users:read

Fetch a workspace user by UUID, including last_sign_in_at (the "Last Active" date on their team page): when they last signed in, or null if they never have.

1 parameter
Name In Type Required Description
user_id path string yes

📄 /api/company/get-our-company-users-user-id.md

GET /our-company/dispatcher-config Scopes: company:read

Get the workspace dispatcher settings — retry policy, pacing/concurrency, and the business-hours CALLING WINDOWS used for scheduled sends and the AI dialler. business_hours.windows is keyed by day (mon…sun); each day is a list of ["HH:MM","HH:MM"] ranges. A null/empty config means the workspace is on the platform defaults (Mon–Fri 09:00–17:00, per-lead timezone). Configured in the UI at https://app.trustpager.com/settings/inbox (Dispatcher).

📄 /api/company/get-our-company-dispatcher-config.md

GET /our-company Scopes: company:read

Get OUR Company — the workspace's own organisation profile (the business that runs this CRM), shown at https://app.trustpager.com/settings/company. NOT a CRM account/customer — for those use get_company / list_companies.

📄 /api/company/get-our-company.md

GET /our-company/pipeline-card-config Scopes: company:read

Get the pipeline (Workflows) opportunity-card configuration. Returns: available_sections (toggleable popout sections with labels + defaults); popout_sections (resolved on/off per section for the hover/popout panel); custom_field_ids (which opportunity custom fields show when the popout "custom_fields" section is on); and card_face ({ opportunity_type, custom_field_ids }) for what shows on the card FACE itself (the opportunity name, customer and value always show; the type and chosen custom fields are opt-in). Popout: notes/next_action/tasks default ON, custom_fields OFF. Card face: all default OFF.

📄 /api/company/get-our-company-pipeline-card-config.md

GET /our-company/referral-settings Scopes: company:read

Get the workspace referral settings: referral_form_template_id (the form pinned onto every referral link, where referrers/clients submit a referral) and referrer_portal_template_id (the portal template instantiated for a referrer). Configured in the UI at https://app.trustpager.com/settings/tools.

📄 /api/company/get-our-company-referral-settings.md

POST /our-company/users/invite Scopes: users:write

Invite a new user to the workspace.

3 parameters
Name In Type Required Description
email body string yes
role body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/company/post-our-company-users-invite.md

GET /our-company/ai-fill-prompts Scopes: company:read

List every "Fill with AI" form in the workspace with its label, what it controls, the built-in default master prompt, and the current workspace override (if any). Call this first when configuring how AI Fill writes fields — it returns the valid form_ids to pass to set_ai_fill_prompt. The create-opportunity form is form_id "create-deal-multi-step"; per-entity custom fields are "custom-fields-deal" / "custom-fields-contact" / "custom-fields-account".

📄 /api/company/get-our-company-ai-fill-prompts.md

GET /our-company/users Scopes: users:read

List workspace users, as shown on https://app.trustpager.com/settings/team. Each user carries last_sign_in_at (the "Last Active" column there): when they last signed in, or null if they never have. Use it to tell which account a person actually signs in with before removing a spare one.

📄 /api/company/get-our-company-users.md

DELETE /our-company/users/:user_id Scopes: users:delete

Remove a user from the workspace.

2 parameters
Name In Type Required Description
user_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/company/delete-our-company-users-user-id.md

POST /our-company/users/:user_id/resend-invite Scopes: users:write

Resend the workspace invitation to a user who was invited but has not accepted yet (status "pending"), shown at https://app.trustpager.com/settings/team. Keeps the SAME user id, so existing report recipients, task and work-order assignments stay intact. Never use remove_user plus invite_user to re-invite someone: that mints a new id and silently breaks those references. Get user_id from list_company_users. Fails if the user has already accepted and is active.

2 parameters
Name In Type Required Description
user_id path string yes UUID of the pending workspace user to re-invite. Get it from list_company_users (their status will be "pending").
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/company/post-our-company-users-user-id-resend-invite.md

DELETE /our-company/ai-fill-prompts/:form_id Scopes: company:write

Remove the workspace master-prompt override for one "Fill with AI" form, reverting it to the built-in default. Get valid form_ids from list_ai_fill_prompts.

2 parameters
Name In Type Required Description
form_id path string yes Form to reset to its built-in default.
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/company/delete-our-company-ai-fill-prompts-form-id.md

PUT /our-company/ai-fill-prompts/:form_id Scopes: company:write

Set the master prompt for one "Fill with AI" form. The master prompt is prepended to the AI Fill system prompt for that form, so it steers tone, format, and field rules every time AI Fill runs there (e.g. set form_id "create-deal-multi-step" to enforce a consistent Opportunity Title format). Get valid form_ids from list_ai_fill_prompts. Overrides only the named form; other forms are untouched.

3 parameters
Name In Type Required Description
form_id path string yes Form to configure. Get valid ids from list_ai_fill_prompts (e.g. "create-deal-multi-step").
master_prompt body string yes Instruction prepended to the AI Fill system prompt for this form.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/company/put-our-company-ai-fill-prompts-form-id.md

PUT /our-company/birthday-messages Scopes: company:write

Replace the reusable birthday message templates a user can pick from when sending a contact a birthday greeting. FULL REPLACE, not a merge: call get_birthday_messages first and send the existing templates back along with any new ones, or the omitted templates are removed.

2 parameters
Name In Type Required Description
messages body array yes The complete set of templates the workspace should have after this call.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/company/put-our-company-birthday-messages.md

PATCH /our-company/crm-settings Scopes: company:write

Update workspace CRM settings. Includes payment_details (bank-transfer / BPAY remittance details printed on invoices when there is no online pay link) and approval_routing_config (the ordered chain that decides who a queued approval — auto-responder reply or API write — is assigned to). Also covers list_view_defaults (default grouping per CRM list, which surfaces the left-nav bucket sub-list), detail_layout_config (detail-page card order/visibility), product_bucket_labels, approval_sla_config (approval breach SLA), auto_queue_config (Auto Queue guards, including the minimum gap between automated messages to the same contact), and notetaker_config / transcription_config (meeting notetaker + transcription settings).

39 parameters
Name In Type Required Description
lead_sources body array no Available lead sources for opportunities. Full-replace array — send the complete list you want kept.
lost_reasons body array no Available reasons when marking an opportunity as lost. Full-replace array.
won_reasons body array no Available reasons when marking an opportunity as won. Full-replace array.
referral_categories body array no Workspace referral categories (modality for radiology, service type for trades, partner type for property services). Renders as the category dropdown on the public referral form at /refer/{token} and surfaces as a chip on the Referrers leaderboard; submissions are validated against this list when it is set. Empty array = no dropdown, any category accepted. Full-replace array, e.g. ["Trade Partner","Supplier"].
custom_fields body object no REPLACES the entire stored value on write — always send the COMPLETE set (call get_crm_settings first, modify, send all of deal/account/contact back); anything omitted is removed. Custom field definitions keyed by entity: { deal: [...], account: [...], contact: [...] }. Each field: { id, label, type, options?, section?, show_on_detail, show_in_table, required?, fill_with_ai?, hidden?, conditional_logic? }. sensitive? (true = a tax file number or similar: text/textarea only, cannot be combined with unique, and once true it cannot be set back; values are stored encrypted, every read returns a masked copy like "••••••782", and the real value is only readable with reveal_sensitive_field). Types: text | textarea | url | dropdown | number | datetime | checkbox. Omit `id` and a slug is generated from the label. Values are read/written per record via the `metadata` parameter on contacts / companies / opportunities.
custom_fields_title body string no Custom label for the "Additional Information" custom-fields section on detail pages.
custom_field_sections body object no Section definitions that group custom fields on detail pages, keyed by entity: { deal: [...], account: [...], contact: [...] }. Each section: { name (must match the `section` string stamped on its member fields), default_state?: "open" | "collapsed" | "pinned" (default "collapsed"), description?, order? }. Full-replace per write — send the complete object.
opportunity_type_options body array no Available opportunity type options (e.g. New Business, Upsell, Renewal, Referral). Full-replace array. Requires opportunity_type_enabled.
account_type_options body array no Available company/account type options (e.g. Client, Supplier, Partner, Referrer). Full-replace array.
contact_type_options body array no Available contact type options (e.g. Accountant, Lawyer, Decision Maker, Referrer). Full-replace array.
transcript_types body array no Transcript type categories (e.g. Sales, Fulfilment, Support). Full-replace array.
transcript_sources body array no Transcript sources (e.g. Zoom, Loom, Manual). Full-replace array.
birthday_messages body array no Reusable birthday message templates selectable when sending a contact a birthday greeting. Each entry: { label, channels: string[] ("email" and/or "sms"), email_subject, email_body, sms_body }. Full-replace array.
form_completion_notify_emails body array no Workspace-wide default email addresses notified when any form is completed. Overridable per form template. Full-replace array.
automation_error_notify_emails body array no Email addresses notified when an automation action fails. Must be valid email addresses. Notifications are throttled to one per error signature per hour. Full-replace array; pass null to clear.
automation_error_notify_phones body array no Phone numbers in E.164 format (e.g. +61431377068) notified by SMS when an automation action fails. Requires an active phone number in the workspace. Throttled to one per error signature per hour. Full-replace array; pass null to clear.
quick_links body array no Workspace-wide Quick Link TYPES — buttons in the right-rail sidebar of opportunity, company and contact pages, each opening a per-record URL in a new tab. Read-modify-write: call get_crm_settings, append, send the whole array back. Per-record URLs live on the entity under metadata.quick_links keyed by the type id.
ai_fill_prompts body object no Per-FORM "Fill with AI" master prompts. Prefer the dedicated list_ai_fill_prompts / set_ai_fill_prompt / reset_ai_fill_prompt actions — they validate form ids and write the correct shape. Shape, for reference: { "<form_id>": { "master_prompt": "...", "updated_at": "ISO" } } keyed by formId (e.g. "create-deal-multi-step"), NOT by custom-field id. Shallow-merges.
accounts_enabled body boolean no Enable the Companies/Accounts feature in this workspace.
contacts_enabled body boolean no Enable the Contacts feature in this workspace.
opportunity_type_enabled body boolean no Show the Opportunity Type field on opportunities. Turn on before relying on opportunity_type_options.
enable_work_orders body boolean no Enable Work Orders on opportunities.
opportunity_value_default_mode body string no How NEW opportunities determine their value. 'auto' (default) sums the value from the opportunity's products; 'manual' means the value is set by the user/automation and products never overwrite it. Overridable per-opportunity via update_opportunity value_is_manual.
invoice_default_status body string no Status the Create Invoice window starts on for new invoices. 'open' (default) makes the invoice payable straight away; 'draft' starts it as a draft so someone checks it first. A draft cannot get a payment link until it is marked open.
needs_analysis_config body object no Workspace-wide config for the Proposal Builder / ai_needs_analysis. Shallow-merges: only the keys you pass change. UI editor: /settings/crm?section=needs-analysis.
referral_statement_template_id body string no document_templates UUID of the Document Builder template used to render referrer commission statements (chosen in the UI at /growth/referrals). Get ids from list_document_templates. Pass null to clear.
payment_details body object no Bank-transfer / BPAY details shown on invoices that have no online pay link. Replaces the whole object on write — send the complete set of fields you want kept.
email_template_settings body object no Workspace-wide email chrome defaults applied to form / signing / document / work-order invite emails. Per-template overrides (form_templates.email_template_settings, document_templates.email_template_settings) take precedence. Replaces the whole object on write.
approval_routing_config body array no Ordered approval-routing chain — who a queued approval (auto-responder reply, API write) is assigned to. Walked top-to-bottom; the first rule that resolves a user wins. Full-replace array. e.g. [{"type":"pipeline_user","map":{"<pipeline_id>":"<user_id>"}},{"type":"specific_user","user_id":"<user_id>"}].
list_view_defaults body object no Default grouping per CRM list, applied for everyone in the workspace (it also surfaces that list's bucket sub-list in the left nav). Each user can still change/turn off grouping on their own screen. Keyed by tableId: "all-deals-table" (Opportunities), "crm-contacts-table" (Contacts), "crm-customers-table" (Companies). Each value is { groupKey } where groupKey is a groupable column on that list — Opportunities: stage.name | opportunity_type | lead_source | contact.contact_type; Contacts: contact_type; Companies: account_type | industry. Shallow-merges per tableId (pass only the lists you want to change). e.g. { "crm-contacts-table": { "groupKey": "contact_type" } }.
detail_layout_config body object no Detail-page card order + hidden cards per surface, workspace-wide (the Opportunity / Contact / Company detail pages). Keyed by surface: opportunity.main, opportunity.side, contact.main, contact.side, company.main, company.side. Each value is { order: string[] (card keys top-to-bottom), hidden: string[] (card keys removed from the page) }. Shallow-merges per surface.
product_bucket_labels body array no Labels for the product grouping buckets used across the Products UI. Full-replace array — send the complete ordered list you want kept.
file_tag_options body array no The workspace file tag palette shown at https://app.trustpager.com/dashboard/files — the quick-pick tags anyone can put on a file. By default a tag is ALSO a photo bucket a contractor uploads into from their portal work order (e.g. Before / After on a job); set portal_bucket false on a tag that is only for the office, so it stays a quick-pick and a filter without appearing on a contractor screen. Full-replace array — send the complete ordered list you want kept. e.g. [{"name":"Before","color":"#f97316"},{"name":"After","color":"#22c55e"},{"name":"Listing","color":"#3b82f6","portal_bucket":false}].
approval_sla_config body object no Approval SLA: notify the assignee once when a pending approval has waited longer than the threshold. Shallow-merges.
auto_queue_config body object no Auto Queue guards shown at https://app.trustpager.com/settings/auto (Auto Queues section). Shallow-merges. min_send_gap spaces out automated messages to the same contact across every Auto Queue and channel: a contact can be enrolled in two queues at once, and each queue times its steps from its own enrolment date, so two messages can otherwise land a minute apart. When a step would fire inside the gap it is rescheduled to just after the previous message rather than skipped, so nothing is dropped.
notetaker_config body object no Workspace defaults for the meeting Notetaker. Shallow-merges (pass only the keys you want to change).
transcription_config body object no Workspace call-transcription settings (configured at /settings/inbox, Transcripts section). Shallow-merges; pass only the keys you want to change.
portal_on_anchor_delete body string no What happens to a portal when the record it hangs off is deleted (its referrer contact, or its anchor opportunity). "keep" leaves the portal alone (default, today's behaviour). "delete" permanently removes the portal and its chat history. "delete_unless_chat" does the same but blocks the parent delete when the portal has a chat. "disable" switches the portal off reversibly.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/company/patch-our-company-crm-settings.md

PATCH /our-company/dispatcher-config Scopes: company:write

Update the workspace dispatcher settings shown at https://app.trustpager.com/settings/inbox (Follow Ups). Pass any of business_hours, retry, concurrency, failure; each section shallow-merges over the current config. There are TWO window sets. business_hours.windows is when we may contact CUSTOMERS (scheduled email/SMS/WhatsApp and the AI dialler), evaluated in the local timezone of each lead. business_hours.staff_windows is when we may alert the workspace OWN STAFF, evaluated in the workspace timezone. That covers every INTERNAL alert: a Notify Our Staff action, and an email/SMS/WhatsApp action addressed to a fixed recipient (recipient_target "custom") rather than the customer. Send "staff_windows": null to stop using separate staff hours. staff_windows is optional, and while it is unset staff alerts follow windows; set it when the calling window is narrower than the working day of the team. Either set, when provided, fully replaces its day set: use minute-precise ranges like { "mon": [["08:30","09:30"],["15:30","17:30"]], "sun": [["10:00","11:00"]] }, and omit a day to skip it.

5 parameters
Name In Type Required Description
business_hours body object no Customer-contact windows, staff-alert windows, and timezone resolution for business-hours-aware sends and the dialler.
retry body object no Per-channel retry policy (email/sms/voice).
concurrency body object no Pacing / concurrency caps.
failure body object no What to do on final failure.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/company/patch-our-company-dispatcher-config.md

PATCH /our-company/settings/embeddings Scopes: company:write

Update embedding-pipeline settings.

1 parameter
Name In Type Required Description
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/company/patch-our-company-settings-embeddings.md

PATCH /our-company Scopes: company:write

Update OUR Company — the workspace's own organisation profile (branding, contact info, address) shown at https://app.trustpager.com/settings/company. NOT a CRM account/customer — to edit a customer record use update_company.

21 parameters
Name In Type Required Description
name body string no Trading name of the business that runs this workspace. Appears on invoices, documents and branded emails.
slug body string no URL-safe short name used in public links for this workspace. Lowercase letters, numbers and hyphens.
description body string no Short description of what the business does. Used as context by AI features and on some public surfaces.
contact_name body string no Primary contact person for the business, as customers should see it.
contact_email body string no Primary contact email for the business. Shown on invoices and public pages; this is NOT the sending address (that lives on the email config).
phone body string no Primary business phone number as customers should see it.
logo_url body string no Absolute URL of the business logo. Used on invoices, documents, portals and branded emails.
primary_color body string no Brand primary colour as a hex value, e.g. "#3b82f6". Must be a hex colour: it is spliced into branded email styles, so anything else is rejected.
secondary_color body string no Brand secondary colour as a hex value, e.g. "#f97316". Same hex-only rule as primary_color.
workspace_color body string,null no Workspace colour: re-colours the CRM app itself for everyone in this workspace, so people who work across several workspaces can tell them apart. Separate from the brand colours, which style emails and public pages. A 6-digit hex value, e.g. "#2563eb". Send null or "" to go back to the default look.
address_line1 body string no Street address line 1 of the business.
address_line2 body string no Street address line 2 (unit, level, PO box).
city body string no City or suburb of the business address.
state body string no State or territory of the business address, e.g. "VIC".
postal_code body string no Postcode of the business address.
country body string no Country of the business address. Defaults to Australia for new workspaces.
timezone body string no IANA timezone the business operates in, e.g. "Australia/Melbourne". Drives scheduling, business hours and date display.
industry body string no Industry the business operates in. Used as context by AI features.
website_url body string no Absolute URL of the business website, e.g. "https://example.com".
abn body string no Australian Business Number, printed on invoices. Digits only or spaced; stored as given.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/company/patch-our-company.md

PATCH /our-company/pipeline-card-config Scopes: company:write

Configure the opportunity card on the pipeline (Workflows) board. Two independent surfaces, pass either or both. Card POPOUT (hover panel): popout_sections as a partial map of section_key -> boolean (only keys you pass change; valid keys notes, next_action, tasks, custom_fields), plus custom_field_ids (opportunity custom-field ids from get_crm_settings.custom_fields.deal) shown when the custom_fields section is on. Card FACE (the card itself): card_face as { opportunity_type?: boolean, custom_field_ids?: string[] } to show/hide the opportunity type and chosen custom fields on the card (name, customer and value always show); partial, only keys you pass change. e.g. { "card_face": { "opportunity_type": true, "custom_field_ids": ["broker_name"] } } or { "popout_sections": { "custom_fields": true }, "custom_field_ids": ["lot_number"] }.

4 parameters
Name In Type Required Description
popout_sections body object no Partial map of popout section_key -> show(boolean). e.g. { "tasks": false, "custom_fields": true }.
custom_field_ids body array no Opportunity custom-field ids to show in the POPOUT when the custom_fields section is on. e.g. ["lot_number","slab_type"].
card_face body object no What shows on the card FACE. { "opportunity_type": true, "custom_field_ids": ["broker_name"] }.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/company/patch-our-company-pipeline-card-config.md

PATCH /our-company/referral-settings Scopes: company:write

Set the workspace referral form and/or referrer portal. referral_form_template_id is a form_templates UUID (the public form referrers/clients submit to start a referral; get ids from list_form_templates); referrer_portal_template_id is a portal_templates UUID (the portal a referrer logs into; get ids from list_portal_templates). Pass either or both; pass null to clear one.

3 parameters
Name In Type Required Description
referral_form_template_id body string no form_templates UUID of the workspace referral form, or null to clear.
referrer_portal_template_id body string no portal_templates UUID of the referrer portal, or null to clear.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/company/patch-our-company-referral-settings.md

PATCH /our-company/settings/tag-palette Scopes: company:write

Replace the workspace tag colour palette. FULL REPLACE, not a merge: call get_tag_palette first and send the existing tags back along with any new ones, or the omitted tags are removed. Tag names are de-duplicated case-insensitively, last one wins.

2 parameters
Name In Type Required Description
tags body array yes The complete palette. Every tag the workspace should have after this call.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/company/patch-our-company-settings-tag-palette.md

PATCH /our-company/users/:user_id/workspace-user-roles Scopes: users:write

Update a user role.

3 parameters
Name In Type Required Description
user_id path string yes
role body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/company/patch-our-company-users-user-id-workspace-user-roles.md

PATCH /our-company/users/:user_id/workspace-user-preferences Scopes: users:write

Update a user preferences.

2 parameters
Name In Type Required Description
user_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/company/patch-our-company-users-user-id-workspace-user-preferences.md

PATCH /our-company/users/:user_id/workspace-user-profile Scopes: users:write

Update a user workspace profile.

2 parameters
Name In Type Required Description
user_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/company/patch-our-company-users-user-id-workspace-user-profile.md

CRM Templates (5 endpoints)

Manage reusable CRM templates for emails, messages, and automated communications.

📄 Full markdown: /api/crm-templates.md

POST /crm-templates Scopes: crm-templates:write

Create a CRM template.

5 parameters
Name In Type Required Description
type body string yes
name body string yes
category body string no
content body object no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/crm-templates/post-crm-templates.md

DELETE /crm-templates/:template_id Scopes: crm-templates:delete

Delete a CRM template by UUID.

2 parameters
Name In Type Required Description
template_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/crm-templates/delete-crm-templates-template-id.md

GET /crm-templates Scopes: crm-templates:read

List workspace CRM templates (default custom-field templates for entities).

1 parameter
Name In Type Required Description
type query string no

📄 /api/crm-templates/get-crm-templates.md

PATCH /crm-templates/:template_id Scopes: crm-templates:write

Update a CRM template.

6 parameters
Name In Type Required Description
template_id path string yes
name body string no
content body object no
type body string no Which kind of template this is, which decides where it is offered.
category body string no Grouping for the template list, so related templates sit together.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/crm-templates/patch-crm-templates-template-id.md

Integrations (6 endpoints)

Read, query, and execute actions on native integrations (Xero, MYOB, etc.). Connecting and disconnecting integrations is done via the platform UI (OAuth browser flows).

📄 Full markdown: /api/integrations.md

POST /integrations/:integration_id/action Scopes: integrations:write

Execute an action on a connected third-party platform (e.g. post Slack message, add calendar event).

4 parameters
Name In Type Required Description
integration_id path string yes
action_type body string yes Platform action to run, e.g. "slack_post_message".
params body object yes Arguments for the action.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/integrations/post-integrations-integration-id-action.md

GET /integrations/:integration_id Scopes: integrations:read

Fetch a single integration by UUID, including its status and capabilities.

1 parameter
Name In Type Required Description
integration_id path string yes

📄 /api/integrations/get-integrations-integration-id.md

GET /integrations/:integration_id/sync-receivables/:job_id Scopes: integrations:read

Check the progress of a receivables sync started by sync_receivables. Returns status (running, complete or failed), how many invoices have been written so far, and any error.

2 parameters
Name In Type Required Description
integration_id path string yes
job_id path string yes

📄 /api/integrations/get-integrations-integration-id-sync-receivables-job-id.md

POST /integrations/:integration_id/query Scopes: integrations:read

Read data from a connected third-party platform (e.g. list channels, get profile).

3 parameters
Name In Type Required Description
integration_id path string yes
query body string yes
args body object no

📄 /api/integrations/post-integrations-integration-id-query.md

POST /integrations/:integration_id/sync-receivables Scopes: invoices:write

Start a backfill / reconcile of the outstanding-invoices (accounts receivable) ledger from a connected Xero integration, and return a job_id to poll. Runs as a background job, so a large ledger no longer times out. Pass modified_since to sync only invoices changed since then, which is much faster for a routine repair. Idempotent and safe to re-run; calling it while a sync is already running returns that job instead of starting a second one.

3 parameters
Name In Type Required Description
integration_id path string yes
modified_since body string no Optional ISO 8601 timestamp. Only sync invoices changed in Xero since this time, which is much faster than walking the whole ledger.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/integrations/post-integrations-integration-id-sync-receivables.md

Webhooks (17 endpoints)

Manage incoming and outgoing webhooks for external system integration.

📄 Full markdown: /api/webhooks.md

POST /webhooks/incoming Scopes: webhooks:write

Create an incoming webhook URL.

6 parameters
Name In Type Required Description
service_type body string yes Which external service posts to this endpoint. Determines how the payload is parsed and normalised.
service_name body string yes Human label for this incoming webhook in the Webhooks list.
config_data body object no Service-specific settings: field mappings, secrets the sender includes, payload shape hints. Full-replace on write.
crm_pipeline_id body string no Pipeline a newly-created opportunity lands in when a payload arrives. Falls back to the workspace default when unset.
crm_initial_stage_id body string no Stage within that pipeline the new opportunity starts at.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/webhooks/post-webhooks-incoming.md

POST /webhooks/outgoing Scopes: webhooks:write

Create an outgoing webhook.

9 parameters
Name In Type Required Description
name body string yes Label for this outgoing webhook in the Webhooks list.
url body string yes Absolute HTTPS endpoint we POST to when a trigger event fires.
enabled body boolean no Whether the webhook fires. False keeps it configured but dormant.
headers body object no Extra HTTP headers sent with every request, as { "Header-Name": "value" }. Use for auth tokens the receiver expects. Full-replace on write.
body_template body object no Custom JSON body to send instead of the default payload. Supports template tokens for record fields. Full-replace on write; omit to send the standard payload.
trigger_events body array no Which events fire this webhook, e.g. ["deal_created","form_submitted"]. Full-replace array.
include_trigger_data body boolean no Whether the full trigger_data snapshot is included in the POST body alongside the record.
website_id body string no Scope the webhook to one website's events. Null fires for the whole workspace.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/webhooks/post-webhooks-outgoing.md

POST /webhooks/subscribe Scopes: webhooks:write

Subscribe a URL to API event notifications.

4 parameters
Name In Type Required Description
url body string yes
events body array yes
secret body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/webhooks/post-webhooks-subscribe.md

DELETE /webhooks/incoming/:webhook_id Scopes: webhooks:delete

Delete an incoming webhook.

2 parameters
Name In Type Required Description
webhook_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/webhooks/delete-webhooks-incoming-webhook-id.md

DELETE /webhooks/outgoing/:webhook_id Scopes: webhooks:delete

Delete an outgoing webhook.

2 parameters
Name In Type Required Description
webhook_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/webhooks/delete-webhooks-outgoing-webhook-id.md

DELETE /webhooks/subscriptions/:subscription_id Scopes: webhooks:delete

Delete a webhook subscription.

2 parameters
Name In Type Required Description
subscription_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/webhooks/delete-webhooks-subscriptions-subscription-id.md

POST /webhooks/outgoing/:webhook_id/test Scopes: webhooks:write

Send a test payload to an outgoing webhook.

2 parameters
Name In Type Required Description
webhook_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/webhooks/post-webhooks-outgoing-webhook-id-test.md

PATCH /webhooks/incoming/:webhook_id Scopes: webhooks:write

Update an incoming webhook.

7 parameters
Name In Type Required Description
webhook_id path string yes UUID of the incoming webhook to update. Get ids from list_incoming_webhooks.
service_type body string no Which external service posts to this endpoint. Determines how the payload is parsed and normalised.
service_name body string no Human label for this incoming webhook in the Webhooks list.
config_data body object no Service-specific settings: field mappings, secrets the sender includes, payload shape hints. Full-replace on write.
crm_pipeline_id body string no Pipeline a newly-created opportunity lands in when a payload arrives. Falls back to the workspace default when unset.
crm_initial_stage_id body string no Stage within that pipeline the new opportunity starts at.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/webhooks/patch-webhooks-incoming-webhook-id.md

PATCH /webhooks/outgoing/:webhook_id Scopes: webhooks:write

Update an outgoing webhook.

10 parameters
Name In Type Required Description
webhook_id path string yes UUID of the outgoing webhook to update. Get ids from list_outgoing_webhooks.
name body string no Label for this outgoing webhook in the Webhooks list.
url body string no Absolute HTTPS endpoint we POST to when a trigger event fires.
enabled body boolean no Whether the webhook fires. False keeps it configured but dormant.
headers body object no Extra HTTP headers sent with every request, as { "Header-Name": "value" }. Use for auth tokens the receiver expects. Full-replace on write.
body_template body object no Custom JSON body to send instead of the default payload. Supports template tokens for record fields. Full-replace on write; omit to send the standard payload.
trigger_events body array no Which events fire this webhook, e.g. ["deal_created","form_submitted"]. Full-replace array.
include_trigger_data body boolean no Whether the full trigger_data snapshot is included in the POST body alongside the record.
website_id body string no Scope the webhook to one website's events. Null fires for the whole workspace.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/webhooks/patch-webhooks-outgoing-webhook-id.md

PATCH /webhooks/subscriptions/:subscription_id Scopes: webhooks:write

Update a webhook subscription.

6 parameters
Name In Type Required Description
subscription_id path string yes UUID of the webhook subscription to update. Get ids from list_webhook_subscriptions.
url body string no Absolute HTTPS endpoint that receives the event POST.
events body array no Event names this subscription listens for. Full-replace array — send the complete set you want kept.
secret body string no Shared secret used to sign the payload so your endpoint can verify it came from us. Treat as a credential.
is_active body boolean no Whether the subscription delivers. False pauses delivery without deleting it.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/webhooks/patch-webhooks-subscriptions-subscription-id.md

AI (15 endpoints)

AI-powered features including text editing, call coaching, needs analysis, form filling, pipeline generation, image generation, reference-guided image editing, text-to-speech generation, and AI tool generators (form, document, whiteboard, website page) that create fully-structured workspace assets from a plain-language prompt.

📄 Full markdown: /api/ai.md

POST /ai/call-coaching Scopes: ai:use

Analyze a phone call transcript for coaching feedback. Costs credits.

2 parameters
Name In Type Required Description
transcript_id body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/ai/post-ai-call-coaching.md

POST /ai/edit-image Scopes: ai:use

Edit an image based on a prompt. Costs credits.

8 parameters
Name In Type Required Description
prompt body string yes How to edit the reference image. 2 to 3000 characters.
reference_image_url body string yes Public http(s) URL of the image to edit. Provide this or reference_image_urls.
reference_image_urls body array no Up to 3 public http(s) image URLs, as an alternative to reference_image_url.
width body number no Output width. Defaults to 1024; must form a supported width/height pair.
height body number no Output height. Defaults to 1024; must form a supported width/height pair.
seed body number no Optional seed for reproducible output.
output_format body string no Output image format.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/ai/post-ai-edit-image.md

POST /ai/edit-text Scopes: ai:use

Rewrite text you already have: make it more formal, shorten it, fix grammar. Runs a small, fast model, so do NOT use it to write new content from facts (a report, audit or summary a customer will read): it can invent details. For that, run a Workflow Agent pinned to a stronger model (dispatch_agent_run) and pass the facts in as its input. Costs credits.

4 parameters
Name In Type Required Description
text body string yes
instruction body string yes
tone body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/ai/post-ai-edit-text.md

POST /ai/fill-form Scopes: ai:use

Auto-fill a form template from natural-language input. Pass template_id (the form template to fill) and prompt (the source text / instructions to extract field values from). The workspace master prompt configured for this template (via set_ai_fill_prompt) is applied automatically; pass master_prompt to override it for this one call. Costs credits.

4 parameters
Name In Type Required Description
template_id body string yes UUID of the form template to fill. Get ids from list_form_templates.
prompt body string yes Natural-language source text / instructions the AI extracts field values from.
master_prompt body string no Optional one-off override of the system steer for this call. Leave unset to use the workspace master prompt configured via set_ai_fill_prompt (or the built-in default).
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/ai/post-ai-fill-form.md

POST /ai/generate-document Scopes: ai:usedocuments:write

Generate a new document from a prompt — proposals, scopes, summaries. Returns a document UUID. Costs credits.

5 parameters
Name In Type Required Description
prompt body string yes
opportunity_id body string no
contact_id body string no
title body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/ai/post-ai-generate-document.md

POST /ai/generate-embedding Scopes: ai:use

Generate a vector embedding for a text string. Free.

1 parameter
Name In Type Required Description
text body string yes

📄 /api/ai/post-ai-generate-embedding.md

POST /ai/generate-form Scopes: ai:useforms:write

Generate a form template from a natural-language prompt.

2 parameters
Name In Type Required Description
prompt body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/ai/post-ai-generate-form.md

POST /ai/generate-image Scopes: ai:use

Generate an image from a prompt. Costs credits.

3 parameters
Name In Type Required Description
prompt body string yes
aspect_ratio body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/ai/post-ai-generate-image.md

POST /ai/generate-music Scopes: ai:use

Generate music audio from a prompt. Costs credits.

3 parameters
Name In Type Required Description
prompt body string yes
duration_ms body number no Track length in MILLISECONDS. Defaults to 30000 (30s).
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/ai/post-ai-generate-music.md

POST /ai/generate-pipeline Scopes: ai:usepipelines:write

Generate a new pipeline (with stages) from a natural-language description.

7 parameters
Name In Type Required Description
description body string yes Natural-language description of the process the pipeline should model.
business_type body string no Optional business type, to tune the generated stages.
industry body string no Optional industry, to tune the generated stages.
success_criteria body string no Optional definition of what a won opportunity looks like.
lost_criteria body string no Optional definition of what a lost opportunity looks like.
additional_context body string no Optional extra context for the generator.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/ai/post-ai-generate-pipeline.md

POST /ai/generate-speech Scopes: ai:use

Generate speech audio from text. Costs credits.

3 parameters
Name In Type Required Description
text body string yes
voice body string no Voice to speak with.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/ai/post-ai-generate-speech.md

POST /ai/generate-whiteboard Scopes: ai:use

Generate a whiteboard layout from a prompt.

2 parameters
Name In Type Required Description
prompt body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/ai/post-ai-generate-whiteboard.md

POST /ai/needs-analysis Scopes: ai:use

Analyse an opportunity — activities, transcripts, notes — to surface needs, objections, next steps. Costs credits.

3 parameters
Name In Type Required Description
opportunity_id body string yes
focus body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/ai/post-ai-needs-analysis.md

POST /ai/transcript-summary Scopes: ai:use

Summarise a call or meeting transcript — key topics, decisions, action items. Costs credits.

3 parameters
Name In Type Required Description
transcript_id body string yes
focus body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/ai/post-ai-transcript-summary.md

POST /ai/upscale-image Scopes: ai:use

Upscale an image to higher resolution. Costs credits.

4 parameters
Name In Type Required Description
image_url body string yes Public http(s) URL of the image to upscale.
upscale_factor body number no How much to enlarge by. Defaults to 2.
output_format body string no Output image format.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/ai/post-ai-upscale-image.md

Billing (4 endpoints)

View billing plans, credit balance, and pricing.

📄 Full markdown: /api/billing.md

Service Requests (8 endpoints)

Submit and manage platform improvement requests. Designed for AI agents to report missing capabilities, suggest new features, and flag issues with the MCP, API, or platform. Supports listing, filtering, note threads, inline note editing, and two-way ticket linking.

📄 Full markdown: /api/service-requests.md

POST /service-requests/:request_id/notes Scopes: service-requests:write

Add a note to a service request.

3 parameters
Name In Type Required Description
request_id path string yes
content body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/service-requests/post-service-requests-request-id-notes.md

POST /service-requests Scopes: service-requests:write

File a platform improvement / capability request. Use when you hit a missing tool, missing field, confusing error, or bug.

6 parameters
Name In Type Required Description
title body string yes
description body string yes
category body string no
priority body string no
about_company_id body string no Optional. The client workspace this request is about, when an agency files it for one of its clients.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/service-requests/post-service-requests.md

POST /service-requests/:request_id/links Scopes: service-requests:write

Link (or unlink) service requests to each other. Links are two-way: adding B to A also adds A to B. Provide at least one of "add" or "remove".

4 parameters
Name In Type Required Description
request_id path string yes The service request to update links on.
add body array no Service request UUIDs to link to this one (two-way).
remove body array no Service request UUIDs to unlink from this one.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/service-requests/post-service-requests-request-id-links.md

GET /service-requests Scopes: service-requests:read

List platform improvement / capability requests submitted by AI agents.

2 parameters
Name In Type Required Description
status query string no
limit query number no

📄 /api/service-requests/get-service-requests.md

POST /service-requests/search Scopes: service-requests:read

Semantic search across this workspace's service requests using vector embeddings. Use to find duplicates before filing, group related open requests, or find historical resolutions. Returns ranked SRs by cosine similarity.

4 parameters
Name In Type Required Description
query body string yes Natural-language description of what you're looking for
status body string no Optional: restrict to a single status
limit body number no Max results, 1-50 (default 10)
threshold body number no Minimum cosine similarity 0-1 (default 0.5)

📄 /api/service-requests/post-service-requests-search.md

PATCH /service-requests/:request_id/notes/:note_id Scopes: service-requests:write

Update a note on a service request.

4 parameters
Name In Type Required Description
request_id path string yes
note_id path string yes
content body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/service-requests/patch-service-requests-request-id-notes-note-id.md

PATCH /service-requests/:request_id Scopes: service-requests:write

Update a service request's status, e.g. mark it completed/resolved after shipping the fix, or reopen a completed one.

3 parameters
Name In Type Required Description
request_id path string yes
status body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/service-requests/patch-service-requests-request-id.md

Agency (11 endpoints)

For TrustPager agencies: read your clients and their health, your clients' service requests and your commission, with an API key made on your agency's own workspace (agency:read). Read only, and never a client's own business data.

📄 Full markdown: /api/agency.md

POST /agency/service-requests/:request_id/notes Scopes: service-requests:write

Add a note to one of your agency's client service requests (an id from list_agency_service_requests). The client sees it on their request. Anyone on the agency's staff can, account managers included, while your agency still looks after that client. A note never reopens a finished request and never changes its status or queue.

3 parameters
Name In Type Required Description
request_id path string yes id from list_agency_service_requests
content body string yes The note, up to 20,000 characters. The client sees it on their service request.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agency/post-agency-service-requests-request-id-notes.md

GET /agency Scopes: agency:read

Your agency's name, commission rate, signup link and how many clients and templates it has. Needs an API key made on the agency's own workspace.

📄 /api/agency/get-agency.md

GET /agency/clients/:client_id Scopes: agency:read

One of your agency's clients, with the same health columns as list_agency_clients. client_id is the client's workspace id.

1 parameter
Name In Type Required Description
client_id path string yes The client's workspace id

📄 /api/agency/get-agency-clients-client-id.md

GET /agency/commission Scopes: agency:read

Your agency's commission: this month's estimate so far and the last 24 monthly statements with their lines. Never includes bank details. The owner sees the agency's figures (scope 'agency'); an account manager sees only the clients they brought in (scope 'mine'); systems architects are refused.

📄 /api/agency/get-agency-commission.md

GET /agency/team-commission Scopes: agency:read

What your agency pays its own salespeople. For each person: their commission plan (one_off_percent on setup fees and services; first_months_percent on the client's first first_months paid months of subscription, then ongoing_percent, for ongoing_months more months or forever when null), this month so far, and the last 24 months of payouts with each client payment, its rate and the commission. Payout status: owed, paid, nothing_owed, or carried_forward (refunds took it below $0, so it comes off next month). Finished months are worked out when you ask. The owner sees the whole team; anyone else sees only themselves. TrustPager never pays salespeople: the agency pays them and marks the month paid.

📄 /api/agency/get-agency-team-commission.md

GET /agency/clients Scopes: agency:read

Every client workspace (and template) your agency looks after, with its health: plan, what it pays us, trial end, users, website, last activity, open service requests and unfinished setup. Never returns the client's own business data. 100 a page.

4 parameters
Name In Type Required Description
link_type query string no Only clients, or only templates. Leave out for both.
search query string no Part of the client name
limit query number no Rows per page, 1-100 (default 100)
after query string no pagination.next_cursor from the previous page

📄 /api/agency/get-agency-clients.md

GET /agency/service-requests Scopes: agency:read

Service requests from your agency's clients. queue 'agency' is waiting on your agency; 'platform' has been passed to TrustPager. Filter by status or client.

5 parameters
Name In Type Required Description
queue query string no
status query string no
client_id query string no Only this client's requests (its workspace id)
limit query number no
after query string no

📄 /api/agency/get-agency-service-requests.md

GET /agency/websites Scopes: agency:read

Your clients' websites: the GitHub repo and branch each is built from, and its latest syncs with their status (queued, copying, building, ready, published, failed), preview address and, for a failed build, the end of the build log.

📄 /api/agency/get-agency-websites.md

PATCH /agency/service-requests/:request_id/notes/:note_id Scopes: service-requests:write

Change the text of a note you wrote on one of your agency's client service requests. Only your own notes, and only while your agency still looks after that client.

4 parameters
Name In Type Required Description
request_id path string yes id from list_agency_service_requests
note_id path string yes notes[].id on that request
content body string yes The note, up to 20,000 characters. The client sees it on their service request.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agency/patch-agency-service-requests-request-id-notes-note-id.md

PATCH /agency/team-commission/plans/:user_id Scopes: agency:write

Agency owner only: set or change one salesperson's commission plan. Rates are percentages (10 = 10%, up to 100). Anything left out keeps its current value. Example, 100% of the first month then 10% of everything: one_off_percent 10, first_months 1, first_months_percent 100, ongoing_percent 10, ongoing_forever true. Every finished month is locked in at the old plan first, so a change applies from this month on; a person's first plan also covers the months since they joined.

8 parameters
Name In Type Required Description
user_id path string yes The salesperson (people[].user_id from get_agency_team_commission)
one_off_percent body number no Percent of one-off fees (setup fees, Website Design, AI Agents), 0 to 100
first_months body number no How many of the client's first paid months get first_months_percent (0 for none)
first_months_percent body number no Percent of the subscription in those first months, 0 to 100
ongoing_percent body number no Percent of the subscription after the first months, 0 to 100
ongoing_months body number no How many months ongoing_percent lasts after the first months, then it stops. For as long as the client stays, send ongoing_forever true instead.
ongoing_forever body boolean no true: ongoing_percent lasts as long as the client stays (clears ongoing_months)
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agency/patch-agency-team-commission-plans-user-id.md

PATCH /agency/team-commission/payouts/:payout_id Scopes: agency:write

Agency owner only: mark a salesperson's month paid once the agency has paid them (paid true, with an optional payment_reference such as the bank transfer reference), or undo that (paid false). Only a month with money owed can be marked paid.

4 parameters
Name In Type Required Description
payout_id path string yes payouts[].id from get_agency_team_commission
paid body boolean yes true: the agency has paid this month. false: not paid after all.
payment_reference body string no Optional bank transfer reference, up to 200 characters
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agency/patch-agency-team-commission-payouts-payout-id.md

Approvals (6 endpoints)

Manage API actions queued for human approval. When an API key has a "with Approval" permission level, write operations are held here until a team member reviews them.

📄 Full markdown: /api/approvals.md

POST /approvals/:approval_id/approve Scopes: approvals:write

Approve a pending action and execute it. Destructive — runs the underlying queued operation.

2 parameters
Name In Type Required Description
approval_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/approvals/post-approvals-approval-id-approve.md

POST /approvals/:approval_id/assign Scopes: approvals:reassign

Reassign a pending approval to a different reviewer. Does not approve or reject.

4 parameters
Name In Type Required Description
approval_id path string yes
primary_assignee_id body string yes
secondary_assignee_id body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/approvals/post-approvals-approval-id-assign.md

GET /approvals/:approval_id Scopes: approvals:read

Fetch a single approval by UUID — full action details, request body, status, reviewer assignment.

1 parameter
Name In Type Required Description
approval_id path string yes

📄 /api/approvals/get-approvals-approval-id.md

GET /approvals Scopes: approvals:read

List pending approvals — actions queued for human review. Use after a 202 to check what is waiting.

3 parameters
Name In Type Required Description
status query string no pending (default), approved, rejected, expired, executed, failed
resource query string no Resource the queued action targets, e.g. sms or phone-numbers.
limit query number no

📄 /api/approvals/get-approvals.md

POST /approvals/:approval_id/reject Scopes: approvals:write

Reject a pending action. Cancels the queued operation.

3 parameters
Name In Type Required Description
approval_id path string yes
reason body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/approvals/post-approvals-approval-id-reject.md

POST /approvals/:approval_id/edit Scopes: approvals:edit

Rewrite the request_body of a pending approval. Lets agents iterate on AI-drafted content (e.g. fix an email body) without approving.

3 parameters
Name In Type Required Description
approval_id path string yes
request_body body object yes New request body to replace the queued one
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/approvals/post-approvals-approval-id-edit.md

AI Instructions (1 endpoint)

Structured behavioral instructions for AI agents. Call once per session, cache the result. Free (0 credits).

📄 Full markdown: /api/ai-instructions.md

GET /ai-instructions

Get structured behavioural instructions for AI agents using this workspace API, including this workspace's own instructions (this_workspace) on how its CRM is organised when it has written them. Call before filing records. Free read — cache once per session.

📄 /api/ai-instructions/get-ai-instructions.md

Voices (8 endpoints)

Manage saved workspace voices and browse platform-recommended voices. Workspace voices are saved TrustPager AI voices scoped to your company. Platform voices are pre-seeded recommended voices available to all workspaces (read-only). Use these with POST /ai/generate-speech.

📄 Full markdown: /api/voices.md

POST /voices Scopes: voices:write

Add a voice to the workspace (resolves metadata from provider on create).

8 parameters
Name In Type Required Description
name body string no
provider_voice_id body string yes The voice id as the speech provider knows it. This is what actually selects the voice.
provider body string no Which speech provider this voice comes from. Defaults to the workspace default.
model body string no Speech model used to render this voice.
settings body object no Per-voice tuning passed through to the provider, e.g. stability and similarity.
is_default body boolean no Whether this is the workspace default voice. Setting it true moves the default off whichever voice held it.
description body string no A note about when to use this voice, shown in the voice list.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voices/post-voices.md

DELETE /voices/:voice_id Scopes: voices:delete

Delete a workspace voice.

2 parameters
Name In Type Required Description
voice_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voices/delete-voices-voice-id.md

GET /voices/:voice_id Scopes: voices:read

Fetch a workspace voice by UUID.

1 parameter
Name In Type Required Description
voice_id path string yes

📄 /api/voices/get-voices-voice-id.md

GET /voices/provider-catalog Scopes: voices:read

List the built-in voice catalogue available to the workspace: the voices you can use straight away without importing your own.

📄 /api/voices/get-voices-provider-catalog.md

GET /voices Scopes: voices:read

List workspace voices.

1 parameter
Name In Type Required Description
limit query number no

📄 /api/voices/get-voices.md

POST /voices/:voice_id/provider-mapping Scopes: voices:write

Map a workspace voice to a provider voice id so agent creation uses the right voice.

3 parameters
Name In Type Required Description
voice_id path string yes
retell_voice_id body string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voices/post-voices-voice-id-provider-mapping.md

POST /voices/sync-provider-catalog Scopes: voices:write

Auto-map every catalogue voice to a workspace voice row.

1 parameter
Name In Type Required Description
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voices/post-voices-sync-provider-catalog.md

PATCH /voices/:voice_id Scopes: voices:write

Update a workspace voice.

9 parameters
Name In Type Required Description
voice_id path string yes
name body string no
provider_voice_id body string no The voice id as the speech provider knows it. This is what actually selects the voice.
provider body string no Which speech provider this voice comes from. Defaults to the workspace default.
model body string no Speech model used to render this voice.
settings body object no Per-voice tuning passed through to the provider, e.g. stability and similarity.
is_default body boolean no Whether this is the workspace default voice. Setting it true moves the default off whichever voice held it.
description body string no A note about when to use this voice, shown in the voice list.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/voices/patch-voices-voice-id.md

Agent Proposals (6 endpoints)

Agent action proposals submitted by AI agents for human review and approval. Agents submit proposals when they identify an action that should be taken but requires a human decision. Proposals surface in the Agent Hub at /auto/agent-hub?tab=proposals.

📄 Full markdown: /api/agent-proposals.md

POST /agent-proposals/:proposal_id/approve Scopes: agent-ops:write

Approve an agent proposal — EVE handler executes it.

2 parameters
Name In Type Required Description
proposal_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-proposals/post-agent-proposals-proposal-id-approve.md

POST /agent-proposals Scopes: agent-ops:write

Create a new agent action proposal for human review.

5 parameters
Name In Type Required Description
agent_name body string yes
category body string yes
title body string yes
proposed_action body object yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-proposals/post-agent-proposals.md

GET /agent-proposals Scopes: agent-ops:read

List agent action proposals. Filter by status (pending, approved, rejected) and agent_name.

4 parameters
Name In Type Required Description
status query string no
agent_name query string no
category query string no
limit query number no

📄 /api/agent-proposals/get-agent-proposals.md

POST /agent-proposals/:proposal_id/reject Scopes: agent-ops:write

Reject an agent proposal with optional reason.

3 parameters
Name In Type Required Description
proposal_id path string yes
reason body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-proposals/post-agent-proposals-proposal-id-reject.md

POST /agent-proposals/:proposal_id/execute-result Scopes: agent-ops:write

EVE handler reports the result of executing an approved proposal.

4 parameters
Name In Type Required Description
proposal_id path string yes
status body string yes
result body object no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-proposals/post-agent-proposals-proposal-id-execute-result.md

Lead Generation (10 endpoints)

Search for businesses by query and location, match against existing CRM records, save search configurations, and import results as contacts, customers, and deals. Requires lead-gen:read, lead-gen:write, or lead-gen:delete scopes.

📄 Full markdown: /api/lead-gen.md

POST /lead-gen/searches/:search_id/cancel Scopes: lead-gen:write

Force-cancel a running lead-gen search and refund any credits that were charged. Use this to clear searches stuck in "running" — typically because the dispatch edge function timed out / was killed before it could mark the row completed or failed, leaving the row orphaned and the user's credits locked up. Only searches in status="running" can be cancelled; completed/failed/cancelled rows return a 400. On success the row flips to status="cancelled" with error_message="Cancelled by user" and credits_charged is refunded via a "refund" ledger transaction. Response includes credits_refunded and credit_balance_after for confirmation.

2 parameters
Name In Type Required Description
search_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/lead-gen/post-lead-gen-searches-search-id-cancel.md

POST /lead-gen/saved-searches Scopes: lead-gen:write

Save a lead-gen search query for reuse.

10 parameters
Name In Type Required Description
name body string yes What to call this saved search.
search_query body string yes What to search for, e.g. "plumbers" or "dental clinics".
location body string yes Where to search, e.g. "Perth WA" or "Melbourne".
radius_km body number no How far around `location` to search, in kilometres.
max_results body number no Cap on how many leads a run of this search returns. Each result costs credits, so this is the spend control.
default_pipeline_id body string no Pipeline that leads from this search land in. Get real ids from list_pipelines.
default_stage_id body string no Stage within default_pipeline_id that new leads start at. Must be a stage OF that pipeline.
default_tags body array no Tags applied to every lead this search creates, so a batch can be found again later. Replaces the whole list.
enrich_emails body boolean no Whether to also look up an email address for each lead found. Costs extra credits per result.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/lead-gen/post-lead-gen-saved-searches.md

DELETE /lead-gen/saved-searches/:saved_search_id Scopes: lead-gen:delete

Archive a saved lead-gen search.

2 parameters
Name In Type Required Description
saved_search_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/lead-gen/delete-lead-gen-saved-searches-saved-search-id.md

POST /lead-gen/import Scopes: lead-gen:write

Convert lead_gen_results into CRM contacts + customers (+ optional pipeline opportunity). Every conversion requires a per-result contact mapping — first_name is mandatory for each row so the CRM contact has a real person attached, not the business name. Find-or-create semantics: existing customers matched by name and contacts matched by email are reused instead of duplicated. Importing a lead that's already fully in CRM is a no-op; partial/zombie imports (imported flag flipped but contact record never created) are retried automatically.

6 parameters
Name In Type Required Description
contacts body array yes Required. Per-result contact mapping. Each entry creates or reuses a contact with the supplied name + email + phone, links to the lead's customer record (by business name), and optionally creates a deal in the named pipeline + stage.
pipeline_id body string no Optional. When set with stage_id, creates a deal per imported lead and places it in this pipeline.
stage_id body string no Optional. Required when pipeline_id is provided.
lead_source body string no Defaults to "Lead Generation".
tags body array no Optional tags applied to the created deal(s).
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/lead-gen/post-lead-gen-import.md

POST /lead-gen/search Scopes: lead-gen:write

Start a new lead-gen search for businesses matching a query and location. Costs credits.

4 parameters
Name In Type Required Description
query body string yes
location body string yes
limit body number no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/lead-gen/post-lead-gen-search.md

PATCH /lead-gen/saved-searches/:saved_search_id Scopes: lead-gen:write

Update a saved lead-gen search.

11 parameters
Name In Type Required Description
saved_search_id path string yes
name body string no What to call this saved search.
search_query body string no What to search for, e.g. "plumbers" or "dental clinics".
location body string no Where to search, e.g. "Perth WA" or "Melbourne".
radius_km body number no How far around `location` to search, in kilometres.
max_results body number no Cap on how many leads a run of this search returns. Each result costs credits, so this is the spend control.
default_pipeline_id body string no Pipeline that leads from this search land in. Get real ids from list_pipelines.
default_stage_id body string no Stage within default_pipeline_id that new leads start at. Must be a stage OF that pipeline.
default_tags body array no Tags applied to every lead this search creates, so a batch can be found again later. Replaces the whole list.
enrich_emails body boolean no Whether to also look up an email address for each lead found. Costs extra credits per result.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/lead-gen/patch-lead-gen-saved-searches-saved-search-id.md

Lead Gen Initiatives (12 endpoints)

Lead generation results and the multi-step outreach initiatives that work them.

📄 Full markdown: /api/lead-gen-initiatives.md

POST /lead-gen/initiatives/:initiative_id/steps Scopes: lead-gen:write

Add a step to an initiative. Quick mode: pass action_type + action_config + delay. Bring-your-own mode: pass automation_id.

9 parameters
Name In Type Required Description
initiative_id path string yes
action_type body string no send_gmail_email | send_sms | notify_assigned_staff
action_config body object no Config matching the chosen action type (subject/body/etc.)
automation_id body string no Alternative to action_type+config — reuse an existing automation
delay_days body number no
delay_hours body number no
delay_minutes body number no
name body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/lead-gen-initiatives/post-lead-gen-initiatives-initiative-id-steps.md

POST /lead-gen/initiatives Scopes: lead-gen:write

Create an outreach initiative (multi-step sequence) for lead-gen leads. Send-cadence fields are optional — sensible defaults apply (40 emails/day, 09:00-17:00 Mon-Fri Sydney, 90s minimum gap). Override only if the user needs a specific pace.

10 parameters
Name In Type Required Description
name body string yes
description body string no
is_active body boolean no Active initiatives run their dispatcher; paused initiatives queue no new sends. Default true.
daily_send_cap body integer no Max email-channel sends per calendar day in the initiative timezone. Default 40. Personal email accounts typically get flagged above ~50/day for cold outreach.
send_window_start body string no 24-hour HH:MM start of the daily send window in send_window_timezone. Default "09:00".
send_window_end body string no 24-hour HH:MM end of the daily send window in send_window_timezone. Default "17:00".
send_window_timezone body string no IANA timezone for the send window (e.g. "Australia/Sydney"). Default "Australia/Sydney".
send_window_days body array no ISO weekdays the initiative is allowed to send on (1=Mon … 7=Sun). Default [1,2,3,4,5] (weekdays).
min_gap_seconds body integer no Minimum seconds between consecutive successful sends on this initiative. Default 90 — spreads 40 sends across ~60 minutes minimum.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/lead-gen-initiatives/post-lead-gen-initiatives.md

POST /lead-gen/results Scopes: lead-gen:write

Manually insert a lead_gen_result (hand-curated lead, CSV import, smoke fixture).

9 parameters
Name In Type Required Description
business_name body string yes
email body string no
phone body string no
website body string no
city body string no
state body string no
country_code body string no
search_id body string no Optional — if omitted a wrapper search is created
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/lead-gen-initiatives/post-lead-gen-results.md

DELETE /lead-gen/initiatives/:initiative_id Scopes: lead-gen:delete

Delete an initiative and all its steps + underlying automations.

2 parameters
Name In Type Required Description
initiative_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/lead-gen-initiatives/delete-lead-gen-initiatives-initiative-id.md

DELETE /lead-gen/initiatives/:initiative_id/steps/:step_id Scopes: lead-gen:delete

Delete a step from an initiative and its underlying automation.

3 parameters
Name In Type Required Description
initiative_id path string yes
step_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/lead-gen-initiatives/delete-lead-gen-initiatives-initiative-id-steps-step-id.md

POST /lead-gen/initiatives/:initiative_id/enrol Scopes: lead-gen:write

Enrol one or more lead_gen_results into an initiative. Schedules the first step task and tracks the rest as the dispatcher advances each enrolment.

3 parameters
Name In Type Required Description
initiative_id path string yes
lead_gen_result_ids body array yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/lead-gen-initiatives/post-lead-gen-initiatives-initiative-id-enrol.md

POST /lead-gen/initiatives/dispatcher/run Scopes: lead-gen:write

Run the initiative dispatcher synchronously for this workspace. Fires any pending step tasks whose scheduled_for has passed. Same code path as the cron tick.

3 parameters
Name In Type Required Description
max_tasks body number no Cap on how many tasks to claim+run (default 10, max 50)
only_enrollment_id body string no Restrict to a specific enrolment (smoke testing)
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/lead-gen-initiatives/post-lead-gen-initiatives-dispatcher-run.md

PATCH /lead-gen/initiatives/:initiative_id Scopes: lead-gen:write

Update an initiative — name, description, active state, or send-cadence (daily cap, send window, days, min-gap). Only present fields are written.

11 parameters
Name In Type Required Description
initiative_id path string yes
name body string no
description body string,null no
is_active body boolean no
daily_send_cap body integer no Max email-channel sends per calendar day. Personal email accounts typically get flagged above ~50/day.
send_window_start body string no 24-hour HH:MM (e.g. "09:00").
send_window_end body string no 24-hour HH:MM (e.g. "17:00").
send_window_timezone body string no IANA timezone (e.g. "Australia/Sydney").
send_window_days body array no ISO weekdays (1=Mon … 7=Sun).
min_gap_seconds body integer no Minimum seconds between consecutive successful sends.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/lead-gen-initiatives/patch-lead-gen-initiatives-initiative-id.md

PATCH /lead-gen/initiatives/:initiative_id/steps/:step_id Scopes: lead-gen:write

Update an initiative step (delay, ordering, is_active).

3 parameters
Name In Type Required Description
initiative_id path string yes
step_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/lead-gen-initiatives/patch-lead-gen-initiatives-initiative-id-steps-step-id.md

Playbooks (8 endpoints)

Manage Playbooks and their cards. Playbooks are drag-and-drop boards of cards that can embed YouTube videos, external links, iframes, notepads, PDFs, images, and secure files.

📄 Full markdown: /api/training.md

POST /playbooks/:playbook_id/cards Scopes: resources:write

Add a card to a Playbook: a notepad (url = the notepad's id, made first with create_notepad), a YouTube video (url = the video link), a link, an embed, or a document, image or file (url = its id). display_config.layout places it on the board: { lg, md, sm, xs } each { x, y, w, h } on a 12-column grid (a tall notepad on the left is w 8; videos down the right are w 4, h 5).

9 parameters
Name In Type Required Description
playbook_id path string yes
title body string yes
resource_type body string yes
url body string yes YouTube or web link, embed URL, or the notepad / document / file id
description body string no Short line under the title, e.g. the video length
category body string no
position body number no Order on the board (0 first)
display_config body object no Board placement: { layout: { lg, md, sm, xs: { x, y, w, h } } }
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/training/post-playbooks-playbook-id-cards.md

POST /playbooks Scopes: resources:write

Create a new Playbook. Optional template: "blank" or "getting_started".

3 parameters
Name In Type Required Description
name body string yes
template body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/training/post-playbooks.md

DELETE /playbooks/:playbook_id Scopes: resources:delete

Delete a Playbook by UUID.

2 parameters
Name In Type Required Description
playbook_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/training/delete-playbooks-playbook-id.md

DELETE /playbook-cards/:card_id Scopes: resources:delete

Delete a Playbook card.

2 parameters
Name In Type Required Description
card_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/training/delete-playbook-cards-card-id.md

GET /playbooks/:playbook_id Scopes: resources:read

Fetch a Playbook by UUID with its cards.

1 parameter
Name In Type Required Description
playbook_id path string yes

📄 /api/training/get-playbooks-playbook-id.md

GET /playbooks Scopes: resources:read

List Playbooks in the workspace. Free read.

1 parameter
Name In Type Required Description
limit query number no

📄 /api/training/get-playbooks.md

PATCH /playbooks/:playbook_id Scopes: resources:write

Update a Playbook.

3 parameters
Name In Type Required Description
playbook_id path string yes
name body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/training/patch-playbooks-playbook-id.md

PATCH /playbook-cards/:card_id Scopes: resources:write

Update a Playbook card.

9 parameters
Name In Type Required Description
card_id path string yes
title body string no
resource_type body string no
url body string no
description body string no
category body string no
position body number no
display_config body object no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/training/patch-playbook-cards-card-id.md

Roles & Permissions (9 endpoints)

Workspace roles with their scopes and data scope, and the effective permissions a member resolves to.

📄 Full markdown: /api/roles.md

POST /roles/:role_id/users Scopes: permissions:write

Assign a workspace member to a role. You cannot assign a role that grants scopes you do not hold (unless you have admin). The member's prior role is remembered so removal can restore it.

3 parameters
Name In Type Required Description
role_id path string yes
user_id body string yes UUID of the member to assign
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/roles/post-roles-role-id-users.md

POST /roles Scopes: permissions:write

Create a custom role. You can only grant scopes you hold yourself (unless you have the admin scope). Scopes are strings like "contacts:write".

6 parameters
Name In Type Required Description
name body string yes Role name, unique within the workspace
description body string no
scopes body array no Scope strings, e.g. ["contacts:read","contacts:write","opportunities:read"]
data_scope body string no Default data scope (e.g. "all")
visibility body object no Per-resource visibility filters, e.g. {"tasks":["assigned_to","created_by"]}
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/roles/post-roles.md

DELETE /roles/:role_id Scopes: permissions:write

Delete a custom role. Fails if any users are still assigned (unassign them first). Preset roles cannot be deleted.

2 parameters
Name In Type Required Description
role_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/roles/delete-roles-role-id.md

GET /roles/effective Scopes: permissions:read

Resolve a workspace member's live effective permissions (is_admin, scopes, data_scope) via the same engine RLS uses. Pass ?user_id=<uuid>; omit to resolve the calling key's creator. Use this to verify what a user can actually do.

1 parameter
Name In Type Required Description
user_id query string no UUID of the member to resolve; omit for the caller

📄 /api/roles/get-roles-effective.md

GET /roles/:role_id Scopes: permissions:read

Fetch a single role by UUID with its resolved scopes, visibility, and assigned-user count.

1 parameter
Name In Type Required Description
role_id path string yes

📄 /api/roles/get-roles-role-id.md

GET /roles/:role_id/users Scopes: permissions:read

List the workspace members currently assigned to a role.

1 parameter
Name In Type Required Description
role_id path string yes

📄 /api/roles/get-roles-role-id-users.md

GET /roles Scopes: permissions:read

List all roles in this workspace (preset and custom) with their resolved scopes, data scope, visibility, and assigned-user counts.

📄 /api/roles/get-roles.md

DELETE /roles/:role_id/users/:user_id Scopes: permissions:write

Remove a workspace member from a role, restoring their previous role (or viewer if none).

3 parameters
Name In Type Required Description
role_id path string yes
user_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/roles/delete-roles-role-id-users-user-id.md

PATCH /roles/:role_id Scopes: permissions:write

Update a custom role (name, description, scopes, data_scope, visibility). Preset roles cannot be edited. You can only set scopes you hold yourself unless you have the admin scope. Renaming keeps existing assignments intact.

7 parameters
Name In Type Required Description
role_id path string yes
name body string no
description body string no
scopes body array no
data_scope body string no
visibility body object no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/roles/patch-roles-role-id.md

Scheduled Communications (7 endpoints)

Queue deferred emails, SMS, and AI voice calls for future dispatch. The communications_scheduler_tick cron (runs every minute) claims pending rows, enforces business-hours windows, retries with exponential backoff, and dispatches via the automation pipeline. Row lifecycle: pending -> dispatching -> dispatched | failed | cancelled | skipped.

📄 Full markdown: /api/scheduled-communications.md

POST /scheduled-communications/bulk-cancel Scopes: dispatcher:write

Cancel every PENDING scheduled communication for a contact, opportunity, or customer. Use it to stop an AI voice follow-up cadence or a queued SMS/email sequence when a lead is handed to a human. Provide at least one of contact_id, deal_id, customer_id; supplying more than one narrows the match (they are ANDed). Already-sent items are unaffected.

4 parameters
Name In Type Required Description
contact_id body string no Cancel pending items for this contact.
deal_id body string no Cancel pending items for this opportunity.
customer_id body string no Cancel pending items for this customer.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduled-communications/post-scheduled-communications-bulk-cancel.md

DELETE /scheduled-communications/:scheduled_id Scopes: dispatcher:write

Soft-cancel a scheduled communication.

2 parameters
Name In Type Required Description
scheduled_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduled-communications/delete-scheduled-communications-scheduled-id.md

POST /scheduled-communications/:scheduled_id/dispatch-now Scopes: dispatcher:write

Dispatch a pending scheduled communication immediately.

2 parameters
Name In Type Required Description
scheduled_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduled-communications/post-scheduled-communications-scheduled-id-dispatch-now.md

POST /scheduled-communications Scopes: dispatcher:write

Schedule a deferred email, SMS or voice call for future dispatch.

14 parameters
Name In Type Required Description
channel body string yes
scheduled_for body string yes
payload body object yes Channel-shaped body. sms needs to_number and message_body; phone_number_id is OPTIONAL and best omitted, because the sending line is resolved when the message actually goes out, against the recipient's country. email needs email_config_id, recipient_email and subject. voice_call needs voice_agent_outbound_config_id and to_number.
contact_id body string no
deal_id body string no
customer_id body string no
timezone body string no
respect_business_hours body boolean no
related_entity_type body string no
related_entity_id body string no
cancel_policy body object no Which events cancel this while it is still pending. Booleans: on_unsubscribe, on_dnc, on_contact_reply, on_deal_closed, on_pipeline_change. Omitting a key leaves that condition OFF. on_pipeline_change cancels when the opportunity is moved off the pipeline it was enrolled from, which stops a segment-specific sequence following a re-segmented lead.
enrolled_pipeline_id body string no Pipeline this was enrolled from; what on_pipeline_change compares against. Derived from the opportunity automatically when omitted.
enrolled_stage_id body string no Stage this was enrolled from. Derived from the opportunity automatically when omitted.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduled-communications/post-scheduled-communications.md

PATCH /scheduled-communications/:scheduled_id Scopes: dispatcher:write

Update a pending scheduled communication (only while status=pending).

7 parameters
Name In Type Required Description
scheduled_id path string yes
scheduled_for body string no
payload body object no
timezone body string no
respect_business_hours body boolean no
cancel_policy body object no Which events cancel this while it is still pending. Booleans: on_unsubscribe, on_dnc, on_contact_reply, on_deal_closed, on_pipeline_change.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/scheduled-communications/patch-scheduled-communications-scheduled-id.md

Schemas (2 endpoints)

Discoverable reference data for automation trigger types. Returns the shape of trigger_data each trigger publishes, the {{variable}} tokens available in action templates, and CRM-enriched variable namespaces. Free -- 0 credits, no scope required.

📄 Full markdown: /api/schemas.md

GET /schemas/triggers/:trigger_type

Fetch a single trigger schema by trigger_type (e.g. contact_created). Free read.

1 parameter
Name In Type Required Description
trigger_type path string yes Trigger type identifier

📄 /api/schemas/get-schemas-triggers-trigger-type.md

Reputation (22 endpoints)

Public B2B reputation profiles, verified reviews, and case studies. Build credibility with a public reputation page at trustpager.com/reputation/<slug>. Includes a companion anonymous image upload endpoint for reviewer logos and avatars.

📄 Full markdown: /api/reputation.md

POST /reputation/reviews/:review_id/approve Scopes: reputation:write

Approve a pending review for publication.

2 parameters
Name In Type Required Description
review_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reputation/post-reputation-reviews-review-id-approve.md

POST /reputation/case-studies/:case_study_id/archive Scopes: reputation:write

Archive a case study.

2 parameters
Name In Type Required Description
case_study_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reputation/post-reputation-case-studies-case-study-id-archive.md

POST /reputation/reviews/:review_id/archive Scopes: reputation:write

Archive a review.

2 parameters
Name In Type Required Description
review_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reputation/post-reputation-reviews-review-id-archive.md

POST /reputation/case-studies Scopes: reputation:write

Create a case study.

19 parameters
Name In Type Required Description
profile_id body string yes UUID of the reputation profile this case study belongs to.
review_id body string no UUID of a review to attach to this case study, so the testimonial shows alongside the story.
title body string yes Case study headline.
slug body string yes URL-safe segment for the public case study page. Changing it breaks links already shared.
client_name body string yes Name of the client the work was done for, as shown publicly.
client_company body string no Client company name, when different from client_name.
client_logo body string no Absolute URL of the client logo.
problem body string no The situation before the work: what the client was struggling with.
solution body string no What you did about it.
outcome body string no What changed as a result, ideally with concrete numbers.
summary body string no Short summary shown on the case study card in listings.
key_metrics body object no Headline result figures rendered as stat tiles, e.g. { "Revenue": "+38%" }. Full-replace on write.
images body object no Images used on the case study page. Full-replace on write.
tags body array no Tags used to filter case studies on the public profile. Full-replace array.
featured body boolean no Pin this case study to the top of the public profile.
sort_order body number no Manual position in the case study list. Lower sorts first.
source_type body string no Where the case study came from, e.g. authored in-app or imported.
source_reference body object no Provenance details for the source. Full-replace on write.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reputation/post-reputation-case-studies.md

POST /reputation/reviews Scopes: reputation:write

Create a reputation review.

24 parameters
Name In Type Required Description
profile_id body string yes UUID of the reputation profile this review belongs to.
reviewer_name body string yes Full name of the person giving the review, as shown publicly.
reviewer_email body string no Reviewer email. Used for verification and follow-up; never shown publicly.
reviewer_company body string no Company the reviewer works for, shown under their name.
reviewer_company_logo body string no Absolute URL of the reviewer's company logo.
reviewer_role body string no Reviewer job title, shown under their name.
reviewer_avatar body string no Absolute URL of the reviewer photo shown beside the review.
reviewer_linkedin_url body string no Absolute URL of the reviewer's LinkedIn profile.
reviewer_linkedin_verified body boolean no Whether the LinkedIn identity has been verified. Drives the verified badge on the public review.
reviewer_company_size body string no Size band of the reviewer company, used for filtering on the public profile.
reviewer_industry body string no Industry of the reviewer company, used for filtering on the public profile.
rating body number yes Star rating given, 1 to 5.
testimonial_text body string yes The review body as written by the reviewer.
service_category body string no Which service the review is about, used for filtering.
engagement_type body string no Shape of the engagement being reviewed, e.g. project or retainer.
engagement_value_band body string no Value band of the engagement. A band rather than an exact figure so it can be shown publicly.
vendor_response body string no Your public reply to the review, shown beneath it.
featured body boolean no Pin this review to the top of the public profile.
source_type body string no Where the review came from, e.g. collected by request or imported.
source_reference body object no Provenance details for the source, e.g. the original request or import record. Full-replace on write.
consent_given body boolean no Whether the reviewer consented to public display. Publishing without consent is what this field exists to prevent.
consent_method body string no How consent was captured, e.g. via the review form or in writing.
consent_date body string no ISO 8601 timestamp of when consent was given.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reputation/post-reputation-reviews.md

DELETE /reputation/case-studies/:case_study_id Scopes: reputation:delete

Delete a case study.

2 parameters
Name In Type Required Description
case_study_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reputation/delete-reputation-case-studies-case-study-id.md

DELETE /reputation/profile Scopes: reputation:delete

Delete the reputation profile.

1 parameter
Name In Type Required Description
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reputation/delete-reputation-profile.md

DELETE /reputation/reviews/:review_id Scopes: reputation:delete

Delete a reputation review.

2 parameters
Name In Type Required Description
review_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reputation/delete-reputation-reviews-review-id.md

GET /reputation/reviews Scopes: reputation:read

List reputation reviews.

2 parameters
Name In Type Required Description
status query string no
limit query number no

📄 /api/reputation/get-reputation-reviews.md

POST /reputation/case-studies/:case_study_id/publish Scopes: reputation:write

Publish a case study to the public profile.

2 parameters
Name In Type Required Description
case_study_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reputation/post-reputation-case-studies-case-study-id-publish.md

POST /reputation/reviews/:review_id/publish Scopes: reputation:write

Publish a review to the public profile.

2 parameters
Name In Type Required Description
review_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reputation/post-reputation-reviews-review-id-publish.md

POST /reputation/reviews/:review_id/reject Scopes: reputation:write

Reject a review.

2 parameters
Name In Type Required Description
review_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reputation/post-reputation-reviews-review-id-reject.md

POST /reputation/request-review Scopes: reputation:write

Send a tokenised review request to a contact.

3 parameters
Name In Type Required Description
contact_id body string no
email body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reputation/post-reputation-request-review.md

PATCH /reputation/case-studies/:case_study_id Scopes: reputation:write

Update a case study.

20 parameters
Name In Type Required Description
case_study_id path string yes UUID of the case study to update. Get ids from list_reputation_case_studies.
profile_id body string no UUID of the reputation profile this case study belongs to.
review_id body string no UUID of a review to attach to this case study, so the testimonial shows alongside the story.
title body string no Case study headline.
slug body string no URL-safe segment for the public case study page. Changing it breaks links already shared.
client_name body string no Name of the client the work was done for, as shown publicly.
client_company body string no Client company name, when different from client_name.
client_logo body string no Absolute URL of the client logo.
problem body string no The situation before the work: what the client was struggling with.
solution body string no What you did about it.
outcome body string no What changed as a result, ideally with concrete numbers.
summary body string no Short summary shown on the case study card in listings.
key_metrics body object no Headline result figures rendered as stat tiles, e.g. { "Revenue": "+38%" }. Full-replace on write.
images body object no Images used on the case study page. Full-replace on write.
tags body array no Tags used to filter case studies on the public profile. Full-replace array.
featured body boolean no Pin this case study to the top of the public profile.
sort_order body number no Manual position in the case study list. Lower sorts first.
source_type body string no Where the case study came from, e.g. authored in-app or imported.
source_reference body object no Provenance details for the source. Full-replace on write.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reputation/patch-reputation-case-studies-case-study-id.md

PATCH /reputation/profile Scopes: reputation:write

Partially update the reputation profile.

12 parameters
Name In Type Required Description
category_id body string no UUID of the reputation category this profile is listed under.
slug body string no URL-safe segment for the public profile page. Changing it changes the public URL and breaks links already shared.
display_name body string no Brand name shown at the top of the public reputation profile.
tagline body string no One-line positioning statement shown under the display name.
description body string no Longer "about" copy for the public profile.
logo_url body string no Absolute URL of the brand logo shown on the profile.
cover_image_url body string no Absolute URL of the banner image across the top of the profile.
website_url body string no Absolute URL of the brand website, linked from the profile.
linkedin_url body string no Absolute URL of the brand LinkedIn page, linked from the profile.
settings body object no Profile display settings (section visibility, ordering, theme). Full-replace on write.
is_published body boolean no Whether the profile is publicly visible. False keeps it a draft.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reputation/patch-reputation-profile.md

PATCH /reputation/reviews/:review_id Scopes: reputation:write

Update a reputation review.

25 parameters
Name In Type Required Description
review_id path string yes UUID of the review to update. Get ids from list_reputation_reviews.
profile_id body string no UUID of the reputation profile this review belongs to.
reviewer_name body string no Full name of the person giving the review, as shown publicly.
reviewer_email body string no Reviewer email. Used for verification and follow-up; never shown publicly.
reviewer_company body string no Company the reviewer works for, shown under their name.
reviewer_company_logo body string no Absolute URL of the reviewer's company logo.
reviewer_role body string no Reviewer job title, shown under their name.
reviewer_avatar body string no Absolute URL of the reviewer photo shown beside the review.
reviewer_linkedin_url body string no Absolute URL of the reviewer's LinkedIn profile.
reviewer_linkedin_verified body boolean no Whether the LinkedIn identity has been verified. Drives the verified badge on the public review.
reviewer_company_size body string no Size band of the reviewer company, used for filtering on the public profile.
reviewer_industry body string no Industry of the reviewer company, used for filtering on the public profile.
rating body number no Star rating given, 1 to 5.
testimonial_text body string no The review body as written by the reviewer.
service_category body string no Which service the review is about, used for filtering.
engagement_type body string no Shape of the engagement being reviewed, e.g. project or retainer.
engagement_value_band body string no Value band of the engagement. A band rather than an exact figure so it can be shown publicly.
vendor_response body string no Your public reply to the review, shown beneath it.
featured body boolean no Pin this review to the top of the public profile.
source_type body string no Where the review came from, e.g. collected by request or imported.
source_reference body object no Provenance details for the source, e.g. the original request or import record. Full-replace on write.
consent_given body boolean no Whether the reviewer consented to public display. Publishing without consent is what this field exists to prevent.
consent_method body string no How consent was captured, e.g. via the review form or in writing.
consent_date body string no ISO 8601 timestamp of when consent was given.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reputation/patch-reputation-reviews-review-id.md

POST /reputation/profile Scopes: reputation:write

Create or replace the workspace reputation profile.

12 parameters
Name In Type Required Description
category_id body string no UUID of the reputation category this profile is listed under.
slug body string no URL-safe segment for the public profile page. Changing it changes the public URL and breaks links already shared.
display_name body string no Brand name shown at the top of the public reputation profile.
tagline body string no One-line positioning statement shown under the display name.
description body string no Longer "about" copy for the public profile.
logo_url body string no Absolute URL of the brand logo shown on the profile.
cover_image_url body string no Absolute URL of the banner image across the top of the profile.
website_url body string no Absolute URL of the brand website, linked from the profile.
linkedin_url body string no Absolute URL of the brand LinkedIn page, linked from the profile.
settings body object no Profile display settings (section visibility, ordering, theme). Full-replace on write.
is_published body boolean no Whether the profile is publicly visible. False keeps it a draft.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/reputation/post-reputation-profile.md

Referrals (18 endpoints)

Track the referrer -> referred contact -> opportunity chain. Includes workspace-defined category picklist, a leaderboard endpoint, a public token-based submission form, and a create_referral automation action for pipeline-driven partner registration. Every write to the referrals table fires the referral_attributed automation trigger (see GET /schemas/triggers/referral_attributed). Attribution is also cached on the opportunity: crm_deals.primary_referrer_contact_id and crm_deals.primary_referrer_category are maintained by a Postgres trigger and surface in GET /opportunities/:id (including the expand=referrer expansion).

📄 Full markdown: /api/referrals.md

POST /referrals/:referral_id/commissions Scopes: referrals:write

Add a commission instalment to a referral. amount is required; status defaults to scheduled. The cached commission summary (paid-to-date / pending / next-payment) on the referral updates automatically.

7 parameters
Name In Type Required Description
referral_id path string yes
amount body number yes
status body string no scheduled | paid | cancelled. Defaults to scheduled.
due_date body string no ISO date (YYYY-MM-DD) the instalment is expected.
paid_date body string no ISO date (YYYY-MM-DD) the instalment was paid.
notes body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/referrals/post-referrals-referral-id-commissions.md

POST /referrals Scopes: referrals:write

Create a referral entry. Requires referrer_contact_id.

11 parameters
Name In Type Required Description
referrer_contact_id body string yes UUID of the contact who made the referral. The partner being credited.
deal_id body string no UUID of the opportunity this referral produced. Stored as referred_deal_id.
source body string no How the referral reached us, e.g. a partner link, a form, or entered by hand.
referred_contact_id body string no UUID of the contact who was referred in. The person the referrer sent us.
status body string no Where the referral stands. Drives what the partner sees in their portal.
category body string no The referral partner tier this referral is credited under, matching the workspace referral categories. Tiered fee rates are looked up from this, so an off-list value breaks the lookup.
notes body string no Internal notes on the referral. Not shown to the partner.
status_label body string no The status wording shown to the partner in their portal, when you want something more specific than the status itself, e.g. "Quote sent, awaiting approval".
expected_commission body number no What the partner is expected to earn on this referral, shown in their portal. The actual paid amount is tracked separately as commission payments.
last_update_note body string no The most recent progress note shown to the partner in their portal, e.g. "Install booked for the 14th".
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/referrals/post-referrals.md

POST /referrals/links Scopes: referrals:write

Create a stable referral/affiliate link for a referrer contact so every signup through the link is attributed to them. The form it opens is the workspace referral form (Settings -> Tools -> Referrals), resolved automatically — not set per link. slug (the public handle) defaults to the referrer's name. Returns link_url to share.

4 parameters
Name In Type Required Description
referrer_contact_id body string yes Contact UUID of the referrer/partner. Required.
slug body string no Optional public handle for the link (e.g. 'aiafy'). Defaults to a slug of the referrer's name; must be unique in the workspace.
active body boolean no Defaults to true.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/referrals/post-referrals-links.md

DELETE /referrals/:referral_id Scopes: referrals:delete

Delete a referral.

2 parameters
Name In Type Required Description
referral_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/referrals/delete-referrals-referral-id.md

DELETE /referrals/:referral_id/commissions/:payment_id Scopes: referrals:write

Delete a commission instalment from a referral. The cached commission summary on the referral updates automatically.

3 parameters
Name In Type Required Description
referral_id path string yes
payment_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/referrals/delete-referrals-referral-id-commissions-payment-id.md

DELETE /referrals/links/:link_id Scopes: referrals:delete

Delete a referral link. Prefer setting active=false to disable a partner link while keeping its history.

2 parameters
Name In Type Required Description
link_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/referrals/delete-referrals-links-link-id.md

GET /referrals/links/:link_id Scopes: referrals:read

Fetch a referral link by UUID (includes link_url).

1 parameter
Name In Type Required Description
link_id path string yes

📄 /api/referrals/get-referrals-links-link-id.md

GET /referrals/:referral_id/commissions Scopes: referrals:read

List the commission instalments (scheduled / paid / cancelled) attached to a referral, ordered by due date.

1 parameter
Name In Type Required Description
referral_id path string yes

📄 /api/referrals/get-referrals-referral-id-commissions.md

GET /referrals/links Scopes: referrals:read

List referral links — a referrer's stable, reusable referral/affiliate links. Each carries link_url (the shareable app.trustpager.com/r/{workspace}/{handle} link). Filter by referrer_contact_id or active.

2 parameters
Name In Type Required Description
referrer_contact_id query string no
active query boolean no

📄 /api/referrals/get-referrals-links.md

GET /referrals Scopes: referrals:read

List referrals in the workspace.

1 parameter
Name In Type Required Description
limit query number no

📄 /api/referrals/get-referrals.md

POST /referrals/request Scopes: referrals:write

Ask a contact to refer someone — sends the referral-request flow.

3 parameters
Name In Type Required Description
contact_id body string yes
channel body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/referrals/post-referrals-request.md

POST /referrals/attribute Scopes: referrals:write

Set, reassign, or clear the primary referrer on an opportunity. Pass referrer_contact_id to attribute it to a contact, or null to clear. Updates the referral graph in place so commission tracking and history survive a reassignment, and leaves form/automation-sourced referrals intact.

4 parameters
Name In Type Required Description
opportunity_id body string yes Opportunity (deal) UUID to attribute. Required.
referrer_contact_id body string,null yes Contact UUID of the referrer. Pass null to clear the referrer.
category body string no Optional referral category (modality, service type, lead source).
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/referrals/post-referrals-attribute.md

PATCH /referrals/:referral_id Scopes: referrals:write

Update a referral.

10 parameters
Name In Type Required Description
referral_id path string yes
referred_deal_id body string no UUID of the opportunity this referral produced. Named in full here because update_referral does not accept the `deal_id` alias that create_referral takes.
referred_contact_id body string no UUID of the contact who was referred in. The person the referrer sent us.
status body string no Where the referral stands. Drives what the partner sees in their portal.
category body string no The referral partner tier this referral is credited under, matching the workspace referral categories. Tiered fee rates are looked up from this, so an off-list value breaks the lookup.
notes body string no Internal notes on the referral. Not shown to the partner.
status_label body string no The status wording shown to the partner in their portal, when you want something more specific than the status itself, e.g. "Quote sent, awaiting approval".
expected_commission body number no What the partner is expected to earn on this referral, shown in their portal. The actual paid amount is tracked separately as commission payments.
last_update_note body string no The most recent progress note shown to the partner in their portal, e.g. "Install booked for the 14th".
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/referrals/patch-referrals-referral-id.md

PATCH /referrals/:referral_id/commissions/:payment_id Scopes: referrals:write

Update a commission instalment (amount, status, due_date, paid_date, notes). Setting status to paid auto-stamps paid_date when none is supplied.

8 parameters
Name In Type Required Description
referral_id path string yes
payment_id path string yes
amount body number no
status body string no
due_date body string no
paid_date body string no
notes body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/referrals/patch-referrals-referral-id-commissions-payment-id.md

PATCH /referrals/links/:link_id Scopes: referrals:write

Update a referral link — change its handle (slug) or active state (deactivate to disable a partner link without deleting it). The form it opens is a workspace setting, not per-link.

4 parameters
Name In Type Required Description
link_id path string yes
slug body string no
active body boolean no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/referrals/patch-referrals-links-link-id.md

FEEDBACK (8 endpoints)

feedback endpoints.

📄 Full markdown: /api/feedback.md

POST /feedback Scopes: feedback:write

Log a piece of customer feedback. Unhappy feedback (negative, or a rating at or below the workspace setting) is saved as needs_attention, and the team member set to hear about unhappy customers gets a high-priority task and an email.

9 parameters
Name In Type Required Description
summary body string yes One line on what the customer said, in plain words.
details body string no What they said in full, as close to their own words as possible.
rating body number no Their rating from 1 to 5. At or below the workspace "what counts as unhappy" setting (default 2) it counts as unhappy.
sentiment body string no positive, mixed or negative. Optional when rating is given. negative alerts the team.
contact_id body string no The customer (contact) the feedback is from.
deal_id body string no The opportunity the feedback is about, if any.
source body string no Where it came from. Default api.
share_consent body boolean no True when the customer agreed their words can be shared as a testimonial.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/feedback/post-feedback.md

DELETE /feedback/:feedback_id Scopes: feedback:delete

Delete a piece of customer feedback.

2 parameters
Name In Type Required Description
feedback_id path string yes The feedback id.
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/feedback/delete-feedback-feedback-id.md

GET /feedback/:feedback_id Scopes: feedback:read

Get one piece of customer feedback.

1 parameter
Name In Type Required Description
feedback_id path string yes The feedback id.

📄 /api/feedback/get-feedback-feedback-id.md

GET /feedback/settings Scopes: feedback:read

Get the workspace feedback settings: who hears about unhappy customers, what rating counts as unhappy, and how long the Feedback Agent waits before calling the same customer again.

📄 /api/feedback/get-feedback-settings.md

GET /feedback Scopes: feedback:read

List customer feedback, newest first. Filter with status (logged, needs_attention, in_progress, resolved), sentiment (positive, mixed, negative), source, contact_id or assigned_to.

7 parameters
Name In Type Required Description
status query string no
sentiment query string no
source query string no
contact_id query string no
assigned_to query string no
limit query number no
after query string no

📄 /api/feedback/get-feedback.md

POST /feedback/:feedback_id/resolve Scopes: feedback:write

Mark customer feedback resolved, with an optional note on what was done.

3 parameters
Name In Type Required Description
feedback_id path string yes The feedback id.
resolution_note body string no What was done about it.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/feedback/post-feedback-feedback-id-resolve.md

PATCH /feedback/:feedback_id Scopes: feedback:write

Update customer feedback: move its status, assign it, add what was done, or correct what was recorded.

9 parameters
Name In Type Required Description
feedback_id path string yes The feedback id.
status body string no logged, needs_attention, in_progress or resolved. Moving to resolved stamps who resolved it and when.
assigned_to body string no User id of the team member following it up. Send null to unassign.
resolution_note body string no What was done about it.
summary body string no One line on what the customer said.
details body string no What they said in full.
rating body number no Their rating from 1 to 5.
sentiment body string no positive, mixed or negative.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/feedback/patch-feedback-feedback-id.md

PATCH /feedback/settings Scopes: feedback:write

Change the workspace feedback settings.

4 parameters
Name In Type Required Description
alert_user_ids body array no Team members who hear about unhappy customers. The first gets the task, everyone gets the email. Empty means the workspace owner.
unhappy_max_rating body number no Ratings at or below this (1 to 4) count as unhappy. Default 2.
cooldown_days body number no How long the Feedback Agent waits before calling the same customer again (0 to 3650). Default 365.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/feedback/patch-feedback-settings.md

CRM Export (1 endpoint)

One-shot export of CRM data across contacts, companies and opportunities.

📄 Full markdown: /api/crm-export.md

GET /crm/export

Export CRM data (contacts, companies, deals, work-orders) to xlsx or csv. Pass `type` and optional filters via query string. Returns a downloadable file.

21 parameters
Name In Type Required Description
type query string yes contacts, customers, deals, or work-orders
format query string no xlsx (default) or csv
search query string no
status query string no
pipeline_id query string no
stage_id query string no
customer_id query string no
contact_id query string no
archived query string no Archive scope (contacts/companies/deals). "false" (default) = active only, "true" = archived only, "all" = both.
created_after query string no Only records created at or after this ISO 8601 date or time. Every type.
created_before query string no Only records created at or before this ISO 8601 date or time. Every type.
source query string no Contacts only: exact lead source.
email_unsubscribed query string no Contacts only: "true" = unsubscribed from email, "false" = still subscribed.
sms_unsubscribed query string no Contacts only: "true" = unsubscribed from SMS, "false" = still subscribed.
is_customer query string no Companies (type customers) only: filter on the Customer flag.
is_supplier query string no Companies (type customers) only: filter on the Supplier flag.
industry query string no Companies (type customers) only: exact industry.
assigned_to query string no Opportunities (type deals) and work orders: user UUID of the assignee.
status_id query string no Work orders only: work order status UUID.
deal_product_id query string no Work orders only: the opportunity product (line item) UUID the work order belongs to.
schedule_date query string no Work orders only: exact scheduled date, YYYY-MM-DD.

📄 /api/crm-export/get-crm-export.md

Export Templates (13 endpoints)

Reusable export templates and the field catalog they draw on, for pulling workspace data out as CSV or XLSX.

📄 Full markdown: /api/export-templates.md

POST /exports/templates Scopes: exports:write

Create an export template.

7 parameters
Name In Type Required Description
name body string yes Template name, shown in the Spreadsheets list.
root_entity body string yes What one row represents.
description body string no Optional note about what this spreadsheet is for.
columns body array no Columns, left to right. Defaults to none, which produces an empty grid.
filters body array no Filters limiting which records appear. Defaults to none.
output body object no File output settings: { "format": "csv" | "xlsx", "bom": boolean, "filename_template": string }. filename_template accepts the tokens {name}, {YYYY-MM-DD}, {YYYY}, {MM}, {DD}. Defaults to { "format": "csv", "bom": true }.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/export-templates/post-exports-templates.md

POST /exports/templates/:template_id/views Scopes: exports:write

Create a view on an export template. A view holds its own filters and sort, so one template can carry several saved cuts of the same data.

6 parameters
Name In Type Required Description
template_id path string yes
name body string yes View name, shown in the view picker.
description body string no Optional note about what this view shows.
filters body array no Filters for this view. A view holds its own filters, so one template can carry several saved cuts of the same data.
sort body array no Sort order for this view. Only root scalar columns sort at query time.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/export-templates/post-exports-templates-template-id-views.md

DELETE /exports/templates/:template_id Scopes: exports:delete

Delete an export template.

2 parameters
Name In Type Required Description
template_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/export-templates/delete-exports-templates-template-id.md

DELETE /exports/templates/:template_id/views/:view_id Scopes: exports:delete

Delete a view.

3 parameters
Name In Type Required Description
template_id path string yes
view_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/export-templates/delete-exports-templates-template-id-views-view-id.md

POST /exports/templates/:template_id/preview Scopes: exports:read

Preview an export template (free read). Returns the first rows as formatted cells, so it is the fastest way to check a column or filter change did what you meant.

5 parameters
Name In Type Required Description
template_id path string yes
view_id body string no Run against this saved view. Defaults to the most recently updated view on the template.
filters body array no Replace the view filters for this run only. Nothing is saved.
extra_filters body array no Append to the view filters for this run only. Ignored when `filters` is also sent.
sort body array no Override the view sort for this run only. Send null to run unsorted.

📄 /api/export-templates/post-exports-templates-template-id-preview.md

POST /exports/templates/:template_id/run Scopes: exports:read

Run an export template and return the file (CSV or XLSX, per the template output settings).

5 parameters
Name In Type Required Description
template_id path string yes
view_id body string no Run against this saved view. Defaults to the most recently updated view on the template.
filters body array no Replace the view filters for this run only. Nothing is saved.
extra_filters body array no Append to the view filters for this run only. Ignored when `filters` is also sent.
sort body array no Override the view sort for this run only. Send null to run unsorted.

📄 /api/export-templates/post-exports-templates-template-id-run.md

PATCH /exports/templates/:template_id Scopes: exports:write

Update an export template. Send the full columns / filters list, not a partial one.

9 parameters
Name In Type Required Description
template_id path string yes
name body string no Rename the template.
description body string no Replace the note. Send null to clear it.
columns body array no Replaces the whole column list. Send the full set, not a partial one.
filters body array no Replaces the whole filter list. Send the full set, not a partial one.
output body object no File output settings: { "format": "csv" | "xlsx", "bom": boolean, "filename_template": string }. filename_template accepts the tokens {name}, {YYYY-MM-DD}, {YYYY}, {MM}, {DD}. Defaults to { "format": "csv", "bom": true }.
is_archived body boolean no Archive the template (hides it from the Spreadsheets list) or restore it.
delivery body object no Scheduled-delivery settings. Send null to clear.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/export-templates/patch-exports-templates-template-id.md

PATCH /exports/templates/:template_id/views/:view_id Scopes: exports:write

Update a view.

7 parameters
Name In Type Required Description
template_id path string yes
view_id path string yes
name body string no Rename the view.
description body string no Replace the note. Send null to clear it.
filters body array no Replaces the whole filter list for this view.
sort body array no Replaces the sort order. Send null to clear it.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/export-templates/patch-exports-templates-template-id-views-view-id.md

Evie (In-App Agent) (2 endpoints)

Evie is the conversational AI assistant embedded in the TrustPager CRM sidebar. These endpoints sit outside the standard /api/v1 gateway and use OAuth tokens minted specifically for the in-app agent.

📄 Full markdown: /api/evie.md

POST /evie-grant

Mint an Evie OAuth token for the authenticated user. The caller must supply a TrustPager portal session token (JWT) as the Bearer credential. The endpoint verifies workspace membership and returns a tp_oauth_* token scoped to the intersection of the client max-scopes and the user role scopes. Idempotent: re-calling revokes any prior Evie token for the same (user, company) pair and issues a fresh one.

1 parameter
Name In Type Required Description
company_id body uuid yes Target workspace company ID. Must be a workspace the authenticated user belongs to.

📄 /api/evie/post-evie-grant.md

GET /api/v1/_meta/tools

Return the list of CRM tools available to the authenticated API key holder. Each tool entry includes: name (snake_case), description, input_schema (JSON Schema), api_path, required_scopes, is_write, and portal_path. Tools are filtered by the caller scopes, so a tp_live_* key with only contacts:read sees only read-scoped contact tools. Used internally by Evie to populate her tool catalog, and useful for building your own agent on top of the TrustPager API.

📄 /api/evie/get-api-v1-meta-tools.md

Agent Ops (28 endpoints)

Observability and management for AI agent infrastructure. Covers: registry, run log, signals, metrics, tool log, report runs, alert rules, fired alerts, and the aggregated dashboard. All routes require agent-ops:read (reads) or agent-ops:write (writes/actions).

📄 Full markdown: /api/agent-ops.md

POST /agent-ops/alerts/:alert_id/acknowledge Scopes: agent-ops:write

Acknowledge a fired agent alert.

2 parameters
Name In Type Required Description
alert_id path string yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-ops/post-agent-ops-alerts-alert-id-acknowledge.md

POST /agent-ops/alerts Scopes: agent-ops:write

Record a fired agent alert.

6 parameters
Name In Type Required Description
rule_id body string no UUID of the alert rule that fired. Null for an alert raised directly rather than by a rule.
agent_name body string yes Machine name of the agent the alert is about.
alert_type body string yes What kind of alert this is.
message body string yes Human-readable alert text.
context body object no Supporting detail for the alert: the run, the values that tripped the rule. Full-replace on write.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-ops/post-agent-ops-alerts.md

POST /agent-ops/alert-rules Scopes: agent-ops:write

Create an agent alert rule.

7 parameters
Name In Type Required Description
agent_name body string no Machine name of the agent this rule watches. Null watches every agent.
rule_type body string yes What the rule checks for, e.g. a failed run or a run that took too long.
threshold body object yes The condition that trips the rule. A JSON object, not a bare number, because the shape depends on rule_type.
notify_channel body string no How to notify when the rule trips, e.g. chat or email.
notify_target body string no Where to send the notification: the room, address or user for the chosen channel.
enabled body boolean no Whether the rule is active. False keeps it configured but silent.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-ops/post-agent-ops-alert-rules.md

POST /agent-ops/registry Scopes: agent-ops:write

Register a new agent.

18 parameters
Name In Type Required Description
name body string yes Stable machine name for the agent, used as the key in run logs and signals. Lowercase, no spaces.
display_name body string yes Human-readable agent name shown in the UI.
description body string no What this agent does and when it runs.
icon body string no Icon key for the agent card.
color body string no Accent colour for the agent card, as a hex value.
agent_type body string no Category of agent, used to group it in the console.
status body string no Lifecycle state of the agent, e.g. active or paused. A paused agent stays registered but is not scheduled.
capabilities body array no What this agent is allowed to do, used for routing work to it. Full-replace array.
allowed_tools body array no Tool names this agent may call. Full-replace array; an empty list means no restriction is recorded.
schedule body string no Cron expression for when the agent runs. Null means it only runs when triggered.
timezone body string no IANA timezone the schedule is evaluated in, e.g. "Australia/Melbourne".
upstream_agents body array no Agent names whose output this agent consumes. Full-replace array.
downstream_agents body array no Agent names that consume this agent output. Full-replace array.
configuration body object no Agent-specific settings. Shape depends on agent_type. Full-replace on write.
template_notepad_id body string no UUID of the notepad used as the output template for this agent runs.
runtime body string no Where the agent executes. Determines which worker picks up its runs.
kickoff_message_template body string no Message template used to start a run, with placeholders filled from the run context.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-ops/post-agent-ops-registry.md

POST /agent-ops/reports Scopes: agent-ops:write

Create an agent report-run entry.

9 parameters
Name In Type Required Description
eve_id body string no Identifier of the host that ran the report.
report_name body string yes Which scheduled report this run is for.
run_date body string yes Date the report covers, as YYYY-MM-DD.
status body string no Outcome of the report run, e.g. running, complete or failed.
started_at body string no ISO 8601 timestamp the report started.
completed_at body string no ISO 8601 timestamp the report finished. Null while still running.
notepad_id body string no UUID of the notepad the report was written to.
error body string no Failure reason when the report run failed.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-ops/post-agent-ops-reports.md

POST /agent-ops/runs Scopes: agent-ops:write

Log a new agent run.

17 parameters
Name In Type Required Description
eve_id body string no Identifier of the host that executed the run.
agent_name body string yes Machine name of the agent that ran. Matches agent_registry.name.
task_type body string no What kind of work the run performed.
status body string no Outcome of the run, e.g. running, complete or failed.
model body string no Model identifier the run used.
started_at body string no ISO 8601 timestamp the run started.
completed_at body string no ISO 8601 timestamp the run finished. Null while still running.
duration_ms body number no Wall-clock duration of the run in milliseconds.
output_summary body string no Short summary of what the run produced.
notepad_id body string no UUID of the notepad the run wrote its output to.
slack_sent body boolean no Whether the run posted its summary to the team chat room.
prompt_chars body number no Characters of prompt sent, for cost tracking.
output_chars body number no Characters of output produced, for cost tracking.
exit_code body number no Process exit code. 0 means success.
error_message body string no One-line failure reason when the run failed.
error_detail body string no Full error text or stack trace for debugging.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-ops/post-agent-ops-runs.md

POST /agent-ops/signals Scopes: agent-ops:write

Send an inter-agent signal.

10 parameters
Name In Type Required Description
eve_id body string no Identifier of the host that raised the signal.
for_agent body string yes Machine name of the agent this signal is addressed to.
created_by body string yes Who raised the signal: an agent machine name, or a person.
signal body string yes The signal name, i.e. what happened. Text, not an object.
detail body string no Free-text context for the receiving agent.
expires_at body string no ISO 8601 timestamp after which the signal is stale and should be ignored.
actioned_at body string no ISO 8601 timestamp the signal was consumed. Set this to mark it handled.
report_type body string no Which report the signal relates to, when it came from a scheduled report.
contact_id body string no UUID of the CRM contact the signal is about, when it concerns one.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-ops/post-agent-ops-signals.md

POST /agent-ops/tool-log Scopes: agent-ops:write

Log an agent tool call.

10 parameters
Name In Type Required Description
eve_id body string no Identifier of the host that made the tool call.
tool_name body string yes Name of the tool that was called.
caller body string no Which agent or process made the call.
args body object no Arguments the tool was called with. Full-replace on write.
status body string no Outcome of the call, e.g. ok or error.
duration_ms body number no How long the call took in milliseconds.
http_status body number no HTTP status returned, for tools that make a request.
response_size body number no Size of the response in bytes.
error body string no Error text when the call failed.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-ops/post-agent-ops-tool-log.md

DELETE /agent-ops/alert-rules/:rule_id Scopes: agent-ops:delete

Delete an agent alert rule.

2 parameters
Name In Type Required Description
rule_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-ops/delete-agent-ops-alert-rules-rule-id.md

DELETE /agent-ops/registry/:registry_id Scopes: agent-ops:delete

Delete an agent registry entry.

2 parameters
Name In Type Required Description
registry_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-ops/delete-agent-ops-registry-registry-id.md

POST /agent-ops/dispatch Scopes: agent-ops:write

Dispatch a workflow-agent run on the TrustPager workflow engine and return its run id. The agent runs as durable, cheap-model steps (routed per role); poll get_agent_run for the per-step trace + cost. mode:"test" (default) runs in Demo Company only; mode:"live" runs in the current workspace. Steps, tool_catalog, and routing fall back to the agent's saved configuration when omitted; omit everything to fire a one-step engine health check.

8 parameters
Name In Type Required Description
agent_id body string yes agent_registry id to run.
mode body string no test = Demo Company only (default); live = current workspace.
input body object no Run input, threaded into the first step.
steps body array no Optional StepSpec[]; falls back to the agent's configuration.steps, else a one-step health check.
task_type body string no Label shown as the Sessions row Task.
tool_catalog body array no Optional tool definitions for tool_loop / gather steps (falls back to configuration.tool_catalog).
routing body object no Optional role->model routing override { byRole, byStepId } (falls back to configuration.routing).
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-ops/post-agent-ops-dispatch.md

GET /agent-ops/runs/:run_id Scopes: agent-ops:read

Fetch one agent run with its full per-step execution trace (each step's role, model, status, tokens, tool calls, output) from the workflow-agent ledger. Accepts the run id returned by dispatch_agent_run, or an agent_run_log row id.

1 parameter
Name In Type Required Description
run_id path string yes Run id from dispatch_agent_run, or an agent_run_log row id.

📄 /api/agent-ops/get-agent-ops-runs-run-id.md

GET /agent-ops/runs Scopes: agent-ops:read

List agent execution runs.

2 parameters
Name In Type Required Description
agent_name query string no
limit query number no

📄 /api/agent-ops/get-agent-ops-runs.md

GET /agent-ops/tool-log Scopes: agent-ops:read

List agent tool-call log entries. Filter by caller, tool_name, or since.

5 parameters
Name In Type Required Description
limit query number no
after query string no
caller query string no Which agent made the call.
tool_name query string no
since query string no ISO timestamp. Only entries at or after this time.

📄 /api/agent-ops/get-agent-ops-tool-log.md

PATCH /agent-ops/alert-rules/:rule_id Scopes: agent-ops:write

Update an agent alert rule.

8 parameters
Name In Type Required Description
rule_id path string yes UUID of the alert rule to update.
agent_name body string no Machine name of the agent this rule watches. Null watches every agent.
rule_type body string no What the rule checks for, e.g. a failed run or a run that took too long.
threshold body object no The condition that trips the rule. A JSON object, not a bare number, because the shape depends on rule_type.
notify_channel body string no How to notify when the rule trips, e.g. chat or email.
notify_target body string no Where to send the notification: the room, address or user for the chosen channel.
enabled body boolean no Whether the rule is active. False keeps it configured but silent.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-ops/patch-agent-ops-alert-rules-rule-id.md

PATCH /agent-ops/registry/:registry_id Scopes: agent-ops:write

Update an agent registry entry.

19 parameters
Name In Type Required Description
registry_id path string yes UUID of the agent registry entry to update.
name body string no Stable machine name for the agent, used as the key in run logs and signals. Lowercase, no spaces.
display_name body string no Human-readable agent name shown in the UI.
description body string no What this agent does and when it runs.
icon body string no Icon key for the agent card.
color body string no Accent colour for the agent card, as a hex value.
agent_type body string no Category of agent, used to group it in the console.
status body string no Lifecycle state of the agent, e.g. active or paused. A paused agent stays registered but is not scheduled.
capabilities body array no What this agent is allowed to do, used for routing work to it. Full-replace array.
allowed_tools body array no Tool names this agent may call. Full-replace array; an empty list means no restriction is recorded.
schedule body string no Cron expression for when the agent runs. Null means it only runs when triggered.
timezone body string no IANA timezone the schedule is evaluated in, e.g. "Australia/Melbourne".
upstream_agents body array no Agent names whose output this agent consumes. Full-replace array.
downstream_agents body array no Agent names that consume this agent output. Full-replace array.
configuration body object no Agent-specific settings. Shape depends on agent_type. Full-replace on write.
template_notepad_id body string no UUID of the notepad used as the output template for this agent runs.
runtime body string no Where the agent executes. Determines which worker picks up its runs.
kickoff_message_template body string no Message template used to start a run, with placeholders filled from the run context.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-ops/patch-agent-ops-registry-registry-id.md

PATCH /agent-ops/reports/:report_id Scopes: agent-ops:write

Update an agent report-run.

10 parameters
Name In Type Required Description
report_id path string yes UUID of the report-run entry to update.
eve_id body string no Identifier of the host that ran the report.
report_name body string no Which scheduled report this run is for.
run_date body string no Date the report covers, as YYYY-MM-DD.
status body string no Outcome of the report run, e.g. running, complete or failed.
started_at body string no ISO 8601 timestamp the report started.
completed_at body string no ISO 8601 timestamp the report finished. Null while still running.
notepad_id body string no UUID of the notepad the report was written to.
error body string no Failure reason when the report run failed.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-ops/patch-agent-ops-reports-report-id.md

PATCH /agent-ops/runs/:run_id Scopes: agent-ops:write

Update an agent run.

18 parameters
Name In Type Required Description
run_id path string yes UUID of the run log entry to update.
eve_id body string no Identifier of the host that executed the run.
agent_name body string no Machine name of the agent that ran. Matches agent_registry.name.
task_type body string no What kind of work the run performed.
status body string no Outcome of the run, e.g. running, complete or failed.
model body string no Model identifier the run used.
started_at body string no ISO 8601 timestamp the run started.
completed_at body string no ISO 8601 timestamp the run finished. Null while still running.
duration_ms body number no Wall-clock duration of the run in milliseconds.
output_summary body string no Short summary of what the run produced.
notepad_id body string no UUID of the notepad the run wrote its output to.
slack_sent body boolean no Whether the run posted its summary to the team chat room.
prompt_chars body number no Characters of prompt sent, for cost tracking.
output_chars body number no Characters of output produced, for cost tracking.
exit_code body number no Process exit code. 0 means success.
error_message body string no One-line failure reason when the run failed.
error_detail body string no Full error text or stack trace for debugging.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-ops/patch-agent-ops-runs-run-id.md

PATCH /agent-ops/signals/:signal_id Scopes: agent-ops:write

Update a signal (e.g. mark as consumed).

11 parameters
Name In Type Required Description
signal_id path string yes UUID of the signal to update.
eve_id body string no Identifier of the host that raised the signal.
for_agent body string no Machine name of the agent this signal is addressed to.
created_by body string no Who raised the signal: an agent machine name, or a person.
signal body string no The signal name, i.e. what happened. Text, not an object.
detail body string no Free-text context for the receiving agent.
expires_at body string no ISO 8601 timestamp after which the signal is stale and should be ignored.
actioned_at body string no ISO 8601 timestamp the signal was consumed. Set this to mark it handled.
report_type body string no Which report the signal relates to, when it came from a scheduled report.
contact_id body string no UUID of the CRM contact the signal is about, when it concerns one.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-ops/patch-agent-ops-signals-signal-id.md

POST /agent-ops/metrics Scopes: agent-ops:write

Upsert a metric row.

5 parameters
Name In Type Required Description
agent_name body string yes Machine name of the agent the metrics belong to.
run_date body string yes Date the metrics cover, as YYYY-MM-DD. Together with agent_name this is the upsert key.
data body object no The metric values for that agent and date. Full-replace on write.
run_log_id body string no UUID of the run these metrics came from.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/agent-ops/post-agent-ops-metrics.md

Auto Responders (6 endpoints)

Per-identity AI reply automation for email and SMS (AI Auto-Responders). Each config binds one phone number or email address to a reply behaviour: enabled on/off, draft-for-review vs auto-send, persona/instructions, tone, and knowledge base grounding. Use test_auto_responder to dry-run the triage+draft pipeline before enabling. All routes require agent-ops:read (reads) or agent-ops:write / agent-ops:delete (writes/deletes). The actual send/queue decision at runtime happens server-side and is not exposed here — this surface is pure config CRUD plus a dry-run test.

📄 Full markdown: /api/auto-responders.md

POST /auto-responders Scopes: agent-ops:write

Create or update (upsert by identity) an auto-responder config. identity_kind "phone" needs identity_ref = a phone_numbers.id; "email_config" needs identity_ref = an email_config.id. Defaults: enabled=false, mode="draft" (queues replies for human approval, never auto-sends unless mode="auto_send" is explicitly set).

13 parameters
Name In Type Required Description
channel body string yes
identity_kind body string yes
identity_ref body string yes phone_numbers.id or email_config.id matching identity_kind
enabled body boolean no
mode body string no draft (default) queues for approval; auto_send sends immediately
instructions body string no What this responder should and should not say, in plain language. The single biggest lever on reply quality.
tone body string no How replies should sound, e.g. "warm and brief" or "formal".
knowledge_base_id body string no UUID of the knowledge base the responder answers from. Get real ids from list_knowledge_bases. Without one it answers from the instructions alone.
outgoing_from_email body string no Address replies are sent from. Email responders only.
business_hours_only body boolean no When true, the responder only replies during the hours set in business_hours. Messages outside them wait rather than getting an out-of-hours reply.
business_hours body object no The window business_hours_only applies, keyed by day with open and close times. Omit to use the workspace business hours.
config body object no What the AI is told about the customer, what it can do, and how it handles each type of inbound. context_modules: array of opportunity, contact, customer_account, products, work_orders, bookings, invoices, a CRM record snapshot funnelled into the draft as reference only (the AI does not proactively volunteer it). tools: object keyed by tool name (lookup_account, work_order_status, check_pricing, check_availability, book_appointment, cancel_booking, reschedule_booking, request_callback, take_message, unsubscribe, create_invoice_payment_link) with { enabled: boolean, requires_approval?: boolean }. requires_approval only applies to write tools (book_appointment, cancel_booking, reschedule_booking, request_callback, take_message, unsubscribe, create_invoice_payment_link) and defaults to true for customer-visible actions; when true the action queues into /settings/approvals instead of executing immediately. triage_actions: object keyed by inbound category (not_for_you, new_enquiry, existing_customer, general_question, booking_request, complaint, billing_account, sales_pitch, vendor_supplier, recruitment_partnership, automated_notification) mapping each to one of auto_respond, escalate, do_nothing: auto_respond drafts a reply, escalate drafts but always queues for approval, do_nothing ignores it. not_for_you is checked first: a message nobody at this inbox needs to answer (a teammate is already running the conversation, or there is nothing to answer) lands there; everything else lands on its type. Unset categories use sensible defaults (not_for_you, sales pitches, vendor and machine mail do nothing; enquiries auto-respond; complaint/billing escalate).
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/auto-responders/post-auto-responders.md

DELETE /auto-responders/:id Scopes: agent-ops:delete

Delete an auto-responder config. The identity (number/email) still appears in the Hub list, just unconfigured.

2 parameters
Name In Type Required Description
id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/auto-responders/delete-auto-responders-id.md

GET /auto-responders Scopes: agent-ops:read

List per-identity AI auto-responder configs for the workspace (email/SMS reply automation). Optional filters: channel (sms|email), enabled (true|false).

2 parameters
Name In Type Required Description
channel query string no
enabled query boolean no

📄 /api/auto-responders/get-auto-responders.md

POST /auto-responders/:id/test Scopes: agent-ops:write

Fire a synthetic message through the auto-responder's triage and draft pipeline (TrustPager AI) WITHOUT sending or queuing anything for approval. A dry run to verify behaviour before enabling. Returns the triage category (new_enquiry, sales_pitch, complaint, etc.), the resolved action (auto_respond | escalate | do_nothing), whether the reply would be forced to the approval queue, and the drafted text when one is produced. On email, the sender is checked first against the same engagement gates the live rail applies: if the sender is an automated/no-reply mailbox, or belongs to this workspace (the responder's own sending address, or one of its users), the response is `would_engage: false` with a `skip_reason` and no draft, exactly as the live responder would behave. Then the reply gate picks the row of the triage_actions matrix: not_for_you (nobody at this inbox needs to respond: a teammate is already running the conversation, or there is nothing to answer; it weighs who the sender is in the CRM, whether the inbox owner is in To or only Cc, and, with thread_id, who on your team is already in that conversation) or the message type. When that row is do_nothing the response is `would_engage: false` with `skip_reason` "not_for_you" or "do_nothing_type". Every response carries `reply_gate` with the row and its confidence.

8 parameters
Name In Type Required Description
id path string yes
message_body body string yes The synthetic inbound message to test
from body string no Optional synthetic sender address/number
subject body string no Optional synthetic subject (email only)
thread_id body string no Optional email thread id (email only). Tests against that real conversation, so the reply gate sees who on your team is already in it.
to body string no Optional To header (email only), e.g. "a@x.com, b@y.com". Lets the reply gate see whether the inbox owner was addressed.
cc body string no Optional Cc header (email only). Lets the reply gate see whether the inbox owner was only copied in.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/auto-responders/post-auto-responders-id-test.md

PATCH /auto-responders/:id Scopes: agent-ops:write

Update an auto-responder config (enable/disable, change mode, edit persona/instructions/tone/knowledge base, context modules, tools, per-category triage actions).

11 parameters
Name In Type Required Description
id path string yes
enabled body boolean no
mode body string no
instructions body string no What this responder should and should not say, in plain language. The single biggest lever on reply quality.
tone body string no How replies should sound, e.g. "warm and brief" or "formal".
knowledge_base_id body string no UUID of the knowledge base the responder answers from. Get real ids from list_knowledge_bases. Without one it answers from the instructions alone.
outgoing_from_email body string no Address replies are sent from. Email responders only.
business_hours_only body boolean no When true, the responder only replies during the hours set in business_hours. Messages outside them wait rather than getting an out-of-hours reply.
business_hours body object no The window business_hours_only applies, keyed by day with open and close times. Omit to use the workspace business hours.
config body object no context_modules: array of opportunity, contact, customer_account, products, work_orders, bookings, invoices. tools: object keyed by tool name (lookup_account, work_order_status, check_pricing, check_availability, book_appointment, cancel_booking, reschedule_booking, request_callback, take_message, unsubscribe, create_invoice_payment_link) with { enabled: boolean, requires_approval?: boolean }. triage_actions: object keyed by inbound category (not_for_you, new_enquiry, existing_customer, general_question, booking_request, complaint, billing_account, sales_pitch, vendor_supplier, recruitment_partnership, automated_notification) → one of auto_respond, escalate, do_nothing (auto_respond | escalate | do_nothing).
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/auto-responders/patch-auto-responders-id.md

INVENTORY (15 endpoints)

inventory endpoints.

📄 Full markdown: /api/inventory.md

POST /inventory/batches Scopes: products:write

Create an inventory batch/lot for a product. Stock is added separately by recording a receive movement.

13 parameters
Name In Type Required Description
product_id body string yes
batch_number body string yes
initial_quantity body number yes
received_date body string no
expiry_date body string no
supplier_name body string no
purchase_cost body number no
notes body string no
manufactured_date body string no When the batch was made (YYYY-MM-DD). Distinct from received_date, which is when it reached you.
internal_reference body string no Your own reference for this batch, e.g. a goods-in number.
supplier_reference body string no The reference the supplier used for this batch, e.g. their invoice or delivery docket number.
metadata body object no Custom field values on the batch, keyed by field name. Replaces the whole object.
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/inventory/post-inventory-batches.md

POST /inventory/locations Scopes: products:write

Create an inventory location (warehouse / stockroom). Requires name.

10 parameters
Name In Type Required Description
name body string yes
address_line1 body string no Street address, first line.
address_line2 body string no Street address, second line (unit, level, dock).
city body string no Suburb or city.
state body string no State or region, e.g. "VIC", "WA".
postal_code body string no Postcode.
country body string no Country. Defaults to Australia when omitted.
is_default body boolean no
is_active body boolean no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/inventory/post-inventory-locations.md

DELETE /inventory/batches/:batch_id Scopes: products:delete

Delete an inventory batch by UUID. Destructive.

2 parameters
Name In Type Required Description
batch_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/inventory/delete-inventory-batches-batch-id.md

DELETE /inventory/locations/:location_id Scopes: products:delete

Delete an inventory location by UUID. Destructive.

2 parameters
Name In Type Required Description
location_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/inventory/delete-inventory-locations-location-id.md

GET /inventory/stock/summary Scopes: products:read

Per-product stock summary for inventory-tracked products: total_stock (physically on hand), held (reserved right now by in-flight storefront checkouts), available (what a shopper can still buy), plus batch count and earliest expiry. Filter by product_id. Use available, not total_stock, to answer "can someone buy this".

1 parameter
Name In Type Required Description
product_id query string no

📄 /api/inventory/get-inventory-stock-summary.md

GET /inventory/batches Scopes: products:read

List inventory batches/lots. Filter by product_id or search by batch_number.

4 parameters
Name In Type Required Description
limit query number no
after query string no
product_id query string no
search query string no

📄 /api/inventory/get-inventory-batches.md

GET /inventory/locations Scopes: products:read

List inventory locations (warehouses / stockrooms) in the workspace.

4 parameters
Name In Type Required Description
limit query number no
after query string no
search query string no
is_active query boolean no

📄 /api/inventory/get-inventory-locations.md

GET /inventory/movements Scopes: products:read

List inventory stock movements (the ledger). Filter by product_id, batch_id, or reference.

6 parameters
Name In Type Required Description
limit query number no
after query string no
product_id query string no
batch_id query string no
reference_type query string no
reference_id query string no

📄 /api/inventory/get-inventory-movements.md

GET /inventory/stock Scopes: products:read

List current stock on hand (per batch, per location). Filter by batch_id or location_id. Maintained automatically from the movements ledger.

4 parameters
Name In Type Required Description
limit query number no
after query string no
batch_id query string no
location_id query string no

📄 /api/inventory/get-inventory-stock.md

POST /inventory/movements Scopes: products:write

Record a stock movement (receive, ship, transfer, adjust_in, adjust_out, dispose). Stock on hand updates automatically. receive/adjust_in need to_location_id; ship/adjust_out/dispose need from_location_id; transfer needs both. The ledger is append-only — correct mistakes with an offsetting movement.

10 parameters
Name In Type Required Description
batch_id body string yes
movement_type body string yes
quantity body number yes
from_location_id body string no
to_location_id body string no
reference_type body string no
reference_id body string no
reason body string no
notes body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/inventory/post-inventory-movements.md

POST /inventory/stock/set Scopes: products:write

Set a product stock on hand to an exact number, and switch inventory tracking on for it if it was off. The quick way to say "there is 1 of this" for a one-of-one item (a domain, an ex-demo unit, a limited edition, an event seat) so the storefront stops selling it once it is gone. Creates a default location and batch the first time if the workspace has none, then posts the adjust_in/adjust_out movement for the difference, so the ledger stays the only write path. Refuses when the product already holds stock in more than one batch or location, because there is no single place to set a total: use record_inventory_movement there instead.

6 parameters
Name In Type Required Description
product_id body string yes The product whose stock on hand you are setting.
quantity body number yes How many there are, in total. 0 means sold out. Whole numbers only.
location_id body string no Which location holds them. Defaults to the workspace default location, creating one the first time if there is none.
batch_number body string no Name for the batch created the first time this product gets stock. Defaults to DEFAULT. Ignored if the product already has a batch.
reason body string no What to record against the adjustment on the movements ledger. Defaults to "Stock set to N".
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/inventory/post-inventory-stock-set.md

PATCH /inventory/batches/:batch_id Scopes: products:write

Update an inventory batch. The product it belongs to cannot be changed.

6 parameters
Name In Type Required Description
batch_id path string yes
batch_number body string no
expiry_date body string no
supplier_name body string no
notes body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/inventory/patch-inventory-batches-batch-id.md

PATCH /inventory/locations/:location_id Scopes: products:write

Update an inventory location. Pass only the fields you want to change.

11 parameters
Name In Type Required Description
location_id path string yes
name body string no
address_line1 body string no Street address, first line.
address_line2 body string no Street address, second line (unit, level, dock).
city body string no Suburb or city.
state body string no State or region, e.g. "VIC", "WA".
postal_code body string no Postcode.
country body string no Country. Defaults to Australia when omitted.
is_default body boolean no
is_active body boolean no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/inventory/patch-inventory-locations-location-id.md

COMPONENTS (12 endpoints)

components endpoints.

📄 Full markdown: /api/components.md

POST /component-instances/bulk Scopes: products:write

Create up to 500 component instances in one call. Body: { instances: [...] }, each item the same shape as create_component_instance (product_id + opportunity_id required). Used for sample-data imports.

2 parameters
Name In Type Required Description
instances body array yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/components/post-component-instances-bulk.md

POST /product-components/bulk Scopes: products:write

Create up to 500 product template components in one call. Body: { components: [...] }, each item the same shape as create_product_component.

2 parameters
Name In Type Required Description
components body array yes
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/components/post-product-components-bulk.md

POST /component-instances Scopes: products:write

Create a component instance (a unit / lab sample) for a job. REQUIRES both product_id and opportunity_id — an instance always belongs to a product and a specific opportunity. Carry result data in attributes.

8 parameters
Name In Type Required Description
product_id body string yes
opportunity_id body string yes
label body string no
status body string no
attributes body object no
external_ref body string no
source body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/components/post-component-instances.md

POST /product-components Scopes: products:write

Add a template component (a BOM part/bundle) to a product. Product-level, not tied to any opportunity. Requires product_id.

9 parameters
Name In Type Required Description
product_id body string yes
type body string no
label body string no
status body string no
attributes body object no
sort_order body number no
external_ref body string no
source body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/components/post-product-components.md

DELETE /component-instances/:instance_id Scopes: products:delete

Delete a component instance by UUID. Destructive.

2 parameters
Name In Type Required Description
instance_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/components/delete-component-instances-instance-id.md

DELETE /product-components/:product_component_id Scopes: products:delete

Delete a product template component by UUID. Destructive.

2 parameters
Name In Type Required Description
product_component_id path string yes
require_approval query boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/components/delete-product-components-product-component-id.md

GET /component-instances Scopes: products:read

List component instances (the actual units / lab samples processed for jobs), carrying result data in attributes. Filter by opportunity_id, product_id, status, or search by label.

6 parameters
Name In Type Required Description
limit query number no
after query string no
opportunity_id query string no
product_id query string no
status query string no
search query string no

📄 /api/components/get-component-instances.md

GET /product-components Scopes: products:read

List a product's template components (bill-of-materials parts/bundles). Filter by product_id, type, or search by label. These are product-level and reused across opportunities — for the actual lab samples/units of a job use list_component_instances.

5 parameters
Name In Type Required Description
limit query number no
after query string no
product_id query string no
type query string no
search query string no

📄 /api/components/get-product-components.md

PATCH /component-instances/:instance_id Scopes: products:write

Update a component instance. The product and opportunity it belongs to cannot be changed. Pass only the fields you want to change (e.g. status, attributes).

7 parameters
Name In Type Required Description
instance_id path string yes
label body string no
status body string no
attributes body object no
external_ref body string no
source body string no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/components/patch-component-instances-instance-id.md

PATCH /product-components/:product_component_id Scopes: products:write

Update a product template component. The product it belongs to cannot be changed.

7 parameters
Name In Type Required Description
product_component_id path string yes
type body string no
label body string no
status body string no
attributes body object no
sort_order body number no
require_approval body boolean no Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly.

📄 /api/components/patch-product-components-product-component-id.md