Chirply API documentation
One catalog of operations powers Chirply’s REST API, its built-in MCP server, its in-app assistant and its Zapier integration. Anything a person can do in the app, a program can do too — the same permissions, the same validation, the same effects.
Base URL https://app.chirply.io. Every endpoint is HTTPS-only and returns JSON.
Authentication
Every request carries a bearer token. There are two ways to get one, and the API cannot tell them apart once it has one — both resolve to a workspace and a scope set.
- OAuth 2.0 — for anything a third party distributes. The user clicks a button, signs in to Chirply, approves, and never handles a credential.
- API keys — for your own scripts and back-office jobs, where there is no user to send through a browser.
Scopes are read and write. A token never exceeds the permissions of the workspace member behind it: if a screen is manager-only, so is its operation.
OAuth 2.0
Standard authorization-code grant, with PKCE and refresh-token rotation.
Authorize GET https://app.chirply.io/oauth/authorize
?client_id=…&redirect_uri=…&response_type=code
&scope=read%20write&state=…
Token POST https://app.chirply.io/api/v1/oauth/token
grant_type=authorization_code
&code=…&redirect_uri=…&client_id=…&client_secret=…
Refresh POST https://app.chirply.io/api/v1/oauth/token
grant_type=refresh_token&refresh_token=…Access tokens are bearer credentials valid for 8 hours. Refresh tokens rotate: the one you send is retired and a new one comes back in the same response, so store the new one every time. Redirect URIs are matched exactly — register yours with us first.
The user picks one workspace per connection, the way Stripe and Slack do it. Connections are listed and revocable in the app under Developers.
API keys
Create one in the app under Developers. It is shown once, we store only a hash, and it acts with owner-level access for its workspace — treat it like a password.
curl https://app.chirply.io/api/v1 \
-H "Authorization: Bearer chp_live_…"That endpoint is the whoami read: it returns the workspace the credential acts for and the scopes it holds. It is the cheapest way to verify a connection.
Calling an action
Every operation is one POST. The name is <domain>.<verb>, and the body is the operation’s input object.
curl -X POST https://app.chirply.io/api/v1/actions/contacts.create \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"first_name":"Ada","last_name":"Lovelace","phone":"+15551234567"}'GET /api/v1/actions returns the live catalog — every operation with its description and a JSON Schema built from its validator. That catalog is the authoritative reference; the list at the bottom of this page is the same data, minus the schemas.
Operations that spend money, send messages to real people, or destroy data are marked confirm. Holding a credential is itself the confirmation for API and MCP callers; the in-app assistant refuses to run them without a human approving first.
Errors
Failures are a flat object with a stable machine code and a message written for a human. HTTP status carries the category.
{ "error": { "code": "unauthorized", "message": "Invalid or missing API key." } }400 validation · 401 bad or missing credential · 403 scope or role · 404 no such record · 409 conflict · 422 valid but refused · 429 rate limited. The OAuth token endpoint instead uses RFC 6749 error codes.
Webhooks
Register an HTTPS endpoint and Chirply POSTs each occurrence to it, signed and with retries. This is how an integration reacts to activity instead of polling for it.
POST https://app.chirply.io/api/v1/webhooks
{ "url": "https://example.com/hooks/chirply", "events": ["contact_created"] }
→ 201 { "subscriptions": [...], "secret": "whsec_…" } ← shown ONCEEach delivery carries X-Chirply-Event, X-Chirply-Delivery and X-Chirply-Signature: t=<unix>,v1=<hex>, where the hex is HMAC-SHA256(secret, `$${t}.$${rawBody}`). Verify it before trusting a payload. The body is { event, org_id, contact_id, context, at }.
Subscribable events (81)
| Event | Fires when |
|---|---|
| custom_record_created | A custom-object record was created. Runs with its linked contact when present. Event context includes recordId, objectKey, values, previousValues and changedFields. Existing records are not replayed. |
| custom_record_updated | A custom-object record was updated. Runs with its linked contact when present. Event context includes recordId, objectKey, values, previousValues and changedFields. Existing records are not replayed. |
| custom_record_deleted | A custom-object record was deleted. Runs with its linked contact when present. Event context includes recordId, objectKey, values, previousValues and changedFields. Existing records are not replayed. |
| contact_created | A new contact is added — by hand, file upload, tracking script, API, or signup. |
| birthday | On, before, or after each contact's birthday. |
| tag_added | A tag is applied to a contact. |
| tag_removed | A tag is removed from a contact. |
| list_membership_added | A contact is added to a static list. |
| list_membership_removed | A contact is removed from a static list. |
| contact_lifecycle_changed | A contact becomes a lead, active contact, customer, or churned customer. |
| contact_owner_changed | A contact is assigned or reassigned to a team member. |
| deal_stage_changed | A deal moves stages. |
| deal_created | A new opportunity is opened for a contact. |
| deal_won | A contact's deal is marked won. |
| deal_lost | A contact's deal is marked lost or abandoned. |
| deal_reopened | A closed deal is moved back to open. |
| deal_at_risk | The AI's deal check-up finds a deal newly in trouble — its health drops to At risk or Stalled. Fires once per downturn, not again on every re-check of a deal that is already in trouble. Tokens: {{dealTitle}}, {{dealValue}}, {{riskReasons}}, {{winProbability}}. |
| task_created | A follow-up task is created for the contact. |
| task_completed | A task linked to the contact is marked done. |
| note_added | A note is added to the contact's timeline. |
| form_submitted | A contact submits any native Chirply form or a form on a funnel page. |
| facebook_lead_received | A person submits a connected Facebook or Instagram instant form. |
| facebook_message_received | A person enters a connected Facebook Page conversation by message, button, referral link, or ad. |
| facebook_messenger_event_received | A connected Messenger thread reports account linking, feedback, a game play, opt-in, customer information, or an in-thread lead form submission. |
| facebook_comment_received | A person comments on a connected Facebook Page post or an eligible ad post delivered through Meta's Page feed webhook. |
| instagram_message_received | A person enters a connected Instagram conversation by DM, story interaction, button, referral link, or ad. |
| instagram_comment_received | A person comments on a post from a connected Instagram Professional account. |
| whatsapp_message_received | A customer messages a connected WhatsApp Business number. |
| message_received | The contact replies by SMS, email, Facebook Messenger, Instagram DM, or WhatsApp. STOP/unsubscribe replies are excluded. |
| call_completed | A call finishes. |
| call_answered | A contact answers an inbound or outbound call. |
| call_missed | A call is busy, unanswered, or fails to connect. Inbound misses fire even when the caller is brand new — a contact is created so the workflow has someone to enroll — and whether or not the line's own missed-call text back sent. Tokens: {{caller}}, {{number}}. |
| call_queue_added | A contact is put on a call queue — by hand, by an automation, or through the API. |
| call_queue_called | A rep finishes a call with someone on a call queue and records the outcome. |
| call_dispositioned | Someone says how a call went — from the post-call prompt, the power dialer, the call log, or the API. Fires again if the outcome is later corrected, so the automation acts on the answer that is true now. |
| call_analyzed | The AI finishes reading a call — a few minutes after it ends — and reports how it went. Runs only on lines with call analysis switched on. Tokens: {{summary}}, {{sentiment}}, {{topicsText}}, {{objectionsText}}, {{nextStep}}. |
| voice_campaign_result | An outbound IVR or call-blast recipient reaches a final answer result. |
| message_sent | Your team or an automation sends the contact an SMS or email. |
| purchase_made | A funnel order is paid. |
| upsell_accepted | A buyer adds a one-click upsell or downsell. |
| website_visit | A known contact returns to your tracked website (once per visit, not per page). |
| conversion | A lead or sale is reported from your website — via chirply.convert() or the Sale/Lead pixel — optionally above a value. |
| website_identified | An anonymous website visitor becomes a contact. |
| appointment_booked | Someone books a time on one of your calendars. |
| appointment_canceled | A booked appointment is canceled by the invitee or your team. |
| appointment_rescheduled | An invitee moves their appointment to a new time. |
| appointment_no_show | An appointment is marked as a no-show. |
| stripe_payment_succeeded | A payment on your connected Stripe account succeeds. Tokens: {{amount}}, {{currency}}, {{description}}. |
| shopify_checkout_abandoned | A Shopify checkout is left incomplete. Narrow by store, value, product, vendor, or product type. |
| shopify_order_created | A new Shopify order is placed. Tokens include order, customer, product, value, discount, and attribution data. |
| shopify_first_order | A customer places their first order in this Shopify store. |
| shopify_repeat_order | An existing customer places another Shopify order. |
| shopify_order_paid | A Shopify order becomes paid. |
| shopify_order_fulfilled | A Shopify order is fulfilled and ready for post-purchase follow-up. |
| shopify_order_cancelled | A Shopify order is cancelled. This can also be used as a stop trigger. |
| shopify_refund_created | A full or partial Shopify refund is created. This can stop or redirect post-purchase sequences. |
| stripe_payment_failed | A payment on your connected Stripe account fails (e.g. a declined card). Tokens: {{amount}}, {{currency}}. |
| stripe_refund_issued | A charge on your connected Stripe account is refunded. Tokens: {{amount_refunded}}, {{currency}}. |
| stripe_subscription_created | Someone starts a subscription on your connected Stripe account. Tokens: {{plan}}, {{amount}}, {{interval}}. |
| stripe_subscription_canceled | A subscription on your connected Stripe account ends (churn). Tokens: {{plan}}, {{status}}. |
| stripe_dispute_created | A customer disputes a charge (chargeback) on your connected Stripe account. Tokens: {{amount}}, {{currency}}. |
| invoice_paid | A contact pays one of your Chirply invoices or checkout orders. |
| invoice_payment_failed | A payment attempt on a Chirply invoice fails. |
| member_access_granted | A contact receives access to a members product. |
| member_access_revoked | A members product is taken away from a contact. |
| conversation_status_changed | A conversation is opened, closed, or snoozed. |
| conversation_owner_changed | A conversation is assigned or reassigned to a team member. |
| email_opened | A contact opens one of your campaign emails. |
| email_clicked | A contact clicks a link in one of your campaign emails. Token: {{url}}. |
| email_bounced | An email hard-bounces (a permanent failure). The address is suppressed. |
| email_complained | A contact marks one of your emails as spam. The address is suppressed. |
| email_unsubscribed | A contact unsubscribes from your emails — via the footer link, the preference centre, or a spam-free opt-out. |
| community_post_created | A member (or your team) publishes a new post in one of your community groups. |
| community_comment_created | A member (or your team) comments on a community post. |
| course_enrolled | A contact is enrolled in one of your courses — by themselves, your team, or an automation. |
| lesson_completed | A contact finishes a lesson in one of your courses. |
| course_completed | A contact finishes every lesson in a course. Fires once per person per course. |
| member_level_up | A community member earns enough points to reach a new level. |
| app_event | An installed app fired one of its own events (via events.emit). Narrow it to a specific app and event key. |
| gohighlevel_event | A signed event arrived from HighLevel. Choose any documented Marketplace webhook event, or leave it open to every GHL event. |
| klaviyo_event | A signed live event arrived from the connected Klaviyo account. Leave the topic blank to match every topic the account exposes. |
MCP server
Chirply speaks the Model Context Protocol at https://app.chirply.io/api/mcp, with the same bearer token. Every operation below is exposed as an MCP tool — the dot in a name becomes an underscore, so contacts.create is contacts_create. Point any MCP-capable assistant at it and it can run the workspace.
Operation catalog (1832)
Read from the live registry, so it is never out of date. Each entry is callable at POST /api/v1/actions/<name>.
contacts
- contacts.set_blockedBlock contact
Block or unblock a contact. Blocked contacts and their communications are hidden from normal CRM and inbox views; no data is deleted and unblocking restores visibility.
- contacts.listList contacts
List the organization's contacts, newest first. Filter by one or many static lists, tags, lifecycle stages, companies, owners, and lead sources; values inside one field are ORed while different fields stack with AND. You can also filter customer status and search names, business name, email and phone. Each contact carries its source and customer designation. Set `include_revenue` to also get each contact's lifetime value.
- contacts.getOpen a contact
Fetch one contact by id, with every field plus the tags assigned to them.
- contacts.list_phone_numbersList phone numbers
List every phone number saved for one contact, including whether each is mobile, landline or VoIP and which one is primary. Read-only; the primary number is the one existing calling, SMS and merge-token flows use.
- contacts.add_phone_numberAdd phone number
Add a phone number to a contact. It becomes primary automatically when it is the contact's first number; pass primary=true to make it the number used by existing calls, texts and merge tokens. This saves CRM data but sends nothing and costs nothing.
- contacts.update_phone_numberEdit phone number
Change a contact phone number and/or classify it as mobile, landline or VoIP. If this row is primary, changing the number immediately changes the destination used by calls, SMS and merge tokens. Nothing is sent and there is no charge.
- contacts.set_primary_phoneMake primary
Make one of a contact's saved phone numbers primary. Existing calling, SMS, automations and merge tokens immediately use this number instead. This changes routing data but sends nothing and costs nothing.
- contacts.delete_phone_numberRemove phone numberconfirm
Permanently remove one phone number from a contact. If it is primary, the oldest remaining number is promoted automatically; if none remain, calling and SMS flows can no longer reach this contact. This cannot be undone and sends nothing.
- contacts.list_email_addressesList contact email addresses
List every email address saved for one contact, including its optional label and which one is primary. Read-only; the primary address is the one campaigns, automations, merge tokens and one-off sends all use.
- contacts.add_email_addressAdd contact email address
Add an email address to a contact. It becomes primary automatically when it is the contact's first address; pass primary=true to make it the address campaigns, automations and merge tokens send to. Saves CRM data only — it sends nothing and costs nothing. Refused if the address already belongs to a different contact in this workspace, since one address identifies one person.
- contacts.update_email_addressEdit contact email address
Change a saved email address and/or its label. If this row is primary, changing the address immediately changes where campaigns, automations and one-off emails are delivered. Nothing is sent and there is no charge.
- contacts.set_primary_emailMake contact email primary
Make one of a contact's saved addresses primary. Campaigns, automations, merge tokens and one-off emails immediately go to this address instead; the previous primary is kept as a secondary address. This changes routing data but sends nothing and costs nothing.
- contacts.delete_email_addressRemove contact email addressconfirm
Permanently remove one email address from a contact. If it is primary, the oldest remaining address is promoted automatically; if none remain, email can no longer reach this contact at all. This cannot be undone and sends nothing.
- contacts.list_linked_profilesList connected profiles
List the Facebook Messenger and Instagram profiles that resolve to one contact, with the Page each belongs to. Read-only. A contact can hold several: the same person has a different Page-scoped id on every Page they message, and another one on Instagram.
- contacts.unlink_profileUnlink profileconfirm
Detach one Messenger or Instagram profile from a contact. The existing conversation is kept, but the next message from that profile arrives as a NEW contact instead of landing on this one — so this is how you undo a merge that joined up two people who were not the same person. It cannot be undone directly; re-linking means merging the new contact back in.
- contacts.list_automationsContact automations
List every automation a contact has entered, newest automation first. Returns the latest run status, the human-readable current step, when a waiting run resumes, any error, and the total number of times the contact entered each automation. Reads only; it does not start or change a run.
- contacts.list_broadcastsContact broadcasts
List the email, SMS, ringless voicemail and voice broadcasts sent or attempted for one contact, including campaign name, channel, delivery status, time and any error. Read-only; it sends nothing and costs nothing.
- contacts.list_sourcesLead sources
Break this organization's contacts down by where they came from — the channel each lead arrived through, with a count, commonest first. Only sources that actually occur are returned, so an empty result means the org has no contacts. Use the returned `source` values to filter contacts.list. Reads nothing outside this org and changes nothing.
- contacts.get_column_layoutColumns
Read which columns you see on the contacts list and in what order, plus every column this organization could show (built-ins and its custom fields). Personal to you — it does not affect what teammates see. Returns the defaults when you have never changed them.
- contacts.set_column_layoutSave columns
Choose which columns you see on the contacts list and in what order — the array order IS the left-to-right order, and any column you leave out is hidden. Personal to you; teammates' lists are unaffected. Unknown keys are dropped and 'name' is always kept (it is the link into each record), so the list can never be left unusable. Call contacts.get_column_layout for the valid keys.
- contacts.reset_column_layoutReset columns
Forget your saved column choices for the contacts list so it goes back to the default columns (name, contact details, source, company, tags, owner, lifecycle, customer status, lifetime value, and first-added date). Personal to you, and affects only which columns are displayed — no contact data is changed or deleted.
- contacts.find_by_facebook_idFind a contact by Facebook ID
Look up the org's contact by the stable Facebook person/friend id supplied by Friender or another integration. Returns null when no contact holds that id.
- contacts.find_by_phoneFind a contact by phone number
Look up the org's contact at a phone number, matched on any spelling of it ('4098930064', '+14098930064' and '(409) 893-0064' are the same person). Returns null when nobody holds that number. Use this before creating a contact from a call or a text.
- contacts.createCreate a contactconfirm
Create a contact. Requires at least a name, business name, or email. Email addresses and normalized phone numbers are unique within the organization: if another contact already holds either identity, this fails with a conflict naming that contact rather than creating a duplicate — update that one instead. SECOND-ORDER EFFECT: a successful create fires the org's 'Contact created' automations, so a 'welcome every new lead' workflow can send this person a real email or SMS on the org's own Mailgun/Twilio account, at the org's own cost, with no further step.
- contacts.validate_emailValidate nowconfirm
Immediately validate this contact's current email address through the workspace's configured Mailgun or NeverBounce account. This makes a real provider request that may consume a paid validation credit, then stores the result beside the contact's email. It does not send email or change the address.
- contacts.updateEdit a contact
Update fields on an existing contact. Omitted fields are left alone; an explicit null clears one. Moving an email address or phone number onto this contact fails with a conflict when it is already someone else's in this organization.
- contacts.deleteDelete a contactconfirm
Permanently delete a contact and everything that cascades off them — tags, notes and the whole activity timeline. This cannot be undone.
- contacts.bulk_deleteDelete selected contactsconfirm
Permanently delete every listed contact, along with their tags, notes and activity timelines. This cannot be undone.
- contacts.set_lifecycleSet lifecycle stageconfirm
Move every listed contact to a lifecycle stage (lead, active, customer, churned). The stage change itself is reversible — set it back to change your mind — but its SECOND-ORDER EFFECT is not: a database trigger enqueues a 'Lifecycle changed' automation event for EVERY contact in the list, so moving 200 contacts can start 200 automation runs that send real email and SMS to real people, billed to the org's own Mailgun/Twilio account. Setting the stage back does not unsend those.
- contacts.set_feed_visibilityHide from Activity Log
Mute or unmute contacts in the dashboard's org-wide Activity Log — the same switch on a contact's Activity tab. Hidden contacts' events (calls, texts, website visits, and the rest) stop appearing in that shared feed for everyone, while their own timeline and every other screen are untouched. Fully reversible; set `hidden` false to show them again.
- contacts.mark_customerMark as customer
Mark every listed contact as a customer, or clear it. `is_customer` is a durable fact meaning they have bought from us — it is SEPARATE from the lifecycle stage (a churned contact can still be a customer) and it survives churn. Marking sets `customer_since` to now and records the source as 'manual'; already-marked contacts are left untouched. Clearing removes the flag and its date. Reversible, changes nothing outside the CRM. Note the Stripe sync also marks customers automatically when a real purchase is seen, and a later sync will re-mark anyone you clear if it still finds a paid charge or active subscription for them.
- contacts.importImport contactsconfirm
Bulk-create contacts from a list of records — the machine intake path (CSV/lead imports). Unlike contacts.create, a row whose email or phone already matches an existing contact does NOT error and does NOT duplicate: it is reported as matched and skipped. Nothing existing is overwritten. SECOND-ORDER EFFECT: the import deliberately suppresses the 'Contact created' trigger so a 500-row file cannot send 500 welcome messages — but `tag_ids` and `list_ids` do NOT go through that suppression. Every tag applied and every list joined fires its own 'Tag added' / 'Added to list' automation event, per contact, so importing 500 rows with one tag can start 500 automation runs that send real email and SMS on the org's own Mailgun/Twilio account. Import without tags first if you are not certain what is listening.
- contacts.import_csvImport contacts from a fileconfirm
Queue a durable background contact import from raw CSV text — the same job the Contacts screen starts. Returns immediately with a job id; the import continues if the caller disconnects and progress is available through contacts.list_import_jobs. The first row must be headers; obvious contact columns are matched automatically and explicit mapping can override them. A full_name column is split at the first word into first_name and the remaining last_name. Street, city, state, and postal-code columns populate the structured contact address. Existing contacts are matched by email or phone and never overwritten. SECOND-ORDER EFFECT: like contacts.import, the 'Contact created' trigger is suppressed for the batch, but `tag_ids` and `list_ids` are not — each one fires a 'Tag added' / 'Added to list' automation event per contact, so a 5,000-row file with a tag on it can start 5,000 automation runs that send real email and SMS at the org's own provider cost. The job keeps running after the caller disconnects, so there is no way to call it back.
- contacts.list_import_jobsView contact imports
List recent CSV contact-import jobs for this workspace, including queued/running/completed/failed state, rows processed, contacts created, existing contacts matched, skipped rows, and any terminal error. Read-only and safe to poll.
- contacts.list_duplicatesList duplicate phone numbers
DEPRECATED — the Duplicates tab it was built for no longer exists, and neither does the problem: the `contacts_absorb_duplicate` trigger folds a duplicate into the surviving contact the moment it is written, so this always returns empty in practice. Kept only so a machine can verify that. It lists contacts in this org that share a phone number, grouped, oldest first inside each group; read-only. A non-empty result means two contacts were created on the same number simultaneously, neither insert able to see the other — run contacts.merge_duplicates to repair that one case.
- contacts.merge_duplicatesMerge duplicate contactsconfirm
DEPRECATED — the Duplicates tab it was built for no longer exists, and the database now does this on its own: `contacts_absorb_duplicate` merges a duplicate as it is written, so there is normally nothing for this to do. Kept as the repair for the one case the trigger cannot see: two simultaneous inserts of the same new number, neither transaction able to see the other's row. It permanently collapses every set of contacts in this org that share a phone number down to one. The newest record of each set survives and absorbs the others — their calls, messages, tasks, invoices, notes, tags and list memberships all move onto it, and any field it was missing is filled in from the older record. The older rows are then DELETED and their contact ids stop resolving. This cannot be undone.
- contacts.mergeMerge into one contactconfirm
Fold two or more contacts into one, for when the same person exists more than once — they messaged two of your Facebook Pages (a different Page-scoped id each time), replied on Instagram, bought under a work email and were imported under a personal one. Everything moves onto the contact you keep: every phone number and email address (kept as secondary rather than discarded), every Messenger and Instagram profile, tags, list memberships, conversations, calls, notes, deals, invoices and automation history. The kept contact fills any blank field from the others, takes the earliest first-seen date and the source that came with it, joins their notes, and stays a customer if any of them was one. The other contacts are then PERMANENTLY DELETED and their contact ids stop resolving — links and integrations pointing at them will 404. This cannot be undone. Nothing is sent and there is no charge.
- contacts.list_tagsList tags
List the org's contact tags, with the colour each one renders in.
- contacts.create_tagCreate a tag
Create a contact tag. Tag names are unique per organization — creating one that already exists fails.
- contacts.update_tagRename a tag
Rename a tag or change its colour. Every contact carrying it is updated at once.
- contacts.delete_tagDelete a tagconfirm
Permanently delete a tag and strip it from every contact that carries it. This cannot be undone; the contacts themselves are kept.
- contacts.add_tagsTag contactsconfirm
Add one or more existing tags to one or more contacts. Idempotent — a tag a contact already has is left alone, and fires nothing. SECOND-ORDER EFFECT: every tag that is genuinely new on a contact enqueues a 'Tag added' automation event, so tagging 200 contacts can start 200 automation runs that send real email and SMS to real people, billed to the org's own Mailgun/Twilio account. Removing the tag afterwards does not unsend them. 'Tag added' is the single most common automation trigger in this product, so assume something is listening.
- contacts.remove_tagsUntag contactsconfirm
Remove one or more tags from one or more contacts. The tags themselves are kept — use contacts.delete_tag to remove a tag entirely. SECOND-ORDER EFFECT: each removal enqueues a 'Tag removed' automation event, which can start workflows that message real people on the org's own Mailgun/Twilio account, and untagging can also drop contacts out of tag-based audiences and stop campaigns aimed at them.
- contacts.list_fieldsList custom fields
List the org's custom-field definitions — the keys, labels and types that the `custom` blob on contacts, companies and deals is made of.
- contacts.create_fieldCreate a custom field
Define a new custom field on contacts, companies or deals. The storage key is derived from the label and is unique per record type — a second field with the same derived key fails.
- contacts.create_fieldsCreate custom fields for unmapped columns
Create several contact custom fields in one operation, primarily for CSV headers that have nowhere to map. Existing contact fields with the same derived key are returned instead of duplicated; no contact values are changed.
- contacts.update_fieldEdit a custom field
Change a custom field's label, type, choices or required flag. The storage key never changes, so values already recorded stay attached.
- contacts.delete_fieldDelete a custom fieldconfirm
Permanently delete a custom-field definition. The app stops showing and collecting it; values already stored in each record's `custom` blob become unreachable. This cannot be undone.
- contacts.add_noteAdd a note
Add a free-text note to a contact's, company's or deal's activity timeline. Attach it to at least one of them.
- contacts.list_activityOpen the activity timeline
Read the activity timeline — notes, calls, texts, emails, tasks, automation steps, website visits and pipeline stage changes, newest first. Name a contact, company or deal for that record's timeline, or omit all three for the WHOLE workspace's activity feed (the same stream the dashboard's Activity Log widget shows). Read-only.
- contacts.send_smsText contactsconfirm
SEND A REAL TEXT MESSAGE to each listed contact from one of the org's active Twilio numbers. Costs money per message and reaches real people immediately; there is no undo. `{{token}}` merge fields are rendered per contact and each text lands in that contact's own conversation. Contacts with no phone number are skipped.
- contacts.send_emailEmail contactsconfirm
SEND A REAL EMAIL to each listed contact through the org's own Mailgun account. Reaches real inboxes immediately and cannot be recalled. Subject and body both render `{{token}}` merge fields per contact, and each email lands in that contact's own conversation. Contacts with no email address are skipped.
- contacts.enroll_in_automationAdd contacts to automationconfirm
Start a durable automation run for each listed contact. The first step begins immediately, waits resume later, and later steps may send real texts, emails, voicemails, calls, or API requests at the org's expense. Paused automations may be started manually; pausing only prevents event-triggered enrollment.
- contacts.bulk_emailEmail many contacts at onceconfirm
Immediately queues a personalized email to every listed contact that has an email address, each into their own conversation thread — real email, sent through this workspace's own connected provider (Mailgun/Resend) and billed to it. Subject and body support {{merge_fields}} and are rendered per contact. Send from one address, or from an email POOL to rotate across several. The send runs as a background job so it can be paced, watched, paused or stopped; pass start_at to schedule it for later instead of sending now. Through a pool, the members' warmup allowance is a HARD ceiling on today's volume whatever pace is requested — the job sends what the ramp permits and resumes after midnight UTC, so a large list cannot burn a set of new mailboxes on day one. Contacts without an email address are skipped, not failed.
- contacts.bulk_smsText many contacts at onceconfirm
Immediately queues a personalized SMS to every listed contact that has a phone number, each into their own conversation thread — real texts, sent and billed through this workspace's own Twilio account. The body supports {{merge_fields}} and is rendered per contact. The send runs as a background job so it can be paced, watched, paused or stopped; pass start_at to schedule it for later instead of sending now. Contacts without a phone number are skipped, not failed.
- contacts.run_actionsRun actions on contactsconfirm
Run a list of shared-registry actions against each listed contact — the same actions the contacts bulk bar and a single contact's 'Reach out' panel offer. Actions can text, email, drop a ringless voicemail, place an outbound IVR, AI-agent or sales-bridge call, enroll in a campaign, send an invoice, move a deal, create a task, call a webhook, or delete the contact — so this can spend money, reach real people, and destroy data depending on what you pass. Runs immediately by default; pass start_at to schedule it for later, or a per_minute/per_hour/per_day cap to pace it — either turns the run into a background job you watch with bulk_jobs.get.
- contacts.lookup_line_typeDetect line typeconfirm
Queue a Twilio Lookup for each listed contact's phone number to learn whether it is mobile, landline or VoIP. Twilio BILLS the org per number looked up. Results land asynchronously in the shared line-type cache, so numbers already known cost nothing and are not re-queued. Returns how many NEW paid lookups were queued.
- contacts.summarizeSummarize a contactconfirm
Generate a short situational summary plus two or three next-best actions for a contact, grounded in their profile, deals and recent activity. It changes no data, but it SPENDS MONEY: the request is billed to the workspace's own OpenRouter key, which must be connected. Same standard as contacts.validate_email, which is gated for a paid validation credit.
- contacts.draft_follow_upDraft a follow-upconfirm
Draft a ready-to-send follow-up message body for a contact, grounded in their history. Returns the text only — it does NOT send anything; pass it to contacts.send_sms or contacts.send_email to actually reach them. It SPENDS MONEY: the request is billed to the workspace's own OpenRouter key.
- contacts.ai_contextGet AI context
Assemble everything this workspace knows about one contact into a single AI-ready bundle for generating a page, email, or reply personalized to this exact person: their profile and custom fields, tags, open deals and tasks, recent messages, their website behavior (first-touch source, page-view totals, recent pages viewed), and their call history INCLUDING past-call transcripts and AI-call summaries. Also returns `prompt` — a ready-to-paste plain-text briefing, the same caller file the AI phone agent uses — so a server can drop it straight into a model prompt. Read-only and spends NO AI credit (it only reads stored data). The response is SENSITIVE: it contains call transcripts and private notes, so treat it exactly like the contact record itself and never expose it to an unauthenticated browser. A `context_token` can also name a contact owned by a DIFFERENT workspace — an affiliate who sent their own contact to this workspace's page — and that only resolves while the owning workspace has contact-context sharing switched on for this one; revoking it takes effect on the next call.
- contacts.list_lifecyclesList lifecycle stages
List the lifecycle stages this organization can assign to contacts, in display order. Keys are stable values used by filters and automations; labels are the editable wording people see.
- contacts.create_lifecycleCreate lifecycle stage
Add a lifecycle stage this organization can assign to contacts. This changes CRM configuration only and does not move any contacts.
- contacts.rename_lifecycleRename lifecycle stage
Change the visible name of a lifecycle stage while preserving its stable key, existing contact assignments, filters, and automations.
- contacts.delete_lifecycleDelete lifecycle stageconfirm
Permanently remove a custom lifecycle stage, but only when no contacts still use it. System stages cannot be removed. This cannot be undone.
- contacts.set_sender_defaultsSet contact sender defaults
Choose the workspace email identity and Twilio phone number normally used when contacting one CRM contact. Either value can be cleared to resume workspace defaults; sends nothing.
deals
- deals.listList deals
List and search deals, newest first. Filter by pipeline, stage, status, owner, contact, or company, and search deal titles with `query`. Values come back as integer cents.
- deals.getOpen a deal
Fetch one deal by id, with all of its fields including custom fields.
- deals.createCreate a deal
Create a deal on a pipeline board. Only a title is required: without pipeline_id it lands on the org's default pipeline, and without stage_id it goes in that pipeline's first stage. The status is derived from the stage — creating straight into a won/lost stage closes the deal.
- deals.updateEdit a deal
Update any field on a deal — title, value, currency, expected close date, links, custom fields, stage, or status. Omitted fields are left alone. Moving it with stage_id re-derives the status from that stage; passing status explicitly wins and stamps or clears closed_at accordingly. Changing the stage also writes one stage-change entry on the deal's timeline and the workspace Activity Log, recording the previous and new stage and naming the caller honestly — an API key is recorded as the API, not as a person.
- deals.moveMove a deal to another stage
Drop a deal card into a stage of its own pipeline — the drag gesture on the board. Without `position` it lands at the bottom of the target column; with one it lands at that slot and the rest of the column shifts down. Moving to a new stage re-derives the deal's status from that stage's outcome (won/lost stages close the deal and stamp closed_at) and logs a stage-change entry on the deal's timeline. Passing the stage the deal is already in just reorders it inside that column — no status change, no timeline entry.
- deals.mark_wonMark a deal won
Close a deal as won and stamp its close time. If the pipeline has a stage flagged as won, the deal is moved into that column and a stage-change entry is logged, exactly as dragging it there would.
- deals.mark_lostMark a deal lost
Close a deal as lost and stamp its close time. If the pipeline has a stage flagged as lost, the deal is moved into that column and a stage-change entry is logged.
- deals.mark_abandonedMark a deal abandoned
Close a deal as abandoned (walked away, not a loss) and stamp its close time. No stage carries an 'abandoned' flag, so the deal keeps its current column but stops counting as open.
- deals.reopenReopen a deal
Put a closed deal (won, lost, or abandoned) back to open and clear its close time. Pass stage_id to also move it back into a working column — otherwise it stays where it is, which may be a won/lost column.
- deals.assignAssign a deal owner
Set the org member who owns a deal, or pass owner_id=null to leave it unassigned. The user must already be a member of this organization.
- deals.linkLink a deal to a contact or company
Attach a deal to a contact and/or a company so it shows on that record, or pass null to unlink. Supply at least one of contact_id or company_id; both must belong to this organization.
- deals.deleteDelete a dealconfirm
Permanently delete a deal. DESTRUCTIVE: its activity timeline (notes, stage changes) is deleted with it, and any task pointing at it loses the link. The contact and company survive. Cannot be undone.
tasks
- tasks.listList tasks
List the organization's tasks, newest first. Optionally filter by status, priority, assignee, or the contact/deal a task hangs off, and search titles and descriptions.
- tasks.getOpen a task
Fetch one task by id, with all of its fields.
- tasks.createCreate a task
Create a task. Only a title is required; link it to a contact or deal to have it show on that record's timeline.
- tasks.updateEdit a task
Update any field on an existing task. Omitted fields are left alone. Setting status to 'done' stamps the completion time automatically.
- tasks.completeMark a task done
Mark a task complete (or reopen it with done=false). Same as ticking the checkbox in the app.
- tasks.deleteDelete a taskconfirm
Permanently delete a task. This cannot be undone.
conversations
- conversations.listList conversations
List the inbox's conversation threads, most recent activity first. Filter by the inbox's own tabs (unread, needs reply, starred), by channel, Facebook Page or linked Meta asset, status, assigned teammate, whether nobody owns it yet, or contact, and search subjects and last-message previews. Each thread reports whether it is unread, which way the last message went, and when it was starred. This only reads conversations — it sends nothing and does NOT mark anything read.
- conversations.getOpen a conversation
Open one thread and read its history. Returns the conversation, the contact, and — exactly like the inbox — EVERY message exchanged with that contact across all channels and threads plus their calls, merged chronologically oldest to newest, not just this thread's own messages. By default it returns as much of that history as one answer can hold: the newest 700 timeline items, messages and calls counted together. A long-lived contact can exceed that, so the result always reports `has_older` and, when there is more, a `next_before` cursor: pass it back as `before` to read the page immediately older than the one you just got, and keep going until `has_older` is false. Every page is one unbroken run of history, so following the cursor to the end reads the whole thread with nothing skipped and nothing repeated. `limit` returns only the newest N items instead — the same tail-first window the inbox itself opens on. Read-only; nothing is sent, marked read, or changed.
- conversations.latestOpen latest messages
Open one inbox thread for a fast mobile view. Returns the conversation and only its newest messages, newest first, so a phone can start at the latest reply without downloading the contact's complete cross-channel history. Read-only.
- conversations.sync_metaResync messages
Check Facebook or Instagram for messages missing from one existing inbox thread and import up to the latest 500. This repairs missed webhooks, including replies written directly in Facebook or Instagram. It does not send anything or alter provider data.
- conversations.recentRecent Communication
DEPRECATED — use `communications.list`, which this now calls and which adds direction/type filters, paging and the archive. Returns the newest entries of the communication log (calls, texts and emails merged, newest first) — the same rows the dashboard's Recent Communication column shows. Read-only.
- conversations.startStart a conversationconfirm
Open a new SMS or email thread with a contact. If `body` is supplied it is SENT IMMEDIATELY as the first message — a real text or email leaves the org's own Twilio, Mailgun, or Resend account, reaches the recipient, and bills the tenant. Leave `body` empty to open an empty thread without sending anything. Merge tokens like {{first_name}} are rendered against the contact.
- conversations.assignAssign a conversation
Assign a thread to a teammate, or pass assigned_to = null to leave it unassigned. The user must be a member of this organization.
- conversations.set_statusSet conversation status
Move a thread between open, snoozed, and closed — the Status section of the inbox's Manage menu. Use it to close a resolved thread or reopen a closed one; nothing is deleted either way.
- conversations.inbox_countsCount the inbox
Count how much is waiting in the inbox right now: threads nobody has read, threads whose last message came from the contact and so are waiting on a reply, and threads a teammate starred. These are the three numbers on the Conversations tabs. Read-only — it changes nothing and marks nothing read.
- conversations.mark_readMark read
Clear the unread badge on one or more conversations, exactly as opening them in the inbox does. Read state is shared across the whole workspace — a team inbox, not a personal one — so this clears the badge for every teammate, not just the caller. Nothing is sent and no message is changed.
- conversations.mark_unreadMark unread
Put conversations back in the unread pile so they badge again — the inbox's Mark unread button, for a thread you looked at but did not deal with. Shared across the workspace, so every teammate sees it return. A thread nobody has ever written into has nothing to be unread about and is left alone. Nothing is sent.
- conversations.starStar this conversation
Star or unstar conversations so they collect on the inbox's Starred tab — the workspace's own shortlist of threads worth coming back to. Visible to everyone in the workspace. Nothing is sent and the thread is not otherwise changed.
- conversations.mark_all_readMark all readconfirm
Clear EVERY unread conversation in the workspace at once — the inbox's Mark all read button. This is not undoable: which threads were unread is not recorded anywhere afterwards, so anything nobody had got to yet stops badging for the whole team. Use it for a deliberate inbox-zero, not as a way to tidy up before reading. Nothing is sent and no message is deleted.
- conversations.take_overTake over automationconfirm
Pause every automated responder on this conversation so a real teammate can handle it without the bot speaking over them. Active conversation workflow runs are canceled; no customer message is sent.
- conversations.resume_automationResume automationconfirm
Release a human takeover and allow future automated replies and conversation workflows in this thread again. This does not restart canceled runs, but later inbound messages can trigger real outbound messages.
- conversations.deleteDelete conversationconfirm
Permanently delete one conversation thread and every message in that thread. The CRM contact and their call history are kept. This cannot be undone.
- conversations.summarizeSummarize a thread
Summarize a conversation into a few bullets — what the customer wants, the key facts, and the next step. Grounded only in that thread's own messages. Runs on the org's own OpenRouter connection and fails with a plain message when AI isn't connected. Reads only; nothing is sent or saved.
- messages.listList messages
List individual SMS and email messages, newest first. Narrow to one conversation or contact, or filter by channel, direction, or delivery status to find what failed. Use conversations.get instead to read a thread in order.
- messages.sendSend a replyconfirm
SENDS A REAL MESSAGE. Replies on an existing SMS, email, Facebook Messenger, Instagram DM, or WhatsApp thread. SMS/email use the org's own billable Twilio, Mailgun, or Resend account; Meta replies use the connected Page or WhatsApp Business number and are limited to Meta's 24-hour messaging window. It reaches an actual person with no draft or undo. Merge tokens like {{first_name}} are rendered against the contact before sending. Files from the workspace media library can be attached — note that attaching one to an SMS makes it an MMS, which the org's carrier bills at a higher rate than a text.
- messages.resendSend it againconfirm
SENDS A REAL MESSAGE. Takes one outbound SMS or email that failed and sends the same text to the same recipient again, through the workspace's own billable Twilio, Mailgun or Resend account. It reaches an actual person with no draft or undo, and it is billed again — the original attempt may already have been charged for. This is the honest way to confirm a settings fix worked: after a country is switched on in Twilio's console, a text that came back 21408 either goes through now or comes back with the same refusal. Only outbound messages that actually failed can be resent, so a delivered message cannot be duplicated through this.
- messages.send_whatsapp_templateSend approved templateconfirm
SENDS A REAL WHATSAPP MESSAGE. Sends a live Meta-approved template on an existing customer-initiated WhatsApp thread, including after the 24-hour reply window. The connected workspace is billed by Meta; delivery reaches a real person immediately with no draft or undo. Chirply re-checks the exact template language and current APPROVED status, the workspace phone DNC and WhatsApp opt-out lists, and durable WhatsApp consent plus the recipient's valid stored timezone and local messaging hours for marketing templates before every send.
- messages.assist_replyDraft, improve, or retone a reply
The composer's AI buttons. mode='draft' writes the next reply from the thread so far, 'improve' polishes the draft you pass in, and 'tone' rewrites it in the requested tone. Returns TEXT ONLY — nothing is sent; pass the result to messages.send when the human approves it. Runs on the org's own OpenRouter connection.
- conversations.list_autorespondersList autoresponders
List the org's named, phone-attachable autoresponders and the number of keyword/default cases inside each one.
- conversations.get_autoresponderOpen an autoresponder
Fetch one named autoresponder with its ordered keyword cases, default fallback, replies, direct actions, and attached automations.
- conversations.create_autoresponderCreate an autoresponderconfirm
Create a named autoresponder containing ordered keyword/default cases. Once active and attached to a number, matching cases can SEND REAL MESSAGES, modify CRM data, and PLACE BILLABLE CALLS such as sales bridges with no human in the loop. Create it inactive to stage it safely.
- conversations.update_autoresponderEdit an autoresponderconfirm
Replace or update a named autoresponder's ordered cases. Changes take effect on the next inbound message; active cases may immediately send real messages, change CRM data, or place billable calls.
- conversations.toggle_autoresponderActivate or pause an autoresponderconfirm
Flip an auto-reply rule's Active checkbox. Activating it arms real automatic sends on the next matching inbound message; pausing it stops them without deleting the rule.
- conversations.delete_autoresponderDelete an autoresponderconfirm
Permanently delete an auto-reply rule and all of its keyword cases, replies and actions. Inbound messages on any phone number it was attached to stop getting an automatic reply immediately. Messages already sent are unaffected, but the rule itself cannot be recovered — pause it with conversations.toggle_autoresponder instead if you may want it back.
campaigns
- campaigns.generate_copyGenerate copy
Generate or rewrite one email-campaign content block from a plain-language instruction. Returns draft text only; it does not save or send anything. Uses and bills the workspace's own OpenRouter account.
- campaigns.generate_imageGenerate image
Generate an original campaign image from a prompt, store it in the workspace's durable R2 asset storage, and return its public URL. Nothing is sent. Uses and bills the workspace's own OpenRouter account.
- campaigns.listList broadcasts
List the organization's broadcast campaigns, newest first, each with its recipient, sent, delivered, opened and clicked counts. A campaign is a one-off blast on a single channel — email, SMS, an approved WhatsApp template, ringless voicemail, outbound IVR or AI call. Archived campaigns are hidden unless asked for, matching the Campaigns page.
- campaigns.getOpen a broadcast
Fetch one campaign with its message, its saved audience spec, and its delivery counts — the whole composer in one payload.
- campaigns.createCreate a broadcast
Create a draft broadcast campaign and seed it with an empty audience, exactly like the New campaign dialog. A campaign sends ONE message ONCE — for a multi-step follow-up sequence with delays between messages, build an automation instead. Nothing is sent here: a draft has to be given content, an audience, and then sent or scheduled.
- campaigns.updateEdit broadcast details
Rename a campaign or change its sender overrides (the From email address, or the phone number SMS steps send from). Omitted fields are left alone. Content, audience and schedule have their own capabilities.
- campaigns.duplicateDuplicate a broadcast
Copy a campaign — its message, channel, audience, send window and throttle — into a new draft named "<name> (copy)". The copy has no recipients and sends nothing until it is sent or scheduled. This is how a sent campaign is edited: duplicate, then change the copy.
- campaigns.archiveArchive a broadcastconfirm
Archive a campaign so it disappears from the Campaigns list. Its recipients, send ledger and analytics are kept — this is the app's way of removing a campaign; there is no hard delete.
- campaigns.set_messageSave broadcast content
Write the one message this broadcast sends, and pick which channel it goes out on. Nothing is sent by saving — this only stores the content. Only draft or paused campaigns can be edited. A campaign sends a single message; for a multi-step sequence with delays, build an automation instead.
- campaigns.save_stepSave broadcast content (deprecated)
DEPRECATED — use `campaigns.set_message`. Campaigns are one-off broadcasts now: they carry a single message, so there are no steps to order. This still writes that message, and ignores step_id, delay_amount and delay_unit. For a multi-step sequence with delays, build an automation.
- campaigns.delete_stepDelete a broadcast stepconfirm
DEPRECATED and no longer possible. A campaign is a one-off broadcast carrying exactly one message, so there are no steps to remove — clear the message with `campaigns.set_message`, or archive the campaign. Multi-step sequences live in automations.
- campaigns.set_audienceChoose the audience
Replace a campaign's audience spec — who it will go to. The spec is resolved to actual contacts only at send/schedule time, so this write sends nothing. Supplying manual_contact_ids overrides the tag/lifecycle/search filters entirely. Use campaigns.preview_audience to see how many people it matches first.
- campaigns.preview_audiencePreview the audience count
Count how many contacts a campaign would actually reach — channel-reachable and not suppressed — without saving or sending anything. Defaults to the campaign's saved audience; any field you pass overrides that field for the preview only.
- campaigns.preview_voicePreview combined messageconfirm
Hear what a ringless-voicemail or outbound-IVR broadcast will actually sound like: the personalized spoken introduction, rendered with real ElevenLabs speech, followed by the pause and the prerecorded audio the contact hears next. COSTS MONEY — each preview is a real text-to-speech render on the workspace's OWN ElevenLabs account and spends a small number of its credits, so don't call it in a loop. Nothing is saved to the campaign, and no call, voicemail or message reaches anybody. Returns the spoken introduction as inline base64 MP3 (roughly 100–500 KB — pass include_audio=false if you only need to confirm the render worked and read back what will be spoken), plus a short-lived playback link for the recorded tail.
- campaigns.set_scheduleSave the sending window
Set a campaign's timezone, quiet-hours window, allowed weekdays and per-minute throttle. This governs WHEN queued messages go out; it does not start a send. Replaces the whole schedule — omitted fields fall back to their defaults, matching the Schedule tab.
- campaigns.send_nowSend the broadcast nowconfirm
SENDS FOR REAL, IMMEDIATELY, to real people. Resolves the saved audience and starts delivering through the org's OWN Mailgun, Twilio, or connected Meta WhatsApp account — potentially thousands of billed emails, texts, approved WhatsApp templates, voicemail drops, or phone calls. WhatsApp enrolls only real customer-initiated threads with durable opt-in, no DNC/suppression, a currently approved template language, and a policy-valid recipient timezone; Meta can bill every accepted template. Voice channels place actual outbound calls. There is no undo; campaigns.pause/cancel can stop only work not already accepted. Campaign-engine channels send a bounded first wave inline and queue the rest; voicemail/call channels use their paced dispatchers. Requires saved content and a non-empty eligible audience, and refuses a campaign that already sent.
- campaigns.scheduleSchedule the broadcastconfirm
COMMITS A REAL SEND at a future time. Snapshots the eligible audience now and sets the start time; when it arrives the dispatcher contacts every recipient through the org's OWN Mailgun, Twilio, or connected Meta WhatsApp account — potentially thousands of billed emails, texts, approved WhatsApp templates, voicemail drops, or phone calls, unattended. WhatsApp requires a currently approved template plus a real customer-initiated thread, durable opt-in, no DNC/suppression, and a policy-valid recipient timezone; eligibility is checked again before each provider call and Meta can bill each accepted template. Use campaigns.cancel before the start time to stop queued work. Requires saved content, a non-empty eligible audience, and a future time.
- campaigns.pausePause a sending broadcast
Stop a campaign that is mid-blast. Recipients already sent to keep their messages; everyone still queued stays queued until it is resumed. Only a campaign in 'sending' can be paused.
- campaigns.resumeResume a paused broadcastconfirm
RESTARTS A REAL BLAST. Puts a paused campaign back into 'sending' so the dispatcher immediately continues delivering to every recipient still queued — real, billed emails, texts, or Meta-approved WhatsApp templates. WhatsApp consent, DNC/suppression, template approval, timezone, and local-window eligibility are checked again before each provider call, but Meta can bill every template it accepts. Only a paused campaign (including one paused for lack of funds) can be resumed.
- campaigns.cancelCancel a broadcastconfirm
Halt a campaign for good: every pending and in-flight recipient stops immediately and nothing more goes out. Messages already delivered cannot be recalled, and a canceled campaign cannot be sent again — duplicate it instead.
- campaigns.enroll_contactsEnroll contacts in a broadcastconfirm
ENQUEUES REAL MESSAGES. Adds specific contacts to a campaign's recipient list with delivery due immediately, the same as the bulk 'Enroll in campaign' action on the contacts list. If the campaign is sending, they can receive it on the next dispatch tick — real, billed email, SMS, or an approved WhatsApp template. WhatsApp contacts are enrolled only when the selected business number/template is valid and the person has a real initiated thread, durable opt-in, no DNC/suppression, and a policy-valid timezone; eligibility is checked again before the Meta call. Already-enrolled contacts are skipped.
- campaigns.list_recipientsList broadcast recipients
The delivery table for a campaign: who is enrolled, which step they are on, when they run next, and why any of them failed. Includes the per-status counts the Recipients page shows.
- campaigns.statsBroadcast analytics
The channel-aware delivery funnel for one campaign — recipients, sent and delivered, plus email opens/clicks or WhatsApp reads where supported, and bounced, failed and unsubscribed outcomes — computed from the send ledger exactly as the Analytics page renders it.
automations
- automations.listList workflows
List the organization's automation workflows, newest first, with each one's trigger, whether it is active, and how many steps and runs it has. Other products may call these drips, nurture sequences, follow-up campaigns, or autoresponders.
- automations.getOpen a workflow
Fetch one workflow with its ordered steps and the webhook endpoint that can trigger it, matching the workflow editor page.
- automations.createCreate automationconfirm
Create a complete automation/workflow/drip/nurture/follow-up sequence in one call. This is the right action for 'when a new lead is added, send a welcome message', even if the user calls it an autoresponder or campaign. Supply steps and this builds the real editable flow, generates a useful name when omitted, and turns it on by default. Active message/call steps later reach real people and incur the org's provider charges. Omit steps to create a paused visual-canvas draft.
- automations.updateEdit workflow detailsconfirm
Update a workflow's name, description, first visual start trigger or webhook secret. Omitted fields are left alone. Changing the trigger of an ACTIVE workflow changes which real events fire it and can cause its real messages, calls or paid actions to run for a different audience.
- automations.activateActivate a workflowconfirm
ARMS A LIVE AUTOMATION. Once active, every matching event fires this workflow's steps for real — sending SMS/email, placing calls, dropping voicemails, enrolling contacts in campaigns, spending the tenant's money — with no further human approval. Check the steps before turning it on.
- automations.pausePause a workflow
Turn a workflow off. Its trigger stops firing it; the steps and run history are kept and it can be activated again. Manual runs still work while paused.
- automations.deleteDelete a workflowconfirm
Permanently delete a workflow along with its steps and its entire run history. This cannot be undone.
- automations.list_action_typesList available step actions
The shared action registry — every action a workflow step (or a call disposition, or a bulk action) can run, with its editable fields. Read this before writing steps so action_config uses the right keys.
- automations.test_api_callSend test requestconfirm
Send one real HTTPS request using the same Call an API configuration that a workflow step uses, then return the external service's HTTP status, timing, headers, and bounded response body. This does not save or run a workflow, but POST, PUT, PATCH, or DELETE may create data, trigger downstream work, spend money, or otherwise change the external system.
- automations.list_templatesList workflow templates
List this workspace's private normal-automation templates. Bot-flow starters and saved bot templates are deliberately excluded; the list omits full graphs and changes nothing.
- automations.create_from_templateBuild this flow
Create a PAUSED normal automation from a private automation template. Bot-flow templates are rejected, nothing is sent, and the new automation cannot react to live events until separately activated.
- automations.get_templateView workflow template
Return one private normal-automation template with its frozen visual graph. Bot-flow templates are not exposed; this read creates nothing and sends no messages.
- automations.save_as_templateSave as template
Save a private frozen copy of one workflow's current visual graph for reuse in this workspace. This does not activate, run, or send the workflow; later edits to the source do not change the template.
- automations.delete_templateDelete workflow templateconfirm
Permanently delete one private saved workflow template from this workspace. Existing workflows installed from it are unaffected, but the frozen template cannot be recovered; built-in Chirply starters cannot be deleted.
- automations.duplicateDuplicate workflow
Create a separate PAUSED copy of one workflow's current graph in this workspace. It copies no runs, history, legacy steps, or inbound-webhook secret and sends nothing until separately reviewed and activated.
- automations.list_trigger_typesList available triggers
Every event an automation can start on or stop on. Start and stop use the same fully wired catalog and the same optional filters. Read this before writing trigger or stop_trigger nodes with automations.set_flow. A workflow may have SEVERAL start triggers (any one begins a run) and any number of stop triggers (any matching event ends the contact's in-flight run). Read-only.
- automations.add_stepAdd a workflow step
Append an action to the end of a workflow. Adding a step does not run it; it runs on the next trigger or manual run. The action must be one the shared registry knows — see automations.list_action_types.
- automations.update_stepEdit a workflow step
Change what an existing step does. Both the action type and its config are replaced wholesale — send the complete config, not a patch.
- automations.delete_stepDelete a workflow stepconfirm
Remove a step from a workflow. The remaining steps keep their order. This cannot be undone.
- automations.move_stepReorder a workflow step
Move a step one place up or down, swapping it with its neighbour — the arrows in the step list. Moving a step at the top or bottom edge is a no-op.
- automations.set_stepsReplace all workflow stepsconfirm
Replace a workflow's entire step list in one call, in the order given. Every existing step is deleted first, so this is destructive — pass the complete sequence, not just the changes. Nothing runs until the workflow is triggered.
- automations.get_flowOpen the automation flow
Fetch a workflow's flow graph — the nodes and connections the visual builder draws, and the exact structure the runtime walks. A workflow that predates the builder is lifted from its stored steps on the way out, so this always returns a graph. Read-only.
- automations.set_flowSave the automation flowconfirm
Replace a normal automation's entire process graph — business triggers, actions, waits, branches and their connections — in one call. Facebook, Instagram and WhatsApp conversation messages live in the separate Bot Flow product. A graph may hold SEVERAL 'trigger' nodes, all leading to the same beginning, plus stop_trigger nodes that cancel an in-flight run. Destructive: pass the complete graph, not just changes. Saving sends nothing; activating only makes future matching events eligible to run real actions through the workspace's provider accounts. Blocking graphs may be saved as drafts but cannot be active.
- automations.run_for_contactRun a workflow nowconfirm
RUNS THE WORKFLOW FOR REAL, RIGHT NOW, against one contact — the 'Run manually' panel. Every step executes immediately through the tenant's own providers: real SMS and email leave, real calls and voicemails are placed, tags and deals change, and the tenant is billed. Works whether or not the workflow is active. Returns the run id and each step's outcome.
- automations.enroll_contactsAdd contacts to an automationconfirm
STARTS THE WORKFLOW FOR REAL for every contact given — the bulk 'Add to automation' action. Immediate steps can send real SMS/email/calls and incur real charges; wait steps remain scheduled and resume later. This works even while automatic enrollment is paused. Continues past individual failures and reports the tally.
- automations.list_runsList automation runs
Execution history: every time a workflow fired, with its status, how far it got, the contact it ran for and any error. Newest first. Filter by workflow or status.
- automations.get_runOpen an automation run
Fetch one run with its full context payload and error, for debugging why an automation did or didn't do what was expected.
- automations.list_node_eventsList automation node events
Read the workspace's durable visual-workflow execution ledger, including run and node starts, waits, resumes, completions, failures, replies and timeouts. Filter it to one workflow, run, node, event type or time window when diagnosing automation behavior. This is read-only and sends no messages.
invoices
- invoices.listAll invoices
List the organization's invoices and checkout pages, newest first. Filter by invoice kind (standard, product, group, ascending, plan, trial_ascending) or status, and search names. Archived invoices are hidden, exactly as in the app.
- invoices.getOpen an invoice
Fetch one invoice with its line items, pricing rules, pay-page settings and public link.
- invoices.get_public_linkCopy the invoice link
Get the public pay-page URL for an invoice, plus the iframe snippet for embedding it. The link only takes payments once the invoice is published.
- invoices.quotePrice this invoice now
Run the invoice through the pricing engine and return what a buyer would pay right now: priced lines, discount, scarcity increase, tax, total, what's due at checkout, and every future scheduled charge. This is the same calculation the public pay page and the scheduler use — never compute invoice totals yourself.
- invoices.list_line_itemsInvoice line items
List the line items on one invoice, in display order, with their unit prices in integer cents.
- invoices.list_ordersPayments (orders)
List buyers' orders across every invoice, newest first — who bought, what they owe, and what they've paid. Filter to one invoice or one order status.
- invoices.get_orderOpen a payment
Fetch one buyer's order with everything the payment page shows: totals, charges taken so far, the remaining schedule, the timeline, and the buyer's private receipt link.
- invoices.list_paymentsPayment history
List individual charges taken across the organization's invoices — succeeded, failed and refunded — newest first. Filter to one order or one status.
- invoices.list_schedulesScheduled charges
List the future charges queued against orders — payment-plan installments, a group offer's close, a standard invoice's capture date, and dunning retries. Filter by invoice, order, or status to find what's due or what has failed.
- invoices.list_eventsInvoice timeline
The activity timeline for one invoice — created, published, paid, failed, canceled — newest first.
- invoices.summaryInvoicing overview
The money view from the Invoices dashboard: total invoiced, collected, outstanding and failed, plus collections per day for the last N days. Reads real orders and cleared payments, not cached counters.
- invoices.createNew invoice
Create a draft invoice of the chosen kind. It starts empty with that kind's default pricing rules; add line items and then publish it to make its pay page live. Nothing is charged and nobody is emailed. For an ongoing subscription until canceled, use standard (one customer) or product (reusable checkout page) and add a recurring line item; plan is only a finite total split into a fixed number of installments. Other kinds: group (price drops as more join), ascending (price rises per buyer). For trial_ascending: A reusable signup link starts a separate free trial for every buyer, anchored to that buyer's enrollment time. Enrollment saves a card but charges nothing. During the initial discount window they can end the trial and pay the starting price immediately; after that, the price rises once per selected time unit in equal increments until it reaches full price at the trial deadline. Paying early cancels the deadline charge. If they do not pay early, their saved card is charged the full price when their own trial ends.
- invoices.updateEdit invoice detailsconfirm
Update an invoice's name, memo, currency, tax rate, billed contact, connected pay-page domain, pricing rules or pay-page settings. Omitted fields are left alone; `pricing` and `settings` are merged over what's stored, then normalized for the invoice's kind. Money values are integer cents. TWO THINGS HERE REACH THE PUBLIC IMMEDIATELY. (1) `pricing` and `tax_rate` re-price a LIVE pay page — if the invoice is published, the next buyer is charged the new amount with no notice. (2) `settings.headerScript` and `settings.footerScript` inject ARBITRARY JAVASCRIPT into the page where buyers type their card details, and `settings.redirectUrl` sends them anywhere after paying; nothing reviews either. Line items are edited with the invoices.add_line_item / update_line_item / remove_line_item capabilities.
- invoices.list_stripe_pricesSearch Stripe subscription prices
List every active fixed recurring Price in the Stripe account and live/test environment selected by an invoice. Use a returned price_id with Add a line item or Edit a line item to reuse that Stripe Product and Price instead of creating a new catalogue entry. This only reads Stripe and does not charge anyone.
- invoices.add_line_itemAdd a line itemconfirm
Append a line item to an invoice and recompute its cached totals. `unit_amount` is integer cents. Set item_kind to 'recurring' with an interval to bill it as a subscription line; leave it 'one_time' for a single charge. THIS RE-PRICES A PAY PAGE: once the invoice is published its page is public, so the new line is what the very next buyer is charged, and a 'recurring' line signs them up to be charged again every interval until someone cancels. No confirmation reaches the buyer — the amount on the page is simply different from then on.
- invoices.update_line_itemEdit a line itemconfirm
Update one line item's name, price, quantity, taxability, ordering or recurrence, and recompute the invoice's cached totals. Omitted fields are left alone. THIS RE-PRICES A LIVE PUBLIC PAY PAGE: if the invoice is published, the new amount is what the very next buyer is charged, immediately and with no notice to anyone. Changing item_kind to 'recurring' turns a one-off purchase into a subscription that keeps charging. People who already paid are not affected — the change only reaches buyers from now on.
- invoices.remove_line_itemRemove a line itemconfirm
Delete one line item from an invoice and recompute its totals. Orders already placed keep the price they were quoted.
- invoices.publishPublishconfirm
Publish a draft invoice so its public pay page goes LIVE and starts taking real payments on the organization's own Stripe account. Anyone with the link can then buy. Requires at least one line item.
- invoices.closeStop accepting payments
Close an invoice so its pay page stops taking new payments. Nothing is deleted and charges already scheduled against existing orders still run. Publishing it again reopens it.
- invoices.archiveArchiveconfirm
Archive an invoice: it leaves every list, its pay page stops working, and it can no longer be opened. Orders and payments are kept so its financial history remains auditable. Use permanent deletion only for invoices with no financial history.
- invoices.deleteDelete invoiceconfirm
Permanently deletes an invoice and its failed or canceled $0 checkout attempts. This destroys data and cannot be undone. Refuses to delete any invoice with a paid, refunded, active, pending, or otherwise in-flight order; archive those invoices instead.
- invoices.delete_payment_attemptDelete payment attemptconfirm
Permanently deletes one failed or canceled $0 invoice checkout attempt from the Payments list. This destroys data and cannot be undone. Any attempt with a successful or refunded payment is preserved and cannot be deleted.
- invoices.duplicateDuplicate
Copy an invoice and all of its line items into a fresh draft with a new public link. The copy takes no payments until it's published.
- invoices.sendSend the invoiceconfirm
SENDS A REAL EMAIL to a customer with a link to a published invoice's pay page, from the organization's own email provider. The invoice must be published. Give either a contact_id (uses that contact's email and renders merge fields like {{first_name}}) or an explicit `to` address.
- invoices.mark_paidMark paid outside Stripeconfirm
Record that an order's outstanding balance arrived OUTSIDE Stripe — cash, a bank transfer, a legacy invoice. Writes a real payment row for the full outstanding amount, marks the order paid, and cancels anything still scheduled against it. No card is charged; this changes the revenue record, so only use it when the money genuinely arrived.
- invoices.cancel_orderCancel an orderconfirm
Cancel a buyer's order and every charge still scheduled against it, so their card is never charged again. Payments already taken are left untouched — this does NOT refund anything.
- invoices.charge_saved_cardCharge a scheduled payment nowconfirm
CHARGES REAL MONEY. Runs a scheduled charge immediately against the card the buyer already authorized, on the organization's own Stripe account — the same off-session charge the invoice scheduler makes when an installment, a group close or a capture date comes due. Use it to collect early or to retry a failed installment. The amount comes from the schedule row; it cannot be changed here.
activity
- activity.summaryActivity summary
The Activity Calendar's headline totals for a period: emails sent, opened, clicked and bounced; unsubscribes and new subscribers; inbound and outbound phone calls; inbound and outbound one-to-one emails; automation runs; and successful payments, refunds, disputes, customers and subscriptions across every Stripe account connected to the workspace. Money is returned in integer cents plus a formatted string, with the currency. Read-only; changes nothing.
- activity.calendarActivity calendar
The full per-day breakdown behind the Activity Calendar for a period: for each day, emails sent/opened/clicked/bounced, new subscribers, unsubscribes, inbound/outbound phone calls, inbound/outbound one-to-one emails, automation runs, and successful payments, refunds, disputes, new customers and new subscriptions across every Stripe account connected to the workspace, plus the campaigns that went out that day with their send count and open rate. Also returns the period totals and two daily trend series (subscriber growth, and email + revenue). Money is integer cents. Read-only; changes nothing.
- activity.feedActivity Log
The dashboard's live activity feed, newest first — every new contact, call and text landing, auto-reply and broadcast going out, website visit, automation, and Stripe money movement (payments received, failed and refunded; subscriptions started and canceled) across every connected Stripe account, in one stream, each event naming the contact, deal and teammate it involves where known. Website visits are grouped per visitor per sitting rather than one event per page view: each carries the pages read (newest first), how many there were, and the visitor's IP address where the tracker recorded one — personal data under GDPR, so treat it accordingly. This is the org-wide view (use contacts.list_activity for one person's timeline). Optionally narrow to certain event types — 'contacts' is newly added people and 'payments' is the Stripe bucket. Contacts a teammate has muted from the feed (contacts.set_feed_visibility) are left out here too. Reads nothing outside this org and changes nothing.
affiliate_program
- affiliate_program.overviewAffiliate program overview
Headline numbers for this workspace's own affiliate program (the program it runs for its products, not Chirply's): affiliate counts by status, clicks, referrals, referred customers, commission totals by status, and the top-earning affiliates. Optionally narrowed to one program or a start date.
- affiliate_program.list_programsList programs
List this workspace's affiliate programs — the offers it runs (reward terms, cookie window, approval and payout settings) — each with its public signup URL.
- affiliate_program.get_programOpen a program
Fetch one affiliate program with all of its settings — reward terms, tier-2 terms, cookie window, approval switches, hold days, minimum payout — plus its public signup URL.
- affiliate_program.get_signup_linkGet signup link
The public 'become an affiliate' URL for a program — the link to share anywhere you recruit affiliates. Anyone who opens it can apply to join the program (auto-approved if the program is set that way), so treat it as public.
- affiliate_program.list_affiliatesList affiliates
List the affiliates recruited into this workspace's program(s) — name, email, referral code, status, and custom rate — filterable by program and status, searchable by name, email, or code. Portal tokens are never included in lists; open one affiliate to get their portal link.
- affiliate_program.get_affiliateOpen an affiliate
Fetch one affiliate with everything a manager sees: profile and status, their shareable referral link(s), their private portal URL (a bearer link — anyone holding it sees their dashboard, so share it only with the affiliate themself), and their commission balance.
- affiliate_program.list_referralsList referrals
List the people affiliates have referred — leads and customers — with the reward terms snapshot each referral locked in at capture time. Filterable by program, affiliate, and status.
- affiliate_program.list_commissionsList commissions
List the commission ledger — one row per tier per referred payment, with amount, status, and when it clears its hold. Pending + approved rows are real money the workspace owes its affiliates. Filterable by program, affiliate, and status.
- affiliate_program.list_payoutsList payouts
List payouts to affiliates — amount, method, destination, and status. A pending payout has claimed its commissions but the money hasn't been sent yet. Filterable by affiliate, program, and status.
- affiliate_program.create_programCreate programconfirm
Create an affiliate program (a campaign) for this workspace's own products: the offer (percent or flat per sale, one-time or recurring, optionally a multi-level ladder up to 10 levels deep), cookie window, approval and payout settings. THIS COMMITS REAL MONEY: the workspace owes the stated commission on every referred sale from the moment it exists, its signup URL is publicly live immediately, and commission rates are never backdated — so an over-generous rate cannot be corrected on referrals already captured under it.
- affiliate_program.update_programEdit programconfirm
Update a program's settings — name, terms, reward (including the multi-level ladder), cookie window, approval switches, hold days, minimum payout, destination URL, or pause/resume it. Omitted fields are left alone. THIS CHANGES WHAT THE WORKSPACE OWES on every sale from now on, and the public signup and terms pages change with it. Rate changes apply to NEW referrals only — every existing referral keeps the terms it was captured under, so a rate raised by mistake cannot be walked back on referrals captured in the meantime.
- affiliate_program.archive_programArchive programconfirm
Archive a program: its signup page and referral links stop working and no new clicks, referrals, or commissions are tracked. Existing referrals keep their snapshots and commission already earned STAYS OWED — archiving stops future tracking, it does not void money. There is no unarchive in the UI, so treat this as final.
- affiliate_program.add_affiliateAdd an affiliate
Recruit an affiliate by hand (they normally join through the program's public signup link). Mints their referral code, portal link, and default tracked link. The result includes their portal URL — a private bearer link to send to that person, and only that person.
- affiliate_program.approve_affiliateApprove affiliate
Approve a pending affiliate (or reactivate a paused one): their referral links start earning attribution and their portal shows them as active.
- affiliate_program.pause_affiliatePause affiliate
Pause an affiliate: new clicks on their links stop earning attribution until they're approved again. Their existing referrals and earned commission are untouched.
- affiliate_program.ban_affiliateBan affiliateconfirm
Ban an affiliate — an outward-facing action against a real person: their portal login stops working immediately, their links stop earning, and they can't be paid out while banned. Use for fraud or terms violations; use 'Pause affiliate' for anything temporary.
- affiliate_program.set_affiliate_rateSet custom rateconfirm
Give one affiliate a personal commission percent that replaces the program's percent, or clear it back to the program rate. THIS CHANGES WHAT A REAL PERSON IS PAID. It applies to referrals they capture FROM NOW ON — existing referrals keep the terms they locked in, because rates are never backdated, so neither a raise nor a cut can be undone for anything captured while it stood.
- affiliate_program.add_referralAdd a referral
Manually credit a person to an affiliate — for deals that arrived outside the tracked links (a phone call, a conference). Snapshots the program's current reward terms onto the referral; when this person later pays, those terms decide the commission. First touch wins: if the person is already a referral, the existing attribution is returned unchanged.
- affiliate_program.record_saleRecord a saleconfirm
Record a referred payment by hand and write the commission it earns — REAL MONEY the workspace then owes its affiliate(s), one ledger row per tier, at the terms snapshot on the referral. Use for sales that happened outside connected Stripe. Supply external_ref to make retries safe: the same reference is never recorded twice.
- affiliate_program.approve_commissionApprove commission
Approve one pending commission, clearing it for payout — this confirms the workspace owes the money and lets 'Pay an affiliate' claim it.
- affiliate_program.reject_commissionReject commissionconfirm
Refuse a pending or approved commission (fraud, dispute, self-referral) — the affiliate permanently loses this money from their balance. Already-paid commissions can't be rejected.
- affiliate_program.create_payoutPay an affiliateconfirm
Create a payout for an affiliate's approved commission — a COMMITMENT TO PAY REAL MONEY to a real person. Claims their approved commissions (oldest first, whole rows only, up to the optional cap) and marks them paid. The money itself moves outside Chirply (PayPal, bank transfer); mark the payout paid once it's sent, or cancel it to release the balance.
- affiliate_program.mark_payout_paidMark payout paidconfirm
Confirm the money actually left — the PayPal send or bank transfer happened. Stamps who processed it and when, and settles the affiliate's balance so those commissions are never queued for payment again. It does not move money itself, which is exactly the risk: marking a payout paid that was never sent quietly writes off what the workspace still owes a real person, and there is no un-mark.
- affiliate_program.cancel_payoutCancel payout
Call off a payout that hasn't been sent yet: the commissions it claimed return to 'approved' and the affiliate's payable balance is restored. A payout already marked paid can't be canceled.
- affiliate_program.set_affiliate_dealSpecial dealconfirm
Give one affiliate a personal deal that replaces the campaign's standard terms — their own multi-level commission ladder, and/or their own recurring window. THIS CHANGES WHAT A REAL PERSON EARNS on referrals they capture FROM NOW ON, and never touches the past: every referral already captured keeps the terms it locked in, so a deal set by mistake cannot be reversed for anything captured while it stood. Pass null for a field to clear it back to the campaign's terms (an affiliate with no overrides simply follows the campaign, so raising the campaign later raises them too).
- affiliate_program.list_contestsList contests
List this workspace's affiliate contests — time-boxed competitions ('most new customers this month wins $500') with live standings on every affiliate's portal. Each row includes its status (draft = not visible yet, active = live and counting, ended = winners frozen, canceled = never happened), metric, window, and prizes.
- affiliate_program.get_contestOpen a contest
Fetch one contest with its full setup (metric, window, prizes) and its standings: LIVE standings computed over the contest window while it's a draft or active, or the FROZEN winners once it has ended. Names here are the affiliates' real names — this is a manager surface; the affiliates' own portals show peers masked.
- affiliate_program.create_contestNew contest
Create an affiliate contest as a DRAFT — a time-boxed competition with a metric, a window, and prizes by rank. Drafts are invisible to affiliates and cost nothing until you start them (affiliate_program.start_contest); the prizes are your own commitment to deliver, outside Chirply. Ties share a rank and every affiliate tied at a prized rank wins that prize.
- affiliate_program.update_contestEdit contest
Update a draft or active contest's setup — name, description, metric, window, or prizes. Omitted fields are left alone. An ended or canceled contest can't be edited: a result that has been announced is history.
- affiliate_program.start_contestStart contestconfirm
Take a draft contest live: it appears on every affiliate's portal leaderboard page and the standings start counting over its window. This is OUTWARD-FACING and it PROMISES A PRIZE — every ACTIVE affiliate is entered automatically, including ones who hide themselves from the always-on leaderboard, and they will see the contest and its stated reward. Starting one by accident commits the workspace to it in front of everybody.
- affiliate_program.end_contestEnd contest & announce winnersconfirm
End an active contest NOW and FREEZE the final standings as its winners, with prizes attached by rank — affiliates see the result on their portals immediately. This is permanent: an announced result never changes, even if more sales land later, and the prizes you promised are now owed to real people. Ending before the scheduled close counts only what happened up to this moment.
- affiliate_program.cancel_contestCancel contestconfirm
Call off a draft or active contest as if it never happened: no winners are computed, no prizes are owed, and it disappears from every affiliate's portal. Affiliates who were competing lose the contest they were told about — an outward-facing disappointment — and there is no undo.
- affiliate_program.get_leaderboardLeaderboard
The always-on affiliate leaderboard for a program — top active affiliates ranked by a metric over the last 30 days or all time, ties sharing a rank. Names here are real (this is a manager surface); on the affiliates' own portals every peer appears masked as first name + last initial. Affiliates who chose 'Hide me from the leaderboard' are left off this board too — it's a display surface, unlike contest standings which always count everyone.
- affiliate_program.send_payoutSend payoutconfirm
Send a pending payout down its rail RIGHT NOW — this moves real money from your own PayPal or Stripe account to the affiliate immediately. The rail is the affiliate's saved payout method unless you override it: 'paypal' sends a PayPal payout from your connected PayPal (settles within minutes), 'stripe' transfers out of your own Stripe balance into the affiliate's Stripe account instantly, and 'manual' just records that you already sent the money yourself. If the provider refuses, the payout is marked failed and the claimed commissions are released back to the affiliate's balance so nothing is silently swallowed.
- affiliate_program.set_payout_methodSet payout methodconfirm
Save how an affiliate gets paid from now on: 'paypal' (automatic PayPal payout to their saved email), 'stripe' (automatic transfer to the Stripe account they set up from their portal), or 'manual' (a human sends the money and marks it paid). THIS DECIDES WHERE REAL MONEY GOES — affiliate_program.send_payout follows whatever is stored here — and it overwrites the previous setting with no record of it. Changes future sends only; payouts already on their way are untouched.
- affiliate_program.get_payout_railsPayout rails
Check whether this workspace can pay affiliates automatically, and (optionally) where one affiliate stands: is PayPal connected, is Stripe connected, and for a given affiliate their saved payout method, PayPal email, and Stripe Connect onboarding status (none / onboarding / active / restricted — payouts only flow once it's active). The readiness card on the payouts page walks through the same prerequisites, including the one thing this can't verify from here: Stripe Connect must also be enabled on your own Stripe dashboard.
affiliates
- affiliates.get_programYour affiliate link
Get the caller's own affiliate link, referral code, and exactly what they earn at BOTH tiers — tier 1 on customers they refer directly, tier 2 on customers referred by affiliates they recruited. Also returns which campaign and rate table (everyone vs partner) they're on. Read-only; costs nothing. Every account holder is an affiliate automatically, so this always returns something.
- affiliates.set_contact_deliverySave automatic deliveryconfirm
Turn automatic affiliate lead delivery on or off for the caller's current workspace. Enabling saves the destination immediately, starts creating or matching contacts for every existing attributed opt-in and customer in the background, then keeps delivering future opt-ins and sales with source and offer tags. Newly created contacts can trigger the workspace's contact-created automations, which may send real email or SMS, place calls, or incur provider charges. Disabling stops future delivery and never deletes contacts already delivered.
- affiliates.build_linkLink to a specific page
Build the caller's affiliate link pointing at a specific page of the marketing site — the pricing page, a blog post, anything. Any page works; the referral is captured site-wide. Optionally tag it with a campaign to run on that campaign's terms. Read-only.
- affiliates.list_offersOffers & links
List every public offer the caller can promote, including their ready-to-share tracked link, the audience and promise for each funnel, every upsell/downsell in buyer order, what the customer pays at each step, and the caller's exact tier-1 payout on that payment. Read-only; creates no links in a third-party system and spends nothing.
- affiliates.statsYour affiliate earnings
The caller's affiliate numbers: how much is due to be paid out, how much is still in the clearing period, how much has been paid out all time, the split between tier-1 earnings (their own referrals) and tier-2 earnings (their team's), and how many people clicked, signed up, and became paying customers. Read-only.
- affiliates.offer_statsHow each offer is doing
Break the caller's affiliate numbers down by which of the published offers produced them — for every offer separately: visitors, total clicks, opt-ins, paying customers, opt-in rate, conversion rate, earnings per visitor, and the commission that offer has earned split into ready-to-withdraw, still clearing, already paid, reversed, and the tier-1/tier-2 split. Also names the offer that has earned the most so far. Two rows are not offers and are never named as the best: 'other' is referred traffic that landed on pages outside any funnel, such as the blog or pricing, and 'untracked' is earnings recorded before per-offer tracking existed, which cannot be traced to a funnel. Read-only; spends nothing and changes nothing.
- affiliates.downlineYour team
List the affiliates the caller recruited — the people whose sales earn them tier-2 commission — with how many paying customers each has brought in and how much each has earned the caller. Read-only.
- affiliates.leaderboardLeaderboard
The live affiliate leaderboard — who has the most paying customers, the most signups, or the most clicks, across everyone promoting the platform. Returns counts only, never anyone's earnings, and excludes affiliates who opted out. Also returns the caller's own position even when they're outside the top of the board. Use it to find who to reward. Read-only.
- affiliates.list_referralsPeople you referred
List the people the caller referred and where each one got to — signed up, paying, cancelled, or voided — along with the commission terms locked in for each. A voided referral carries the reason it earns nothing (self_referral, fraud, duplicate, dispute, other). Read-only.
- affiliates.list_commissionsYour commissions
List the caller's individual commission entries — one per payment a referred customer made, including renewals, at both tiers. Shows what the customer paid, what the affiliate earned, which tier it came from, whether it has cleared the hold period / been paid out / been reversed by a refund, and WHO each one is for: on tier-1 rows `person_email`/`person_name` is the customer who paid; on tier-2 rows it's the team member (the affiliate the caller recruited) whose sale generated the override. Read-only.
- affiliates.list_payoutsYour payouts
List the caller's payout requests and their state — requested, sending, paid, failed or cancelled. Read-only.
- affiliates.set_payout_methodSave payout detailsconfirm
Set where the caller's affiliate commission should be sent — a PayPal email address, or free-text bank details for a manual transfer. THIS IS A PAYMENT DESTINATION: it overwrites whatever was there before, with no record of the old value, and affiliates.send_payout later pays real money to exactly what is stored here. A wrong PayPal address bounces the payment at best and pays a stranger at worst, so the address must be confirmed with the person it belongs to before this is saved. Only the affiliate themselves can call it — an API key, OAuth token or installed app is refused, because a workspace credential is not the person whose money this is.
- affiliates.connect_stripeSet up with Stripe
Start (or resume) Stripe Express onboarding so the caller can be paid straight to their bank. Returns a one-time Stripe URL the person must open in a browser and complete themselves — the platform never sees their bank details, and this capability cannot finish onboarding on their behalf. Safe to call repeatedly: the Stripe account is created once and reused, and the link is short-lived so a fresh one is minted each time.
- affiliates.stripe_statusStripe payout status
Check whether the caller's Stripe Express account is ready to receive payouts. Re-reads the account from Stripe rather than trusting the stored copy, so it reflects onboarding they finished seconds ago. Read-only.
- affiliates.list_campaignsChirply affiliate campaigns
List campaigns in Chirply's own affiliate program (users earning commission for referring new Chirply customers — not a tenant's own affiliate programs), with their full rate tables: for each campaign, whether it is one tier or two, and what each audience (everyone vs partners) earns at each tier. Read-only. Available to any signed-in caller, since an affiliate is entitled to see the terms on offer.
- affiliates.overviewChirply affiliate dashboard
Everything on the caller's Chirply Affiliates page in one call — their own account in Chirply's affiliate program (earning by referring new Chirply customers, not a tenant's own affiliate programs): their link and both tiers of terms, balances, click and referral counts, their team, and their most recent referrals, commissions and payouts. Use this instead of several separate reads when summarising someone's Chirply affiliate activity. Read-only.
agent_api_tools
- agent_api_tools.listList Agent API Tools
List reusable API connections, draft/published agent tools, agent assignments, published versions, and recent redacted execution status for this workspace. Never returns stored credentials or full external responses.
- agent_api_tools.create_connectionAdd connection
Create a reusable workspace API connection. Authentication secrets are AES-256-GCM encrypted and can never be read back. This does not call the external API.
- agent_api_tools.update_connectionEdit connection
Update a reusable API connection. Omit secret to keep the encrypted credential already stored; supplying one replaces it for every tool using this connection.
- agent_api_tools.createAdd tool draft
Create a reusable Agent API Tool draft. It cannot run on calls until it is tested, published, and assigned to agents.
- agent_api_tools.publishPublishconfirm
Publish the current draft as a new immutable version. Every assigned live voice agent immediately receives this version; write tools may change a real external system without another human approval during a call.
- agent_api_tools.updateEdit tool draft
Update the editable draft of a reusable Agent API Tool. Calls keep using the immutable active version until Publish is run, so incomplete edits cannot break assigned agents.
- agent_api_tools.assignSave assignments
Replace the complete set of agents allowed to use a reusable API tool. Assigned agents receive only its active published version.
- agent_api_tools.testRun testconfirm
Immediately call an external authenticated API using this tool's current draft configuration and supplied test input. A write-classified tool can change the external system for real; the response is bounded and credentials are never returned.
ai_employees
- ai_employees.listAI employees
List the workspace's AI employees — its named, persistent AI coworkers — with each one's status (active or paused) and a plain-language summary of which feature areas it may touch and at what level.
- ai_employees.getOpen an AI employee
Fetch one AI employee: its persona, standing instructions, permission grants, status, its most recent runs (what it has actually done), and how many of its proposed actions are still waiting for approval.
- ai_employees.list_grantable_areasGrantable areas
List the feature areas an AI employee can be granted access to, with a plain-language label, how many actions each area holds, and whether it contains risky (approval-gated) actions. Use these domain names to build a valid `grants` object for ai_employees.hire or ai_employees.update.
- ai_employees.hireHire an AI employeeconfirm
Create a new AI employee: a persistent, named AI coworker that acts inside this workspace with the permissions you grant it. Once hired and given work, it operates real records — and in areas granted at the 'propose' level it can queue actions that, when approved, send real messages to real people, spend the organization's money, or delete data. Its work runs on the organization's own OpenRouter key and is billed to it; set `daily_token_budget` to cap what it may spend in a day.
- ai_employees.updateEdit an AI employee
Update an AI employee's name, title, face emoji, persona, standing instructions, permission grants, model, knowledge-base access, daily token budget, or status. Omitted fields are left alone; a supplied `grants` object REPLACES the previous grants entirely. Setting status to 'paused' stops it taking new work without losing its configuration or history; 'active' puts it back to work. Raising or removing `daily_token_budget` raises what the workspace can spend on this employee's OpenRouter usage in a day.
- ai_employees.deleteDelete an AI employeeconfirm
Permanently delete an AI employee. Its conversations, run history, and pending approvals go with it, and this cannot be undone. Anything it already did to the workspace stays done. To stop one temporarily, pause it with ai_employees.update instead.
- ai_employees.list_runsEmployee activity
The AI employees' activity feed: every run — a task handed to an employee and what came of it — newest first, with its status (running, waiting on an approval, completed, failed), a one-line summary, and how many actions it took. Optionally filter to one employee.
- ai_employees.list_approvalsApprovals inbox
List the actions AI employees have proposed and stopped on — the risky ones (sending real messages, spending money, deleting data) that never run without a deliberate authorization. Each entry names the employee, the capability it wants to run, and the exact arguments. Pending ones are resolved with ai_employees.approve, which IS manager-only.
- ai_employees.approveApprove a proposed actionconfirm
Authorize (or refuse) an action an AI employee proposed and stopped on, then let it carry on. Approving RUNS the action for real — it is one the employee flagged as irreversible, outward-facing, or costly, so it may send messages to real people, spend the organization's money, or destroy data. Refusing tells the employee no and it continues without it. Either way the employee resumes and its follow-up reply is returned.
- ai_employees.askGive an employee work
Hand a task or message to an AI employee and get its reply. The employee acts for real within its granted areas — reading and writing actual workspace records — and its turn consumes the organization's own OpenRouter credits immediately. Anything risky it wants to do (sending real messages, spending money, deleting) is NEVER run off its own decision: those come back in `pending` and appear in ai_employees.list_approvals for a second, deliberate ai_employees.approve call. Continues an existing conversation when you pass thread_id, otherwise starts one. A paused employee refuses new work.
- ai_employees.list_dutiesStanding duties
Lists the standing duties an AI employee runs on a schedule — what each one is briefed to do, how often it runs, when it next runs, and whether it has been failing. A duty runs unattended and spends the organization's own OpenRouter credits every time it fires, so this is the list of what the workspace is paying for on a timer.
- ai_employees.create_dutyAdd a standing dutyconfirm
Puts an AI employee on a schedule: from now on it runs the brief you give here, on its own, with nobody watching. EVERY run consumes the organization's own OpenRouter credits, and a duty set to run every 15 minutes runs ~96 times a day — the cost is recurring, not one-off. The employee acts for real within the areas it was hired with; anything risky (sending messages, spending money, deleting) still stops as a proposal in the approvals inbox rather than happening unattended. The run acts under the authority of the employee's supervisor, and stops working entirely if that person leaves the workspace.
- ai_employees.update_dutyEdit a standing dutyconfirm
Changes a standing duty's brief or its schedule. Tightening the schedule increases how often the workspace's own OpenRouter credits are spent — every run costs, so 'every hour' to 'every 5 minutes' is a twelvefold increase in spend, not a preference. To simply stop or restart a duty, use ai_employees.set_duty_status instead.
- ai_employees.set_duty_statusPause or resume a duty
Switches a standing duty off ('paused') or back on ('active'). Pausing is the stop button: it takes effect before the next run, costs nothing, and keeps the duty and its history intact. Resuming re-arms the schedule from now — it does not replay runs missed while it was off. Resuming also clears the failure counter, so a duty that auto-paused itself after repeated failures starts fresh.
- ai_employees.delete_dutyDelete a standing dutyconfirm
Permanently removes a standing duty. The employee stops running it. Past runs stay in the activity log, but the duty itself and its schedule are gone and cannot be recovered — pause it instead if you might want it back.
- ai_employees.run_dutyRun a duty nowconfirm
Runs a standing duty immediately instead of waiting for its next scheduled time, and returns what the employee did. This is a real run: it acts on real workspace records and spends the organization's own OpenRouter credits now. It does not change the schedule — the next scheduled run still happens as planned. Anything risky the employee wants to do comes back as a proposal in ai_employees.list_approvals rather than being carried out. A paused duty can be run this way; a paused employee refuses.
- ai_employees.budgetToday's AI spend
Reports how many tokens an AI employee has used since midnight UTC against its daily token budget, and whether that budget is currently stopping it from taking work. An employee with no budget set has no ceiling. This is the number that decides whether the next scheduled run happens, so it is worth checking when a duty has gone quiet.
ai_studio
- ai_studio.list_generationsAI Studio library
List everything the AI Studio has generated for this workspace — images, video, voiceovers, music, avatars, songs and transcripts — newest first. Read-only; generates nothing and costs nothing.
- ai_studio.get_generationCheck a generation
Fetch one generation's current state and, once finished, the URL of the file it produced. THIS IS THE POLLING CALL for video, avatar, song and dubbing jobs: keep calling it until status is "succeeded" or "failed". It also advances any of this workspace's in-flight jobs, so polling here is what moves them along. Read-only; generates nothing and costs nothing.
- ai_studio.delete_generationDelete a generationconfirm
Permanently delete a generation and the file it produced from the workspace's storage. This cannot be undone, and any page, email or campaign already pointing at that file will break.
- ai_studio.list_video_modelsVideo models
List the video models the AI Studio can use, with what each one supports — clip lengths, aspect ratios, resolutions, and whether it generates its own audio. Read-only; costs nothing. Use it to pick a model id before calling ai_studio.generate_video.
- ai_studio.list_voicesVoices
List the voices in the workspace's own ElevenLabs account, including any cloned ones. Read-only; costs nothing. Use it to pick a voice id for ai_studio.generate_voiceover or ai_studio.convert_voice.
- ai_studio.generate_imageGenerate an image
Generate an original image from a description, store it in the workspace's permanent storage, and return a public URL that can be dropped straight into a funnel page, email or campaign. Runs on the workspace's own OpenRouter key and is billed to that account (a fraction of a cent per image). Nothing is published or sent.
- ai_studio.generate_videoGenerate a videoconfirm
Generate a video clip from a written description. SPENDS REAL MONEY on the workspace's own fal.ai account — a few seconds of video typically costs several US dollars, far more than an image, and a failed or unwanted result is not refundable. Returns immediately with a generation id; rendering takes minutes, so poll ai_studio.get_generation until it succeeds.
- ai_studio.animate_imageAnimate an imageconfirm
Turn a still image into a moving video clip. SPENDS REAL MONEY on the workspace's own fal.ai account — typically several US dollars per clip. The image must be at a public https URL the provider can download, such as one from ai_studio.generate_image. Returns immediately; poll ai_studio.get_generation until it succeeds.
- ai_studio.upscale_videoUpscale a videoconfirm
Increase a video's resolution and sharpness with Topaz, optionally raising its frame rate. SPENDS REAL MONEY on the workspace's own fal.ai account, priced by the length and resolution of the input. Returns immediately; poll ai_studio.get_generation until it succeeds.
- ai_studio.remove_video_backgroundRemove a video's backgroundconfirm
Cut the background out of a video, leaving the subject on transparency — for overlaying a presenter on a slide or page. SPENDS REAL MONEY on the workspace's own fal.ai account. Defaults to WebM output because it is the only format that carries real transparency; MP4 renders the removed background as solid black. Returns immediately; poll ai_studio.get_generation.
- ai_studio.lipsync_videoLip-sync a video to new audioconfirm
Re-time a person's mouth in a video to match a different audio track — for dubbing a recording or swapping the voiceover. SPENDS REAL MONEY on the workspace's own fal.ai account. Both files must be at public https URLs. Returns immediately; poll ai_studio.get_generation.
- ai_studio.generate_avatar_portraitGenerate an avatar portrait
Generate a photorealistic portrait to use as a presenter, or edit an existing one while keeping the same face. Supply referenceImageUrl to keep a character consistent across shots rather than getting a new stranger each time. Runs on the workspace's own OpenRouter key and costs a fraction of a cent. This is the first half of making a talking-head video — feed the result to ai_studio.generate_avatar_video.
- ai_studio.generate_avatar_videoGenerate a talking-head videoconfirm
Turn a portrait plus a voiceover into a video of that person speaking the words, lips matched. SPENDS REAL MONEY on the workspace's own Replicate account, priced by the length of the audio. Make the voiceover FIRST with ai_studio.generate_voiceover — the video's length and cost follow the audio, so generating in the other order wastes money. Returns immediately; poll ai_studio.get_generation.
- ai_studio.generate_voiceoverGenerate a voiceover
Read text aloud in a chosen voice and save the audio to the library with a public URL. Runs on the workspace's own ElevenLabs account and consumes their character quota. Nothing is played to anyone or attached to a call — it just produces a file you can use in a video, an IVR prompt, or a campaign.
- ai_studio.generate_musicGenerate a music trackconfirm
Compose an original music track from a description of the style and mood. SPENDS REAL MONEY on the workspace's own ElevenLabs account — a full-length track consumes substantially more credit than a voiceover, and music generation is unavailable on the ElevenLabs free tier.
- ai_studio.generate_sound_effectGenerate a sound effect
Generate a short sound effect from a description — a whoosh, a notification chime, ambient room tone. Runs on the workspace's own ElevenLabs account and consumes a small amount of their quota.
- ai_studio.convert_voiceChange the voice in a recording
Re-perform an existing recording in a different voice, keeping the original delivery, timing and emotion. Useful for putting a consistent brand voice over a rough recording. Runs on the workspace's own ElevenLabs account and consumes their quota.
- ai_studio.clean_up_audioClean up a recording
Strip background noise, music and room echo from a recording, leaving clean speech. Runs on the workspace's own ElevenLabs account and consumes their quota. The original is left untouched — this produces a new file.
- ai_studio.transcribe_audioTranscribe a recording
Turn a recording into text, optionally labelling who spoke each line. Returns the transcript directly and saves it to the library. Runs on the workspace's own ElevenLabs account and consumes their quota.
- ai_studio.dub_audioDub into another languageconfirm
Translate a recording or video into another language, re-voiced in the original speakers' own voices. SPENDS REAL MONEY on the workspace's own ElevenLabs account, priced by the length of the media. Returns immediately; dubbing takes minutes, so poll ai_studio.get_generation until it succeeds.
- ai_studio.clone_voiceClone a voiceconfirm
Create a new voice in the workspace's own ElevenLabs account from recordings of someone speaking. The voice is created THERE, occupies one of that account's voice slots, and remains after disconnecting from this workspace. Instant cloning is restricted on some ElevenLabs plans. Only clone a voice you have the speaker's permission to use.
- ai_studio.generate_songGenerate a songconfirm
Write and perform a complete song — music and sung vocals — from a style description and optional lyrics. SPENDS REAL MONEY on the workspace's own Replicate account. Returns immediately; generation takes minutes, so poll ai_studio.get_generation until it succeeds.
ai-agents
- ai_agents.finish_web_testEnd test
Close the AI-agent rehearsal session behind a browser 'Start web call' test that never finished — the row the studio leaves open when the tab is closed, the laptop sleeps, or the network drops mid-test. A stranded session stays 'active' forever otherwise, which keeps it out of the agent's call history and out of its cost totals. Closing one stamps it completed and runs the same wrap-up (summary, outcome, pricing) a normally-ended test gets. Costs nothing and starts no call. Already-finished sessions are left exactly as they are.
- ai_agents.listList AI agents
List the workspace's AI phone agents (AI receptionists) with their voice, model, language and whether each is active.
- ai_agents.getOpen an AI agent
Fetch one AI agent with every setting — persona, greeting, goals, voice and TTS provider, model, transfer rules, guardrails — plus the brain topics it's scoped to.
- ai_agents.createCreate an AI agent
Create an AI phone agent. Only a name is required; it gets the default Polly voice and no knowledge scope, ready to configure with ai_agents.update. IT IS CREATED ACTIVE, NOT PAUSED — `is_active` defaults to true, exactly as it does when a person creates one in the app. It cannot take a call until something points at it, but the moment a number is routed to it (ai_agents.attach_to_number) or a call campaign names it, this unconfigured agent WILL answer or place real calls with no further switch to flip. Pause it with ai_agents.set_active if you are creating it to configure later.
- ai_agents.updateEdit an AI agentconfirm
Update any part of an AI agent — identity, persona and goals, greeting, voice and TTS provider, model, knowledge scope, what it's allowed to do on a call, transfer target, voicemail script and turn limit. Omitted fields are left alone. Everything is validated before anything is written. THIS SPENDS THE WORKSPACE'S MONEY AND CAN REACH REAL PEOPLE UNATTENDED, which is why it needs confirming: `use_relay=true` ADDS $0.07 PER MINUTE to every call this agent ever takes; granting `abilities` such as 'send_text', 'send_email' or 'add_to_campaign' is standing authorization for the AI to message real customers mid-call, on the workspace's own number and domain, with nobody reading the wording first; and `is_active=true` puts it back on the phone immediately.
- ai_agents.set_activeActivate or pause an AI agentconfirm
Turn an AI agent on or off. Activating it PUTS IT ON THE PHONE IMMEDIATELY: from that moment it answers every inbound call on the numbers routed to it and places the outbound calls its campaigns queue, talking to real people and billing the workspace's own Twilio and OpenRouter accounts for every minute (plus $0.07/min if it runs on ConversationRelay), with no further approval. Pausing stops both; everything it's configured with is kept either way.
- ai_agents.deleteDelete an AI agentconfirm
Permanently delete an AI agent. Refused while any call campaign is still using it — deleting mid-flight would leave every remaining recipient dialed, hearing silence, and metered. Numbers pointed at the agent fall back to the team automatically. This cannot be undone.
- ai_agents.list_voicesList agent voices
List every voice an AI agent can speak with: the curated Amazon Polly and Google catalogs (included with the platform, no extra account) and — when the workspace has connected ElevenLabs — the voices in its own ElevenLabs account. Use the returned ids with ai_agents.update. Each ElevenLabs voice carries live_call_safe: it is false for a voice the workspace CREATED itself (an instant or professional clone, a designed voice), because on a live call Twilio does the ElevenLabs synthesis from its own account and can only reach the shared ElevenLabs library — assigning one to an agent is refused. Those voices are still usable for voicemail drops and phone-menu prompts, where the audio is rendered up front with the workspace's own key.
- ai_agents.preview_voicePreview a voiceconfirm
Audition an ElevenLabs voice without placing a call: synthesizes a short fixed sample line in the workspace's OWN ElevenLabs account, which SPENDS ITS CREDITS the first time a given voice is previewed (every preview after that is served from cache, free). Amazon Polly and Google voices cannot be previewed — Twilio only exposes them at call time, and the platform holds no AWS or Google credentials — so a preview is never faked with a substitute voice. Returns a link to play the audio; the bytes themselves aren't inlined.
- ai_agents.list_modelsList agent models
List the OpenRouter models an AI agent can run on, with per-1M-token pricing, context window, and whether each supports tool calling (the voice agent requires it). Flags models measured too slow for live voice — a 'smart' model with a 16-second first token is unusable on a phone call.
- ai_agents.list_abilitiesList what an agent can be allowed to do
List every ability an AI phone agent can be granted for use mid-call — messaging, CRM updates, campaigns, automations, and appointment scheduling. Use it to build `abilities` and the optional per-ability `ability_guardrails` map for ai_agents.update. Each entry says whether it reaches a real person immediately.
- ai_agents.attach_to_numberPoint a number at an AI agentconfirm
Route a phone number's incoming calls to an AI agent. THIS CHANGES WHO ANSWERS A LIVE BUSINESS PHONE LINE: the number's inbound destination becomes 'ai_agent', so from the next call onward real callers reach the AI instead of the team's phones, and every one of those calls bills the workspace's own Twilio and OpenRouter accounts. Whatever the number rang before (the team, another agent) stops receiving those calls until it is pointed back with ai_agents.detach_from_number. Manager-only, matching the number settings page.
- ai_agents.detach_from_numberStop an AI agent answering a number
Hand a phone number's incoming calls back to the team (simulring online browser agents, then voicemail). The agent itself is untouched. Manager-only, matching the number settings page.
- ai_agents.test_callPlace a test callconfirm
Have an AI agent call a phone number right now so you can hear it — the agent page's 'Test call' button. THIS DIALS A REAL PHONE: it bills the workspace's own Twilio for the voice minutes and its OpenRouter account for the tokens the conversation uses (plus $0.07/min if the agent runs on ConversationRelay). The agent must be active and OpenRouter must be connected.
- ai_agents.call_contactHave an AI agent call a contactconfirm
Queue an AI agent to call one of your contacts on their stored phone number, dispatched immediately by the outbound call engine (do-not-contact and the wallet guard still apply; quiet hours are deliberately skipped because this is an explicit 'call them now'). THIS DIALS A REAL PERSON and bills Twilio voice minutes plus OpenRouter tokens. The agent must be active and OpenRouter must be connected.
- ai_agents.list_callsList AI calls
List calls AI agents have handled — which agent took it, which of your phone numbers it ran on, direction, who was on the other end, status, outcome, turn count and what each call cost. Use ai_agents.get_call for the full transcript.
- ai_agents.get_callOpen an AI call
Fetch one AI call in full: which agent handled it and on which of your phone numbers, the turn-by-turn transcript, every tool the agent used, the summary and outcome, any message it took or contact fields it updated, and a link to the recording when one was kept.
- brain.list_topicsList knowledge topics
List the AI Brain's topics — the folders of knowledge agents answer from, and the unit an agent's knowledge scope is set in.
- brain.create_topicCreate a knowledge topic
Create a topic in the AI Brain. Topic names are unique per workspace and become merge-field slugs ({{brain.pricing_faq}}), so pick something an agent can be pointed at.
- brain.update_topicRename a knowledge topic
Change a knowledge topic's name or description. Its knowledge items are untouched.
- brain.delete_topicDelete a knowledge topicconfirm
Permanently delete a knowledge topic AND every knowledge item inside it. Agents scoped to this topic lose that knowledge on their next call. This cannot be undone.
- brain.list_knowledgeList knowledge items
List the AI Brain's knowledge items with their provenance — typed in by hand, extracted from an uploaded document, or crawled off a website — and how many words each holds. Body text is omitted unless you ask for it.
- brain.composePreview what the AI would read
Compose the exact knowledge block an AI writer is handed for one scope — the whole Brain, chosen topics, chosen entries, or nothing — and report how many topics, entries and words it resolves to and whether it had to be cut to fit the prompt budget. Use it to check a scope before paying for a generation; it is read-only, generates nothing, and contacts no provider.
- brain.get_knowledgeOpen a knowledge item
Fetch one knowledge item with its full body text — exactly what an agent reads to a caller — plus where it came from and when it was last extracted.
- brain.add_knowledgeAdd a knowledge item
Add a knowledge item to a topic by typing the text in. This is the same column an uploaded document or a crawled page lands in, so the agent reads it by the identical path. Content may be left empty as a stub.
- brain.update_knowledgeEdit a knowledge item
Update a knowledge item's title, description or body text. Provenance is deliberately kept: a crawled page someone tidied up by hand is still that page, which is what lets a re-crawl update this item instead of duplicating it.
- brain.delete_knowledgeDelete a knowledge itemconfirm
Permanently delete a knowledge item and any source documents stored behind it. Agents stop answering from it on their next call. This cannot be undone.
- brain.list_documentsList source documents
List the original documents stored behind knowledge items — filename, size, which extractor read it and how much text came out. Each row links to a download of the original.
- brain.add_document_from_urlIngest a document from a URLconfirm
Fetch a document from a URL, extract its text, and store it as knowledge — the machine-surface equivalent of the Brain's upload button (binary uploads can't ride a tool call). TXT/MD/CSV/TSV/JSON are decoded in-process for free; PDF/DOCX/DOC/ODT/RTF/XLSX/XLS/HTML are read by Firecrawl, which SPENDS THE WORKSPACE'S FIRECRAWL CREDITS and needs Firecrawl connected. Pass item_id to REPLACE an existing item's content (its old source documents are removed). Files over 20 MB, and scans with no text layer, are refused with the reason.
- brain.crawl_websiteCrawl a website into the Brainconfirm
Crawl a website (or a single page) with Firecrawl and import each page as a knowledge item in a topic. THIS SPENDS THE WORKSPACE'S FIRECRAWL CREDITS — roughly one per page crawled — so keep page_limit tight. Firecrawl must be connected. The crawl runs in the background for minutes; poll brain.get_crawl for progress. Re-crawling the same URL UPDATES the items it produced before rather than duplicating them, which is also how you re-ingest a site that has changed.
- brain.list_crawlsList website crawls
List website crawl jobs with their ingestion status — queued, crawling, done, failed or canceled — plus pages found, imported and skipped, and the Firecrawl credits each one used.
- brain.get_crawlCheck a crawl's progress
Fetch one website crawl job — where it is, how many pages have been crawled, imported and skipped, credits used, and the reason if it failed. Poll this after brain.crawl_website; a crawl runs for minutes.
- brain.cancel_crawlStop a website crawlconfirm
Stop a crawl that is still queued or running, so it stops spending Firecrawl credits. Pages already imported stay in the Brain, and a canceled crawl can't be resumed — start a new one instead.
- brain.delete_crawlDelete a crawl from the historyconfirm
Remove a crawl job from the history panel. The knowledge items it imported are left in place — delete those separately if you want them gone.
appdata
- appdata.setStore app data
Store (create or overwrite) one value in your app's own data, keyed by collection + key, scoped to this install. Only your app can read it back.
- appdata.getRead app data
Fetch one value from your app's own data by collection + key. Returns null if absent.
- appdata.listList app data
List your app's stored records in a collection, optionally filtered by a key prefix.
- appdata.deleteDelete app dataconfirm
Delete one value from your app's own data by collection + key.
apps
- apps.dev_guideHow to build an app
Read this FIRST. Returns the complete guide to building and publishing a marketplace app with your own AI agent: the hosted vs external model, the create → publish_version → submit_for_review → (admin review) → install flow, the manifest (pages/widgets/cards), the App SDK (chirply.call / chirply.data / chirply.context), the scope grammar, and a minimal worked hosted app. After reading it, use apps.create, apps.publish_version, and apps.submit_for_review.
- apps.createCreate an app
Create a new app in this agency's developer account, as a private draft. Doesn't publish or list it — that happens later via apps.submit_for_review and platform review. Requires an agency (white-label or reseller) plan. Both hosting modes are live: 'external' loads your own HTTPS URL in a sandboxed iframe, 'hosted' serves a self-contained HTML document you publish through apps.publish_version. Paid apps are live too — apps.start_purchase and apps.complete_purchase take real money from buyers — so set pricing_kind, price_cents and currency to what you actually intend to charge.
- apps.listList my apps
List the apps this agency owns (drafts and published), newest first.
- apps.getOpen an app
Fetch one of this agency's apps by id, with all of its fields.
- apps.updateEdit an app
Update an app's details, pricing, requested scopes, or visibility (private/unlisted). Doesn't change review status. Omitted fields are left alone. This edits a LISTED app too, not only a draft: the name, copy and price change on the public marketplace card immediately, with no re-review, and price_cents is what the next buyer is really charged. Repointing external_base_url moves every installed copy's UI to a different server.
- apps.publish_versionPublish an app versionconfirm
Publish an immutable version of an app: its manifest (the pages/widgets/cards/actions it adds) and, for a PLATFORM-HOSTED app, its `document` — a single self-contained HTML page (inline CSS/JS + the App SDK) served in a sandboxed iframe. This is how an AI agent ships an app it wrote onto the marketplace. It is OUTWARD-FACING AND PERMANENT: a published version cannot be edited or unpublished, a listed app's new installs pin it straight away, and the code in `document` runs inside other people's workspaces. Version labels are unique per app, so a mistake can only be superseded, never withdrawn.
- apps.submit_for_reviewSubmit an app for reviewconfirm
Move a draft app into the platform review queue so it can be approved and listed on the marketplace. This hands the app, its published code and its listing copy to platform reviewers outside this agency, and approval puts it in front of every workspace on the platform. The app must have a published version first.
- apps.list_directoryBrowse the app marketplace
Browse apps that can be installed into this workspace — everything listed publicly, plus this agency's own apps (so you can install one before it's listed). Each app includes its cumulative download count across workspaces, and an `is_owner` flag that is true for apps this workspace publishes (those can be managed in the developer portal).
- apps.installInstall an appconfirm
Install an app into THIS workspace and issue it an access token with the scopes you grant. This gives third-party code ongoing API access to the workspace's data within those scopes — the token is shown once and can't be retrieved again. IT ALSO STARTS SENDING DATA OUT: if the app's manifest subscribes to events and you grant 'events:read', the install provisions those event subscriptions and returns a `webhook_secret`, after which this workspace POSTs the subscribed events (new contacts, inbound messages, won deals, paid invoices…) to the app developer's own server as they happen, until someone uninstalls. Grant the least it needs, and know where the data is going. Re-installing rotates the existing install.
- apps.list_installsList installed apps
List the apps installed in this workspace, with their granted scopes and status.
- apps.list_updatesView app updates
List version updates for apps installed in this workspace. Each result identifies the installed version, newest published version, app-specific release notes, and whether an update is available. Marketplace app changes live here instead of the product-wide What's new feed.
- apps.list_release_notesView an app's what's new
List the published version history and app-owned release notes for one app installed in this workspace. This is the app's own What's new feed, separate from the product-wide release feed.
- apps.apply_updateUpdate appconfirm
Move one installed Marketplace app to its newest published version. For hosted or external apps this immediately changes the app UI and declared event subscriptions running in the workspace; native apps acknowledge the version of their already-deployed feature. Existing permissions and access token remain unchanged.
- apps.list_mobile_pagesInstalled workspace apps
List the custom pages contributed by active apps installed in this workspace — first-party native apps (each gated by its own feature flag) plus external/hosted Marketplace apps when the app platform is on. This is the member-safe mobile navigation view: it returns only page labels and in-app routes, never install tokens, secrets, granted scopes, or configuration. Read-only.
- apps.entitlementCheck app ownership
Check what this workspace has PAID for on one app, which is a different question from whether it is currently installed. Returns how it was bought (paid once, monthly, or yearly), whether the workspace may install it again without paying, when a subscription's paid-up period ends, and whether that subscription is already cancelling. Reads only — spends no money and changes no install. Use it before apps.install on a paid app: a workspace that already owns one reinstalls for free, so a purchase is not always needed.
- apps.uninstallUninstall an appconfirm
Revoke an app's install in this workspace. Its access token stops working immediately and the app can no longer reach any of this workspace's data. If the app is on a paid subscription this ALSO cancels that subscription at the end of the current period — billing stops, access continues until the period ends, and it can be reinstalled free until then, after which it switches off. Uninstalling never refunds and never destroys a one-time purchase: an app bought outright can always be reinstalled at no charge.
- apps.rotate_tokenRotate an install's tokenconfirm
Issue a fresh access token for an installed app and invalidate the old one immediately. Use if a token may have leaked. The new token is shown once.
- apps.get_listingView a marketplace listing
Fetch one marketplace app's public listing detail by slug or id: its full description, screenshots, icon, pricing, category, requested scopes, developer name, and how many workspaces have it installed. Returns publicly-listed apps, plus your own agency's apps before they're listed. This is the read behind the app's detail page.
- apps.statsApp install stats
For an app your agency OWNS, how many workspaces have installed it — total, active, revoked, and suspended. This is your developer analytics: it counts installs across all workspaces (aggregate only, it never reveals which workspaces).
- apps.developer_connectConnect Stripe for payouts
Start (or resume) Stripe Connect onboarding so your agency gets PAID for paid app installs. Returns a Stripe-hosted onboarding URL — open it, finish the steps, come back. Money from paid installs settles to this connected account (you're the merchant of record), minus the marketplace fee. Required before you can actually charge for an app.
- apps.developer_connect_statusPayout account status
Check your agency's Stripe Connect payout account for paid app installs — whether it's connected, can accept charges, and can receive payouts.
- apps.start_purchaseContinue to payment
Prepare the payment form for a PAID app in THIS workspace. Creates an internal pending order and may create a short-lived Stripe CustomerSession to show an existing saved card; it does not create a Stripe PaymentIntent, charge money, or install the app. Submit payment details with apps.complete_purchase. Free apps use apps.install instead.
- apps.complete_purchasePay nowconfirm
Submit payment for a paid app prepared with apps.start_purchase. On the first call, confirmation_token must come from the buyer's completed Stripe Payment Element; only then does this create and confirm a REAL-MONEY one-time charge or monthly subscription on the developer's Stripe account. If customer authentication is required, call again without the token after Stripe.js completes it. Installs the app only after Stripe verifies payment.
assets
- assets.listList files
List the organization's media library (uploaded images, videos, audio and documents), newest first, each with its permanent public URL. Filter by kind or folder and search names.
- assets.getOpen a file
Fetch one library file by id, including its permanent public URL.
- assets.statsLibrary totals
The four headline counts on the Media Library screen: how many files are in the library, how many of those are images, how many Design Studio projects exist, and how many folders. Whole-workspace lifetime counts with no date window and no filters — assets.list answers 'which files' and pages, this answers 'how many'. Read-only.
- assets.uploadUpload files
Upload a file into the organization's media library by sending its bytes base64-encoded, and get back its permanent public Chirply URL — the same URL the Upload button produces, usable in emails, funnel pages, posts, designs and as an outbound message attachment. Accepts PNG, JPG, GIF, WebP, SVG, ICO, MP4, MOV, WebM, MP3, WAV, M4A, OGG and PDF files, up to 25 MB per file. THE EXTENSION ON `name` DECIDES THE TYPE: the file's real header bytes must match what that extension claims or the upload is rejected, and the stored content type comes from the extension alone — never from anything the caller asserts. Costs nothing and sends nothing; it stores the bytes and adds one row to the library, where anyone in the workspace can see and delete it. If the file is already on the public web, assets.import_from_url is cheaper than base64.
- assets.import_from_urlImport a file from a URLconfirm
Download a file from a public https URL into the organization's media library and return its permanent Chirply URL. Accepts images, video, audio and PDF up to 25 MB. Use this instead of hotlinking: imported files can't break when the source disappears. THIS MAKES AN OUTBOUND REQUEST FROM CHIRPLY'S SERVERS to whatever address is given, so everything in the URL — path, query string, fragment — is disclosed to whoever operates it. That is why it asks for confirmation.
- assets.generate_imageGenerate an AI imageconfirm
Generate an image from a description and save it straight into the media library, returning its permanent URL. THIS SPENDS REAL MONEY: the request runs on the organization's OWN OpenRouter API key, so the image-generation charge lands on the tenant's OpenRouter bill directly — there is no credit pool and no free allowance to absorb it. Each call is a separate billed generation whether or not the result is any good.
- assets.renameRename a file
Change a library file's display name. The URL never changes.
- assets.moveMove files to a folder
File one or more library files into a folder, or out of any folder.
- assets.deleteDelete filesconfirm
Permanently delete library files and their stored bytes. Anything embedding the file's URL (emails already sent, funnel pages, designs) will show a broken image. This cannot be undone.
- assets.list_foldersList folders
List the library's folders, alphabetically.
- assets.create_folderNew folder
Create a library folder to organize files into.
- assets.rename_folderRename folder
Change a library folder's display name. Nothing else moves: the files inside stay in it, and every file URL is unaffected because a folder is only a label on the library screen.
- assets.delete_folderDelete a folderconfirm
Permanently delete a library folder. The files inside are NOT deleted and their URLs keep working — they just become unfiled and reappear under 'All files'. The folder itself cannot be restored; recreating it does not re-gather what was in it, so the tidying has to be redone by hand.
assistant
- assistant.askAsk the assistant
Send a message to the in-app AI assistant and get its reply. In 'chat' mode it only explains how the product works and changes nothing. In 'do' or 'smart' mode it can operate the workspace on your behalf — creating and editing records, running the same actions this API exposes — so treat its replies as having had real effects. Anything irreversible, outward-facing or costly (sending messages, spending money, deleting) is NEVER run off its own decision: those come back in `pending` for you to authorize with assistant.approve. Continues an existing conversation when you pass thread_id, otherwise starts one. Runs on the organization's own OpenRouter key and is billed to it; with no key connected, only 'chat' works.
- assistant.approveApprove a proposed actionconfirm
Authorize (or refuse) the action the assistant stopped to ask about, then let it carry on. Approving RUNS the action for real — it is the one the assistant flagged as irreversible, outward-facing or costly, so it may send messages to real people, spend the organization's money or destroy data. Refusing tells the assistant no and it continues without it.
- assistant.threadsPast chats
List your conversations with the assistant, most recently used first. Titles are taken from the first thing said in each. This is a retention WINDOW, not a permanent archive: a conversation nobody has touched for `retention_days` (returned alongside the list) is deleted automatically, messages and all. Anything worth keeping should be copied out before then.
- assistant.threadRead a conversation
Read one conversation back: what was asked, what the assistant answered, and every action it took along the way.
- assistant.delete_threadDelete a conversationconfirm
Permanently delete one conversation and every message in it. This cannot be undone. Anything the assistant already did to the workspace stays done — only the record of the conversation goes.
attribution
- attribution.source_outcomesSource outcomes and costs
Read leads created, noncanceled appointments booked and collected revenue by the contact's currently recorded source in an explicit UTC window. Only live-mode payments are included. Revenue stays separated by currency and deduplicates mirrored Stripe charges. Costs are manually entered evidence, not synchronized ad-platform spend; absent costs are unknown. Returns the latest 100 cost entries. Counts describe activity during the same period, not a single acquisition cohort; revenue/cost ratios are observational and exclude service costs. Read-only; makes no ad-provider calls and charges nobody.
- attribution.record_spendRecord source cost
Record one manually verified source cost, currency, UTC date and evidence reference for reporting. Does not create ads, charge an account or transmit anything to an ad platform. Duplicate source/date/currency/reference combinations are rejected. Entries remain in the audit history; correct an error by voiding the entry and recording a replacement with a new reference. Owner/admin only.
- attribution.void_spendVoid source cost
Exclude an erroneous manually recorded cost from future source-outcome totals while preserving its original values and who voided it. Changes reporting only; it does not refund money or change an ad account. A replacement uses a new evidence reference. Owner/admin only.
- attribution.revenue_by_sourceRevenue by Source
Which lead source made the workspace money: collected revenue (invoices, funnels, and connected Stripe, deduped so the same charge is never counted twice) grouped by each PAYING CONTACT's recorded source, for an optional date range. Attribution is single-touch: every payment counts toward the one source stamped on the contact when it was created — no fractional multi-touch. Each source also carries its paying-contact count, refunds, and a first-touch UTM campaign breakdown from the contact's earliest tracked website visit. Payments with no linked contact are reported in an explicit 'unattributed' bucket rather than guessed at. Read-only; changes nothing and charges nobody.
backups
- backups.preview_recovery_copyInspect recovery copy
Inspect one completed backup in the workspace's current customer-owned Supabase destination. Checks current inventory compatibility, stored row counts, unique IDs and selected CRM references, then proposes an isolated recovery schema in that same project. Limited to 50,000 rows. Reads the external database and may consume its normal query resources; copies nothing, sends nothing and does not restore a running Chirply workspace. Owner/admin only.
- backups.create_recovery_copyCreate isolated recovery copyconfirm
Copy a completed backup into a new private recovery_<snapshot> schema inside the workspace's own connected Supabase project. Consumes that customer's storage and database resources. Rechecks counts, unique IDs and selected references within an atomic transaction; refuses incomplete, incompatible or over-50,000-row snapshots and never overwrites an existing schema. Copies text-valued data tables only: no running Chirply workspace, credentials, triggers, workflow execution or message sending. Repeated calls cannot create duplicate copies. Owner/admin approval required.
- backups.get_destinationBackup destination
Shows where this workspace backs its data up to — which of the workspace's own connected Supabase projects receives it, the schema the tables land in, the schedule, the retention setting, and when the last and next backups run. Also lists the Supabase projects available to choose from. Reads only; nothing is written anywhere.
- backups.set_destinationSet backup destinationconfirm
Points this workspace's backups at one of its own connected Supabase projects and sets how often they run. Chirply will then write the workspace's entire business data — contacts, companies, deals, tasks, appointments, conversations and full message bodies, call metadata and transcripts, invoices and orders, forms and their responses, custom objects, the activity log and the unsubscribe lists — into that project as ordinary Postgres tables the customer can query and restore from. The data lands in a database the CUSTOMER owns and pays Supabase for, and each backup consumes their storage. Provider credentials, API keys, encrypted columns and other workspaces' data are never included. Replaces any destination already set; a workspace has one.
- backups.remove_destinationTurn off backupsconfirm
Stops backing this workspace up: removes the destination and cancels any schedule. Snapshots already written to the customer's Supabase project are left exactly where they are — this deletes nothing from their database, and the tables stay queryable. Setting a destination again later resumes with new snapshots alongside the old ones.
- backups.run_nowBack up nowconfirm
Starts a backup immediately, writing this workspace's entire business data into the connected Supabase project set as its destination, as a new snapshot alongside every previous one. The data lands in a database the CUSTOMER owns and pays Supabase for, and consumes their storage. A large workspace is written across several passes: this call returns as soon as its time budget is spent, reporting status 'running', and the scheduled worker carries the same snapshot forward until it is done. Calling it again while one is in flight continues that snapshot rather than starting a second. Provider credentials, API keys, encrypted columns and other workspaces' data are never included.
- backups.list_runsBackup history
Lists this workspace's backup snapshots, newest first — when each ran, whether it was scheduled or asked for, how many rows and datasets landed, and whether it completed. A snapshot marked 'incomplete' finished with at least one dataset short of the workspace; 'running' is still in progress. Reads Chirply's own record of the runs and touches the destination database not at all.
- backups.get_runView a backup
One backup snapshot in full: its status, when it ran, and its manifest — the per-dataset row counts, the true workspace counts beside them so a short dataset is obvious, the table each dataset landed in, and the list of everything deliberately excluded from any Chirply backup. This is the record to check before trusting a snapshot to restore from. Reads only.
billing
- billing.get_membershipMy plan
The signed-in user's own platform membership: plan name, price and interval, live subscription status, next billing date, whether it's set to cancel, any paid add-ons, and the card on file as brand plus last four digits only. Read-only; nothing is charged. This is the person's personal subscription, not the workspace's invoicing.
- billing.list_upgrade_offersAvailable upgradesconfirm
The one-click plan upgrades offered to a founder on the billing page (White-Label Partner, Reseller Partner), their current prices, and how long the founder deal window has left. When the window has closed, `reset` also gives the one-time price to reopen it and, in `restores`, the exact price each upgrade drops back to if they do — the reopen price climbs hourly to a $497 ceiling and is then withdrawn for good at `reset.final_deadline`, after which `reset` is null forever. NOT A FREE READ, despite charging nothing: like opening the billing page, the FIRST call permanently anchors this founder's 24-hour deal window to the moment it runs. Call it on someone's behalf before they are ready and their discount starts counting down without them, and the only way back is to BUY a window reset. Ask before running it.
- billing.list_plansPlans
The plan ladder — Spark, Build, Launch, Grow and Scale — with monthly and yearly prices, which one the signed-in user is on, and, for every other rung, whether moving there counts as an upgrade or a downgrade and exactly what that would cost and when. Also reports any plan change already scheduled for the end of the current billing cycle. Read-only; nothing is charged. Use billing.change_plan to actually move.
- billing.change_planChange planconfirm
DURING A FREE TRIAL no money moves in either direction: the trial keeps running to its original end date whichever plan is chosen, and the new plan simply decides what is charged on that day. Outside a trial: Moves the signed-in user's membership to another plan. UPGRADING (a bigger plan, or the same plan switched from monthly to yearly) CHARGES THE CARD ON FILE IN FULL IMMEDIATELY and switches them over on the spot; the plan they were on is not refunded or prorated, it simply stops renewing at the end of the cycle already paid for. DOWNGRADING (a smaller plan, or yearly to monthly) charges nothing today — the current plan and everything in it runs to the end of the billing cycle, then cancels, and the cheaper plan starts and takes its first payment that same day. Booking a new change replaces any change already scheduled. Founder and complimentary memberships cannot be switched this way. Check billing.list_plans first to see which direction a given plan is and what it costs.
- billing.cancel_plan_changeKeep my current planconfirm
Calls off a plan change that was scheduled for the end of the current billing cycle and keeps the member on the plan they're on, renewing as normal. Nothing is charged or refunded — the scheduled plan never started. Only affects a booked-but-not-yet-started change; an upgrade that already took effect and was charged cannot be undone this way.
- billing.start_card_updateUpdate payment method (start)
Begin replacing the card on the caller's membership. Creates a Stripe SetupIntent on their billing account and returns its client secret, which a browser Payment Element uses to collect the new card. Card details are never sent through this platform and nothing is charged. Finish with billing.finish_card_update.
- billing.finish_card_updateUpdate payment method (finish)confirm
Finish a card update: verifies the confirmed SetupIntent belongs to the caller, then makes its card the default for their billing account and every subscription on it. Nothing is charged now, but all FUTURE membership charges move to this card.
- billing.cancel_membershipCancel membershipconfirm
Cancel the caller's membership at the end of the period they've already paid for. Access continues until then and no refund is issued. DURING A FREE TRIAL this costs the member nothing at all — the card is never charged, access simply runs out when the trial would have converted — so it is the correct way to decline before the first payment. A founder who cancels loses their locked-in founder rate — resuming before the end date keeps it. Reversible with billing.resume_membership until the period ends.
- billing.resume_membershipResume membership
Undo a scheduled cancellation so the membership keeps renewing at its existing rate. Nothing is charged now — the next renewal bills as normal.
- billing.upgrade_to_yearlySwitch to yearlyconfirm
CHARGES REAL MONEY NOW. Moves a monthly founder membership to the yearly plan at today's yearly ladder price. The switch is immediate: Stripe credits the unused part of the month and invoices the full annual term against the card on file straight away. There is no refund path back to monthly.
- billing.accept_upgradeAdd a plan upgradeconfirm
CHARGES REAL MONEY NOW. Adds a paid add-on to the caller's membership — 'partner' (White-Label) or 'reseller' — as a NEW monthly subscription billed off-session to the card already on their founder subscription. The price is decided by the server: the founder deal price while their 24-hour window is open, the regular price after it. Already owning the tier is a no-op rather than a second charge.
- billing.reset_upgrade_windowReopen the founder windowconfirm
CHARGES REAL MONEY NOW — a one-time payment to the card on the caller's founder subscription that reopens their 24-hour founder-pricing window, putting the White-Label and Reseller upgrades back at their founding prices. The price is computed on the server and climbs the longer the window has been closed, so pass the price the user was shown as `expected_price_dollars`; if it has moved, nothing is charged. It stops climbing at a $497 ceiling, and 24 hours after it tops out the reopen is withdrawn permanently — calling it then fails and no later call can bring founder pricing back. If the window is still open this is a no-op.
- billing.get_walletWallet balance
Read the workspace's prepaid usage wallet: current balance, currency, and the auto-recharge settings. This balance pays for platform-billed usage like SMS and calls — outbound sends pause when it reaches zero. Read-only; nothing is charged.
- billing.list_wallet_transactionsWallet activity
List the workspace wallet's ledger entries, newest first — top-ups (positive cents), usage debits (negative cents), adjustments and refunds. This is the same activity the Billing page shows. Read-only; nothing is charged.
- billing.topup_walletAdd wallet fundsconfirm
CHARGES REAL MONEY: immediately bills the card saved on this workspace's billing account (off-session, via Stripe) for the given amount and credits the wallet once the charge settles. There is no undo — reversing it means a refund. Fails without charging if the workspace has no saved card yet; a person must add funds once from the Billing page first, which saves one. Also fails without charging on a workspace provided by an agency: those buy usage credits from that agency on the agency's own Stripe, not from Chirply.
- billing.get_dollar_a_day_offerView dollar-a-day offer
Reads the availability and exact recurring prices of Chirply's standalone dollar-a-day Scale offer. It grants no access and charges no money. This offer and its optional upgrades are excluded from affiliate commissions.
- billing.start_dollar_a_day_checkoutGet Scale for $365/yearconfirm
Creates a secure Stripe Checkout link for the signed-in person's own account to buy Scale for $365 today and every year until canceled. No card is charged by this action; the buyer must complete payment and any bank verification at Stripe. Existing paid memberships are refused. Connected provider usage is billed separately and no affiliate commission is earned.
- billing.get_dollar_a_day_journeyView my dollar-a-day purchase
Reads the signed-in buyer's paid Scale offer, current optional upgrade step, annual renewal status and activation link. It exposes only that person's purchase and does not charge a card or change an offer step.
- billing.finalize_dollar_a_day_checkoutConfirm my dollar-a-day paymentconfirm
Verifies the signed-in buyer's Checkout payment directly with Stripe and provisions access only after the full payment settled. It never initiates a charge. Completing a paid monthly upgrade stops the base annual renewal while retaining the $365 payment and access for that paid year, without refund or credit.
- billing.start_dollar_a_day_upgradeAdd my dollar-a-day upgradeconfirm
Creates the buyer's current optional upgrade Checkout: White Label at $247 per month, or the Partner Program at $97 per month only after declining White Label. The buyer must pay at Stripe. Once paid, monthly billing starts now, the $365 annual payment is retained without credit or refund, and annual renewal stops. Both upgrades include Scale and earn no affiliate commissions.
- billing.decline_dollar_a_day_upgradeKeep my current dollar-a-day planconfirm
Declines the buyer's current optional upgrade and permanently advances to the next offer step. White Label leads to the Partner Program downsell; declining that completes the purchase. Any unpaid Checkout for the skipped step is expired first so it cannot charge later. No money is charged or refunded, and the paid Scale subscription remains.
bookfunnel
- bookfunnel.get_settingsOpen BookFunnel settings
Read the BookFunnel connection, default and landing-page routing, available destinations, and the latest 100 webhook receipts with processing errors. It never returns the private webhook token or URL credential and changes nothing.
- bookfunnel.save_mappingSave mappingconfirm
Set the list, tags and automation used for future BookFunnel new_subscriber events, either as the default or for one landing-page id. Saving sends nothing now, but the selected automation may send real email/SMS or spend money as soon as a real reader subscribes. Non-opt-in book_claimed events never use this routing.
- bookfunnel.retry_eventRetry syncconfirm
Retry one failed BookFunnel webhook receipt. Contact upsert, tags and list membership are idempotent, but a selected automation may reach real people and spend money if the earlier attempt failed after partially starting it, so this always requires confirmation.
booking
- booking.list_event_typesBooking types
List this organization's bookable event types (meeting types) — for each: name, public URL slug, kind (one_on_one, group, collective, round_robin), duration in minutes, price, and the shareable public booking URL an invitee visits to pick a time. Read-only; changes nothing and sends nothing. By default only active types are returned.
- booking.get_event_typeOpen a booking type
Fetch one bookable event type by id — its full configuration (kind, duration, buffers, min-notice, location, booking form, reminders, price), the users hosting it, and the shareable public booking URL. This is everything the edit screen shows, so it is what you read before calling booking.update_event_type. Read-only.
- booking.get_availabilityOpen time slots
List the open, bookable time slots for an event type over a date range — exactly what an invitee would see on the public booking page, computed live from the hosts' working hours, existing appointments, buffers and minimum notice. Returns slots grouped by day, each an ISO UTC start/end. Read-only; reserves nothing. The range may span at most 62 days.
- booking.list_appointmentsList appointments
List this organization's booked appointments, soonest first, optionally filtered by date range, status (confirmed, canceled, completed, no_show), event type, or contact. Read-only; changes nothing.
- booking.get_appointmentOpen an appointment
Fetch one appointment by id — the event, host, contact, invitee details, start/end time, location, status and payment state, plus the invitee's manage (reschedule/cancel) URL. Read-only.
- booking.list_schedulesMy availability
List the saved weekly availability schedules in this workspace — for each: its name, whether it is the owner's default, the timezone it is interpreted in, and the full set of working hours keyed by weekday (sun–sat), each day a list of "HH:MM"–"HH:MM" windows. Read this BEFORE calling booking.set_availability: that capability REPLACES the whole week, so editing Tuesday without first reading Monday through Sunday would wipe them. By default you get the calling user's own schedules, which is exactly what the My availability screen shows; an API key with no user attached sees the whole workspace. Read-only.
- booking.create_event_typeCreate event type
Create a new bookable event type (a meeting people can book), and return its public booking URL. Nothing is sent to anyone and nobody is charged by this call — but if `is_active` is left on (the default) the event type's public booking page goes live immediately and strangers holding the link can book real time on a host's calendar from that moment. Owners and admins only, matching the Calendars editor. Hosts must be members of this workspace; when `host_user_ids` is omitted the calling user hosts it, and an API key (which has no user) must name at least one host.
- booking.update_event_typeSave event type
Change the configuration of an existing bookable event type. Only the fields you pass are changed; everything omitted is left exactly as it was. Existing appointments already on the calendar are NOT moved or re-priced — changes apply to future bookings. Changing duration, buffers, notice or hosts changes which times the public booking page offers from that moment on. Owners and admins only. Passing `questions`, `reminders` or `host_user_ids` REPLACES that whole list, so read the current one with booking.get_event_type first. To show or hide the public booking page, use booking.set_event_type_active — this capability deliberately cannot.
- booking.set_event_type_activeAccepting bookingsconfirm
Turn an event type's PUBLIC booking page on or off. Switching it off takes the page down for everyone holding the link — nobody can book that meeting any more — without deleting the event type or any appointment already on the calendar; switching it back on republishes it instantly and strangers can book real time again. Owners and admins only.
- booking.delete_event_typeDelete event typeconfirm
Delete a bookable event type. Its public booking page stops working immediately and it disappears from the Calendars list. This is a soft delete — the record is retained so appointments already booked against it keep their history, and nothing already on anyone's calendar is cancelled or refunded (cancel those separately with booking.cancel_appointment if that's what you mean). There is no undo in the app.
- booking.book_appointmentBook a meetingconfirm
Books a real slot on an event type's calendar for an invitee. FREE EVENT TYPES are confirmed immediately: the invitee and the host are emailed/texted a confirmation from the organization's OWN Mailgun/Twilio, reminders are scheduled, and the contact may be enrolled in 'appointment booked' automations. PAID EVENT TYPES (require_payment on, with a price) ARE NOT CONFIRMED HERE — exactly as on the public booking page, the slot is held unconfirmed, nothing is sent to the invitee, and this returns `checkout_url`: a Stripe Checkout link on the business's own account. Give that link to the invitee; the meeting is only confirmed, and the confirmation only sent, once Stripe reports them paid. The requested time is re-validated against live availability, so it fails if the slot was just taken; a round-robin host is assigned automatically. Matches or creates the contact from the invitee's email/phone. Provide the start exactly as one of the ISO UTC starts returned by booking.get_availability.
- booking.cancel_appointmentCancel an appointmentconfirm
Cancels a booked appointment and immediately notifies the invitee of the cancellation by email/SMS from the organization's own Mailgun/Twilio, drops its pending reminders, and fires 'appointment canceled' automations. This frees the slot for someone else. Owners and admins only. To move an appointment to a new time instead, use booking.reschedule_appointment.
- booking.reschedule_appointmentReschedule an appointmentconfirm
Moves a booked appointment to a new time: the new slot is re-validated against live availability, a fresh appointment is created linked to the old one (reschedule_of) with its payment record carried over, the old one is canceled, and the invitee is emailed/texted the new time from the organization's own Mailgun/Twilio — the customer is always notified, never silently moved. Fires 'appointment rescheduled' automations. Owners and admins only. Provide the new start exactly as one of the ISO UTC starts returned by booking.get_availability, or set allow_outside_availability to book a time the engine wouldn't offer (the host still can't be double-booked). Refuses if the new slot is no longer open, or if the appointment was canceled or has already started.
- booking.set_appointment_statusMark appointment completed
Record how an appointment actually went — 'completed' if it happened, 'no_show' if the invitee never turned up, or 'confirmed' to put it back the way it was. This is a PRIVATE record change: it sends the invitee nothing, changes no times, fires no automations, refunds nothing, and does not free the slot. It is the app's 'Mark completed' / 'Mark no-show' / 'Back to confirmed' menu. To actually call the meeting off and tell the invitee, use booking.cancel_appointment instead. Owners and admins only.
- booking.set_availabilitySet your working hours
Set YOUR OWN weekly booking availability — the recurring working hours the slot engine offers to invitees for events you host, interpreted in the workspace timezone from Business profile. Replaces your existing weekly hours (does not touch teammates' schedules or one-off date overrides). Personal to the calling user; API keys act as the user that issued them.
- booking.connect_calendarConnect calendarconfirm
Start linking a host's own Google Calendar or Microsoft 365 / Outlook account, so their real commitments block booking slots and meetings booked here are written onto their calendar. This does NOT complete the link on its own: connecting requires the account holder to approve access on the provider's own consent screen, so what comes back is the URL that starts that flow. Open it in a browser where the host is already signed in to this workspace — the link acts as whoever is signed in, so it connects THAT person's calendar, and it grants this workspace ongoing read/write access to their calendar until it is disconnected. Any member may connect their own; use booking.list_calendar_connections to see what is already linked.
- booking.list_calendar_connectionsCalendar connections
List the external calendars (Google Calendar, Microsoft 365 / Outlook) connected for scheduling — for each: the provider, the connected account's email, whether it feeds busy times into availability (inbound) and receives booked meetings (outbound), when it last synced, and any current sync error. Access tokens are NEVER returned. When called by a specific user (Copilot) only that user's own connections are shown; an API key sees the whole workspace. Read-only.
- booking.disconnect_calendarDisconnect
Disconnect a linked external calendar (Google or Outlook) by its connection id, removing this workspace's stored access for that calendar. After this, its busy times no longer block booking slots — so times the host is actually busy start being offered to invitees — and new meetings are no longer written to it. Restoring sync means going through the provider's consent screen again: booking.connect_calendar returns that link. Does not delete any existing calendar events. A member can only disconnect their own calendar; managers (and API keys) can disconnect any in the workspace.
bot_flows
- bot_flows.listList bot flows
List only this workspace's conversational bot flows, never normal CRM automations, with trigger and paused/live state. This is read-only and sends no messages.
- bot_flows.getOpen a bot flow
Fetch one conversational bot flow with its complete graph and legacy ordered steps. A normal automation id is treated as not found and no messages are sent.
- bot_flows.createCreate bot flow
Create a new conversational bot-flow draft with one social or manual entry trigger. It is always PAUSED, creates an editable visual graph, sends no messages, and must be activated separately after review.
- bot_flows.updateEdit bot flow details
Update a bot flow's internal name or teammate description. This does not change its graph, activation state, recipients, or message delivery.
- bot_flows.get_flowOpen the bot canvas
Fetch the complete graph drawn by the bot-flow canvas, including entry triggers, rich messages, AI replies, questions, logic, waits, actions, and handoffs. This is read-only.
- bot_flows.set_flowSave the bot canvasconfirm
Replace a bot flow's complete graph. If the bot is live, future matching people immediately follow the new graph and its real provider messages, AI replies billed to the workspace's OpenRouter account, CRM changes, calls, or paid actions; pass the whole graph, not a partial patch.
- bot_flows.activateActivate bot flowconfirm
ARM A LIVE BOT. Every matching Facebook, Instagram, WhatsApp, comment, ad, link, button, or menu entry can immediately send real provider messages and run paid or mutating actions with no further approval.
- bot_flows.pausePause bot flow
Stop a bot flow from accepting new matching entries while retaining its graph and run history. It can be activated again later and this action sends no messages.
- bot_flows.deleteDelete bot flowconfirm
Permanently delete one bot flow, its legacy steps, and its complete run history. It can no longer answer connected entry points and this cannot be undone.
- bot_flows.list_templatesList bot templates
List Chirply's social bot starters and this workspace's private saved bot-flow templates. Normal automation templates are excluded; this read creates nothing.
- bot_flows.get_templateView bot template
Return one bot-flow starter or private bot template with its frozen visual graph. Normal automation templates are hidden and this read changes nothing.
- bot_flows.create_from_templateUse bot template
Create a PAUSED bot flow from a social starter or private bot template. It sends nothing until separately reviewed and activated; normal automation templates are rejected.
- bot_flows.save_as_templateSave bot template
Save a private frozen copy of a bot flow's current graph for reuse in this workspace. It sends nothing, preserves bot-flow identity, and rejects normal automation ids.
- bot_flows.delete_templateDelete bot templateconfirm
Permanently delete one private saved bot-flow template. Existing bot flows remain intact, normal automation templates are rejected, and the deleted template cannot be recovered.
- bot_flows.duplicateDuplicate bot flow
Create a separate PAUSED copy of one bot flow's graph without copying runs or history. It sends nothing, preserves bot-flow identity, and rejects normal automation ids.
- bot_flows.list_runsList bot-flow runs
List execution history only for bot flows, including status, contact, current step, and errors. Normal automation runs are excluded and this read changes nothing.
- bot_flows.get_runOpen a bot-flow run
Fetch one bot-flow run with full execution context and error details for debugging. A normal automation run id is treated as not found and nothing is changed.
browser_agent
- browser_agent.list_automationsView standing approvals
List the standing approvals in this workspace — the automations that let a paired browser run one approved action on one website on a schedule, with nobody watching it happen. Each entry shows the browser that granted it, the exact origin and path it may act on, how many packaged steps it runs, whether it ends by activating an outward control such as Post or Submit and what that control is labelled, its hourly run cap, when it was approved, when it must be re-approved, and whether it has been revoked. Page contents, task text, and field values are never stored and are not returned.
- browser_agent.revoke_automationRevoke standing approvalconfirm
Stop one standing approval from running unattended ever again. The paired browser checks Chirply before every unattended run, so this takes effect on that browser's next sweep — within about five minutes — without anyone touching the machine. Scheduled work that the automation was handling returns to the manual queue for a person to do. The record is kept, not deleted, so the run history still shows what used to run on its own.
- browser_agent.delete_automationDelete standing approvalconfirm
Permanently erase one standing approval and its link to past unattended runs. This cannot be undone, and it removes the audit trail's record of what used to run on its own — prefer revoking, which stops the automation but keeps that history. The paired browser drops its local copy on the next sweep.
- browser_agent.list_runsView browser runs
List the redacted approval and execution audit trail for Browser Agent. It returns page origins, packaged action types, risk classifications, statuses, and result counts; page contents, task text, form values, cookies, credentials, and extracted records are never retained.
- browser_agent.delete_runDelete browser runconfirm
Permanently delete one redacted Browser Agent approval and execution audit record from this workspace. This cannot be undone; it does not affect page data or extracted results because Chirply never receives or retains them.
- browser_agent.clear_runsClear browser historyconfirm
Permanently delete every redacted Browser Agent approval and execution audit record in this workspace. This cannot be undone; local extracted results remain only in each browser until that browser clears its extension storage.
- browser_agent.list_devicesView paired browsers
List the Chrome browsers currently paired with this workspace's Browser Agent Marketplace app. Returns display names, release channels, extension versions, last-seen times, and non-secret token prefixes; it never returns device credentials.
- browser_agent.create_pairingCreate pairing code
Create a single-use Browser Agent pairing code for this workspace. The code expires in ten minutes and can mint one revocable browser credential; it does not publish, message, or modify any social account by itself. The response also returns installUrl, the Chrome Web Store listing a person adds the extension from before entering the code in its side panel.
- browser_agent.revoke_deviceRevoke browserconfirm
Immediately revoke one paired browser's access to this workspace. The extension keeps only its local task draft and last local result, but it can no longer create audited browser runs, read handoffs, or update Chirply until paired again.
bulk_jobs
- bulk_jobs.listList background jobs
List this workspace's background bulk jobs — bulk emails, bulk texts, mass tagging, bulk deletes — newest first, with progress and the sending pace of each. Read-only; starting a job is done by the capability for that operation (for example contacts.send_email).
- bulk_jobs.getCheck a background job
Fetch one background job's progress: how many have been processed, succeeded, failed and skipped, how many are left, and how long the rest will take at the current pace. Polling this also nudges the job along, so it is the right way to wait for one to finish.
- bulk_jobs.detailsSee a job's message and sender
Fetch everything about one background job beyond its progress: the exact subject and body it is sending, whether that copy contains merge fields, which address or sending pool it goes out from (and whether those addresses can currently send), when it started, when the last message actually went out, when it is on course to finish at its current pace, who started it, and the full delivery picture so far — delivered, opened, clicked, bounced, reported as spam and unsubscribed, each as a count and as the percentage it is judged on. Read-only. Use this to answer "what exactly did this send, and who is it coming from?" — bulk_jobs.get answers only "how far along is it".
- bulk_jobs.recipientsList a job's recipients
List who a background job is working through, in the order it sends them, one page at a time. Each row gives the contact, the address the job reaches them on, the position in the queue, and where they stand: already sent (with the address it went out from, the time, and whether it was delivered, opened, bounced or failed), skipped because they had no address, currently being sent, or still waiting. Read-only. This is how to answer "has this person been contacted yet?" and "who has not received it?" for a send that runs over hours or days. Pass `outcome` to get only the people behind one delivery number instead — the handful who reported it as spam, whose address bounced, who unsubscribed — which is otherwise unfindable in a 15,000-contact send, since nothing about someone's position in the queue says what happened to their copy.
- bulk_jobs.pausePause a background job
Pause a running bulk job. Anything already sent stays sent; nothing further goes out until it is resumed. Use this to stop a bulk send mid-flight without abandoning it.
- bulk_jobs.resumeResume a background jobconfirm
Resume a paused bulk job. It picks up where it stopped and continues at its current pace — for an outbound job this means real emails or texts start going out again, billed to the workspace's own provider account.
- bulk_jobs.cancelStop a background jobconfirm
Permanently stop a bulk job. Whatever has already been sent or changed stays that way — this cannot recall sent messages or undo completed updates — but nothing further is processed and the job cannot be restarted.
- bulk_jobs.rescheduleChange when a job startsconfirm
Move a bulk job's start time, or start it right now. Only works while the job has not begun sending — once the first messages are out, the rest can be paused or re-paced but not postponed. Passing null for start_at drops the wait and lets the job begin immediately, which for an outbound job means real emails, texts or calls start going out at once.
- bulk_jobs.set_paceChange a job's sending pace
Change how fast a bulk job sends, while it is running. Applies only to what has not gone out yet. Set a per-minute, per-hour or per-day cap; pass null for a window to remove that cap, and clear all three to send as fast as possible. Slowing a send down is the usual reason to call this — provider rate limits and deliverability.
- bulk_jobs.edit_messageEdit a job's messageconfirm
Rewrite the subject or body of a bulk email or text job that has not finished. Only contacts who have NOT been reached yet get the new wording — messages already sent cannot be recalled or changed, and the count of what has already gone out with the old wording is returned so it can be checked. Applies to email and text jobs only; jobs that enrol contacts in a workflow or run an action sequence have no message of their own to edit. Merge fields such as {{first_name}} are stored raw and filled in per contact at the moment each one is sent.
business
- business.get_profileView business profile
Read the workspace's customer-facing business identity (name, logo, contact details, address and tax ID), IANA timezone, and weekly opening hours. These identity fields appear on invoices. Read-only; changes nothing.
- business.update_profileSave business settings
Update the workspace's business identity used on invoices, hosted logo URL, IANA timezone, or weekly opening hours. Changing the timezone immediately changes which local calendar day timestamped revenue and activity appear under; it does not alter payment timestamps or money.
call_queues
- call_queues.listList call queues
List every call queue in the workspace with how many people are still waiting to be called, how many have been called, and how many were skipped.
- call_queues.getOpen a call queue
Fetch one call queue by id, with its live counts of who is still to call, who has been called, and who was skipped.
- call_queues.createNew call queue
Create a call queue — a named list of people for the team to phone through the power dialer. Creating one calls nobody; it only makes the list.
- call_queues.updateEdit a call queue
Rename a call queue, change its description or colour, attach a call script to it, or take it out of use. Taking a queue out of use removes nobody — it just stops being offered when someone picks a queue.
- call_queues.deleteDelete a call queueconfirm
Permanently delete a call queue and everyone's place in it. The contacts and their call history are untouched, but the record of who still needed calling is gone and cannot be recovered.
- call_queues.list_entriesWho is on a call queue
List the people on a call queue in the order they will be dialled, with each one's outcome and note if they have already been called.
- call_queues.add_contactsAdd to call queue
Put contacts on a call queue so a person rings them. This dials nobody and sends nothing — it only adds them to the list a human works through. Anyone already on the queue is moved back to “still to call” instead of being added twice.
- call_queues.remove_contactsRemove from call queue
Take contacts off a call queue so nobody rings them. Their call history and any outcome already recorded stay on the contact; only their place in this queue is removed.
- call_queues.nextWho to call next
Read the next people waiting on a call queue, in the order they will be dialled, with their phone numbers. Anyone without a phone number is left out. Reads only — it starts no calls.
- call_queues.record_outcomeRecord a call queue outcome
Mark someone on a call queue as called (with the outcome that was chosen) or skipped, so they stop coming up as still-to-call. This only writes the queue entry — it sends nothing to anybody and spends no money. KNOWN GAP: unlike the power dialer in the app, this does NOT fire the 'called from a queue' automation trigger and does NOT stamp the queue onto the call log, so a workspace whose follow-up texts hang off that trigger will not send them for an outcome recorded this way.
- call_queues.resetCall everyone again
Put people who have already been called or skipped back to still-to-call, so the queue can be worked through again. Their previous outcome and notes are kept on record.
- call_queues.clearEmpty a call queueconfirm
Take everybody off a call queue, keeping the queue itself. The record of who was on it and what happened on those calls is destroyed and cannot be recovered; the contacts are untouched.
call_tracking
- call_tracking.list_campaignsList call-tracking campaigns
List this organization's call-tracking campaigns, newest first. A campaign ties tracking numbers to a routing plan and a conversion rule. Returns each campaign's status, conversion rule, and routing settings — not its call log.
- call_tracking.get_campaignOpen a call-tracking campaign
Fetch one call-tracking campaign by id, with its full routing list, geo filter, conversion rule, and the signing secret for its external conversion webhook.
- call_tracking.create_campaignCreate call-tracking campaign
Create a call-tracking campaign. Set the ordered list of destinations calls should route to (`route_to`), how a call counts as a conversion, and any geo / repeat-caller filters. Creating a campaign costs nothing and places no calls; it starts in 'draft' unless you set a status. Point a tracking number at it with call_tracking.assign_number to go live.
- call_tracking.update_campaignEdit call-tracking campaignconfirm
Edit a call-tracking campaign — its routing list, conversion rule, filters, recording, or status. Omitted fields are left alone, INCLUDING the four geo fields: sending only geo_states leaves the stored area codes, countries and mode as they were. Passing `route_to` REPLACES the whole destination list, and this campaign may be answering live calls right now: repointing it sends every subsequent caller somewhere else, and setting status to 'paused' stops the campaign's tracking numbers connecting anyone at all.
- call_tracking.pause_campaignPause or resume a campaign
Pause a call-tracking campaign (stop routing new calls to it) or resume it. A convenience wrapper over the status field.
- call_tracking.archive_campaignArchive a campaignconfirm
Archive a call-tracking campaign. Its tracking numbers stop routing to it (calls fall through to the team bridge) and it drops out of the active list. The call history is kept. Reversible by setting status back to active.
- call_tracking.list_numbersList tracking numbers
List the phone numbers wired as tracking numbers, optionally for one campaign. Each maps a `phone_numbers` row to a campaign (or a DNI pool).
- call_tracking.assign_numberMake a number a tracking numberconfirm
Point one of the organization's existing phone numbers at a call-tracking campaign. THIS REPOINTS A LIVE BUSINESS PHONE LINE: it switches that number's inbound routing away from whatever answers it today (the team, an IVR, an AI receptionist) to the campaign engine, so the very next real caller is routed by the campaign's destination list instead. If the number is already assigned elsewhere in call tracking, this silently re-points it. Does not buy a number — provision one first with the telephony tools, then assign it here.
- call_tracking.release_numberStop tracking on a numberconfirm
Detach a tracking number from its campaign or pool and return it to normal team routing. THIS REPOINTS A LIVE BUSINESS PHONE LINE — the next real caller reaches the team instead of the campaign's destinations — and if the number is in a DNI pool, every web visitor currently holding it loses their attribution. The number is not released from Twilio; it just stops being a tracking number. Past calls ARE kept in full: ct_calls rows reference the campaign and the phone number, not this assignment, so reports and recordings survive. What does NOT survive is the tracking-number record itself — its report label ('Billboard I-35') and its publisher attribution are hard-deleted with no undo, and re-assigning the number creates a fresh one.
- call_tracking.list_callsList tracked calls
List calls that came through tracking numbers, newest first, with their attribution (campaign, source/UTM/keyword, caller state) and outcome (answered, duration, conversion). Filter by campaign, conversion status, or a date window.
- call_tracking.get_callOpen a tracked call
Fetch one tracked call by its ct_calls id, with full attribution, routing outcome, and conversion + money fields.
- call_tracking.call_statsCall-tracking stats
Headline numbers over a recent window: total tracked calls, how many were answered, how many converted, and the running revenue / payout / margin. Optionally scoped to one campaign. Read-only.
- call_tracking.list_conversionsList conversions
List conversion signals recorded against tracked calls (duration, webhook, or manual), newest first. Optionally for one campaign or one call.
- call_tracking.mark_conversionMark a call convertedconfirm
Manually mark a tracked call as converted (or rejected). Records a conversion audit entry and — once the pay-per-call money layer is live — is what bills the buyer and credits the publisher for that call. Use for campaigns whose conversion is decided by a human, or to correct an automatic decision.
- call_tracking.list_poolsList number pools
List DNI number pools, optionally for one campaign. A pool rotates a set of tracking numbers so each web visitor sees a unique number keyed to their source.
- call_tracking.get_poolOpen a number pool
Fetch one DNI number pool with the tracking numbers in it and the exact <script> snippet to paste on the page whose calls it should attribute.
- call_tracking.create_poolCreate a number pool
Create a DNI number pool for a campaign and mint its public embed key. Add tracking numbers to it with call_tracking.add_number_to_pool, then paste the returned snippet on the page. Creating a pool costs nothing; the numbers you add are real Twilio numbers the tenant already pays for.
- call_tracking.update_poolEdit a number pool
Edit a DNI number pool — its name, status, target size, stickiness, or allowed origins. Omitted fields are left alone. This pool is feeding a live public web page: setting status to 'paused' stops handing out numbers, so visitors fall back to whatever static number the page shows and their calls stop being attributed.
- call_tracking.add_number_to_poolAdd a number to a poolconfirm
Add one of the organization's phone numbers to a DNI pool. It becomes a rotating tracking number: the DNI script hands it to a visitor, and a call to it is attributed to that visitor's source. THIS REPOINTS A LIVE BUSINESS PHONE LINE — the number's inbound routing switches to the campaign engine, so it stops reaching whoever answers it today, and it starts being shown to strangers on a public web page. If the number is already assigned elsewhere in call tracking, this silently moves it. Provision the number first with the telephony tools.
- call_tracking.get_dni_snippetCopy DNI snippet
Return the one-line <script> tag that enables dynamic number insertion for a pool. Paste it on the page whose phone numbers should swap per visitor; mark the numbers with data-ct-number.
cards
- cards.scanScan a business cardconfirm
Reads a photo of a physical business card with AI vision and saves the details as a CRM contact — creating a new contact, or updating the one it matches by phone/email — with the card's front (and optional back) images attached. Uses this workspace's own OpenRouter/AI key, so the AI cost is billed to the workspace. Images are supplied as a data URL (data:image/jpeg;base64,…) or an https image URL. AN https IMAGE URL IS FETCHED FROM CHIRPLY'S SERVERS and handed to the vision provider, so everything in it — host, path, query string — is disclosed to whoever operates that address. That, and the AI spend, is why it asks for confirmation.
- cards.list_cardsList digital cards
Lists the digital business cards in this workspace — each person's shareable card, whether it's published, its public URL and view count.
- cards.get_cardGet a digital card
Returns a digital business card and its share links (public page + vCard). Defaults to the calling user's own card; pass user_id for a specific teammate, slug for a card by its public URL segment, or ble_code after discovering a nearby digital business card over Bluetooth.
- cards.save_cardSave my digital card
Creates or updates a person's digital business card (name, title, company, phones, emails, website, socials, bio, theme) and can publish or unpublish it. Publishing makes it world-readable at its public URL. Defaults to the calling user's card; an API key must pass user_id.
- cards.import_cardSave a shared card to contacts
Saves someone's published digital business card (by its public slug) into this workspace's CRM as a contact. If the person is already a contact (matched by phone/email) they're linked, not duplicated.
cms
- cms.list_collectionsList collections
List the structured content collections owned by a website, including each collection's field schema, public path, and visual template page.
- cms.create_blogCreate blog
Create an SEO-ready Blog collection and a published visual article template inside an existing website. This adds private content infrastructure but does not publish any article or send anything externally.
- cms.create_collectionCreate collection
Create a structured content collection for one website. Its entries remain drafts until individually published; nothing becomes public from this action.
- cms.update_collectionSave collection
Update a collection's name, URL prefix, field schema, status, or visual template page. Changing fields does not delete stored entry data; published entry URLs may change when path changes.
- cms.delete_collectionDelete collectionconfirm
PERMANENTLY delete a collection and every draft and published entry inside it. All of its public entry URLs stop resolving immediately. This cannot be undone.
- cms.list_entriesList entries
List draft, scheduled, published, or archived entries in a collection with their structured data and SEO settings.
- cms.create_entryCreate entry
Create a CMS entry as a private draft. Structured values are validated against the collection schema and nothing becomes public until Publish entry is run.
- cms.update_entrySave entry
Save an entry's working draft, slug, excerpt, or SEO settings. Published visitors continue seeing the previous published snapshot until Publish entry is run again.
- cms.publish_entryPublish entryconfirm
Publish the current draft data and SEO as a public snapshot. Real visitors can immediately open the entry at the collection path on the website; the collection must have a published visual template page.
- cms.schedule_entrySchedule entryconfirm
Freeze the current saved draft and SEO as a public snapshot that automatically becomes available at the specified future time. Real visitors can open it after that time; later draft edits do not change the scheduled snapshot.
- cms.unpublish_entryUnpublish entryconfirm
Take a published CMS entry offline immediately while preserving its working draft and last published snapshot for later republishing.
- cms.import_wordpressImport WordPress postsconfirm
Fetch up to 100 posts from a public WordPress REST API and add them as private drafts in an existing collection. Existing entries with the same slug are preserved; this publishes nothing. THIS MAKES AN OUTBOUND REQUEST FROM CHIRPLY'S SERVERS to whatever address is given, so everything in the URL — host, path, query string — is disclosed to whoever operates it, and whatever it answers with is stored in this workspace. That is why it asks for confirmation.
- cms.audit_seoAudit SEO
Audit every CMS entry on a website for missing or oversized search metadata, missing social images, thin content, long slugs, and published no-index mistakes. This read-only check changes nothing.
- cms.delete_entryDelete entryconfirm
PERMANENTLY delete a CMS entry, including its working draft and published snapshot. Its public URL stops resolving immediately and this cannot be undone.
commerce
- commerce.get_storeOpen store
Fetch this workspace's storefront branding, publishing status, payment routing and public address.
- commerce.create_storeCreate store
Create the workspace's storefront in draft. It remains private until Publish store is run.
- commerce.update_storeSave storefrontconfirm
Update customer-facing storefront branding, merchandising copy, support details or payment routing. Omitted fields are unchanged. Two of these fields move real money and are the reason this needs approval: changing stripe_account_id redirects where every future sale is PAID INTO, and setting payment_mode to 'test' makes a live public storefront stop collecting real money entirely while still appearing to take orders.
- commerce.publish_storePublish storeconfirm
Publish the storefront to the open internet, or pause an already-public store. Publishing immediately exposes all active products and enables real checkout when payment mode is live.
- commerce.connect_domainConnect domainconfirm
Connect a customer-owned hostname through the platform's Cloudflare for SaaS service and make it this store's public address. This changes public routing and may automatically create a CNAME in the workspace's connected Cloudflare account; it does not purchase a domain.
- commerce.attach_domainUse this domainconfirm
Make an already-connected, unused workspace domain the store's public address. This immediately changes public routing when the domain is active and replaces any prior custom address on this store; it does not modify registrar ownership.
- commerce.verify_domainCheck again
Recheck the store domain's Cloudflare hostname and SSL status, then save the latest verification state. This does not change DNS or spend money.
- commerce.setup_domain_dnsSet up with connected Cloudflareconfirm
Create or repair the store hostname's CNAME in the workspace's connected Cloudflare account and recheck SSL. This writes a real public DNS record but does not purchase a domain or charge money.
- commerce.detach_domainRemove from storeconfirm
Stop serving the native store from its custom hostname. The store remains available at its built-in platform address and the domain remains connected to the workspace for reuse; no registrar or DNS ownership is deleted.
- commerce.list_productsList products
List products shared by storefronts and funnels, with price, visibility, SKU, product type and stock state.
- commerce.get_productOpen product
Fetch one catalog product with pricing, inventory, fulfillment and storefront fields.
- commerce.create_productCreate product
Create a reusable catalog product for storefronts and funnels. Active products become sellable immediately when the storefront is published.
- commerce.update_productSave productconfirm
Update a catalog product. This edits an item that may be ON SALE RIGHT NOW on a published storefront: a new price_amount is what real customers are charged from the moment it saves, and storefront_status can put the product on sale or pull it off in public immediately. Price changes affect future purchases only; completed order snapshots are never rewritten.
- commerce.adjust_inventoryAdjust inventoryconfirm
Add or remove sellable product inventory and record the manual adjustment in the stock ledger. This can make an item available or sold out immediately on a live storefront. IMPORTANT SIDE EFFECT: it also switches stock tracking ON for that product permanently, even if it was selling with tracking off — so from then on the count is enforced, and a product whose inventory policy is 'deny' will REFUSE real customer orders as soon as the count reaches zero.
- commerce.list_variantsList product variants
List the sizes, colors or other purchasable variants for one product, including option values, price overrides and stock.
- commerce.create_variantAdd variant
Add a purchasable size, color or option combination to an existing product, with optional SKU, price override and independent inventory.
- commerce.list_collectionsList collections
List storefront collections used to merchandise related products into browsable groups.
- commerce.create_collectionCreate collection
Create a storefront collection. Products can be assigned separately without changing or duplicating them.
- commerce.delete_collectionDelete collectionconfirm
Permanently delete a storefront collection and its product arrangement. Products themselves and completed orders are not deleted.
- commerce.list_discountsList discounts
List discount codes, eligibility, limits, active dates and redemption counts.
- commerce.create_discountActivate discountconfirm
Create a discount code and put it LIVE immediately. Every code created here starts ACTIVE, applies to EVERY product in the store, and — unless you set usage_limit — can be redeemed an UNLIMITED number of times by anyone who learns the code. It comes straight off what real customers pay at storefront checkout, so a percentage value of 10000 basis points is a 100%-off code that gives the whole catalogue away for free. There is no draft state and no approval step after this call: use commerce.pause_discount to stop one.
- commerce.pause_discountPause discount
Pause or reactivate a discount code. Pausing prevents it from reducing any new customer checkout immediately.
- commerce.list_ordersList store orders
List storefront orders with customer, payment totals and fulfillment state. Funnel-only orders are excluded.
- commerce.get_orderOpen store order
Fetch one storefront order with payment, customer, delivery and fulfillment fields.
- commerce.fulfill_orderMark fulfilledconfirm
Mark a paid storefront order fulfilled and record optional carrier/tracking details. This is an outward operational claim that the seller has shipped or delivered the order.
- commerce.list_subscriptionsList subscriptions
List the recurring orders (subscriptions) sold through this workspace's storefront and funnels: buyer, amount, billing state (active, past_due after a failed renewal, canceled) and the Stripe subscription id on the seller's own Stripe account. Read-only.
- commerce.cancel_subscriptionCancel subscriptionconfirm
Cancel a customer's recurring subscription on the seller's OWN Stripe account. This stops REAL future charges to a real customer's card: with at_period_end (the default) they keep what they paid for until the current billing period runs out and are never charged again; with at_period_end false the subscription ends IMMEDIATELY, any member access the purchase granted is revoked right away, and no refund of the current period is issued by this action. It does not delete the customer, the order history, or any member data.
- commerce.list_cartsList open carts
List persistent storefront carts, including identified buyers, discounts, totals and last activity, for abandoned-cart recovery and support.
- commerce.get_cartOpen store cart
Fetch one shopping cart on the public storefront by its token: every line with its product, chosen variant, quantity, unit price and whether it is still in stock, plus the server-calculated subtotal, discount, shipping, tax and total in the smallest currency unit. Prices are recalculated live from the catalogue, so this is the authority on what checkout will actually charge. Reads only — it holds no stock and charges nothing. Returns nothing if the cart has expired or was already checked out.
- commerce.add_to_cartAdd to cart
Add a product to a shopping cart on the public storefront, starting a new cart when no token is given. The product must be active and published on the storefront, a product with options requires a variant_id, and the request is refused when tracked stock would be exceeded. Nothing is charged and no stock is held — this only builds the cart; money moves at commerce.checkout. ALWAYS keep the cart_token it returns: it is how every later call finds this cart.
- commerce.update_cart_itemUpdate cart quantity
Change how many units of one line are in a storefront cart, or remove that line entirely by setting the quantity to 0. Totals are recalculated on the server. The change is refused when tracked stock would be exceeded. Nothing is charged and no stock is held.
- commerce.apply_discount_codeApply discount code
Apply a discount code to a storefront cart, or clear the one already on it. The code is checked against the store's active discounts on the server and rejected if it isn't valid, so this is also how you test whether a code works. The saving is recalculated into the cart's totals and will really be taken off the customer's charge at checkout. Nothing is charged here.
- commerce.checkoutCheckoutconfirm
Place a REAL order for the contents of a storefront cart. This is the point of no return on the buyer's path: it re-prices every line, HOLDS the tracked stock so nobody else can buy it, creates a real pending order in the seller's books, creates or updates a CRM contact for the buyer from their email, and opens a live Stripe payment for the full total on the seller's OWN Stripe account (test mode only if the store is set to test). It returns that payment's client secret — the card is charged the moment that secret is confirmed, which for a shopper is them clicking Pay. Use only with a real buyer's real details and a total they have agreed to.
- commerce.get_order_by_tokenOpen order receipt
Fetch a storefront order using the order token from its confirmation link — the receipt page a buyer is sent to after checkout. Returns the order's number, payment status (whether Stripe has confirmed it yet), fulfilment status, buyer details, totals and every line item. Use this to poll whether a checkout you started has actually been paid. Reads only. Staff who have the order's internal id should use commerce.get_order instead.
communications
- communications.mark_spamMark as spam
Mark one email, text, or phone call as spam so it leaves normal communication views. This is reversible and sends nothing.
- communications.listCommunication log
Read the communication log: every call, text, email, social message, and website live chat in this workspace on one timeline, newest first, with both sides of each exchange named. Calls carry duration, recording URL and transcript; messages and chats carry their latest content. Outbound email also carries an `engagement` object — when it was first opened and how many times, when a link was clicked, when it bounced, and when the recipient reported it as spam — each null until the email provider reports it, and all null when that provider has open tracking switched off, so a missing open never means the message went unread. Read-only. Archived entries are excluded unless `archived` is true, and entries marked as spam are excluded unless `spam` is true; live chats are always read from their dedicated inbox and have neither an archive nor a spam state. ONE DELIBERATE DIFFERENCE FROM THE SCREEN: the /communications page and the dashboard's Recent Communication column both collapse the timeline to the newest entry per contact, and this returns every entry by default so a machine caller can page the raw history. Pass `group_by_contact: true` to see exactly what the screen shows.
- communications.channelsMy channels
List the workspace's OWN ends of a conversation — the phone numbers it calls and texts from, the email addresses it sends from, and the Facebook Pages and Instagram accounts it answers DMs on. Read-only, and the companion to `communications.list`: each entry's `address` is exactly the string to pass as that tool's `address` filter (E.164 for a number, the bare address for email, Meta's numeric id for a Page or Instagram account), which is the same list the communication log's channel filter shows. The far side of a conversation is not here — that is every contact the workspace has ever spoken to, and `communications.list`'s `search` is how you find those.
- communications.unreadNew since you last looked
Count what has come in on each channel since the caller last opened the communication log — the numbers the log's filter pills and the top-bar badge show. Inbound only, archived entries excluded, and CALLS ARE COUNTED ONLY WHEN MISSED (no answer, busy, failed or canceled), because an answered call was already handled by a person. Never reaches further back than 30 days. Read-only — this does not mark anything as seen. The read line is per person: an API key has no personal one, so for key callers this returns everything inbound in the whole 30-day window rather than anything about a particular user.
- communications.mark_seenMark the log as seen
Clear the caller's unread counts by moving their read line up to now — the same thing that happens automatically when they open the communication log in the app. Pass `types` to clear only some channels (reading the Emails view shouldn't dismiss missed calls). Destroys nothing: every call and message stays exactly where it was, and only this one person's badge changes. Requires a signed-in user; an API key has no personal read line to move.
- communications.archiveArchive
Take one entry out of the communication log once it's been dealt with. NOT a delete: the call or message stays on file with its recording, transcript and body intact, still visible in its conversation thread and on the contact's timeline — it just stops appearing in the log and in the dashboard's Recent Communication column. Reversible with communications.unarchive.
- communications.unarchiveRestore to log
Put a previously archived call, text or email back into the communication log, where it returns to its original place in the timeline.
community
- community.list_spacesList spaces
List the spaces (feed categories, e.g. 'General', 'Wins') in one community group, in display order — including each space's access rules (level gate, required access product, staff-only posting).
- community.create_spaceCreate a space
Add a space (feed category) to a community group. Members see it in the feed sidebar immediately. Optionally gate it behind a member level or an access product, or lock posting so only staff can start threads (an announcements space).
- community.update_spaceEdit a space
Update a space's name, description, emoji, position, access rules (level gate / required access product), or staff-only posting. Omitted fields are left alone. Tightening access hides the space from members who no longer qualify immediately.
- community.delete_spaceDelete a spaceconfirm
Permanently delete a space AND every post, comment, and reaction inside it. Members lose that content immediately and it cannot be recovered. To hide a space without destroying content, gate it with update_space instead.
- community.get_settingsCommunity settings
Read one group's community configuration: whether the community is enabled, the description and welcome message, the level curve (level names + point thresholds), and the points awarded for post/comment likes. Also returns the EFFECTIVE levels and points rules (defaults applied), which is what members actually experience.
- community.update_settingsUpdate community settings
Create or update a group's community configuration. Turning `enabled` on makes the community live at the site's /c URL for signed-in members (a default 'General' space is created if none exists); turning it off takes it away immediately. Levels and points rules are member-visible: changing thresholds can re-rank existing members' levels at once. Omitted fields keep their current value.
- community.list_postsList posts
List a community group's feed — pinned posts first, then most recent activity. Optionally narrow to one space, or include removed (moderated-away) posts to review moderation history.
- community.get_postOpen a post
Fetch one community post with its full body, media, counters, and every published comment (threaded one level).
- community.create_postPost as staff
Publish a staff post (announcement) into a community space. Members see it in their feed immediately, marked as coming from the team, and any 'Community post created' automations fire. Authored as the acting user's contact — so this needs a signed-in user (Copilot), not an API key.
- community.remove_postRemove a postconfirm
Moderate a post away: it disappears from every member's feed immediately (comments go with it). The content is kept and can be brought back with community.restore_post — but members see it vanish, so treat it as a public moderation act.
- community.restore_postRestore a post
Bring a removed post back — it reappears in members' feeds immediately.
- community.pin_postPin a post
Pin a post to the top of the feed for every member (or unpin it with pinned=false). Newest pin sits highest.
- community.remove_commentRemove a commentconfirm
Moderate a comment away: it disappears from the post for every member immediately. The row is kept (status 'removed') and can be brought back with 'Restore a comment'.
- community.restore_commentRestore a comment
Bring back a comment that was removed by moderation. It reappears on its post for every member immediately.
- community.leaderboardLeaderboard
The top point earners for a community group (or the whole workspace when no funnel is given), over the last 7 days, 30 days, or all time — the same board members see. All-time entries include each member's level.
- community.award_pointsAward pointsconfirm
Add community points to a contact's score, or subtract them with a negative number. Points are MEMBER-VISIBLE: they move leaderboards and can change the member's level, which may unlock (or re-lock) level-gated spaces and course content, and a level-up fires the org's 'Member levels up' automations.
- community.member_activityMember activity
One contact's community footprint: their points total and level (in one group or org-wide), plus their most recent posts and comments — including removed ones, so moderators see the whole picture.
companies
- companies.listList companies
List the organization's companies, A–Z. Search matches the company name.
- companies.getOpen a company
Fetch one company by id, with all of its fields. Use contacts.list with company_id for the people who work there.
- companies.createCreate a company
Create a company record. Only a name is required; attach contacts to it afterwards with contacts.update.
- companies.updateEdit a company
Update fields on an existing company. Omitted fields are left alone; an explicit null clears one.
- companies.deleteDelete a companyconfirm
Permanently delete a company and its activity timeline. Contacts at the company are kept but are detached from it. This cannot be undone.
compliance
- trust.get_profileBusiness profile
Read the organization's Twilio business profile: the business details on file, whether Twilio has approved it, and anything still missing. This profile is the foundation — SMS brand registration and every voice-trust product depend on it being approved first.
- trust.save_profileSave business details
Save the legal business details Twilio and the carriers require: registered name, business type, tax/registration number, address, website, and the authorized representatives. Saved locally only — nothing is sent to Twilio until the profile is submitted, so this can be filled in over several sittings. Editing an already-submitted profile stages the change for the next resubmission.
- trust.link_profileFind my Twilio profile
Look on the organization's own Twilio account for the business profile they created in the Twilio Console, and link it to this workspace. Twilio provides no API to CREATE that primary profile — it has to be started in their Console — so this is how the two sides get connected. Prefers an already-approved profile when several exist.
- trust.check_profileCheck before submitting
Run Twilio's own free requirement check against the business profile and return which requirements pass and which fail. Costs nothing and does not submit anything. Worth running before every submission — a rejection costs days of review time for problems this check reports instantly.
- trust.submit_profileSubmit for reviewconfirm
Send the business profile to Twilio for review. Pushes the saved details to Twilio, runs the free pre-check, and refuses to submit if that check fails. Review typically takes up to 72 hours and the profile can't be edited freely while it's in review. Free, but it gates everything else: no SMS brand and no voice-trust product can be registered until this is approved.
- trust.refresh_profileRefresh profile status
Re-read the business profile's review status from Twilio right now, instead of waiting for the background check that runs every 15 minutes.
- trust.list_productsCarrier trust products
List every carrier-trust product — SHAKEN/STIR call signing, CNAM caller-ID name, Voice Integrity spam protection, Branded Calling, and the A2P messaging profile — with the organization's registration status for each, what each one costs, and how long Twilio takes to review it.
- trust.add_cnam_nameAdd another caller ID name
Start an additional CNAM registration, so different phone numbers can display different business names — useful when one organization trades under more than one name. Each registration carries exactly one name and covers whichever numbers are added to it. Creates a draft only; nothing is sent to Twilio and nothing is charged until it's registered.
- trust.delete_registrationDelete a registrationconfirm
Delete a carrier-trust registration that was never submitted. Only works while it is still a draft — once Twilio holds it, the bundle exists on the organization's Twilio account and has to be removed there instead.
- trust.save_productSave trust product details
Save the extra answers a specific trust product needs before it can be registered. CNAM needs the caller-ID name to display (15 characters maximum — carriers cut it off past that). Voice Integrity needs what the organization uses calling for, its employee count, and its average calls per business day. SHAKEN/STIR and the A2P messaging profile need nothing beyond the business profile. Saved locally; nothing is sent to Twilio until the product is submitted.
- trust.submit_productRegister with the carriersconfirm
Register a carrier-trust product with Twilio: creates the bundle, attaches the approved business profile and the product's own details, runs Twilio's free pre-check, and submits it. SHAKEN/STIR, CNAM and Voice Integrity are all free to register and take roughly 24–72 hours. Requires the business profile to be approved first. Branded Calling cannot be registered this way — Twilio has no API for it and it needs a signed Letter of Authorization, so it has to be started in the organization's own Twilio Console.
- trust.link_productLink an existing registration
Adopt a trust bundle that already exists on the organization's own Twilio account, by its bundle SID (starts with BU). This is how a Branded Calling registration — which has to be done in the Twilio Console — becomes visible here so its phone numbers can be managed in this workspace.
- trust.refresh_productRefresh registration status
Re-read one carrier-trust product's review status from Twilio right now, instead of waiting for the background check that runs every 15 minutes.
- trust.add_numberCover a number
Add one of the organization's phone numbers to an approved trust product, so calls from that number get its benefit — the highest SHAKEN/STIR attestation, the registered caller-ID name, or Voice Integrity's spam protection. The product must be approved first. Free.
- trust.remove_numberUncover a numberconfirm
Remove a phone number from a trust product. Calls from it immediately lose that product's benefit — a number pulled off CNAM stops showing the business name, and one pulled off SHAKEN/STIR drops to a lower attestation and is more likely to be labeled spam.
- a2p.get_brandSMS brand registration
Read the organization's A2P 10DLC brand: its registration status with the carriers, its trust score (which sets how many texts a day it can send), and whether a sole-proprietor phone verification is still outstanding.
- a2p.submit_brandRegister SMS brandconfirm
Register the organization as an A2P 10DLC brand with The Campaign Registry, which US carriers require before any business texting. COSTS REAL MONEY on the organization's own Twilio account: $4.50 to register, plus $41.50 for secondary vetting unless skipped. Both are NON-REFUNDABLE and are charged even if the carriers reject the brand. Requires an approved business profile and an approved A2P messaging profile. Sole proprietors must additionally give the owner's personal mobile — Twilio texts it and the owner has to reply YES within 24 hours, which no software can do for them.
- a2p.refresh_brandRefresh brand status
Re-read the SMS brand's status from Twilio right now, instead of waiting for the background check that runs every 15 minutes.
- a2p.resend_verificationResend owner verification textconfirm
Send the sole-proprietor verification text again. This sends a REAL text message to the business owner's personal mobile, and they must reply YES to it. Only applies to sole-proprietor brands. The whole verification expires 30 days after the brand was created.
- a2p.request_vettingBuy secondary vettingconfirm
Order third-party secondary vetting for the SMS brand. COSTS $41.50 on the organization's own Twilio account, NON-REFUNDABLE, and it cannot be undone or repeated. In exchange the brand gets a trust score, which raises how many messages a day the carriers will accept from it. Only worth doing for a brand registered without vetting that is now hitting its daily limit.
- a2p.list_campaignsList messaging campaigns
List the organization's A2P 10DLC campaigns — the registered descriptions of what it texts people about — with each one's carrier-approval status.
- a2p.get_campaignOpen a messaging campaign
Read one A2P 10DLC campaign in full: its use case, the description and opt-in wording registered with the carriers, its sample messages, its status, and anything still missing before it can be submitted.
- a2p.create_campaignNew messaging campaign
Start a new A2P 10DLC campaign as a draft. This only creates a row in this workspace — it does NOT create anything in Twilio, nothing reaches the carriers, and nothing is charged. The Twilio Messaging Service the campaign's numbers will send through is created later, the first time something needs it (a2p.list_use_cases or a2p.submit_campaign). Fill the draft in with a2p.update_campaign, then a2p.submit_campaign, which is the step that costs money.
- a2p.update_campaignEdit messaging campaign
Edit a draft campaign's registration details. What goes here is read by human carrier reviewers, and vague answers are the single most common reason campaigns get rejected — describe what the business actually texts people and how those people asked for it. Saved locally; nothing reaches the carriers until the campaign is submitted.
- a2p.list_use_casesAvailable campaign types
List the campaign use cases this organization's brand is actually eligible for, with each one's monthly carrier fee and whether it needs extra carrier approval. Always read this before setting a campaign's use case — the list depends on the brand's type and trust score, so a hardcoded guess gets rejected. NOT READ-ONLY despite the name: the eligibility list has to be queried through a Messaging Service, so if this campaign does not have one yet this CREATES a real Messaging Service in the organization's own Twilio account and saves it to the campaign. Twilio does not charge for a Messaging Service, but the resource is real, it persists, and it is the one the campaign's numbers will later send through. Nothing reaches the carriers and no registration fee is incurred here — that is a2p.submit_campaign.
- a2p.submit_campaignSubmit campaign to carriersconfirm
Submit an A2P 10DLC campaign to the carriers for approval. COSTS REAL MONEY on the organization's own Twilio account: a $15 NON-REFUNDABLE vetting fee charged once per campaign, plus a recurring monthly fee of $1.50 to $30 depending on the use case, billed for as long as the campaign exists. Carrier review takes 10–15 days and sometimes longer. Requires an approved brand. If a campaign is rejected it must be fixed and RESUBMITTED — deleting it and creating a new one is charged the $15 again.
- a2p.refresh_campaignRefresh campaign status
Re-read a campaign's carrier-approval status from Twilio right now, instead of waiting for the background check that runs every 15 minutes.
- a2p.add_campaign_numberAdd number to campaign
Put one of the organization's phone numbers behind a registered campaign. From then on texts sent from that number route through the campaign's Messaging Service, which is what makes them A2P-compliant and stops US carriers filtering them. A Messaging Service holds at most 400 numbers.
- a2p.remove_campaign_numberRemove number from campaignconfirm
Take a phone number off a campaign. Its texts immediately go back to sending unregistered, which US carriers routinely filter or block — do this only when moving the number to a different campaign.
- a2p.get_kitOpt-in site answers
Read the questionnaire behind this workspace's generated opt-in website, and the addresses it published — the sign-up page, privacy policy, terms and contact page that carrier reviewers open when judging an A2P 10DLC campaign. Read-only; returns nulls when nothing has been generated yet.
- a2p.save_kitSave opt-in site answers
Save the answers used to write this workspace's opt-in page, privacy policy, terms and A2P campaign wording. Saving alone changes nothing a visitor or a carrier can see — a2p.generate_site is what builds and publishes the pages. Omitted fields keep their current value. Fields left blank on the business profile (legal name, address, notification email) are filled in from it at generate time rather than asked for twice.
- a2p.generate_siteGenerate opt-in siteconfirm
Build and PUBLISH four public web pages from the saved answers — a sign-up page with an unticked SMS consent checkbox, a privacy policy carrying the mobile-data clause carriers require, terms carrying the full messaging disclosures, and a contact page — then write the matching description, opt-in flow, sample messages and policy links onto the A2P campaign draft (and its campaign type, once the brand is approved and its real eligibility can be read). The pages go live immediately at the workspace's own domain when one is given, or on its Chirply address otherwise, and are visible to anyone with the URL. RUNNING IT AGAIN OVERWRITES those four pages, discarding any edits made to them in the page builder. Free: nothing here contacts Twilio or the carriers and nothing is charged — submitting the campaign, which costs $15, is still a separate deliberate step (a2p.submit_campaign).
- a2p.apply_kit_to_campaignFill campaign from the kit
Rewrite an A2P campaign draft's description, opt-in flow, sample messages, policy links and confirmation text from the saved answers and the already-published opt-in site. Use it after editing the answers when the pages themselves have not changed. Only ever touches a draft or a rejected campaign — one already accepted by the carriers keeps the wording they reviewed. The campaign type is set too, but only once the brand is approved and the campaign already has a Messaging Service through which its real eligibility can be read; before that it is left blank rather than guessed, because an ineligible code looks answered and is rejected. Nothing is submitted and nothing is charged.
- a2p.start_autopilotSet up texting for meconfirm
Run the whole US texting registration end to end, unattended. From a workspace that has done nothing at all it will: adopt the Twilio business profile, submit the business details for review, register the A2P messaging profile, publish the opt-in website and policy pages, register the brand, write and register the campaign, and finally put the workspace's phone numbers behind it — each step starting automatically as the previous review is approved, over the days or weeks the carriers take. IT SPENDS THE ORGANIZATION'S OWN MONEY: Twilio bills their Twilio account $4.50 for the brand and $15 for the campaign, both non-refundable and charged even if the carriers reject. Nothing is charged unless approve_spend is true; without it the run still does everything free and stops before the first charge. It stops and asks for a human whenever a fact is missing, a review is rejected, the Twilio Console business profile does not exist yet (Twilio has no API for creating that one), or a sole proprietor has not replied YES to Twilio's verification text.
- a2p.stop_autopilotPause texting setup
Stop the unattended registration run and withdraw its permission to spend, so nothing further is submitted to the carriers and nothing further is charged to the organization's Twilio account. Anything already submitted keeps its own course — a registration under carrier review cannot be recalled — and its status goes on being tracked. Deliberately not gated behind a confirmation: stopping is the safe direction.
- a2p.autopilot_statusTexting setup progress
Where the unattended registration run has got to: which link of the chain it is on, whether it is working, waiting on a carrier review, blocked on a human, or finished, and the plain-language reason. Read-only, and it never contacts Twilio — safe to poll while waiting out a review.
- a2p.preflightTexting registration checklist
The whole path to sending registered business texts in one read — business profile, A2P messaging profile, opt-in site, brand, campaign and the numbers behind it — each marked done, waiting on a review, still to do, or blocked, with the reason and where to go next. Read-only: it reports what is already stored and never contacts Twilio, so it is safe to poll.
- unsubscribe.listUnsubscribed
List everyone the organization can no longer contact, with what each person is excluded from, whether they actually opted out (STOP, an unsubscribe link, the opt-out page, or a call keypress) or were excluded by staff, a provider, an import, or a connected app such as Stripe, and when. Read-only.
- unsubscribe.countsUnsubscribe totals
How many people are unsubscribed, broken down by what they're unsubscribed from. Useful for checking how much of an audience a campaign will actually reach before sending it.
- unsubscribe.get_contactCommunication preferences
Read one contact's communication preferences: which kinds of message they can still be sent, which they've been unsubscribed from, who unsubscribed them and how, plus the full history of every opt-out and resubscribe on their record. Check this before sending anyone anything one-to-one.
- unsubscribe.addUnsubscribe
Stop contacting someone. Immediately blocks every campaign, automation, and bulk send on the channels given — real messages that would otherwise have been sent are not sent. Works on a contact, or on a bare email address or phone number for someone who isn't in the CRM. Recorded with who did it and when, and reversible with unsubscribe.remove.
- unsubscribe.bulk_addUnsubscribe selected contactsconfirm
Unsubscribe a whole list of contacts at once, on the channels given. Immediately blocks every campaign, automation and bulk send to those people on those channels — real messages that would otherwise have gone out do not go out. Recorded as a bulk sweep rather than a series of one-offs, so the unsubscribed list shows it as one action. Reversible per person with unsubscribe.remove, which asks for confirmation.
- unsubscribe.removeResubscribeconfirm
Put someone back on the list, so campaigns and automations can contact them again on the channels given. ONLY do this when the person has actually asked to be contacted again — resubscribing someone who opted out is how a business ends up messaging people who told it to stop, which in the US carries statutory penalties per message. The original opt-out stays in the record permanently either way.
- unsubscribe.checkCan we contact them?
Check whether one email address or phone number is unsubscribed, before sending to it. Returns which kinds of message are still allowed and which are blocked. Cheap, read-only, and worth calling before any one-to-one outreach.
- unsubscribe.consent_proofProof of consent
The consent trail for one phone number or email address: every time it was opted in or out, how, and — for a tick-box on a web form — the exact wording that was displayed, the page it was on, the IP address and the timestamp. This is the evidence a mobile carrier, a complainant, or a TCPA claim asks for, and the reason to keep it in one place rather than in a form submission nobody can find. Read-only.
concepts
- concepts.listBrowse the glossary
List every Chirply concept the product explains in plain language — contacts, companies, lists, smart segments, tags, custom fields, lifecycle stages, unsubscribes, deals, campaigns and workflows — each with the one-line summary shown in the app. Read-only, costs nothing, and returns the same wording the user sees on screen.
- concepts.explainExplain a concept
Explain one Chirply concept in full: what it is, why a business would use it, how the rest of the product uses it, a worked example, and — most usefully — what it is NOT, so a list is not confused with a smart segment or a company with a client workspace. Accepts the user's own words ('smart segments', 'dynamic list', 'what's a tag'). Read-only and free.
connections
- connections.providersList connectable providers
List everything a client can be sent a connection link for — providers such as Facebook & Instagram, and calendars such as Google Calendar — and how each one completes: "oauth" means one click through the provider's own login, "form" means the client pastes an API key. Use the returned `target` value when creating a link. Read-only.
- connections.list_linksLinks you've sent
List this workspace's client connection links, newest first, with whether each one is still waiting, already connected, expired or cancelled. The link URL itself is NOT included — fetch a single link with connections.get_link to get it.
- connections.get_linkOpen a connection link
Fetch one client connection link by id, INCLUDING the URL to send. Anyone holding that URL can connect an account into this workspace until it is used, expires, or is cancelled — treat it as a credential and send it only to the intended person.
- connections.create_linkCreate linkconfirm
Create a named link that lets someone OUTSIDE this workspace — a client, a business owner — connect their own provider account into it. Returns a URL. Anyone who holds that URL can attach an account to this workspace until it is used once, expires, or is cancelled, so send it only to the person it is meant for. No message is sent by this action; delivering the link is up to you.
- connections.revoke_linkCancel linkconfirm
Cancel a connection link that hasn't been used. It stops working immediately, including for someone part-way through the provider's login screen. This cannot be undone — issue a new link instead. Any connection the link ALREADY produced keeps working; disconnecting that is a separate action on the integration itself.
consent
- consent.get_policyCookie consent settings for a site
Reports the cookie-consent banner settings for one tracked website or hosted-page set — whether a banner is shown and to whom, what happens before a visitor chooses, the wording, the link to the workspace's privacy policy, which categories a visitor can pick, and how long a choice is remembered. Use it to audit whether consent is switched on across a workspace's sites. Read-only.
- consent.set_policyChange cookie consent for a siteconfirm
Turns the cookie-consent banner on or off for one tracked website or hosted-page set, and sets its wording, privacy-policy link, categories and how long a choice is remembered. Switching it on has a REAL EFFECT ON VISITORS AND ON DATA: with mode 'eu' or 'all' and prior 'block', affected visitors see a banner and NOTHING is collected about them — no page views, no heat maps, no session recordings — until they accept, so analytics for those regions will drop. Switching it off stops asking and resumes collecting immediately, which may be unlawful in the EEA and UK. The banner always offers Reject as prominently as Accept and never pre-ticks a category; that is not configurable.
copywriting
- copywriting.list_frameworksBrowse frameworks
List the 45 built-in email copywriting frameworks — AIDA, PAS, Soap Opera Sequence, SPIN Selling and the rest — each with the reader awareness it assumes, the psychological levers it pulls, and how many emails its own arc runs to. Read-only and free; nothing is generated or sent.
- copywriting.get_frameworkOpen a framework
Read one copywriting framework in full — its origin, the psychology it relies on, its step-by-step arc, strategic notes, the mistakes it warns against, the objections it pre-handles, a worked example email, and its campaign breakdown (the email-by-email brief used to generate a sequence). Read-only and free.
- copywriting.suggest_frameworkSuggest a framework
Rank the frameworks that fit a particular audience and goal, with the reason for each. Say where the audience stands with the business (cold, engaged, customer, gone quiet) and what the email is for, and this matches that against the reader-awareness stage each framework was built for. Read-only and free — it picks nothing and writes nothing.
- copywriting.get_brand_voiceView brand voice
Read the workspace's writing rules — audience, what it sells, tone, reading level, whether it speaks as 'I' or 'we', sign-off, banned words, emoji policy and any pasted writing sample. These shape every AI-generated email, subject line and sequence. Read-only.
- copywriting.set_brand_voiceSave brand voice
Replace the workspace's writing rules. Every field is overwritten with what you pass, so send the whole voice, not just the parts you are changing. This changes how all future AI-generated copy sounds; it does not rewrite anything already written, and sends no email.
- copywriting.write_emailWrite an email
Write one complete email — subject line, preheader and body blocks — following a chosen copywriting framework, in the workspace's brand voice, grounded in the facts stored in its AI Brain. Returns a draft for review: nothing is saved to a campaign and nothing is sent. Runs on the workspace's own OpenRouter account and bills it for the tokens used.
- copywriting.write_seriesWrite a sequence
Write a complete multi-email sequence from a framework's own campaign breakdown — each email written to its role in the arc ('Episode 2 - High Drama'), with a sending delay chosen to suit the framework's pacing. Returns drafts for review: no automation is created, nothing is scheduled and nothing is sent. Pass the result to copywriting.build_series_workflow to turn it into a real automation. Runs on the workspace's own OpenRouter account and bills it for the tokens used; a long sequence is a large generation.
- copywriting.grade_emailGrade an email
Score an existing email against a copywriting framework, step by step: what the draft does at each stage of the arc, the specific fix for each, a subject-line score with three stronger alternatives, and which of the framework's known mistakes the draft is making. Read-only — it changes nothing and sends nothing. Runs on the workspace's own OpenRouter account and bills it for the tokens used.
- copywriting.rewrite_emailRewrite an email
Rewrite an existing email so it follows a chosen framework, keeping every fact, offer, price and link from the original and changing only the structure and language. Returns a draft for review: the original is untouched, nothing is saved and nothing is sent. Runs on the workspace's own OpenRouter account and bills it for the tokens used.
- copywriting.build_series_workflowBuild the sequenceconfirm
Turn a generated sequence into a real automation: one 'send email' step per email with a genuine wait between them, saved onto an existing workflow's flow graph so it can be edited in the visual builder. DESTRUCTIVE — it replaces that workflow's entire flow, so use an empty or throwaway workflow unless you mean to overwrite. The automation is left PAUSED with a manual start trigger: no contact is enrolled and no email is sent until someone chooses a trigger and turns it on.
courses
- courses.list_coursesList courses
List the workspace's courses in display order, with status (draft/published/archived), which group's classroom each is mounted in, access rules, and gamification settings. Optionally filter by status or by the funnel a course is mounted on.
- courses.get_courseOpen a course
Fetch one course with its full outline — every module and lesson in order, including drafts and each lesson's quiz — the builder's view, not the member's.
- courses.create_courseCreate a course
Create a new course as a DRAFT (members can't see it until you publish). Optionally mount it in a group's classroom right away. The URL slug is generated from the title.
- courses.update_courseEdit course settings
Update a course's settings: title, description, cover, which group's classroom it's mounted in (funnel_id; null unmounts it), access rules (level gate / required access product), the points members earn per lesson and on completion, and the completion certificate. Omitted fields are left alone. Changes affect what enrolled members see immediately.
- courses.publish_coursePublish a courseconfirm
Publish a course so members who qualify (mounted group + access rules) can see and take it. This is outward-facing: the moment it lands, everyone with access sees the course in their classroom, half-finished modules included — only published LESSONS are visible, and publishing the course does not publish its draft lessons.
- courses.archive_courseArchive a course
Archive a course: members can no longer open it, but every enrollment, progress record, and certificate is kept. Re-publish it to bring it back. Use this instead of delete when people have taken the course.
- courses.delete_courseDelete a courseconfirm
Permanently delete a course AND everything under it: modules, lessons, quizzes, every member's enrollment and progress, and their issued certificates (public certificate links stop working). This cannot be undone — archive instead if anyone has taken it.
- courses.create_moduleAdd a module
Add a module (section) to a course, appended at the end. Optionally drip it (unlock N days after each member's enrollment) or gate it behind a community level.
- courses.update_moduleEdit a module
Update a module's title, description, position, drip delay, or level gate. Omitted fields are left alone. Gate changes apply to enrolled members immediately.
- courses.delete_moduleDelete a moduleconfirm
Permanently delete a module AND every lesson in it, including members' completion records for those lessons. Members' overall course progress recalculates without them. Cannot be undone.
- courses.create_lessonAdd a lesson
Add a lesson to a module, appended at the end. A lesson can carry an embedded video, rich HTML content, downloadable attachments, and an optional multiple-choice quiz (set quiz_required to make passing it the only way to complete the lesson). New lessons start as DRAFTS members can't see until you set status to 'published'.
- courses.update_lessonEdit a lesson
Update a lesson's content, video, attachments, quiz, position, duration, or status. Omitted fields are left alone; pass quiz null to remove the quiz. Publishing/unpublishing changes what counts toward every enrolled member's completion percentage.
- courses.delete_lessonDelete a lessonconfirm
Permanently delete a lesson, including every member's completion record and quiz attempts for it. Members' course progress recalculates without it. Cannot be undone.
- courses.enroll_contactEnroll a contactconfirm
Enroll a contact in a course. They see it in their classroom right away (once they can sign in), drip timers start counting from now, and the org's 'Enrolled in a course' automations fire — which can send that real person a real welcome email or SMS, billed to the org's own Mailgun/Twilio account, without any further step. Idempotent — enrolling someone already enrolled changes nothing and fires nothing.
- courses.unenroll_contactUnenroll a contactconfirm
Remove a contact from a course AND delete their progress — every completed-lesson record and quiz attempt for this course is destroyed and cannot be recovered (re-enrolling starts them from zero). An already-issued certificate is kept.
- courses.list_enrollmentsList enrollments
List who is enrolled in a course, newest first, with each person's name, how they were enrolled, completion percentage, and whether (and when) they finished.
- courses.get_contact_progressA contact's course progress
One contact's progress through one course: their enrollment (when, how, finished or not), the completion percentage, exactly which lessons they've completed, and their certificate serial if one was issued.
- courses.complete_lessonMark a lesson completeconfirm
Mark a published lesson complete for a contact, exactly as if they finished it themselves: it awards the course's per-lesson community points, enrolls them if they weren't yet, and — when it's their last remaining lesson — completes the whole course, awards the completion bonus, ISSUES A REAL SERIAL-NUMBERED CERTIFICATE in that person's name (if enabled), and fires the 'Lesson completed' / 'Course completed' automations, which can email or text them for real on the org's own provider accounts. It falsifies a learning record on someone's behalf and there is no un-issue for a certificate, so it always asks first. If the lesson requires a passed quiz, this refuses unless skip_quiz_gate is set.
- courses.list_certificatesList certificates
List completion certificates this workspace has issued, newest first — each with its public serial (the verification token printed on the certificate), recipient, and course. Optionally filter by course or contact.
- courses.get_certificateLook up a certificate
Verify a completion certificate by its serial (the public token printed on it). Returns the recipient, course, and issue date if the serial is genuine and belongs to this workspace.
- courses.generate_quizGenerate quiz with AIconfirm
Draft a 5-question multiple-choice quiz from a lesson's title and content using the workspace's own AI connection. This SPENDS MONEY: the request is billed to the org's own AI provider key (requires one under Settings → AI), so it is not a free read despite saving nothing. Returns the quiz for review — NOTHING is stored; persist it by passing the result to 'Update a lesson' as its quiz.
credits
- credits.get_balanceMy credits
Your workspace's prepaid credit balance for usage that runs on your provider's pooled account (calls, texts, email, AI, leads). Shows your remaining balance, any monthly allowance, whether you're paused for running out, your per-item prices, and recent activity. Read-only.
custom_objects
- custom_objects.list_typesCustom objects
List this workspace's custom object types — the user-defined record types beyond contacts, companies and deals (e.g. Properties, Pets, Policies) — including each type's field definitions.
- custom_objects.create_typeNew object type
Create a custom object type — a new kind of record this workspace can store (e.g. "Property"). Takes the singular and plural names plus the typed fields records of this kind carry. The type immediately appears in the app's navigation and its records become creatable everywhere. Owners and admins only: a type is workspace SCHEMA, not a record — it changes the sidebar and the shape of everyone's data.
- custom_objects.update_typeEdit object type
Rename a custom object type or change its icon, and optionally add new fields to it. Existing fields and records are untouched; the URL name (key) never changes. Owners and admins only, same as creating one.
- custom_objects.list_recordsList records
List the records of one custom object type, most recently updated first. Optionally filter to the records linked to a specific contact, or text-search across each record's stored values.
- custom_objects.get_recordOpen a record
Fetch one custom-object record by id, with all of its stored field values and the contact it's linked to.
- custom_objects.create_recordNew recordconfirm
Create a custom-object record with validated field values and optional contact linkage. Queues custom_record_created for enabled workflows, which may send messages or incur provider charges according to their configured steps.
- custom_objects.update_recordEdit a recordconfirm
Update supplied fields or contact linkage on a custom record, validating its field definitions. Actual changes queue custom_record_updated for enabled workflows, which may send messages or incur provider charges according to their configured steps. Unchanged values do not emit events.
- custom_objects.delete_recordDelete a recordconfirm
Permanently delete one custom-object record and all of its stored values. This cannot be undone.
dashboard
- dashboard.operationsWhat needs attention
What needs attention in this workspace right now — the triage cards on the default dashboard, read in one call. Returns up to six: Attention Required (everything currently failing, reconciled across appointments, automation runs, unpaid and failed invoices, broken provider connections and AI sessions, with the same total the red badge shows), Automation Health (active workflows, runs, failures and anything stuck running), Inbox & Response Queue (open and unassigned conversations, split by SMS and email), Workspace & Provider Health (which connected providers — telephony, email, payments — are healthy and which are broken), Lead Capture & Conversion (form views, responses, completion rate and funnel opt-ins), and Campaign Performance (attempted, delivered, opened and clicked). Each card comes back as headline metrics plus named rows, every row carrying the exact screen that resolves it. These are CROSS-DOMAIN reductions, which is why they live here and not in one of the feature domains — no per-domain capability can produce the reconciled attention total. Counting windows: the failure and health cards are as-of-now, while the volume cards (campaigns, lead capture) count over the requested day range. Read-only — it inspects, changes nothing, and sends nothing.
- dashboard.get_layoutDashboard layout
Read how your dashboard is arranged — which widgets are on it, in what order, how wide each one is (in columns of a 12-column grid), and which you have hidden. Also returns every widget this workspace could show and the widths each one supports, which is what dashboard.set_layout accepts. Personal to you: it does not affect or reveal what teammates see. Returns the default arrangement when you have never customized it. Changes nothing.
- dashboard.set_layoutSave dashboard layout
Rearrange your dashboard: the array order IS the top-to-bottom, left-to-right order of the widgets, `span` is how many of the 12 grid columns each one takes, `half_height` selects compact height so the card hugs its content instead of stretching to the tallest card in its row, and `hidden` drops it off the page without deleting anything. Width also selects the LAYOUT — most widgets have a compact build and a roomier one (Phone Numbers is a list at a third and a table at two-thirds), so dashboard.get_layout publishes which build each span renders. Personal to you; teammates' dashboards are unaffected, and no contact, call or pipeline data is touched. Unknown widget ids are ignored, a span the widget has no layout for is snapped to its nearest supported width, and any widget you leave out is kept HIDDEN (available to re-add, not deleted) — so a partial list shows exactly the widgets it names and nothing it doesn't. Call dashboard.get_layout for the valid ids and spans.
- dashboard.reset_layoutReset dashboard layout
Forget your saved dashboard arrangement so it goes back to the default: Activity and Overview full width, the Activity Log at two-thirds beside Tasks Due, then Live Visitors, Phone Numbers, Pipeline, Recent Communication, Revenue and Call Volume side by side, then New Contacts, the Activity Calendar beside Generate Leads, and Sub-accounts where entitled. Personal to you, and affects only where the widgets sit — nothing on them is changed or deleted. Widgets you had hidden come back if they are part of the default; widgets that launched hidden stay off the page until you add them.
data_subject
- data_subject.export_personEverything held about one personconfirm
Collects everything this workspace holds about one contact, across all 89 tables that can reference them — their profile, every message and conversation, calls and recordings metadata, page views and session recordings, form submissions, orders and invoices, course and community activity, and their suppression status. This is what you send someone who makes a GDPR Article 15 access request or an Article 20 portability request. Returns the rows themselves as JSON, capped per table. Nothing is changed or deleted.
- data_subject.erase_personErase one person permanentlyconfirm
PERMANENTLY DESTROYS everything this workspace holds about one contact and CANNOT BE UNDONE. Deletes their profile, messages, conversations, calls, session recordings, page views, IP addresses, form submissions and community activity outright; unlinks them from financial records (invoices, payments, orders), which survive without a name because the workspace needs them for its own accounting; and deliberately KEEPS their unsubscribe and do-not-contact entries, because deleting those is how you start messaging them again. This is how you answer a GDPR Article 17 erasure request. Writes an audit record proving it was done. Every affected table is reported, and if any table fails the result says the erasure is incomplete — re-running it is safe.
- data_subject.erasure_policyWhat an erasure does to each table
Explains, table by table, what erasing a person destroys, what it keeps but unlinks, and what it deliberately retains — with the reason for each. Read this before running data_subject.erase_person if you need to tell someone exactly what will happen to their data, or to answer an auditor asking how erasure is implemented. Read-only and takes no arguments.
- data_subject.get_retentionHow long this workspace keeps data
Reports the workspace's own automatic-deletion windows: how many days website visitor data, session recordings, call recordings and transcripts, message content and form submissions are kept before being deleted. A value of 0 means that kind is kept forever, which is the default for all of them. Read-only.
- data_subject.set_retentionChange how long this workspace keeps dataconfirm
Sets the workspace's automatic-deletion windows. SWITCHING A WINDOW ON PERMANENTLY DESTROYS DATA ON A TIMER and cannot be undone: anything already older than the window you set is deleted on the next nightly run. Pass a number of days per kind, or 0 to keep that kind forever. Contacts, deals, invoices and orders are never affected. Call retention removes the audio and transcript but keeps the call record itself. A value below a kind's documented minimum or above its maximum is stored as 0 (keep forever) rather than being clamped, so a mistyped number never becomes a deletion schedule nobody chose.
deal_templates
- deal_templates.listList deal templates
List the saved starting points for new cards. Pass pipeline_id to get exactly what the Add-deal form on that board offers: the templates tied to it plus the org-wide ones. Values come back as integer cents.
- deal_templates.createNew deal template
Save a starting point for new cards on a board: a title, value, starting stage, owner, custom fields and how many days out the expected close should be. Leave pipeline_id off to offer it on every board. Setting is_default makes the Add-deal form on that board open pre-filled with it, and clears the flag from any other template on the same board. Creates nothing on the board itself — it's a form pre-fill.
- deal_templates.updateEdit a deal template
Change a saved template. Omitted fields are left alone. Existing deals created from it are untouched — a template is a pre-fill, not a link. Setting is_default clears the flag from any other template on the same board.
- deal_templates.useAdd a deal from a template
Create a real deal on the board using a template as the starting point — the same thing as picking it under 'Start from' and submitting. The template's title, value, stage, owner, custom fields and 'close in N days' are applied, and anything you pass here overrides them. The template itself is unchanged.
- deal_templates.deleteDelete a deal templateconfirm
Permanently delete a saved template. Deals already created from it are NOT affected — a template is only a form pre-fill. Cannot be undone.
designs
- designs.listList designs
List the organization's Design Studio projects, most recently edited first, with canvas size and thumbnail URL.
- designs.getOpen a design
Fetch one design with its full document: pages of positioned text, image and shape elements on a fixed-size canvas. PNG export happens in the browser editor; machines read the doc here and the thumbnail URL from designs.list.
- designs.list_templatesBrowse design templates
List the built-in design templates — slug, name, category and canvas size — plus the size presets for a blank canvas. Pass include_documents to also get each template's full starter document, which is the actual arrangement of text, image and shape elements it would create; that payload is large, so it is off by default. Use a template's slug, or a preset's width and height, with designs.create. Read-only.
- designs.createNew design
Create a Design Studio project — blank at a given size, from a built-in template slug, or as a copy of an existing design (an org template, say). Returns the new design; edit its doc with designs.update.
- designs.updateEdit a design
Rename a design, replace its document, or promote/demote it as a reusable org template. `doc` REPLACES the whole document — there is no per-element patch and NO VERSION HISTORY, so whatever was on the canvas before is gone the moment this returns. Always read the current document with designs.get, change what you need, and send the whole thing back. The document is normalized on save: unrecognised element types are dropped and every number is clamped (20 pages, 200 elements per page, 10000 px canvas), so an element that silently disappears is one the schema did not recognise.
- designs.deleteDelete a designconfirm
Permanently delete a design project. Exported PNGs already in the media library are kept. This cannot be undone.
developers
- api_keys.listList API keys
List this workspace's API keys — name, display prefix, scopes, last-used time, and whether each is revoked. The secret itself is never stored in readable form and is never returned here.
- api_keys.createCreate an API keyconfirm
Mint a new API key for this workspace and return the full secret — this is the ONLY time it can ever be read, so hand it to the user immediately. The key can do anything an owner can within this org, limited only by its scopes. Treat it as a live credential.
- api_keys.revokeRevoke an API keyconfirm
Revoke an API key immediately. Any integration authenticating with it starts failing on its next request, and the key can never be un-revoked.
- api_keys.deleteDelete an API keyconfirm
Permanently delete an API key row, removing it from the list entirely. Same live effect as revoking — anything using it breaks — but it also loses the audit trail, so prefer api_keys.revoke unless the row is genuinely unwanted.
- connected_apps.listList Connected Apps
List the third-party applications connected to this workspace over OAuth — which app, who approved it, what it is allowed to do, and when it last made a request. Tokens themselves are stored only as hashes and are never returned.
- connected_apps.revokeDisconnect an Appconfirm
Disconnect a third-party application from this workspace. Its access stops immediately and every token it holds is destroyed, so any automation running through it — Zaps, scripts, scheduled syncs — stops working at once and cannot be resumed without the owner approving the connection again. There is no undo.
- webhooks.subscribeSubscribe a webhookconfirm
Register an HTTPS endpoint to receive platform events as they happen — the automation trigger names (contact_created, message_received, deal_won, invoice_paid, …). From then on this workspace POSTs every occurrence of the subscribed events to that URL, HMAC-signed, with retries — workspace data (contact ids, event context) flows to whoever controls the endpoint until the subscription is deleted. Returns the signing secret exactly ONCE; it cannot be read back, so hand it to the user immediately.
- webhooks.listList webhook subscriptions
List this workspace's outbound webhook subscriptions — each event × endpoint pair with its status and creation time — plus the full catalog of event names that can be subscribed to. Signing secrets are never returned here.
- webhooks.unsubscribeDelete a webhook subscriptionconfirm
Delete one outbound webhook subscription. Event deliveries to its endpoint stop immediately and its delivery log is removed with it. This cannot be undone — re-subscribing mints a NEW signing secret, so the integration on the other end must be reconfigured.
- webhooks.list_deliveriesView webhook deliveries
The delivery log for one webhook subscription, newest first: each attempt's status (pending, delivered, failed, or dead once retries run out), attempt count, last error, and timestamps — for debugging an endpoint that isn't receiving events. Read-only.
- webhooks.read_eventsRead the event stream
Read the platform events this workspace's webhook subscriptions have produced — oldest first, with each event's full payload — and walk forward with a cursor. This is the PULL side of webhooks, for a caller that cannot receive a POST: an AI agent connected over MCP has no HTTPS endpoint to deliver to, so it asks what happened since it last looked instead. Subscribe with webhooks.subscribe first (an endpoint URL is still required to register the subscription); every event that matches then shows up here whether or not that endpoint answered. Pass the previous reply's next_cursor as `after` to continue without re-reading or skipping. Read-only and costs nothing.
devices
- devices.listList desk phones
List the physical VoIP/SIP desk phones registered to this workspace — each with its label, SIP username, assigned seat, enabled state, assigned phone-number ids, and exact *1 through *9 outbound caller-ID codes — plus the SIP server address. Read-only, costs nothing, and NEVER returns SIP passwords.
- devices.set_outbound_orderSave keypad order
Choose the per-phone order behind the *1 through *9 outbound caller-ID shortcuts. This changes which owned business number a future desk-phone call presents when its prefix is dialed; it does not place a call, send a message, change inbound ringing, or spend money by itself. Active assigned numbers omitted from the list are appended after the listed numbers.
- devices.provisionAdd a desk phone
Provision a new physical VoIP/SIP desk phone on the workspace's OWN Twilio account and return the SIP server, username, and password to enter into the handset. The password is shown ONCE and can never be retrieved again (only regenerated). The workspace's SIP domain is created automatically on the first device. This spends no money by itself; once registered, the phone rings the workspace's numbers and can dial out, billed as ordinary Twilio calls.
- devices.set_enabledEnable or disable a desk phone
Turn a registered desk phone on or off. A disabled phone keeps its credential but stops ringing on inbound calls and can't dial out. Fully reversible.
- devices.assignAssign a desk phone to a seat
Assign a desk phone to a workspace member (seat), or clear the assignment by passing null. This records who the phone belongs to; it does not change which numbers ring it.
- devices.set_numbersSave phone numbers
Set exactly which workspace phone numbers belong to a desk phone. These numbers ring the phone on inbound team calls and become its allowed outbound caller IDs; on the handset, dialing normally uses the available workspace default and prefixes *1 through *9 select a listed number for one call. Pass an empty list to use EVERY team-routed number. Inbound routing still does not override numbers sent to an AI receptionist, IVR, blind forward, or conference.
- devices.regenerate_passwordReset a desk phone's password
Generate a new SIP password for a desk phone and return it ONCE, along with the SIP server and username. The old password stops working immediately, so the handset must be updated with the new one to keep working. Use if a password may have leaked or was lost (it can't be read back any other way).
- devices.removeRemove a desk phoneconfirm
PERMANENTLY remove a desk phone: its SIP credential is deleted on Twilio, so the handset can no longer register, ring, or dial. This CANNOT be undone — setting the phone up again means provisioning a fresh device with new credentials.
dialer_settings
- dialer_settings.getDialer settings
Read how the power dialer and the predictive dialer are set to behave — pace, whether an outcome is required, voicemail drop, lines per rep, answering-machine screening, whether AI answers first, and the calling window. Omit queue_id for the workspace defaults; pass one to see what that queue actually uses, which is the defaults with its own overrides applied.
- dialer_settings.updateChange dialer settingsconfirm
Change how the dialers behave. Only the fields you pass are changed; everything else keeps its current value. Omit queue_id to move the WORKSPACE defaults — which also moves every queue that has never disagreed with the setting you're changing. Pass queue_id to change one queue only. This spends nothing by itself, but it decides how hard the dialer works: raising lines_per_agent reaches more people per hour and drops more calls, and turning the AI on puts a synthetic voice in front of every person who answers. A session already running keeps the settings it started with.
- dialer_settings.follow_workspaceFollow the workspace settings again
Drop a call queue's own dialer settings so it goes back to using the workspace defaults — and keeps following them as they change. Nothing about the workspace defaults themselves is altered.
directories
- directories.getView directory
Return one directory website's configuration, publication state, paid-license state, branding, and public address.
- directories.update_settingsSave website settingsconfirm
Update one directory website's name, niche, geography, description, logo, brand color, and public listing terminology without changing its paid license or running data acquisition.
- directories.list_categoriesView categories
List the public browse categories configured for one directory website.
- directories.list_fieldsView listing fields
List the niche-specific structured fields and public filter settings configured for one directory.
- directories.list_listingsView listings
List business or entity listings in one directory, including draft, published, claimed, and archived records.
- directories.get_listingView listing
Return one directory listing with its public details, niche-specific values, and visibility state.
- directories.update_listingSave listingconfirm
Update a directory listing's public content, category, niche-specific values, and visibility. Publishing or archiving changes what visitors can see immediately on a live directory.
- directories.list_postsView articles
List draft, scheduled, and published CMS articles for one directory website.
- directories.get_postView article
Return one directory CMS article with its Markdown body, publication schedule, image, and search metadata.
- directories.update_postSave articleconfirm
Update a directory CMS article and set it to draft, scheduled, or published. Publishing makes the content immediately public; scheduling publishes it when its timestamp arrives.
- directories.delete_postDelete articleconfirm
Permanently delete one directory CMS article. The article and its public URL cannot be recovered after this action.
- directories.get_acquisition_statusView acquisition coverage
Return budget, spend, completion, saturation, unique-result, and duplicate counts for one directory's tracked Outscraper campaigns without submitting any paid searches.
- directories.connect_domainConnect domainconfirm
Connect and provision a custom hostname for one directory website through Cloudflare, including SSL setup. This changes live DNS routing and may replace a conflicting record when the workspace connected its Cloudflare account.
- directories.add_acquisition_budgetIncrease acquisition limitconfirm
Increase the hard Outscraper spend ceiling on an existing resumable acquisition campaign so it can continue through its still-uncovered cells without repeating completed searches.
- directories.create_fieldAdd listing field
Add a structured niche-specific field to one directory, optionally exposing it as a public browse filter.
- directories.create_categoryAdd category
Add a public browse category to one directory website. This changes that directory's taxonomy without affecting other directory instances.
- directories.create_listingCreate listing
Create a directory listing and create or reuse its canonical company in the CRM. Publishing makes it immediately visible on an already-live directory website.
- directories.create_postCreate articleconfirm
Create a CMS article for one directory. Publishing immediately makes the article publicly accessible and indexable on a live directory website.
- directories.submit_claimSubmit claimconfirm
Submit a public ownership claim for one published directory listing and email a real 24-hour verification link to the claimant. The claim reaches the directory review queue only after that email is verified. The verification is sent from THIS workspace's own verified sending address (or its parent agency's), billed to the workspace's own Mailgun/Resend account — never from Chirply, because the claimant is the workspace's user and not Chirply's. If the workspace has no verified sending address, nothing is written and the call fails: connect one under Settings → Email routing first (see the "directories" feature in readiness.status).
- directories.submit_leadSend request
Create a real inbound CRM contact and lead from a visitor request on one published directory listing. This stores the visitor's submitted contact details and message but sends no outreach.
- directories.list_claimsView listing claims
List email-verified ownership claims submitted against listings in one directory so staff can review them without exposing unverified submissions.
- directories.review_claimReview claimconfirm
Approve or reject a pending listing claim. Approval marks the listing claimed and creates or reuses the claimant as a CRM contact; rejection leaves the listing ownership unchanged.
- directories.list_leadsView directory leads
List visitor inquiries captured by one directory website, including their linked CRM contact when available.
- directories.update_lead_statusUpdate lead status
Move a directory inquiry between new, contacted, and closed while retaining its CRM contact and source attribution.
- directories.publishPublishconfirm
Publish a paid directory website immediately, making its homepage, listings, categories, and published CMS articles available to the public and search engines.
- directories.listView directories
List every independently configured directory website owned by this workspace, including its publication and billing status.
- directories.createCreate another directory
Create a new independently configured directory website in draft state. This prepares the site but does not publish it, run Outscraper, or spend money.
- directories.plan_acquisition_from_geographySave acquisition planconfirm
Resolve a plain-language market with one request to the workspace's connected Outscraper account, then create a budget-capped coordinate grid and permanent query ledger. The geography lookup may consume provider usage; this does not purchase any business records.
- directories.create_acquisition_campaignPlan data acquisition
Create a budget-capped Outscraper acquisition plan for one directory. This records the strategy and search terms but does not submit paid searches or spend money.
- directories.retry_failed_acquisition_queriesRetry failed queriesconfirm
Requeue every failed Outscraper query in one directory acquisition campaign and resume its worker. Retries can purchase real Google Maps records from the organization's Outscraper account, but the campaign's approved budget remains enforced.
- directories.run_acquisition_queryRun next uncovered cellconfirm
Submit one planned Google Maps query to Outscraper for a tracked geographic cell. This returns real billable records, publishes net-new directory listings, records duplicates and cost, and refuses to exceed the campaign's approved budget.
documents
- documents.listList documents
List the workspace's proposals, estimates, and contracts, including delivery and signature status. This only reads data and sends nothing.
- documents.getOpen document
Fetch one proposal, estimate, or contract with its linked invoice, signature certificate data, and audit trail. This only reads data.
- documents.createNew document
Create a draft proposal, estimate, or contract. This saves a private draft only; it does not email the recipient or create a charge.
- documents.updateSave changes
Edit a draft or outstanding commercial document. Signed, declined, expired, and archived records are immutable. This saves changes but sends no email and creates no charge.
- documents.sendSend for signatureconfirm
Immediately emails the real recipient a private link to review and electronically sign this document through a connected email identity. Sending email can incur the workspace's provider charges. No payment is taken until the recipient separately completes the linked invoice.
domain_leads
- domain_leads.searchSearch domain leads
Search the 26,086,322-record historical domain-registration dataset by a keyword in the domain name and return the person or business who registered each one, with their name, company, phone, email and postal address. Registration dates span 1985 through 2024; this is a historical dataset, not live WHOIS. Domains registered behind privacy/proxy services may not carry reachable contact details. Costs nothing to run. Requires the Domain Leads app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required and no data.
- domain_leads.getGet a domain's registrant
Look up one specific domain and return everything known about who registered it — name, company, phone, email, full postal address, registrar, and registration/expiry dates. Returns null when the domain isn't in the database or was registered behind a privacy proxy. Requires the Domain Leads app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required and no data.
- domain_leads.registrant_portfolioList a registrant's other domains
Given a registrant's email address, list the other domains that same person or business registered. Useful for gauging whether a lead is a real business (a handful of domains) or a domain speculator (hundreds). Requires the Domain Leads app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required and no data.
- domain_leads.list_filtersList domain endings and countries
List the domain endings (TLDs) and registrant countries available as search filters, each with how many domains carry it, most common first. Use this to discover valid values for the tld and country arguments of domain_leads.search. Requires the Domain Leads app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required and no data.
- domain_leads.get_inventoryView Domain Leads inventory
Return the exact number of records currently available in the shared Domain Leads dataset, plus its earliest and latest valid registration dates. Read-only and costs nothing. Requires the Domain Leads app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required and no data.
- domain_leads.export_csvExport Domain Leads CSV
Search Domain Leads and return the matching registration records as CSV text for download or downstream processing. Read-only and costs nothing. One machine call returns at most 5,000 rows; use offset to page through larger result sets. Requires the Domain Leads app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required and no data.
- domain_leads.import_to_contactsImport domain leads into contactsconfirm
Run a domain-leads search and copy the matching registrants into this organization's CRM as contacts, with their name, company, email, phone and website. Creates up to 500 contact records in one call — these are real people's contact details and will appear in the org's contact list, where they can then be called, texted or emailed. Does not send anything by itself and spends no money, but bulk-importing thousands of contacts is tedious to undo, so it asks for confirmation. Requires the Domain Leads app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required and no data.
domains
- domains.listDomains
List every custom domain this workspace has connected, with what is currently published on each one — the site or funnel at its root, how many short links point at it, and how many invoices are served from it. Includes whether each domain was bought through this platform or brought from another registrar, its DNS/certificate status, and its renewal date.
- domains.getDomain details
Read one domain: its status, how it was obtained, the DNS record required (if any), its expiry and auto-renew setting, and a breakdown of everything published on it.
- domains.connectConnect a domainconfirm
Connect a domain the organization ALREADY OWNS at another registrar (e.g. go.acme.com) and request a public TLS certificate for it. Does not buy anything and costs no money. If the workspace has its Cloudflare account connected (the cloudflare integration), the required CNAME is CREATED AUTOMATICALLY in the organization's own Cloudflare zone — replacing whatever DNS record previously answered at that exact hostname. Otherwise the domain does not serve traffic until the owner adds the returned CNAME record at their DNS provider; use domains.verify afterwards to check. Once live it can serve short links, a funnel or site, and invoices at the same time.
- domains.setup_dnsSet up DNS automaticallyconfirm
Create the CNAME for an already-connected domain inside the organization's OWN Cloudflare account (requires the cloudflare integration). WRITES to the organization's live DNS: any A/AAAA/CNAME record answering at that exact hostname is REPLACED with the platform CNAME, which changes where that hostname resolves for everyone on the internet. Touches only that one hostname, never the rest of the zone. Use on domains stuck 'Waiting for DNS' that were connected before Cloudflare was, or whose record was removed.
- domains.cloudflare_statusCloudflare DNS status
Report whether this workspace's own Cloudflare account is connected for automatic DNS, and list the zones (domains) that account can manage. Read-only — changes nothing in Cloudflare. Zone names reveal which domains the organization runs, so this is manager-only like the integration itself.
- domains.verifyCheck again
Re-check a domain's DNS and certificate with Cloudflare and store the result. Safe to call repeatedly; changes nothing except the recorded status. Owners and admins only, matching the button on /domains.
- domains.healthVisitors can't reach this domain
Read the stored reachability verdict for the workspace's custom domains — whether real HTTP requests to each hostname actually arrive at the platform, which Cloudflare's own status checks cannot see (a domain can show 'active' with a valid certificate while every visitor dies at the customer's misconfigured DNS proxy). Verdicts come from the automatic sweep that runs every few hours; a broken domain also carries plain-language fix steps. Reads stored state only — probes nothing and changes nothing.
- domains.updateDomain settingsconfirm
Change what a domain does: which website answers at its root, where the bare domain redirects when nothing is attached, where unknown paths go, and whether short links are allowed on it. Additional funnels use domains.add_funnel_route. Turning links off instantly stops every short link on this domain from resolving.
- domains.add_funnel_routeAdd routeconfirm
Publish an additional funnel on a named route of a connected domain. The funnel home becomes /route and its pages live below it (for example /summer/checkout). If the funnel is already published, this makes it reachable at the new address immediately. The route is rejected if a website page, short link, another funnel, or a reserved system route already uses it.
- domains.remove_funnel_routeRemoveconfirm
Remove one funnel route from a custom domain. The funnel and all of its pages remain in this workspace, but every URL below this domain route stops working immediately until the route is added again.
- domains.disconnectDisconnectconfirm
Stop serving a domain and release its certificate. Every short link, funnel page and invoice published on it stops working immediately and falls back to the platform's own address. Link names are unique platform-wide on that shared address, so a link whose name is already taken there is renamed with a numeric suffix as it moves — its old URL stops working either way. If the domain's CNAME was created in the organization's own Cloudflare, that record is removed too. A domain BOUGHT through this platform stays registered to the organization — this only stops serving it, it does not give up the domain or refund anything.
- domains.searchFind a domain
Search for domain names that are available to buy, with the price the organization would pay. Read-only — nothing is reserved or charged. Returns first-year and yearly renewal prices, which often differ.
- domains.check_availabilityCheck a domain
Check whether specific domain names are available and what they would cost. Read-only — nothing is reserved or charged.
- domains.purchaseBuy a domainconfirm
START buying a domain. THIS DOES NOT COMPLETE A PURCHASE and nothing is registered by this call: it prices the domain, creates a PENDING order, and puts an UNCAPTURED hold on a card in Stripe. No money is taken, the domain is NOT registered, and the organization does not own it when this returns — so do not report the domain as bought. Finishing requires a HUMAN in a browser at the returned /domains/buy link to confirm the card in Stripe's payment form, which no machine surface can do; that is what the returned requires_card_confirmation flag means. Only after that confirmation is the domain registered, the hold captured, and DNS configured automatically. Registration is non-refundable once it completes, and it renews yearly at the quoted renewal price unless auto-renew is turned off. Note the hold itself can reduce the card's available balance until it is captured or released.
- domains.set_auto_renewAuto-renewconfirm
Turn yearly auto-renewal on or off for a domain bought through this platform. With it on, the organization's card is charged about 30 days before expiry at the current renewal price. With it OFF the domain will EXPIRE at the end of its term and everything published on it will stop working.
- pixels.listPixels
List the organization's retargeting pixels. A pixel is defined once and can fire on any public surface — link interstitials, funnel pages, invoice pay pages — either everywhere or only where attached.
- pixels.createAdd a pixelconfirm
Add a retargeting pixel. THIS PUTS THIRD-PARTY TRACKING CODE ON LIVE PUBLIC PAGES that real visitors load, and it starts collecting their behaviour for the ad platform the moment it is live — so it is a privacy and consent decision, not just a setting. For a known provider give the ID from their ads manager and the snippet is generated safely; for anything else use provider 'custom', which injects the RAW SNIPPET verbatim and will run whatever JavaScript it contains. Setting all_surfaces fires it on every public page the organization serves — every funnel, link interstitial and invoice pay page at once — which is usually what's wanted. Attaching a pixel to a short link forces that link's interstitial page on, because a bare redirect renders no page for a pixel to fire on.
- pixels.updateEdit a pixelconfirm
Change a pixel's name, ID, snippet, placement, or whether it fires on every public page. Every change takes effect on LIVE public pages on the next page load: replacing custom_html swaps the JavaScript running in real visitors' browsers, and turning all_surfaces on starts firing it across every funnel, link interstitial and invoice pay page the organization serves. Pointing it at a different pixel ID sends visitor data to a different ad account.
- pixels.deleteDelete a pixelconfirm
Delete a pixel and remove it from everything it was attached to. It stops firing immediately and the audience it was building stops growing.
- pixels.attachAttach a pixelconfirm
Attach or detach a pixel from one specific link, funnel, page or invoice. Attaching starts firing it for real visitors to that live surface immediately, and on a SHORT LINK it also FORCES THE INTERSTITIAL PAGE ON — a link that used to redirect straight through now shows a page first, which changes what every existing recipient of that link experiences. Detaching stops the pixel there and the audience it was building stops growing. Not needed for pixels marked all_surfaces — those already fire everywhere.
- pixels.attachmentsWhere a pixel fires
List everything one pixel is currently attached to.
- scripts.listTracking scripts
List the organization's tracking scripts — Google Tag Manager, heatmaps, chat widgets, affiliate tags. Defined once and reusable on any public page, rather than pasted into each invoice or funnel separately.
- scripts.createAdd a tracking scriptconfirm
Add a tracking snippet that runs on the organization's public pages. The snippet executes in visitors' browsers on pages the organization controls. Setting all_surfaces runs it on every public page. Maximum 8000 characters.
- scripts.updateEdit a tracking scriptconfirm
Change a tracking script's name, snippet, placement, or whether it's active. Setting active to false stops it running everywhere at once.
- scripts.deleteDelete a tracking scriptconfirm
Delete a tracking script and remove it from everything it was attached to. It stops running immediately, and anything it was measuring stops being recorded.
- scripts.attachAttach a tracking scriptconfirm
Attach or detach a tracking script from one specific link, funnel, page or invoice. Attaching STARTS RUNNING THAT JAVASCRIPT in real visitors' browsers on that live page from the next page load; detaching stops it and anything it was measuring stops being recorded. Not needed for scripts marked all_surfaces.
- email.list_connectionsList email providers
List the workspace's connected Mailgun and Resend accounts, their verified domains, connection state, and whether inbound authentication is configured. Read-only; returns no provider secrets.
- email.list_identitiesList email addresses
List every ready or pending workspace email identity — the From addresses this workspace can send as — with its Mailgun/Resend routing, display name, reply-to and which one is the workspace default. This is the list the inbox and broadcast composers' sender pickers show, so any member can read it. Read-only; returns no provider secrets.
- email.connect_providerConnect email provider
Connect another Mailgun or Resend account to this workspace. The supplied API key is verified with the provider and encrypted before storage. This does not send email or create provider charges by itself.
- email.discover_domainsShow my sending domains
List every sending domain that already exists inside a Mailgun or Resend account, using an API key supplied with the call, and mark which of them this workspace has already connected. This is the lookup behind 'paste one key, pick your domains': the key is used for this read and discarded, nothing is stored, and no provider secret is returned. Costs nothing and sends nothing.
- email.connect_domainsConnect sending domains
Connect several sending domains from ONE Mailgun or Resend account in a single call, and optionally create the From addresses to send as on each. Every domain is verified with the provider before it is saved; the API key is encrypted at rest. This also changes settings inside the workspace's own provider account — it registers delivery webhooks, switches open tracking on, and, for domains marked to receive, points the provider's inbound route at this workspace so replies land in Conversations. Sends no email and spends no money by itself; the workspace's provider still bills any mail later sent through it.
- email.create_identityAdd email address
Create a selectable workspace From address on a verified Mailgun or Resend connection and optionally route inbound mail through a different connected provider. Sends nothing.
- email.update_identitySave sender details
Update the visible From address, friendly From name, and Reply-to address for an existing workspace email identity. A changed From address must stay on the identity's connected sending domain (and its receiving domain when inbound mail is enabled). This changes what future recipients see and where their replies are delivered; it sends nothing.
- email.set_default_identityMake default sender
Make this address the workspace's default From. It is used for two things: any email that doesn't name its own sender, and the workspace's authentication email — password resets, invitations and address confirmations are branded with it, so recipients see this address on the messages that get people into the account. Existing conversations keep their sticky identity, and contact-specific preferences still win. Only one address can be the default, so this replaces the current one. Sends nothing.
- email.update_identity_routingChange email routing
Change which connected provider sends and receives for an existing workspace email identity. Sends nothing.
- email.check_receivingCheck incoming email
Check whether every workspace address that is set to receive replies can actually be reached, and report the ones that cannot. A saved inbound provider is not enough on its own: if the domain's MX records still deliver mail to Google, Microsoft or its own mail server, incoming mail never reaches the platform and every reply is silently lost while the address still reads as receiving. Names where each broken domain's mail is going today and whether the DNS can be repaired automatically. Read-only — reads public DNS and the workspace's own Cloudflare zone list, changes nothing, and returns no provider secrets.
- email.fix_receiving_dnsPoint a domain's mail hereconfirm
Rewrite a receiving domain's MX records in the workspace's own connected Cloudflare account so incoming mail is delivered to the platform instead of wherever it goes now. THIS TAKES OVER ALL MAIL FOR THAT DOMAIN AND IS NOT LIMITED TO THIS ONE ADDRESS: every existing mailbox behind the domain's current mail servers — staff inboxes, a helpdesk, anything else — stops receiving the moment DNS propagates, and the displaced records are deleted, not kept. Only run this when the person owning the domain understands they are moving its mail. Requires a connected Cloudflare account that holds the zone; it fails cleanly with the records to add by hand otherwise. Changing the MX back is a manual DNS edit at their provider.
- email.set_ai_repliesSave AI repliesconfirm
Choose who answers email sent to one workspace address: nobody, or one of the workspace's AI agents. In draft mode the agent writes a reply that waits in the conversation until a person reads it and presses Send — nothing leaves the address on its own. In send mode the agent answers real people from that address, unattended, with nobody checking first; those replies are real email billed to the workspace's own Mailgun or Resend account and cannot be recalled. The agent never answers bounces, out-of-office notices, mailing lists or no-reply addresses, and never someone who asked to unsubscribe. The address must already be set up to receive mail, and the agent must be active. Changes take effect on the next email that arrives.
- email.list_ai_draftsAI replies waiting for approval
List the email replies an AI agent has written that are still waiting for a person to approve or discard, with the agent that wrote each one, the address it would go out from, and the full text. Read-only; sends nothing.
- email.send_ai_draftSend AI replyconfirm
Approve one AI-written email reply and send it immediately to the contact, as real email from the workspace's own Mailgun or Resend account — billed to them, and impossible to recall once gone. Optionally replace the subject or body first, in which case the edited text is what is sent and what is kept on the record.
- email.discard_ai_draftDiscard AI reply
Throw away one AI-written email reply so it is never sent. The contact hears nothing back from the agent on that message; a person can still write their own reply. The text is kept on the record as what the agent wanted to say.
- email.delete_identityDelete email addressconfirm
Permanently delete one workspace From address. The provider connection, its domain and every other address on it are left alone — only this sender is removed. Past conversations, campaigns and messages keep their history but lose their pinned sender, so their next reply goes out from the workspace default instead, changing what those recipients see. If the address belongs to any sending pool it is dropped from that rotation, concentrating the volume on the pool's remaining members. The workspace default sender cannot be deleted while another address could take over, because it is what brands authentication email such as password resets.
- email.send_testSend testconfirm
Immediately sends one real test email through the selected workspace Mailgun or Resend identity to the requested recipient. The workspace's provider may charge for this delivery.
- email.tracking_statusCheck open tracking
Report, for each connected Mailgun or Resend account, whether the provider is currently tracking email opens and clicks. Read-only; asks the provider directly rather than reporting a stored value, so it is the way to tell 'nobody opened it' apart from 'nothing was tracking it'. Returns no provider secrets.
- email.enable_open_trackingTurn on open trackingconfirm
Switch open tracking on for the workspace's connected Mailgun or Resend accounts, so the provider inserts its tracking pixel and reports opens. This changes a setting inside the workspace's OWN provider account and applies to every email that account sends, including mail sent by other tools using the same domain. Click tracking is deliberately left alone. Sends nothing and costs nothing.
- email.set_trackingSet email trackingconfirm
Turn open tracking and click tracking on or off for ONE connected Mailgun or Resend account. This changes the setting inside the workspace's own provider account, so it applies to every email that account sends from that domain — including mail sent by other tools using the same domain, and including mail already queued. Turning open tracking off stops the provider inserting its invisible tracking pixel, so opens stop being recorded for recipients from that moment on; turning it on starts recording them. For Resend this also names the tracking subdomain and publishes its CNAME in the workspace's own DNS, because Resend applies neither flag until that record verifies. Sends no email and costs nothing.
- email.set_warmupSet sender warmup
Turn a warmup ramp on or off for one workspace email address. While warming, pool rotation caps that sender at its current daily allowance (start volume + increment per elapsed day, leveling off at the maximum, counted per UTC day). Direct sends from the address are still counted but not blocked. Changes future pacing only; sends nothing.
- email.sender_usageSender usage today
Show today's send count for every workspace email address alongside its effective bulk daily cap (warmup allowance and any automatic reputation slow-down combined; null when uncapped), so you can see which senders have headroom left and which have been slowed down for high bounce/complaint rates — and exactly why. Counts reset at midnight UTC. Read-only. Owner/admin only, matching the Email pools settings screen this table appears on.
- email.check_domain_healthCheck sending-domain health
Run a deliverability health check on the workspace's connected email sending domains: provider verification, SPF, DKIM, DMARC policy, whether the domain can receive replies (MX), and its sending reputation. SPF/DKIM come from the provider's own verification; DMARC and MX are looked up in public DNS. Reputation is graded from bounce, spam-complaint and open rates over the last 30 days — from this workspace's own send history where there is enough of it, otherwise from the provider's own totals for the domain, which cover ALL mail sent from it including mail not sent through Chirply. Read-only and free — it queries DNS and the provider API, sends nothing.
- email.sender_healthSender health
Show how warm and how healthy every workspace sending address is, from what the email provider actually reported over the last 30 days. Each address gets a temperature — cold (no recent history, inbox providers have nothing to go on), warming (ramping, or sending below the volume of an established sender), warm (established and safe for normal campaign volume) or hot (sustaining high daily volume) — plus its bounce rate, spam-complaint rate, open rate, a reputation verdict, and a one-line answer to 'is this good to send from?'. Also reports any address currently resting. Read-only and free: it reads the workspace's own send history, contacts no provider and sends nothing. Owner/admin only, matching the Email pools settings screen.
- email.rest_senderRest a sender
Take one sending address out of pool rotation for a few days so its reputation can recover — the right response to a spiking bounce or spam-complaint rate. While resting, bulk sends through any pool skip this address and lean on the pool's other members, which concentrates the same volume on them; one-to-one replies and direct sends from the address still go out, so nobody mid-conversation is cut off. Pass 0 days to end a rest immediately and put the address straight back in the rotation. Reversible at any time; sends nothing and costs nothing.
- email.dmarc_summaryDMARC report summary
Show what mailbox providers have reported back about mail claiming to be from this workspace's sending domains, over the last 30 days. DMARC aggregate reports come from the RECEIVERS — Google, Microsoft, Yahoo and everyone else — so unlike delivery statistics they cover every provider and include mail this workspace did not send. For each domain: how many messages were seen, how many passed authentication, how many were sent to spam or rejected, the DMARC policy currently published, which receivers reported, and the sending IPs that FAILED authentication with the domains they signed as. A failing source is usually a legitimate service missing from the domain's SPF or DKIM records; one nobody recognises is someone sending as the domain. Read-only and free — it reads reports already received, contacts nobody and sends nothing. Owner/admin only, matching the Email pools settings screen.
email_policy
- email_policy.getView email preferences
Read the workspace's unsubscribe-link and double-opt-in preferences, its org-wide daily email sending limit, and the automatic reputation slow-down thresholds. Changes nothing and sends no email.
- email_policy.updateSave settings
Update whether workspace opt-out links are appended to email, which subscriber sources require double opt-in (including the confirmation email copy), the org-wide daily email sending limit, and the automatic slow-down thresholds for senders whose bounce/complaint rates spike. Saves configuration only and sends no email immediately — but a daily limit changes when bulk campaigns pause, and throttle thresholds change how fast struggling senders may send.
email_pools
- email_pools.listList email pools
List the workspace's email sending pools — named sets of From addresses that bulk sends rotate across — with each pool's member count, reply-to address, active/paused status, warmup plan, and how many emails it has sent today. Read-only. Owner/admin only, matching the Email pools settings screen.
- email_pools.getView email pool
Show one email pool in full: its reply-to, status, the warmup plan applied to its members, and every member address with today's send count, its current warmup allowance, and whether it is resting. Read-only. Owner/admin only, matching the Email pools settings screen.
- email_pools.createCreate email pool
Create an email sending pool. Bulk sends addressed to the pool rotate across its member addresses (least-used-today first, respecting each member's warmup allowance). The pool's reply-to overrides every member's individual reply-to; leave it blank and replies return to whichever address sent each message. Creating a pool sends nothing.
- email_pools.updateUpdate email pool
Rename an email pool, change or clear its reply-to, pause or reactivate it, or replace its member list. A paused pool refuses new sends; in-flight campaigns pointing at it park until it is active again. Omitted fields are left alone. Sends nothing.
- email_pools.add_senderAdd to pool
Add ONE workspace email address to a sending pool, appended to the end of the rotation. Use this instead of replacing the whole member list when a single sender is joining. The address must already be ready to send, since a pending sender in a pool is skipped at send time. Re-adding an existing member does nothing and keeps its place in the rotation. If the pool has a warmup plan with auto-enrol switched on, the new member starts that ramp from its own day one rather than joining at the volume the established members already send. From the next bulk send onward, some recipients will see this address as the From — it does not send anything by itself.
- email_pools.remove_senderRemove from pool
Take ONE email address out of a sending pool's rotation. The address itself is not deleted and keeps working on its own; every other member of the pool is left in place. Later bulk sends through this pool stop coming from this address, which concentrates the same volume on the remaining members — worth checking against their warmup allowances. Sends nothing.
- email_pools.deleteDelete email poolconfirm
Permanently delete an email pool and its member list. The member email addresses themselves are NOT deleted and keep working individually. Any broadcast still pointing at this pool falls back to the workspace default sender on its next send, which changes what recipients see — that is why this asks for confirmation.
- email_pools.set_warmupWarm up pool
Put every address in a sending pool onto one warmup ramp, in a single call. While warming, pool rotation caps each member at its own daily allowance (day-one volume, plus the increment for each elapsed day, levelling off at the maximum, counted per UTC day) and parks the rest of a campaign until the next day rather than failing it. Pick a named plan — gentle (5/day +5, up to 100), standard (10/day +10, up to 200) or fast (25/day +25, up to 500) — or 'custom' with your own numbers. Staggering starts each member's ramp a few days after the last, so a pool of new mailboxes does not step up in lockstep. Members that are not ready to send are skipped rather than enrolled, since their ramp clock would otherwise run down while they sat idle. Changes future pacing only; sends nothing and costs nothing.
email_signatures
- email_signatures.listList signatures
List every email signature in the workspace — shared ones and each teammate's own — with the rendered HTML each would append. Reads only.
- email_signatures.getOpen a signature
Fetch one signature by id, with its fields and the exact HTML and plain text it appends to an email.
- email_signatures.createCreate signature
Create an email signature and render it. Nothing is sent — but the FIRST signature in a scope automatically becomes that scope's default, so a workspace signature created here starts appending to outbound email from the next send. Image URLs (logo, portrait, banner) must be publicly reachable: mail clients fetch them directly and a login-protected URL shows as a broken image in every inbox.
- email_signatures.updateSave changes
Update an existing signature and re-render it. Omitted fields are left alone, except socials, which replaces the whole list when supplied. Sends nothing — but this signature is appended to email from the next send onwards, so a change here changes what recipients see.
- email_signatures.set_defaultMake default
Make one signature the default within its own scope, demoting the current default there. A workspace default is what signs email from anyone who has not made their own, so this changes what recipients see on the next send.
- email_signatures.deleteDelete signatureconfirm
Permanently delete a signature. This cannot be undone. Email already sent keeps the signature it went out with; new email stops using this one, and if it was a default the oldest remaining signature in that scope takes over.
- email_signatures.previewPreview a signature
Render signature HTML and plain text from a set of fields WITHOUT saving anything. Use it to check a layout before creating a signature, or to produce markup to paste into Gmail, Outlook or Apple Mail. Changes nothing and sends nothing.
email_validation
- email_validation.getView email validation settings
Read whether automatic email validation is enabled, which provider it uses, and which contact-capture channels are covered. Never returns provider credentials and performs no billable validation. Any member can read this, exactly as /settings/email-policy shows the panel to everyone and only disables the inputs for non-managers. `never_bounce_key_saved` says whether a NeverBounce key is on file; it is reported as false to members, because the page withholds that one fact from them too.
- email_validation.saveSave settings
Save automatic email-validation settings for the workspace. When enabled, each selected contact-capture channel can make a real single-address request to the workspace's Mailgun or NeverBounce account and may incur that provider's validation charges. An explicit invalid or disposable result blocks that email from entering the CRM; provider errors and timeouts fail open.
- email_validation.start_bulkValidate emailconfirm
Start a background job that validates each listed contact's email address through the workspace's Mailgun or NeverBounce account and stamps the verdict (valid, catch-all, unknown, invalid, disposable) on each contact. Every check is a real provider request that can use a paid validation credit; by default contacts whose current address was already checked are skipped. Returns the job id immediately — poll email_validation.job_status for progress.
- email_validation.job_statusValidation run progress
Read the progress of background email-validation runs: processed/total, live verdict counts, skips, failures, and final status. Pass an id for one run, or omit it for the most recent runs. Reading progress also nudges an unfinished run forward.
- email_validation.cancel_jobCancel validation run
Stop a queued or running background email-validation run. Contacts already checked keep their stamped verdicts; no further provider credits are spent.
enrichment
- enrichment.runEnrich contactconfirm
Fill a contact's EMPTY fields by cascading through data sources the workspace already has, cheapest first: facts captured about them as a tracked website visitor, the company domain inferred from a business email, the Domain Leads WHOIS database (only when the workspace owns that app), and a queued line-type lookup on the workspace's own Twilio (~$0.008). With allow_paid=true it may additionally spend ONE Outscraper Google-Maps lookup per contact (~$0.003, billed to the workspace's own Outscraper account) for business contacts still missing phone/address details. Existing values are never overwritten — this only fills blanks — and every step is recorded in the enrichment history with what it filled and what it cost.
- enrichment.historyEnrichment history
List what enrichment has done to a contact, newest first: for each run and step, which fields were filled with what values, which steps were skipped and why (paywall, paid lookups off, cap reached), and the estimated cost of any paid lookups.
events
- events.emitEmit an app event
Fire one of your app's own events into the workspace. Any automation whose trigger is 'App event' (matched to your app + this event key) runs. Use it to let people build workflows that start when something happens in your app. Needs the events:write scope.
forms
- forms.listList forms
List the workspace's standalone forms, surveys, quizzes, and applications, newest first. This only reads data.
- forms.getOpen a form
Fetch one standalone form in full: its draft question definition, behavior settings, design theme, and the separate published snapshot visitors currently see. The `definition.questions[].id` values in the result are what a response's `answers` object is keyed by, so this is where you get them before submitting an answer — and `definition`/`settings`/`theme` here are exactly the objects forms.update expects back, so read this first whenever you intend to edit one. This only reads data.
- forms.createCreate form
Create a new draft standalone form from a built-in starting point. This does not publish it or contact anyone.
- forms.updateSave form
Save a form's draft name, description, question definition, behavior, or design. WHOLE-OBJECT REPLACEMENT: `definition`, `settings` and `theme` are each stored as a single JSON blob, so passing one REPLACES it outright — send a definition with two questions and a form that had ten now has two, and the eight you left out are gone along with any logic rules that referenced them. Never assemble one of these from just the parts the request mentioned; read the current object with forms.get, change what you need, and send the whole thing back. Fields you omit entirely are untouched. Published visitors keep seeing the prior snapshot until Publish is run, so a mistake here is recoverable right up until then.
- forms.publishPublishconfirm
Publish the current draft as a new public snapshot at the form's hosted URL. Real visitors can immediately submit it, create CRM contacts, and trigger configured workflows.
- forms.unpublishUnpublishconfirm
Take a public form offline. Existing responses remain available, but its hosted link stops accepting new ones.
- forms.duplicateDuplicate form
Create a private draft copy of a form's questions, rules, settings, and design. Responses are not copied and nothing is published.
- forms.deleteDelete formconfirm
Permanently delete a form and every partial and completed response it collected. This cannot be undone.
- forms.get_share_linksShare a form
Get everything needed to put a form in front of people: its hosted public URL, the ready-to-paste <iframe> embed snippet for someone else's website, and whether it is actually published yet. This is the app's Share tab, which until now was the only place the slug was ever joined into a URL. A DRAFT form still returns its would-be link, but that link does not work for visitors until forms.publish has been run — check `published` before handing the URL to anyone. This only reads data and shares nothing on your behalf.
- forms.statsForm results
Get the four headline numbers from a form's Results screen: how many times the public form was VIEWED, how many responses were completed, how many were left partial, and the conversion rate (completed as a percentage of views, rounded). forms.list_responses returns the responses but has no view count, so conversion cannot be worked out from it — this is the only place that number exists. Counts cover the form's whole lifetime. This only reads data.
- forms.export_responsesExport responses CSV
Export a form's responses as CSV text, byte-for-byte what the Results screen's Export CSV button downloads: a header row of Status, Started, Completed, Score followed by one column per question (statement blocks are skipped because nobody answers them), then one row per response. Multi-select answers are joined with '; ' and an uploaded file shows as its filename. Columns follow the DRAFT question list, so a question added since a response came in appears as an empty column for that row. Returns the CSV as a string for you to save — nothing is emailed or uploaded anywhere. This only reads data.
- forms.list_responsesList responses
List partial or completed responses for a form, including answers, score, outcome, and linked CRM contact. This only reads data.
- forms.get_responseOpen response
Fetch one form response with its answers, completion status, score, outcome, and linked CRM contact. This only reads data.
- forms.submit_responseSubmit responseconfirm
Submit answers to a published form as a real completed response. This can create or update a CRM contact and immediately start any workflows listening for this form, which may send real email, SMS, or calls billed to the workspace.
- forms.save_partial_responseSave form progress
Save a respondent's answers SO FAR as a partial (unfinished) response — what the public form does in the background while someone is still typing, so a half-filled form is not lost. Deliberately quiet: unlike forms.submit_response this creates NO CRM contact and starts NO workflows, so nothing is emailed, texted, called or billed. The response is stored against your session_id and is upserted, so calling this repeatedly with the same session_id updates the same row as the person progresses; finish with forms.submit_response using that SAME session_id and the partial becomes the completed response. Required questions are not enforced until then. The form must be published. Note that the app only auto-saves partials when the form's 'Save partial responses' setting is on, whereas this capability — like the public route behind it — writes one either way.
- forms.upload_response_fileUpload response fileconfirm
Upload one file as the answer to a form question of type 'file' — the only way to answer that question type, since an answer to it is a stored file rather than text. Limits match the public form exactly: at most 10 MB, and only JPEG, PNG or WebP images, PDFs, plain text, or Word documents (.doc/.docx). THE SERVER DECIDES THE FILE'S TYPE, from the extension on `filename` plus the uploaded bytes' own signature — nothing you declare is stored or served, and a file whose contents do not match its extension (an HTML page named .png) is refused outright rather than filed away under the type it claimed. The file is written to the workspace's private form-uploads storage and STAYS THERE permanently, counting against the workspace's storage; there is no capability that deletes it. This does not answer the question by itself — take the object it returns and put it in `answers` under that question's id when you call forms.save_partial_response or forms.submit_response.
funnels
- funnels.listList funnels
List the organization's funnels, most recently edited first. Each funnel is a sequence of landing pages served at /f/<slug>.
- funnels.getOpen a funnel
Fetch one funnel with its steps (pages), connected custom domains, theme, and total form submissions. Page documents are omitted — use funnel_pages.get for a page's content.
- funnels.createCreate a site
Create a draft funnel, standalone website, or members-only member site and seed its home page with a ready-made layout. A member site (kind 'course', kept for compatibility) enables member login but does not publish or grant anyone access. Nothing is visible until funnels.publish is called. Actual courses (modules/lessons/quizzes) are built with the courses.* capabilities and mounted on a member site.
- funnels.updateRename a funnelconfirm
Rename a funnel or change its description or public slug. CHANGING THE SLUG MOVES THE FUNNEL'S PUBLIC URL: every link already shared at the old /f/<slug> — in sent emails, in live ads, on printed material, in other people's posts — breaks immediately and there is no redirect from the old address. Renaming alone is internal and safe; the slug is the irreversible part. Omitted fields are left alone.
- funnels.duplicateDuplicate a funnel
Copy a funnel — its theme and every page's working draft — into a brand-new DRAFT funnel on a fresh URL. Component ids are regenerated so the copy and the original can never interfere. Nothing is published, and the original is untouched.
- funnels.publishPublish a funnelconfirm
PUT THIS FUNNEL ON THE PUBLIC INTERNET. Every page of it that is itself published becomes reachable at /f/<slug> (and on any active custom domain) to anyone with the link, with no login. Publish only when the content is ready to be seen by customers.
- funnels.unpublishTake a funnel offlineconfirm
Take a live funnel off the public internet. Visitors on /f/<slug> and on any custom domain immediately stop being able to reach it. Content and pages are kept — publish again to restore it.
- funnels.deleteDelete a funnelconfirm
PERMANENTLY DESTROY a funnel and everything under it: every page and its published content, every saved version, the funnel's captured submissions, and its connected custom domains. This cannot be undone. Templates already saved from it, and funnels built from those templates, are unaffected.
- funnels.list_theme_presetsList theme presets
List the curated funnel theme presets — id, name, the mood each one suits, and its design tokens. These are the options the theme picker offers and the palette funnels.set_theme accepts.
- funnels.set_themeChange a funnel's themeconfirm
Restyle an entire funnel — colors, typography scale, fonts, surfaces, motion, corners, buttons and spacing — in one write. Every page reads these structured tokens, so no page edits are needed. Pass a preset id, explicit token overrides, or both (overrides layer on top of the preset). THIS IS OUTWARD-FACING AND IMMEDIATE: there is no draft step for a theme, so every ALREADY-PUBLISHED page of this funnel changes appearance for real visitors the moment it is saved, and the previous theme is not kept anywhere to restore from.
- funnels.statsFunnel performance
Conversion stats per step for a funnel over the last N days: views, unique visitors, form submissions, paid orders and revenue (cents, net of refunds). This is what the Performance report on the funnel page shows.
- funnels.list_submissionsList funnel submissions
List the leads captured by a funnel's opt-in and order forms, newest first, with the raw submitted field values and the contact each one was promoted into.
- funnels.list_ab_testsList A/B tests
List the A/B tests in this workspace — per test: the page it runs on, label, traffic weight, lifecycle status (draft/running/paused/winner/archived), and when it started or concluded. Filter by funnel, page, or status. Use funnels.ab_test_results for the numbers.
- funnels.create_ab_variantTest a different version
Create an A/B test on a funnel page: a full, independently editable copy of the page's current live content ("version B"). Created as a DRAFT — no visitor sees it and nothing changes on the live page until funnels.start_ab_test is called. A page can hold one active test at a time; creating a second returns an error naming the existing one.
- funnels.update_ab_variantEdit an A/B test variantconfirm
Change an A/B test's challenger: its document (same Puck shape as a page's content), its label, or the share of traffic that sees it. OUTWARD-FACING WHILE THE TEST RUNS: on a running test, a content change is served to real visitors in arm B within about a minute, and a weight change re-buckets visitors — which contaminates the results, so set the split before starting instead. Concluded tests (winner/archived) cannot be edited.
- funnels.start_ab_testStart an A/B testconfirm
PUT THE TEST IN FRONT OF REAL VISITORS. From the moment this runs (within about a minute of cache), the chosen percentage of visitors to the page is served the challenger document instead of the live page. Assignment is deterministic and sticky per visitor. Also resumes a paused test. The variant must not have concluded.
- funnels.stop_ab_testPause an A/B test
Pause a running A/B test: within about a minute every visitor sees the original page again. Results collected so far are kept, and the test can be resumed with funnels.start_ab_test or concluded with funnels.declare_ab_winner.
- funnels.ab_test_resultsA/B test results
Per-arm results for an A/B test: visitors, views, form submissions, paid orders, revenue (cents), conversion rate, and a plain-language statistical readout. Conversions = submissions + orders. A verdict is only called "confident" at p < 0.05 with at least 100 visitors in each arm; below that the readout says to keep collecting.
- funnels.discard_ab_testDiscard an A/B testconfirm
Archive a draft or paused A/B test without adopting it: the challenger document stops being served or editable, its collected results are closed out, and the test cannot be restarted. The LIVE PAGE IS NOT TOUCHED — visitors keep seeing it exactly as published. A running test must be paused (funnels.stop_ab_test) or concluded (funnels.declare_ab_winner) instead.
- funnels.declare_ab_winnerDeclare an A/B test winnerconfirm
CONCLUDE AN A/B TEST, IRREVERSIBLY CHANGING WHAT VISITORS SEE. Declaring 'b' OVERWRITES the live page's published content (and its draft) with the challenger's document — every visitor sees it immediately; the beaten version is kept only as a restore point in the page's version history. Declaring 'a' keeps the page exactly as it is and archives the challenger. Either way the traffic split stops and the test cannot be restarted.
- funnels.generateBuild with AIconfirm
Describe a business and an outcome, and the AI plans a conversion funnel or complete website, establishes a shared creative direction, writes every page, and picks a theme. Everything is saved as a DRAFT with no published content — generation can never put itself live, and nothing is shown to a visitor until you publish it. SPENDS REAL MONEY: it runs on the organization's OWN OpenRouter API key, so the model charge lands directly on the tenant's OpenRouter bill. There is no credit pool and no free allowance — every call is billed and metered against this workspace, whether or not the result is any good. Several model calls per run (a plan, then every page), and it takes up to a minute.
- funnels.match_screenshotMatch a screenshotconfirm
Analyze a reference website screenshot and restyle a supplied page document to match its visual direction using only structured, editable builder components. Returns the revised draft document but does not save or publish it — pass it to funnel_pages.save_content to keep it. SPENDS REAL MONEY: it runs on the organization's OWN OpenRouter API key, so the model charge lands directly on the tenant's OpenRouter bill. There is no credit pool and no free allowance — every call is billed and metered against this workspace, whether or not the result is any good. TWO billed calls per run: the vision pass over the screenshot, then the page edit.
- funnels.rewrite_copyRewrite page copyconfirm
Rewrite one snippet of page copy — a headline, a subhead, a button label — and return the new text. No page is changed; write the result back with funnel_pages.save_content yourself. SPENDS REAL MONEY: it runs on the organization's OWN OpenRouter API key, so the model charge lands directly on the tenant's OpenRouter bill. There is no credit pool and no free allowance — every call is billed and metered against this workspace, whether or not the result is any good.
- funnels.generate_imageGenerate a page imageconfirm
Generate an image from a description, store it in the organization's media bucket, and return its permanent URL for use in a page component's image prop. SPENDS REAL MONEY: it runs on the organization's OWN OpenRouter API key, so the model charge lands directly on the tenant's OpenRouter bill. There is no credit pool and no free allowance — every call is billed and metered against this workspace, whether or not the result is any good.
- funnel_pages.listList funnel steps
List a funnel's pages in step order. Page documents are omitted — call funnel_pages.get for a page's content.
- funnel_pages.getOpen a funnel page
Fetch one page with BOTH documents: `content` is the working draft you edit, `published_content` is what visitors currently see. Read this before editing a page — funnel_pages.save_content expects a document in the same shape.
- funnel_pages.update_seoSave SEO
Save a page's search title, meta description, canonical URL, social image, and search-index visibility. This changes metadata on the working page record; already published pages reflect it immediately.
- funnel_pages.createAdd a funnel step
Add a page to the end of a funnel, seeded with a ready-made starter layout for the chosen step type. It is created as a draft; publish it separately with funnel_pages.publish.
- funnel_pages.updateRename a funnel stepconfirm
Rename a page, change its URL path, or change its step type. CHANGING THE PATH MOVES THE PAGE'S PUBLIC URL: anyone who already has the old /f/<slug>/<path> — from an email, an ad or a link on another site — gets a not-found, with no redirect left behind. Renaming alone is internal and safe; the path is the irreversible part. The home page cannot be edited here — it is the funnel root and its path is fixed at ''.
- funnel_pages.generateBuild the pageconfirm
Use AI to write and design a complete new page, then append it to an existing funnel or website as a private DRAFT. It does not publish the page and contacts nobody. SPENDS REAL MONEY: it runs on the organization's OWN OpenRouter API key, so the model charge lands directly on the tenant's OpenRouter bill. There is no credit pool and no free allowance — every call is billed and metered against this workspace, whether or not the result is any good.
- funnel_pages.set_redirectRedirect a pageconfirm
Make a published page send visitors somewhere else instead of rendering. Takes effect on the live page immediately, on every address it is served at, including the funnel's custom domain. The page's content is kept untouched — pass an empty redirect_url to switch it back on. Works on the home page too, which is how a whole single-page site gets redirected.
- funnel_pages.reorderReorder funnel steps
Set the order visitors move through a funnel's steps. Pass every page id of the funnel in the order you want; the first one must be the funnel's home page. Order only — no content or URL changes.
- funnel_pages.save_contentSave a page draft
Replace a page's WORKING DRAFT with a Puck document. Visitors are unaffected — the live page keeps serving `published_content` until you call funnel_pages.publish — so this is safe to call repeatedly. NO VERSION IS SNAPSHOTTED: unlike funnel_pages.publish and funnel_pages.ai_edit, which each save a restore point first, this overwrites the draft outright and the previous draft is not recoverable from funnel_pages.list_versions. Take your own restore point with funnel_pages.snapshot before a large replacement. Read the page with funnel_pages.get first and edit the document it returns; components the registry doesn't recognise are dropped on save.
- funnel_pages.publishPublish a pageconfirm
PUT THIS PAGE ON THE PUBLIC INTERNET. Copies the working draft over the live version, so whatever the draft currently says becomes what visitors see at /f/<slug>/<path>. If the funnel itself is still a draft it is published too, because a live page inside an offline funnel is unreachable. A restore point is saved first, so this is undoable via funnel_pages.restore_version.
- funnel_pages.unpublishTake a page offlineconfirm
Take one live page off the public internet — visitors on its URL immediately stop being able to reach it. The rest of the funnel stays live. The draft and the last published content are kept, so publishing again restores it.
- funnel_pages.deleteDelete a funnel stepconfirm
PERMANENTLY DESTROY a page, its live content and its entire version history. This cannot be undone. The home page cannot be deleted — it is the funnel's entry point; delete the funnel instead.
- funnel_pages.list_versionsList page versions
List a page's saved restore points, newest first — every publish, every AI edit, and every manual snapshot. Documents are omitted; funnel_pages.restore_version brings one back.
- funnel_pages.snapshotSave a restore point
Save a named restore point for a page. Nothing about the page changes — this only records what the draft looks like now so it can be brought back later.
- funnel_pages.restore_versionRestore a page version
Bring a saved version back into the page's WORKING DRAFT. The live page is not touched — publish afterwards if you want visitors to see it. The draft being replaced is snapshotted first, so a restore is itself undoable.
- funnel_pages.generate_contentGenerate a page with AIconfirm
Generate a complete page document from a prompt and RETURN it for review — no page is created or changed. Pass the result to funnel_pages.save_content to keep it. SPENDS REAL MONEY: it runs on the organization's OWN OpenRouter API key, so the model charge lands directly on the tenant's OpenRouter bill. There is no credit pool and no free allowance — every call is billed and metered against this workspace, whether or not the result is any good.
- funnel_pages.ai_editEdit a page with AIconfirm
Apply a natural-language change to a page's draft document ("make the headline punchier", "add a testimonial section") and RETURN the new document — nothing is saved, so pass it to funnel_pages.save_content to keep it. The pre-edit document is snapshotted automatically. If the instruction implies a restyle, the funnel's theme IS changed immediately — that write is not held back for review, and it restyles every page of the funnel including any already published. SPENDS REAL MONEY: it runs on the organization's OWN OpenRouter API key, so the model charge lands directly on the tenant's OpenRouter bill. There is no credit pool and no free allowance — every call is billed and metered against this workspace, whether or not the result is any good.
- funnel_domains.listList custom domains
List the custom domains connected to the organization's funnels, with their verification and TLS status and the CNAME record the tenant has to create.
- funnel_domains.attachConnect a custom domainconfirm
Point a real hostname the organization owns (e.g. offers.acme.com) at a funnel and register it for a public TLS certificate. This publishes the funnel on a NEW address on the open internet as soon as the tenant's CNAME resolves. Returns the DNS record they must create; use funnel_domains.verify afterwards.
- funnel_domains.verifyVerify a custom domain
Re-check a connected domain with Cloudflare and update its status. Run this once the CNAME record exists — certificate issuance is asynchronous and completes on its own within a few minutes.
- funnel_domains.detachDisconnect a custom domainconfirm
Disconnect a custom domain and release its Cloudflare hostname and certificate. Everything served on that address STOPS immediately — the funnel, any single pages, and every LinkWizard link hosted on it, including copies already shared. The funnel itself, its /f/<slug> URL, and the links themselves are kept and can be moved elsewhere.
- funnel_templates.listList templates
List the funnel and website templates available to start from: the organization's own saved ones plus the built-in niche library. Returns each template's niche, tags, page names and paths, and setup checklist so you can choose a complete starting point. Every template is a frozen, fully editable copy of its theme and pages; listing creates and publishes nothing.
- funnel_templates.getOpen a template
Fetch one funnel or website template with its theme, niche, tags, setup checklist, and the full editable document of every page it carries. Use funnel_templates.use to copy all its pages as a private draft, then funnel_pages.ai_edit and funnel_pages.save_content to customize them. This read creates and publishes nothing.
- funnel_templates.save_from_funnelSave a funnel as a template
Freeze a funnel's theme and every page's WORKING DRAFT into a reusable template. It is a snapshot, not a link — editing the funnel afterwards never changes the template, and funnels built from it are never coupled back to it.
- page_templates.save_from_pageSave page as template
Freeze one funnel page's current working draft as a reusable individual-page template. It is a private snapshot; later edits to either the source page or a copied page never change the other.
- page_templates.add_to_funnelAdd page from template
Add a new draft page to an existing funnel or website from an individual-page template. Complete multi-page website templates must use funnel_templates.use so no pages are discarded. The copied builder document receives fresh component ids, stays fully editable, and is never published automatically.
- section_templates.save_from_pageSave section as template
Freeze one top-level section from a page's working draft as a reusable section template, including its nested columns, copy, images, and styles. Creating the template publishes nothing.
- section_templates.insert_into_pageAdd saved section
Append a fresh editable copy of a saved section to the bottom of a page's working draft. The public page is unchanged until someone publishes it.
- funnel_templates.useUse template
Create a new funnel or website from a template — theme and every page copied in, with fresh component ids. Returns each new page id and builder link, plus a setup checklist. Every section remains editable in the builder and by AI: call funnel_pages.ai_edit with a page id, then funnel_pages.save_content to keep the returned document. Everything lands as a DRAFT and is never published automatically; installation sends no messages and charges no payments.
- funnel_templates.deleteDelete a funnel templateconfirm
PERMANENTLY DESTROY one of the organization's saved funnel templates, including every page document frozen inside it. This cannot be undone. Funnels already built from it are unaffected. Built-in platform templates can't be deleted.
- products.listList products
List what the organization's funnels sell. A product can be the main offer on one funnel's order form and the order bump or one-click upsell on another. Amounts are integer cents.
- products.getOpen a product
Fetch one product with its pricing, currency and billing interval.
- products.createCreate a product
Add a sellable product to the catalogue so it can be attached to an order form, an order bump or a one-click upsell inside the builder. Creating a product charges nobody — checkouts run on the organization's own connected Stripe account, and nothing can be sold until Stripe is connected.
- products.updateEdit a productconfirm
Update a product's name, price, currency, billing kind, artwork, or which Stripe account and live/test mode it charges on. THIS CHANGES WHAT REAL BUYERS ARE CHARGED: a new price takes effect at the next checkout on every funnel offering this product, and switching payment_mode to 'test' means live customers' cards stop actually being charged while the pages carry on looking like they are working (switching to 'live' does the reverse). Past orders are never rewritten — an order item copies the amount at purchase time. Omitted fields are left alone.
- products.archiveArchive or restore a product
Take a product out of the builder's pickers (archive) or put it back (restore). Products are archived rather than deleted because past orders reference them and a sold product has to stay resolvable for reporting.
- funnels.list_ordersList funnel orders
List orders taken through the organization's FUNNEL checkouts — the Orders screen under Funnels — newest first, each with buyer, payment status, amount, refunded amount and, unless you turn them off, its line items (main product, order bump, upsells and downsells). This is the funnel side of the business and it has no other reader: commerce.list_orders answers only for the storefront and hard-excludes everything here. Amounts are in the smallest currency unit (cents), and collected revenue is total minus refunded counting ONLY orders whose status is 'paid' — a 'pending' order is an abandoned checkout, not a sale. Read-only. The order token is never returned: it is a bearer credential that can charge a buyer's saved card.
- funnels.get_orderOpen a funnel order
Fetch one FUNNEL checkout order with its buyer, payment status, amount and refunded amount, plus every line item — the main product, any order bump, and each upsell or downsell that was accepted, with what each was charged. commerce.get_order refuses anything that is not a storefront order, so this is the only reader for these. Amounts are in the smallest currency unit (cents). Read-only. The order token is never returned: it is a bearer credential that can charge the card saved on this order.
- funnels.set_payment_routeSave payment settingsconfirm
Choose which connected Stripe account collects the money for a funnel, and whether it charges in live or test mode. THIS DECIDES WHERE REAL CUSTOMER MONEY LANDS. Pointing it at the wrong account sends this funnel's takings to another business's Stripe; setting mode to 'test' means every checkout on the funnel's LIVE published pages silently stops charging anyone — the pages keep working and buyers keep getting confirmation, and nothing is collected. It applies from the next checkout onward: orders already placed keep the account and mode pinned onto them at the time, so nothing in the past moves. The equivalent for a storefront is commerce.update_store; funnels.update deliberately does not accept these two columns, so this is the only way to set them.
- funnels.submit_formSubmit formconfirm
Submit a real opt-in form on a PUBLISHED funnel page, exactly as a visitor filling it in would. THIS IS NOT A TEST: it creates or updates a REAL CRM CONTACT from the email and phone in the answers, stores a funnel submission against the page, counts toward that page's conversion stats, and immediately fires every `form_submitted` automation in the workspace — which can send real email, SMS or ringless voicemail to that person and bill the tenant's own provider accounts for it. There is no draft and no undo. Field names and which are required are read from the form's OWN published definition, not from what you send, so an answer for a field the form does not have is ignored and a missing required field is an error. Deduplication is the platform's: one phone number is one contact, matched on email first and then phone in any spelling it might be stored under, so submitting twice updates one person rather than creating two. Use funnels.list_submissions to read what has already come in.
- funnels.checkoutComplete my orderconfirm
Start a REAL purchase through a published funnel's order form: creates the order, creates or matches a Stripe customer on the seller's own account, and returns a PaymentIntent client secret for collecting the card. THIS IS THE LIVE CHECKOUT — unless the funnel is set to test mode (funnels.set_payment_route), the card charged is charged for real, on the tenant's own Stripe, and Stripe's fees apply. What is being sold and what it costs come from the ORDER FORM'S OWN published definition and from the product table, never from this call: you name the block, and the price is re-derived server-side, so there is no way to buy a $997 offer for $1. Nothing is collected yet at this point — the order is created as 'pending' and the card still has to be confirmed by whoever holds the client secret; call funnels.complete_checkout with the returned token once it clears. The returned `token` is a BEARER CREDENTIAL: for the next two hours it alone authorises funnels.accept_upsell to charge the saved card again with no further card entry, so treat it like a password and never store it where a reader of orders could reach it.
- funnels.complete_checkoutSettle a funnel orderconfirm
Settle a funnel order once its card payment has cleared: asks STRIPE whether the payment actually succeeded — the caller's word is never taken for it — and, if it did, marks the order paid, saves the card for one-click upsells, links or creates the buyer's CRM contact, records the revenue against the page, and fires the workspace's `purchase_made` automations, which can send real email, SMS or voicemail billed to the tenant's own provider accounts. Idempotent: an order already settled (by this call or by Stripe's webhook, which is authoritative and will settle it either way) simply reports 'paid' and does nothing again. This exists because the very next funnel step is usually a one-click upsell, which needs the order to be paid with a saved card NOW rather than whenever the webhook lands. It does not charge anything itself.
- funnels.accept_upsellYes, add it to my orderconfirm
Accept a one-click upsell or downsell on a funnel — the button on the offer page after a purchase. THIS CHARGES MONEY IMMEDIATELY AND WITH NO CARD ENTRY: it creates a NEW Stripe PaymentIntent and confirms it off-session against the card already saved on the original order, so the buyer is charged again the moment this returns. The only credential involved is the order token, which is why the window is short — the original order must be PAID and less than two hours old, or the charge is refused. What is sold and what it costs come from the upsell block's OWN published definition and the product table, never from this call, so a token buys that page's offer at that page's price and nothing else. Charging the same offer twice on the same order is prevented by a unique constraint, so a repeat call is a no-op rather than a double charge. On success it records the revenue against the page and fires the workspace's `upsell_accepted` automations, which can send real messages billed to the tenant's own provider accounts. There is no undo — reversing it means refunding in Stripe.
gohighlevel
- gohighlevel.read_history_archiveHighLevel history archive
Read locally archived HighLevel conversations and messages for this workspace. Returns twenty threads or a hundred messages per selected thread, with original IDs, dates, source status and attachment links. Imports cover mapped contacts only; partial threads are labeled. Original email HTML is displayed as text, attachment links can expire, and source-provided bodies may omit full email headers/content. This is an archive, not live inbox synchronization. Reading makes no provider calls, sends no messages and changes nothing. Owner/admin only.
- gohighlevel.provisioning_statusCheck free GHL sub-account
Check whether the platform's own GoHighLevel agency can provide this workspace a free sub-account and whether one is already provisioning, linked, or failed. Returns no platform or tenant OAuth credentials and changes nothing.
- gohighlevel.provision_subaccountGet free GHL sub-accountconfirm
Create a real GoHighLevel sub-account under the platform's own Agency Pro account and immediately attach encrypted location-level API access to this workspace. This consumes one of the platform's agency sub-account slots and creates persistent external CRM infrastructure, but sends no customer messages and charges the workspace nothing.
- gohighlevel.catalogList GHL integration coverage
List the official HighLevel API families covered by the universal request action and every named HighLevel Marketplace webhook trigger currently shown in the automation builder. Changes nothing.
- gohighlevel.webhook_statusCheck GHL webhook status
Read recent signed HighLevel webhook delivery status for this workspace, including received, processed, and failed totals. Returns no API token and changes nothing.
- gohighlevel.connection_statusCheck GHL connections
Check the connected HighLevel sub-account and agency grants, their granted scope counts, token expiry metadata, and a live read against each available authority. Returns no token and changes nothing.
- gohighlevel.browseBrowse GHL resources
Browse a guided, read-only HighLevel resource collection using the correct connected authority and account identifier. Supports contacts, pipelines, calendars, workflows, campaigns, conversations, products, forms, surveys, users, sub-accounts, and snapshots; changes nothing.
- gohighlevel.run_guided_actionRun a guided GHL actionconfirm
Run one of the named HighLevel CRM or agency actions using guided fields instead of an endpoint path or JSON. Depending on the selected operation, this can send real messages, create or alter CRM and agency records, spend provider funds, or permanently delete data in HighLevel; the side effect happens immediately.
- gohighlevel.readRun GHL read request
Run any GET endpoint in the official GoHighLevel/LeadConnector API with this workspace's encrypted token. The request is pinned to services.leadconnectorhq.com; the token itself is never returned. HighLevel may expose sensitive CRM or agency data, limited by the token's own scopes.
- gohighlevel.writeRun GHL write requestconfirm
Run any POST, PUT, PATCH, or DELETE endpoint in the official GoHighLevel/LeadConnector API with this workspace's encrypted token. This can create sub-accounts, send real messages, charge money, change live CRM data, or delete data depending on the path and token scopes; HighLevel applies the real side effect immediately.
- gohighlevel.inventoryScan GHL account
Read a migration inventory from the connected GHL sub-account: contacts, pipelines, calendars, workflows, forms, surveys, and custom fields. Missing token scopes are reported per resource instead of aborting the scan. Changes nothing in either system.
- gohighlevel.list_snapshotsList GHL snapshots
List snapshots owned or imported by the connected GHL agency. GHL's API exposes snapshot metadata, share links, and push status, but does not expose a downloadable snapshot payload; this read changes nothing.
- gohighlevel.import_contactsImport GHL contactsconfirm
Copy up to 100 contacts from the connected GHL sub-account into this workspace. THIS CAN SEND REAL MESSAGES TO REAL PEOPLE: every contact it genuinely creates fires this workspace's 'contact created' automations, so if any workflow is set to greet new contacts, importing 100 people sends up to 100 real SMS or emails — billed to the workspace's own Twilio/Mailgun account — the moment the import lands. Check the workspace's automations before running it on a list you have not seen. Existing contacts here are matched by normalized phone or email and are never overwritten or re-triggered; by default, contacts previously deleted here stay deleted. New contacts retain their GHL ids, tags, and custom-field payload in source metadata. Nothing in GHL is changed.
- gohighlevel.sync_statusView GHL migration and sync status
Read the connected workspace's HighLevel migration mode, continuous-sync settings, mapped-record count, supported resource coverage, and ten most recent durable jobs — each with per-resource counts and a per-item report of every record that was skipped or degraded, with the reason. This returns no OAuth credentials and changes neither system.
- gohighlevel.configure_syncConfigure GHL synchronizationconfirm
Choose whether supported records are copied once, synchronized inbound from HighLevel, synchronized outbound to HighLevel, or kept synchronized both ways, and which resources the inbound migration moves (contacts, pipelines & stages, opportunities → deals, booked appointments). Enabling outbound or two-way mode changes live HighLevel CRM records during future sync runs; only contacts sync outbound. This never sends messages or charges customers.
- gohighlevel.start_migrationStart GHL migrationconfirm
Start a durable, restartable HighLevel migration using the configured direction, conflict policy, and resource selection. Inbound mode creates or updates real contacts, pipelines & stages, deals (from opportunities), and booked appointments in this workspace — created deals and contacts can fire this workspace's own automations, which may send real messages if those automations are configured to. Outbound and two-way modes also create or update real HighLevel contacts. A background cron advances the job every few minutes, so it finishes even after the browser closes. The migration itself sends no customer messages and performs no cross-system deletions.
- gohighlevel.run_sync_nowRun GHL sync nowconfirm
Queue and immediately advance the configured HighLevel synchronization. Depending on direction and configured resources, this creates or updates real contacts, pipelines, deals, and appointments in this workspace, or real contacts in HighLevel, or both. It sends no messages, charges nothing, and does not propagate deletions.
- gohighlevel.retry_sync_jobRetry GHL migration jobconfirm
Retry a failed or canceled HighLevel migration/synchronization job from its saved cursor and phase. The job is idempotent through permanent HighLevel-to-workspace record mappings, but it can create or update real contacts, pipelines, deals, and appointments here — or real contacts in HighLevel — according to the job's direction and resources.
- gohighlevel.cancel_sync_jobCancel GHL migration jobconfirm
Stop an active HighLevel migration/synchronization job before its next leased batch. Records already copied or updated remain in place; this does not roll back or delete anything in either system.
gym
- gym.list_programsPrograms
List the gym's training programs (BJJ, Muay Thai, Kids Karate…) with their colors and active/archived state. Archived programs are included only when requested. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.create_programAdd program
Create a training program. Optionally seed its belt ladder from a preset (BJJ adults/kids, Karate/TKD, or generic levels) so ranks with real class/time requirements exist immediately; without a preset the ladder starts empty and ranks are added one by one. Creates only records inside this workspace — nothing outward-facing. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.update_programEdit program
Update a program's name, description, color, sort order, or active state. Omitted fields are left alone. Setting is_active to true restores a previously archived program. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.archive_programArchive programconfirm
Archive a program — it disappears from the schedule and program pickers across the app, though its classes, enrollments, belt ladder and history all remain and it can be restored later with gym.update_program. Asks for confirmation because members and staff stop seeing it immediately. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.list_belt_ranksBelt ladder
List belt ranks in ladder order (lowest first), each with its color and the class/time-in-grade minimums required to earn it from the rank before. Filter to one program to see that program's ladder. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.create_belt_rankAdd belt rank
Add one rank to a program's belt ladder. Minimums are advisory eligibility hints shown to instructors — they never hard-block a promotion. Appends to the end of the ladder unless a sort_order is given. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.update_belt_rankEdit belt rank
Update a belt rank's name, color, ladder position, or eligibility minimums. Omitted fields are left alone. Members already holding the rank keep it — only the rank's own definition changes. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.delete_belt_rankDelete belt rankconfirm
Permanently delete a rank from a program's ladder. This cannot be undone. Members currently holding it are left with no rank (fix them with gym.update_enrollment), and past promotions to it keep their history but lose the rank name. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.list_membersMembers & Belts
List the gym's members — each enrollment with the member's name and contact details, the program, their current belt rank and stripes, and status. Filter by program, enrollment status, or current rank. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.enroll_memberEnroll member
Enroll a CRM contact in a program as a training member. Unless a starting rank is given, they start at the first belt of the program's ladder (worn on day one). A contact can hold one enrollment per program; enrolling again returns a conflict rather than a duplicate. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.update_enrollmentEdit enrollment
Correct an enrollment: pause, resume or end a membership, or fix the recorded rank/stripes when they're wrong. This is the corrections path — it does NOT write a promotion record; award an earned rank with gym.promote_member so the member's history stays true. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.promote_memberPromote memberconfirm
Award a member a new belt rank, or add a stripe to their current belt. Writes a PERMANENT promotion record to the member's history (visible on their belt card forever) and updates their enrollment. Promoting to a new belt resets stripes to 0. Asks for confirmation because promotion history is append-only — a mistaken award stays in the record. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.list_promotionsPromotion history
The append-only promotion history — who was awarded which belt or stripe, when, by whom, and any grading notes. Newest first. Filter by member or program. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.promotion_eligibilityReady for promotion
List active members who meet the next rank's minimums — enough classes attended since their last promotion AND enough months at their current rank, per the next rank's min_classes/min_months. Advisory only (the instructor always decides); members whose ladder has no higher rank are skipped. Scans up to `limit` active enrollments per call. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.list_classesSchedule
The weekly recurring class schedule, ordered by day then start time — each class with its day of week (0 = Sunday … 6 = Saturday), start time, duration, instructor, location, capacity, and program. These are recurring weekly slots, not calendar events. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.create_classAdd class
Add a recurring weekly class to the schedule — it repeats every week on its day at its start time and appears on the schedule and the check-in kiosk immediately. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.update_classEdit class
Update a scheduled class — move it to a different day or time, change its duration, instructor, location, capacity, or program, or toggle it off the live schedule with is_active. Omitted fields are left alone. Past check-ins are unaffected. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.delete_classDelete classconfirm
Permanently remove a class from the weekly schedule. This cannot be undone; members can no longer check in to it. Past check-ins keep the class name as a snapshot, so attendance history survives. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.check_in_memberCheck in
Record that a member attended — the same thing the lobby kiosk does when they tap their name. Recorded with source 'api' and the class name snapshotted onto the record. If the member is already checked in to that class today, this reports success with an already-checked-in note instead of failing, so double-taps are harmless. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.list_checkinsAttendance log
List check-ins, newest first — who attended, which class (name preserved even if the class was later deleted), the date, and whether it came from the kiosk, staff, or the API. Filter by member, class, or a date range. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.attendance_reportAttendance
The attendance overview: total check-ins and distinct members this calendar week (Sunday start) and this calendar month, plus the going-quiet list — actively enrolled members with no check-in in more than 14 days, the people worth a retention call before they cancel. Dates are computed in UTC. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.get_kiosk_linkKiosk link
Get the check-in kiosk URL for the lobby tablet. TREAT IT LIKE A KEY: anyone holding this link can open the kiosk with no login and check any member in (or see the member roster the kiosk shows). Links stay valid until rotated with gym.rotate_kiosk_link. Creates the gym's settings row on first use. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.rotate_kiosk_linkRotate kiosk linkconfirm
Revoke every kiosk link ever issued and mint a fresh one — the 'lost or stolen tablet' action. EVERY tablet currently running the kiosk disconnects immediately and stays down until someone opens the new link on it, so check-ins stop until the tablets are re-set-up. Asks for confirmation for exactly that reason. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- gym.update_settingsKiosk welcome message
Set or clear the welcome message shown at the top of the lobby check-in kiosk (for example 'Welcome to Apex Martial Arts — tap below to check in'). Takes effect the next time a kiosk screen refreshes; changes nothing else about the kiosk or its link. Requires the Martial Arts Gym app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
helpdesk
- helpdesk.listList tickets
List the workspace's help desk tickets (support requests from its OWN customers), most recently updated first. Optionally filter by status, priority, assignee, queue, or the linked contact.
- helpdesk.getOpen a ticket
Fetch one help desk ticket by id, with its full thread — public replies the customer can see and internal agent-only notes, each marked by its `internal` flag.
- helpdesk.createNew ticket
Create a help desk ticket on the customer's behalf. Stamps a first-response SLA deadline from the workspace's per-priority policy, links (or creates) the CRM contact from the requester's email/phone, and notifies the assignee — or the desk's default assignee, or the whole workspace. Sends no email or SMS to the customer (the 'we got your request' confirmation email goes out only for the customer's own public-portal submissions).
- helpdesk.replyReply on ticketconfirm
Post an agent message on a ticket. A public reply (internal=false) is shown to the customer on their ticket status page, stops the first-response SLA clock, and moves a new/open ticket to 'waiting' — and when the workspace's help desk email updates are on (the default while an email provider is connected) and the ticket has a requester email, the reply is ALSO emailed to that real customer through the workspace's own connected email account, with a reply-to address that threads their answer back onto the ticket. It is outward-facing customer communication. An internal note (internal=true) stays agent-only and is never emailed.
- helpdesk.set_statusSet ticket status
Move a ticket between statuses. Setting 'solved' stamps the solve time and makes the customer's ticket page offer a one-time 1–5 CSAT rating; reopening a solved/closed ticket clears the solve stamp. Reversible at any time.
- helpdesk.set_prioritySet ticket priority
Change a ticket's priority. While the first-response SLA clock is still running, the deadline is recomputed under the new priority's target.
- helpdesk.assignAssign ticket
Assign a ticket to a team member (they get a notification), or unassign it by omitting the assignee.
hosting
- hosting.digitalocean_statusCheck DigitalOcean connection
Checks whether this workspace has authorized a DigitalOcean team and, when connected, verifies the live account without returning OAuth credentials.
- hosting.disconnect_digitaloceanDisconnect DigitalOceanconfirm
Revokes the stored DigitalOcean OAuth token and removes the workspace connection. Refuses while any WordPress server still exists so live sites are not stranded.
intelligence
- intelligence.list_callsList analyzed calls
List calls that have been analyzed by AI, newest first, with each call's summary, outcome, sentiment, objections raised, lead score and agreed next step. Read-only and free — it returns analysis that already exists and never runs a model. Calls only appear here if the number they came in on has call analysis turned on.
- intelligence.get_callOpen a call's analysis
Fetch the full AI analysis of one call by the CALL's id: summary, what the call was about, objections raised, sentiment, the commitments made on it, coaching for the rep, and the lead score with its reasoning. Read-only and free. Returns not-found when that call has never been analyzed.
- intelligence.analyze_callAnalyze a callconfirm
Run AI analysis on one completed call right now, instead of waiting for the background sweep. COSTS MONEY: this makes a model call billed to the organization's own OpenRouter account. Use `force` to re-analyze a call that already has analysis — otherwise an already-analyzed call is left alone. Fails cleanly when the call has no transcript, which is the case unless transcription was enabled on the number.
- intelligence.breakdownObjection & topic breakdown
Rank what the organization's calls have been about and what keeps stopping them, over a window of days: objections raised, topics discussed, how calls ended, sentiment, and the average lead score. Read-only and free. The counts describe the analyzed calls in the window and say so — an unanalyzed call is invisible to this.
- intelligence.forecastPipeline forecast
The revenue forecast for open deals, showing both numbers side by side: what the pipeline is worth weighted by each stage's historical win rate, and what it is worth weighted by the AI's per-deal probability, which is informed by what was actually said on the calls. Also returns deals broken down by health, the riskiest deals by value, and the most common risks. Read-only and free.
- intelligence.get_dealOpen a deal's risk assessment
Fetch the AI assessment of one open deal: its adjusted win probability against the stage's historical one, its health, the specific risks on it, the recommended next step, and the evidence the assessment rests on — call counts, objections raised, sentiment over time, days since anyone made contact. Read-only and free.
- intelligence.score_dealScore a dealconfirm
Run the AI risk assessment on one open deal right now. COSTS MONEY: this gathers the deal's evidence and makes a model call billed to the organization's own OpenRouter account. Re-scores a deal that already has a score.
- intelligence.get_settingsIntelligence settings
Read the organization's intelligence settings: whether open deals are scored automatically, and how many days without contact makes a deal count as stalled. Read-only and free.
- intelligence.update_settingsChange intelligence settingsconfirm
Turn automatic deal scoring on or off and set how many days of silence makes a deal stalled. ONGOING COST: with scoring on, every open deal that changes is re-scored by a background job at most once a day, each one a model call billed to the organization's own OpenRouter account.
- intelligence.set_call_analysisTurn call analysis on for a numberconfirm
Turn AI call analysis on or off for one phone number. ONGOING COST: with it on, every completed call on that number is analyzed by a background job — one model call each, billed to the organization's own OpenRouter account. Analysis reads the transcript, so it does nothing unless transcription is also enabled on the number; this capability reports that rather than silently doing nothing.
klaviyo
- klaviyo.catalogList Klaviyo API coverage
Search the generated catalog of every operation in Klaviyo's current stable OpenAPI revision. This reads local documentation only and does not call or change the connected Klaviyo account.
- klaviyo.readRun Klaviyo API read
Run any GET endpoint under Klaviyo's fixed a.klaviyo.com /api or /client origin using this workspace's encrypted credential. This can read customer profiles, consent, events, campaigns, flows, reports, catalogs, custom objects, reviews, and other protected account data but does not modify Klaviyo.
- klaviyo.writeRun Klaviyo API writeconfirm
Run any official Klaviyo POST, PUT, PATCH, or DELETE endpoint under the fixed Klaviyo API origin. Depending on the path, this can create or delete profiles and custom objects, change consent, schedule campaigns, send messages or template previews to real recipients, publish flows, or alter live account data; provider charges may apply.
- klaviyo.connection_statusCheck Klaviyo connection
Read the connected Klaviyo account identity, authentication mode, granted scope count, sync health, and webhook health stored for this workspace. No secret or token value is returned and Klaviyo is not modified.
- klaviyo.list_mirrored_resourcesList synced Klaviyo records
List bounded, tenant-scoped Klaviyo JSON:API resources mirrored into this workspace for browsing and automations. This may return protected customer or marketing data but does not call or change Klaviyo.
- klaviyo.list_webhook_eventsList Klaviyo events
List signed Klaviyo webhook deliveries and their processing state for this workspace. Payload bodies are omitted because they may contain message content and profile data. This does not call or change Klaviyo.
- klaviyo.start_syncSync Klaviyo dataconfirm
Start a resumable import of Klaviyo profiles, consent, predictive analytics, events, audiences, campaigns, flows, forms, templates, catalogs, coupons, custom objects, reviews, tags, tracking settings, and webhooks. It creates missing contacts here without overwriting existing contacts and does not modify Klaviyo.
- klaviyo.run_sync_nowContinue Klaviyo syncconfirm
Advance queued or interrupted Klaviyo synchronization jobs by one resumable batch. This may create missing contacts here but does not overwrite existing contacts or modify Klaviyo.
- klaviyo.enable_webhookEnable Klaviyo eventsconfirm
Create a signed system webhook in the connected Klaviyo account and subscribe it to the selected live account event topics. Real customer messaging and commerce activity will begin flowing into this workspace's automations; this does not send messages or charge customers, but Klaviyo Webhooks API eligibility is required.
- klaviyo.disable_webhookDisable Klaviyo eventsconfirm
Delete the system webhook from the connected Klaviyo account. New Klaviyo events will stop entering this workspace's automations; events already received and existing data remain stored.
knowledge
- knowledge.searchSearch knowledge base
Browse or search product help guides and references by topic and category. Returns article summaries and links, with pagination. Reads documentation only; does not change records, send messages or incur AI charges. Vendor pricing articles are hidden for white-label brands.
- knowledge.readRead help article
Read a complete product help article, including setup instructions and troubleshooting. Uses the same knowledge base users browse in Help & setup. Read-only, with no AI charge or workspace changes; hides vendor pricing under white-label brands.
- knowledge.ask_adminAsk an admin
Save an unresolved product question or answer correction in the responsible admin's private learning inbox. Product questions follow support ownership; workspace policy questions go to the workspace admin. No customer message or email is sent, no model is called, and no answer becomes shared until reviewed.
- knowledge.my_questionsYour clarification requests
Read the signed-in person's latest 50 clarification requests in the active workspace, including review status. Private evidence from other users is excluded. Returns an empty list for API keys without a person; does not contact anyone or change records.
- knowledge.learning_listLearning inbox
Read automatically captured bug reports, chat corrections, human support replies and unresolved assistant questions for the admin's support team. Private review evidence never becomes public through this read. Platform admins see Chirply-owned items; workspace admins see their own workspace or client support queue.
- knowledge.learning_readReview learning item
Read one authorized learning item's private source evidence, current answer and last 20 review decisions. Allows an admin to verify a proposed correction before reusing it. Does not publish guidance, contact customers or change report statuses.
- knowledge.learning_reviewSave learning reviewconfirm
Publish an admin-verified reusable answer, dismiss an item, require a code correction, or retire previously approved guidance. Publication changes what future users and AI chats read; global publication is platform-admin-only, and commercial terms must be corrected in code. Records an immutable review with evidence and expiry; sends no customer message and spends no money.
leads
- leads.outscraper_statusCheck the Outscraper connection
Report whether the organization has connected its own Outscraper account, plus its remaining credit and this cycle's usage. Read-only — call it before a paid search to see what the tenant has to spend.
- leads.list_searchesList lead searches
List past lead searches, newest first, with each one's status, result count, duplicates skipped and estimated cost — plus the org's running totals. Purely historical; it runs nothing and spends nothing.
- leads.get_searchOpen a lead search
Fetch one lead search by id — its query, status, result and duplicate counts, estimated cost, and any provider error.
- leads.list_resultsSearch and filter lead search results
Page through the businesses one lead search found — name, phone, email, website, rating, phone line type, and whether each is already in the CRM — narrowing them exactly as the results table does. Use the filters to isolate the leads actually worth importing: `q` free-text matches name, phone, email, address, city, category and website; `has_phone`/`has_email`/`has_website` drop the unreachable ones; `line_type: "mobile"` keeps only numbers that can receive a text or voicemail drop; `min_rating` keeps the well-reviewed ones. Reads staged results only — nothing is fetched from Outscraper, so this costs nothing. Feed the returned ids straight into leads.import.
- leads.search_mapsSearch Google Maps for leadsconfirm
Scrape Google Maps for businesses matching a category and location, via the organization's OWN Outscraper account. THIS SPENDS THE TENANT'S MONEY: Outscraper bills per record returned (roughly $3 per 1,000 Maps records after the first 500 free each cycle, more with the emails enrichment), and duplicates already in the CRM are still scraped and still billed. Set the limit deliberately. Jobs over 80 results, or any search with emails on, are submitted in the background and finish later — poll with leads.check_status.
- leads.search_yelpSearch Yelp for leadsconfirm
Scrape Yelp for local businesses matching a category and location, via the organization's OWN Outscraper account. THIS SPENDS THE TENANT'S MONEY: Outscraper bills per listing returned, and duplicates already in the CRM are still scraped and still billed. Know what you get: business name, phone, street address, rating, review count, categories, price range, neighborhood, a link to the Yelp listing, and the business's own website when Yelp has one on file — but NEVER an email address. Set `emails: true` to chain the website-finder and contact-scraper enrichments on top, which is the only way a Yelp lead becomes emailable; it costs several times more per record. EVERY Yelp search runs in the background — Yelp is slow (about 90 seconds for ten listings) — so this returns a search id immediately and you collect the results with leads.check_status or leads.list_results a couple of minutes later.
- leads.search_databaseSearch the business database for leadsconfirm
Search Outscraper's B2B business database with structured filters (category, country/state/city/postal, name, minimum rating or review count, has-website/has-phone/verified). Instant and cursor-paginated. THIS SPENDS THE TENANT'S MONEY: Outscraper bills per record returned (roughly $2 per 1,000 for the first 5,000 each cycle, more above that, plus a surcharge per record for the emails or insights enrichments), and duplicates already in the CRM are still billed. At least one filter or a keyword query is required so the whole database isn't scanned.
- leads.load_moreLoad more resultsconfirm
Fetch and append the next page of an existing business-database search using its stored cursor. THIS SPENDS THE TENANT'S MONEY exactly like a new search — another page of records is billed to the organization's Outscraper account. Only database ('b2b') searches can load more.
- leads.check_statusCheck a running search
Poll a background Google-Maps or Yelp search and stage its results if Outscraper has finished. Costs nothing extra — the scrape was already billed when it was submitted; this only collects what was paid for.
- leads.delete_searchDelete a lead searchconfirm
Permanently delete a lead search and every staged result under it. Contacts already imported into the CRM are kept, but the un-imported leads are gone and re-finding them means paying Outscraper for the search again.
- leads.importImport leads into the CRMconfirm
Import staged lead-search results into the CRM as contacts, and optionally run actions on them in the same pass. Pass result_ids for specific leads, or just search_id to import every not-yet-imported result of that search — which can create hundreds of contacts in one call and is not undoable in bulk. Deduped by phone and email: a lead matching an existing contact links to it instead of creating a duplicate. The import itself costs nothing extra (the search was already billed), BUT the optional `actions` run once per imported contact and are a real mass send — depending on the actions chosen they text, email, drop ringless voicemails, place automated or AI calls, or enroll people into campaigns, immediately and irreversibly, billed through the organization's own Twilio/Mailgun accounts. Actions also hit the existing contacts that leads deduped onto, not just the newly created ones. Optionally add every imported contact to a list.
legal
- legal.dpa_statusData Processing Agreement status
Reports whether this workspace has accepted the current Data Processing Agreement, and returns the signature record — the version accepted, the legal entity, who accepted it, their job title, and when. Use it to answer 'do we have a DPA in place?' or to find workspaces still on a superseded version. Read-only; accepts nothing and changes nothing.
- legal.accept_dpaAccept the agreementconfirm
Legally binds this workspace's company to Chirply's Data Processing Agreement, including the Standard Contractual Clauses for transfers out of the EEA and UK. This is an electronic signature on a real contract: it records the named person, their job title, the legal entity, the timestamp and the agreement version permanently, and the record cannot afterwards be edited or deleted. Only accept on behalf of someone with authority to bind that company. Costs nothing. Accepting a version already accepted is harmless and changes nothing.
- legal.list_subprocessorsSub-processors
Lists every vendor Chirply may use to process personal data on a customer's behalf — what each one does, the categories of data it can see, where it processes, and the date it was added. This is Annex III of the Data Processing Agreement. Poll it and diff the result to detect a change before the notice period expires. Read-only.
links
- links.listList links
List the organization's links, newest first, with their click, unique-visitor and conversion counts. Optionally filter by kind, status, domain or tag, and search the name, label and destination.
- links.getOpen a link
Fetch one link by id with all of its rules, its rotation destinations and the retargeting pixels attached to it, plus its public URL.
- links.createCreate a link
Create a link and return its public URL. Costs nothing and sends nothing — the link is live immediately but nobody sees it until it is shared. Leave the name blank for a short random one. Leave domain_id blank to host it on the platform's own short address; supply one of the organization's connected domains to brand it. Rotating, sticky and overflow links need `targets`; a file link needs a file URL. To turn one natural-language brief into both an opt-in link and an AI-designed editable page, use links.generate_optin_page instead.
- links.generate_optin_pageBuild opt-in with AIconfirm
Create a working LinkWizard opt-in link and an AI-designed, fully editable Page Builder draft from one plain-language brief. The link itself is active immediately and its built-in fallback form works, but the AI design does NOT replace that fallback until a human publishes the builder page. This SPENDS MONEY — the generation is billed to the organization's own AI provider key — and it uses one page/funnel slot against the plan's limit. It sends no messages and applies no tags or automations until a real visitor submits the form.
- links.updateEdit a linkconfirm
Update any field on an existing link. Omitted fields are left alone. THREE OF THESE FIELDS REACH THE PUBLIC IMMEDIATELY. `slug` and `domain_id` change the link's public URL, so every copy already shared, printed, emailed or posted to an ad platform starts returning a not-found page. `status` is the same write `links.set_status` gates: 'paused' or 'archived' kills the link dead for everyone holding it. And supplying `targets` REPLACES the whole destination list, which resets the per-destination click counts that overflow caps rely on. None of that can be undone by editing the field back — traffic lost in the meantime is gone.
- links.set_statusPause or resume a linkconfirm
Set a link live, paused or archived. A paused link stops resolving immediately — everyone who opens it, including people who already have it, gets a not-found page.
- links.design_optin_pageDesign in Page Builder
Create or reopen the dedicated drag-and-drop page for an opt-in link. This uses one page/funnel slot when first created, sends no messages, and does not change what visitors see until the page is published. The visual page controls layout and content while LinkWizard continues to control captured details, verification, workspace senders, tags, automations, and the final destination.
- links.duplicateDuplicate a link
Copy a link with all of its rules and destinations under a fresh random name. The copy is created PAUSED on purpose — an exact duplicate that went live immediately would start splitting traffic with the original before anything had been changed. Retargeting pixels are not copied.
- links.deleteDelete a linkconfirm
Permanently delete a link, along with every click and conversion recorded against it. Anyone who already has the link gets a not-found page from then on. This cannot be undone — pause the link instead if you only want to stop it temporarily.
- links.statsLink report
Click and conversion statistics for one link over a window of days: totals, unique visitors, blocked clicks, conversions and their value, a per-day series, and rankings by country, device, browser, operating system and referring site.
- links.clicksRecent clicks
The most recent individual clicks on a link — when, from which country and city, on what device and browser, where they came from, and whether they were sent through or stopped by a rule.
- links.overviewLinks overview
Totals across every link in the organization: how many links, how many clicks and how many conversions.
- links.list_pixelsList retargeting pixels
DEPRECATED — use pixels.list, which reads the same rows and is the maintained version. Lists the retargeting pixels set up for this organization, ready to attach to links.
- links.create_pixelAdd a retargeting pixelconfirm
DEPRECATED — use pixels.create, which writes the same rows and is the maintained version. Adds a retargeting pixel that can be attached to links. Supply the provider's own ID (from Meta Events Manager, Google Ads, and so on) — the snippet is generated from it. Use provider 'custom' with `custom_html` for anything else, which injects THAT HTML VERBATIM INTO A PUBLIC PAGE every visitor passing through the link loads: it is arbitrary third-party JavaScript running on the organization's own branded domain, and there is no review step. `scripts.create` is confirmed for exactly this payload.
- links.delete_pixelDelete a retargeting pixelconfirm
DEPRECATED — use pixels.delete, which deletes the same rows and is the maintained version. Permanently deletes a retargeting pixel and removes it from every link it was attached to. Those links stop building that audience from the next click on. This cannot be undone.
- links.set_pixelsChoose which pixels fire on a linkconfirm
DEPRECATED — use pixels.attach, which writes the same rows and is the maintained version. Sets exactly which retargeting pixels fire on a link, replacing whatever was attached before. This changes what a LIVE public link does: attaching any pixel forces the link to show its short branded waiting page — that page is the only moment a pixel can fire on a click passing through to somebody else's site — and it starts running that pixel's third-party JavaScript for every visitor.
- links.list_vendorsList traffic sellers
List the people this organization buys clicks from, for assigning to links and tracking delivery against what was ordered.
- links.create_vendorAdd a traffic seller
Add someone this organization buys clicks from. Assign them to a link (with clicks_ordered and order_amount) to see how much of what was paid for actually arrived.
- links.delete_vendorDelete a traffic sellerconfirm
Permanently delete a traffic seller. Links assigned to them keep working and keep their ordered-click figures, but are no longer grouped under anyone. This cannot be undone.
- links.get_trackingConversion tracking snippet
The organization's conversion-tracking state, every website it tracks with the script tag for each, and the custom event names already seen. There is ONE script (`/embed/t.js`) which does visitor tracking and conversions together — there is no separate conversion-only snippet any more — but each website carries its OWN key, because that key is what scopes the site's origin allow-list and keeps one site's visitors out of another's reports. So `sites` is a list and there is deliberately no one default snippet: handing out one site's key for a different site produces beacons the origin allow-list refuses. Returns nothing configured if tracking has never been switched on.
- links.enable_trackingTurn on conversion trackingconfirm
Switch on full-loop conversion tracking and return the one script to install. Once on, every tracked link appends a small `cwc` parameter to the DESTINATION URL — the organization's live public links start carrying an extra query parameter to whatever site they point at, which some destinations reject or log. THERE IS NO CAPABILITY TO TURN THIS BACK OFF: once a workspace has a tracker it keeps it, so this is one-way from the machine surfaces. Safe to call repeatedly — it returns the existing token if there is one.
- links.recipientsWho a link was sent to
The people this link was sent to individually, and which of them opened it — highest engagement first. Only populated when per-person link tracking is on: each recipient of an email or text gets their own copy of the link, so a click can be attributed to a named contact rather than an anonymous visitor.
- links.set_link_rewritingTurn per-person links on or offconfirm
Switch per-person link rewriting for the whole workspace. When ON, every link inside outgoing emails and texts is rewritten so each recipient gets their own URL — clicks then carry the contact's identity, and so do any conversions they produce. All recipients still share ONE link, so editing where it points changes every message already delivered. Unsubscribe links are never rewritten. Turning it OFF does not break links already sent; it only stops new ones being minted. Switching it on also enables conversion tracking, which adds a `cwc` parameter to destination URLs, and every click on a rewritten link stamps a signed `ct` parameter naming the recipient onto the destination — that is what lets a landing page running the Chirply tracking script recognise which contact just arrived.
- links.set_link_domainDomain for tracking linksconfirm
Choose which connected domain rewritten tracking links go out on, instead of Chirply's own short host. Affects links minted from now on; links already sent keep resolving on the domain they went out with. `match_sending_domain` additionally puts each link on whichever connected domain shares a registrable domain with the address the message is actually sent from (so mail from hello@acme.com gets links on go.acme.com), falling back to `domain_id` when there is no match or the sender is not known yet — a link domain that does not match the From address reads as a forwarding service to mailbox providers, which costs deliverability.
- links.get_contact_sharingCross-workspace contact sharing
Whether this workspace lets OTHER workspaces read contact context for people it sent to them on a tracked link, and which workspaces may do it. Relevant when this workspace's contacts click through to a landing page somebody else owns — an affiliate or partner arrangement. Off by default. Read-only.
- links.set_contact_sharingShare contacts with partner sitesconfirm
Let other workspaces read the CRM context — name, email, tags, deals, messages, and call transcripts — of a contact of yours who clicked one of your tracked links onto THEIR landing page. This discloses your own customers' personal data to another business: they can use it to personalize the page that contact lands on. It never works in reverse, only applies to contacts who actually clicked one of your links, and turning it off takes effect immediately for every future request. Leave `destination_org_ids` empty to allow any destination (the usual choice for a public affiliate program); list workspace ids to restrict it to a named few.
- links.connect_domainConnect a domainconfirm
DEPRECATED — use domains.connect, which writes the same rows and is the maintained version. Registers a domain the organization owns so links and pages can be served on it. Creates a Cloudflare custom hostname and starts certificate issuance; the domain will NOT serve anything until the owner adds a CNAME record at their DNS provider and the certificate is issued. Nothing is charged, but this claims the hostname on the platform's Cloudflare zone.
- links.update_domainChange a domain's settingsconfirm
DEPRECATED — use domains.update, which writes the same rows and is the maintained version. Sets what a connected domain serves: which funnel or single page is attached, where the bare domain redirects when nothing is attached, where unknown addresses go, and whether links are allowed on it at all. This reconfigures a LIVE public hostname: setting links_enabled false immediately stops every link hosted there from resolving, for everyone already holding one, and repointing funnel_id changes what the world sees at that address.
lists
- lists.listList lists
List the organization's contact lists with each one's member count. Lists are named STATIC segments — membership is explicit, not a saved filter.
- lists.getOpen a list
Fetch one list by id, with its description and current member count.
- lists.createCreate a list
Create an empty contact list. Only a name is required, and it must be unique within the organization. Add contacts afterwards with lists.add_contacts.
- lists.updateEdit a list
Rename a list or change its description. Omitted fields are left alone; membership is untouched.
- lists.deleteDelete a listconfirm
Permanently delete a list and every membership row on it. The contacts themselves are NOT deleted, but the segment is gone and cannot be restored.
- lists.membersView list members
Page through the contacts on a list, newest addition first, with each member's name, email and phone.
- lists.add_contactsAdd contacts to a list
Add one or more existing contacts to a list. Idempotent — a contact already on the list is silently kept, not duplicated. Contact ids that don't belong to this organization are ignored.
- lists.remove_contactsRemove contacts from a list
Remove one or more contacts from a list. This only drops the membership — the contacts themselves are untouched.
- lists.action_typesBrowse runnable actions
List the action types lists.run_actions accepts, with each one's configurable fields. Call this before lists.run_actions so the action config is built with the right keys.
- lists.run_actionsRun actions on a listconfirm
Run one or more actions against EVERY contact on a list — the same 'Run actions' bulk surface the app offers. This is a mass send: depending on the actions chosen it texts, emails, drops ringless voicemails, places automated or AI calls, enrolls people into campaigns, emails invoices, or deletes contacts, once per member, immediately and irreversibly, billed through the organization's own Twilio/Mailgun accounts. Pass contact_ids to restrict it to specific members. Confirm the list's exact member count with lists.get before calling. Runs on up to 2,000 members.
live_chat
- live_chat.list_widgetsList live-chat widgets
List this workspace's branded website live-chat widgets and their publication, AI, placement, and appearance settings. Reads only and sends nothing.
- live_chat.get_widgetOpen live chat
Fetch one branded website live-chat widget with everything its builder shows: its publication status, the AI agent connected to it, which devices the launcher appears on, its full normalized live-chat settings (brand colors, launcher label and position, panel heading, welcome/offline/handoff messages, visitor name/email/phone intake, white-label powered-by link, and every piece of editable visitor-facing copy), the websites it is allowed to appear on, and its install snippet. Live chat only — a click-to-call widget's id returns not-found here, because its settings are a completely different shape; read those with widgets.get. Read-only: it changes nothing, sends nothing, and returns no visitor transcripts (use live_chat.get for those).
- live_chat.embed_snippetInstall live chat
Get the one-line script tag that installs a live-chat widget on a website — paste it into the site's HTML just before </body> — plus the widget's standalone chat page URL. This is the LIVE-CHAT loader (/embed/chat.js and /c/<key>); the click-to-call loader returned by widgets.embed_snippet is a different script and will not open a chat panel, so do not substitute one for the other. The launcher only renders once the widget is published and the site has been added as an allowed origin, so the returned status and origin list are the two things to check when it does not appear. Read-only.
- live_chat.create_widgetCreate live chat
Create a draft branded live-chat widget. It does not appear on a website until it is configured, given an allowed site, installed, and published.
- live_chat.configure_widgetSave changes
Update a live-chat widget's complete branding, visitor intake, AI handoff, white-label link, and device placement. Existing live conversations keep their transcript; published embeds use the changes immediately.
- live_chat.set_widget_statusPublish widgetconfirm
Publish or unpublish a live-chat widget. Publishing makes the installed launcher visible on every allowed website; unpublishing removes it on the loader's next refresh without deleting conversations.
- live_chat.delete_widgetDelete widgetconfirm
Permanently delete a live-chat widget, all visitor sessions, and every message transcript it collected. The installed launcher stops working and this cannot be undone.
- live_chat.listList live chats
List website live-chat sessions, newest activity first, including visitor identity, current AI/human ownership, assignee, source page, and connected widget. Reads only.
- live_chat.getRead live chat
Read one website chat and its complete ordered transcript, including visitor, AI, teammate, and system messages. Reads only and does not change ownership.
- live_chat.take_overTake over
Move a waiting or AI-handled website chat to human control and assign it to the acting user when there is one. AI stops answering until someone explicitly hands the chat back.
- live_chat.hand_to_aiHand to AI
Return a human-controlled website chat to its connected AI agent. The AI will answer the visitor's next message using that agent's persona and Brain.
- live_chat.replySend replyconfirm
SENDS A REAL LIVE-CHAT MESSAGE to the website visitor immediately. The reply appears in their open widget, moves ownership to the human team, assigns the acting user when there is one, and pauses AI. When the widget's "keep the conversation going by text" setting is on and the visitor shared a phone number but has since left the page, the reply is ALSO delivered to them as a real SMS from the workspace's own number — billed to the org's own Twilio account (the result reports texted_to when that happened; visitors still on the page, or numbers opted out of SMS, are never texted). There is no draft or undo.
- live_chat.closeCloseconfirm
Close a website live-chat conversation and prevent more visitor, AI, or teammate messages. Its transcript is retained, but the current UI cannot reopen it.
marketplace
- marketplace.browseBrowse marketplace
Searches snapshots other users have published, by keyword and category. Installing one still requires buying or being granted it.
- marketplace.submit_templateSubmit template for reviewconfirm
Submit one of this workspace's saved funnel, page, or section templates for marketplace review. This exposes the frozen builder document to platform reviewers but does not make it public until a platform admin approves it.
- marketplace.unlist_templateUnlist templateconfirm
Withdraw this workspace's funnel, page, or section template from marketplace browsing. Copies already created in other workspaces remain intact.
- marketplace.get_my_listingMy listing
This workspace's marketplace listing for a snapshot, including its review state and any note the reviewer left.
- marketplace.upsert_listingEdit listingconfirm
Creates or edits the marketplace listing copy for a snapshot — the title, tagline, description and category buyers read. Editing does NOT publish an unpublished listing; submit it for review separately. But it does NOT re-enter review either: if the listing is already public, the new copy replaces the approved copy immediately and everyone browsing the marketplace sees it, with no moderator in between. Treat it as editing live public text.
- marketplace.submit_listingSubmit for reviewconfirm
Sends the listing to the platform team for review and public publication. A platform admin reads the whole configuration first, including every outbound URL it would contact.
- marketplace.unlistUnlistconfirm
Removes a listing from public browsing. Existing licences and anything already installed are unaffected.
- marketplace.report_listingReport a listingconfirm
Flags a marketplace listing to the platform team as spam, malicious, broken or infringing. Three open reports suspend a listing pending review.
members
- members.listList site members
List the site members (end users with login access to this workspace's funnels/websites), newest first. Optionally filter by status or search by email. These are the tenant's own customers/leads who signed up for a members area — not platform staff accounts.
- members.getOpen a site member
Fetch one site member by id, with the CRM contact it's linked to.
- members.inviteAdd a site memberconfirm
Create a member (login) account for an email address, linking or creating the CRM contact behind it. Optionally email them a sign-in link right now — that sends a REAL email from the workspace's own email provider. Passwordless: they set no password unless they choose to.
- members.send_login_linkEmail a sign-in linkconfirm
Email an existing member a fresh passwordless sign-in code and magic link for a given funnel/site. Sends a REAL email from the workspace's own email provider.
- members.set_statusSuspend or reactivate a member
Set a member's status. 'suspended' immediately blocks new sign-ins (existing sessions stop resolving too); 'active' restores access. Reversible.
- members.sign_outSign a member out everywhere
Revoke all of a member's active sessions, forcing them to sign in again on every device. Does not delete the account.
- members.removeRemove a site memberconfirm
Delete a member's login account and revoke access. The linked CRM contact is kept — only the ability to log in is removed. Cannot be undone.
- members.set_page_accessSet a page's access
Set who can view a funnel page: 'public' (anyone), 'members' (any signed-in site member), or 'entitlement' (only members who hold a specific access product — pass product_id). A gated page shows the sign-in or no-access card instead of its content.
- members.list_productsList access products
List this workspace's access products — the named units of access you gate pages behind and grant to members. Excludes archived ones unless include_archived is set.
- members.create_productCreate an access product
Create an access product — a named unit of access (e.g. 'Gold Course') you can gate pages behind and grant to members. Creating it grants no one anything on its own.
- members.archive_productArchive an access product
Archive an access product so it can no longer be granted or chosen. Existing grants and any pages already gated on it keep working; archived just hides it from the pickers.
- members.grant_accessGrant a member access
Grant a contact an access product, unlocking every page gated on it. Idempotent — re-granting a revoked one turns it back on. Identify the person by contact_id or by member email.
- members.revoke_accessRevoke a member's accessconfirm
Revoke a contact's access product. Pages gated on it lock again immediately. Identify the person by contact_id or member email.
- members.list_entitlementsList a member's access
List the access products a contact currently holds (and any revoked ones), newest first.
- members.set_site_loginTurn member login on/off for a site
Enable or disable the member login area for a funnel/website. Turning it on exposes the /member sign-in routes and the Members management for that site; it does not by itself gate any page (use members.set_page_access for that).
meta
- meta.campaign_drafts_listAll campaigns
List every unarchived Magic Ads campaign in the current workspace, including drafts and launched campaigns, their objective, destination, creative count, and whether a companion funnel or automation exists. This is read-only and does not contact Meta or change delivery.
- meta.campaign_draft_archiveArchive draft
Move one unpublished Magic Ads draft out of the current campaign list and into the reversible archive. This does not delete creative records, contact Meta, change delivery, publish anything, message anyone, or spend money. Published campaigns cannot be archived here.
- meta.campaign_draft_restoreRestore draft
Restore one archived Magic Ads draft to the current campaign list with its brief, creative records, and saved builder state intact. This does not contact Meta, change delivery, publish anything, message anyone, or spend money.
- meta.campaign_draft_deleteDelete permanentlyconfirm
PERMANENTLY delete one unpublished or archived Magic Ads draft and cascade-delete its saved creative metadata from Chirply. This cannot be undone. Generated image files, funnels, and automations remain in their own libraries, and this does not delete or pause anything in Meta. Published campaign records are refused.
- meta.campaign_draft_getResume campaign
Load one tenant-scoped Magic Ads campaign workspace exactly where it was saved, including its brief, generated plan, creative versions, delivery settings, funnel choice, and automation choice. This is read-only and does not contact Meta or change delivery.
- meta.campaign_draft_saveSave draft
Create or update a private, tenant-scoped Magic Ads draft with the exact resumable builder state. This stores data in Chirply only; it does not generate media, publish a funnel, contact Meta, launch ads, message leads, or spend money.
- meta.campaign_system_buildBuild the rest of this campaignconfirm
Optionally generate and immediately PUBLISH a complete public conversion funnel for one saved Magic Ads draft, mount it below a route on a connected domain, and/or create a PAUSED editable follow-up automation using the exact Facebook Page/form and funnel triggers. AI generation consumes the workspace's OpenRouter credits. Publishing makes the generated pages public; the workflow does not contact anyone until a person reviews and activates it.
- meta.lead_campaign_from_spoken_briefUse what I saidconfirm
Turn one natural-language or dictated advertising brief into three editable, durably saved Meta ad concepts and recommend the best awareness, traffic, engagement, leads, sales, or app-promotion objective. The offer, audience, location, desired next step, differentiator, and tone are extracted without requiring form fields. This uses the workspace's own OpenRouter model and may incur that provider's text-generation charge; it does not create anything in Meta, contact anyone, or spend ad money.
- meta.ad_performance_listCompare ad performance
Compare each ad's last-30-day spend, impressions, reach, clicks, leads, and cost per lead in one connected Meta ad account. This reads Meta reporting data and does not change delivery or spend money.
- meta.campaign_resultsHow my ads are doing
Report how every Facebook and Instagram campaign this workspace launched through the app is actually doing: whether each one is still in Meta's review queue, live and delivering, paused, or rejected, plus impressions, people reached, clicks, click-through rate, cost per click, amount spent, leads, and cost per lead for the chosen window — broken down per creative version. Campaigns run directly in Ads Manager are deliberately excluded, because this workspace did not launch them here. Read-only: it never changes delivery, never turns anything on or off, and never spends money.
- meta.conversion_sources_listPixels and conversions
List every Meta Pixel (dataset) and every custom conversion on one connected ad account, with each Pixel's name and whether it has ever received an event. These are what a website campaign optimizes around: the Pixel watching the site, and the thing happening on it that counts as success. Read-only — it creates nothing, changes no delivery, and spends no money.
- meta.audiences_listSaved audiences
List every custom audience on one connected Meta ad account — website visitors, lead-form activity, Page and Instagram engagement, and uploaded customer lists — with its rough size and whether Meta considers it ready to advertise to. Use it to pick an audience to retarget or to exclude. Read-only: it creates no audience, changes no delivery, and spends no money.
- meta.retargeting_audience_createBuild a retargeting audienceconfirm
Create a real, durable custom audience on the connected Meta ad account for people who already showed interest — visited the website (needs a Pixel), opened or submitted a lead form, or engaged with the Facebook Page or Instagram account. Meta backfills it from history, so it is populated with real people and is immediately usable for targeting or exclusion. It costs nothing and shows no ads by itself, but it permanently creates an advertising object on the advertiser's account and counts against Meta's per-account audience limits. An audience Chirply already built for the same source and window is reused rather than duplicated.
- meta.lead_campaign_planBuild my campaignconfirm
Generate and durably save 3–10 editable Meta ad concepts from a truthful business brief and recommend the best awareness, traffic, engagement, leads, sales, or app-promotion objective. This uses the workspace's own OpenRouter model and may incur that provider's text-generation charges; it does not create anything in Meta or contact anyone.
- meta.lead_campaign_image_generateGenerate with AIconfirm
Generate one real square advertising image through the workspace's own OpenRouter account and store it as a public asset that Meta can fetch. This incurs the image model's provider charge and permanently stores the generated image, but does not create or launch a Meta ad.
- meta.lead_campaign_assets_listView saved versions
List the workspace's saved Meta campaign creative versions, including every generated image, its ad copy snapshot, creative direction, reference images, model, cost, and launched Meta ad id. This is read-only.
- meta.lead_campaign_asset_saveSave uploaded creative
Save an already-public image URL as a durable creative version in a saved Meta campaign draft, preserving the exact copy, direction, and references. This stores metadata but does not generate an image, contact anyone, create a Meta object, or spend ad money.
- meta.instant_form_createCreate lead formconfirm
Create and activate a real Meta instant lead form on a connected Facebook Page, collecting full name, email, and phone. New submissions enter this workspace and may trigger its contact-created automations. This permanently creates an external Meta object but does not launch an ad or spend ad money.
- meta.lead_campaign_simulateComplete test launch
Validate a complete Meta awareness, traffic, engagement, leads, sales, or app-promotion campaign in test mode, including its CTA and placement rules. This creates no Meta objects, contacts no one, incurs no ad spend, and does not require a live Meta connection.
- meta.lead_campaign_launchLaunch lead campaignconfirm
Create and activate one real Meta awareness, traffic, engagement, leads, sales, or app-promotion campaign with a validated destination, CTA, and placement strategy. This immediately makes the campaign eligible to reach real people and spend the selected ad account's money up to the daily budget; Meta bills the advertiser directly. The campaign is created paused and activated last so an incomplete setup cannot spend.
- meta.lead_campaign_variations_launchLaunch selected adsconfirm
Create and activate one real Meta campaign using the selected awareness, traffic, engagement, leads, sales, or app-promotion objective, then launch every selected saved creative as a separate real ad using the shared CTA and placement strategy. If an instant-form lead destination uses form_id __auto__, this also permanently creates an active Meta form. The campaign becomes eligible to reach real people and spend the selected ad account's money up to the shared daily budget; Meta bills the advertiser directly.
- meta.connection_getView Facebook connection
Show whether Facebook, Instagram, and WhatsApp are connected to this workspace, which permissions were granted, and whether Meta requires the owner to reconnect. Tokens and other secrets are never returned.
- meta.whatsapp_accounts_listList WhatsApp numbers
List the WhatsApp Business phone numbers assigned to this workspace, including verified display names, quality ratings, webhook delivery status, setup errors, and current grounded-AI routing. This is read-only and sends no messages.
- meta.whatsapp_accounts_discoverDiscover business numbers
Read the connected Meta businesses, discover their WhatsApp Business accounts and phone numbers, subscribe this workspace to inbound WhatsApp Cloud API webhooks, and refresh this workspace's saved number catalog. This changes webhook configuration but sends no message and incurs no messaging charge.
- meta.whatsapp_ai_automation_updateSave WhatsApp AIconfirm
Turn the tenant-grounded AI fallback on or off for one connected WhatsApp Business number. Turning it on causes future inbound messages that no visual workflow handles to receive REAL AUTOMATIC REPLIES from the selected agent, spending the workspace's own OpenRouter credits and WhatsApp provider resources; unknown, sensitive, upset, or human-requested conversations are handed to the team.
- meta.whatsapp_templates_listList WhatsApp templates
List approved and pending WhatsApp message templates for one WhatsApp Business account assigned to this workspace. This is read-only and sends no message.
- meta.instagram_accounts_listList Instagram profiles
List Instagram Professional profiles linked to this workspace's Facebook Pages, including current usernames, follower counts, media counts, webhook delivery, and live direct-message access status. This is read-only.
- meta.instagram_media_listList Instagram media
List recent posts from one Instagram Professional profile assigned to this workspace, including captions, permalinks, likes, and comment counts. This is read-only.
- meta.instagram_image_publishPublish imageconfirm
Publish a REAL PUBLIC IMAGE POST to one Instagram Professional account assigned to this workspace. The image and caption become visible to that account's audience immediately; this does not buy ads but permanently creates external content until someone deletes it in Instagram.
- meta.pages_listList Facebook Pages
List Facebook Pages and linked Instagram Professional accounts available to this workspace, including permissions, webhook ownership, and delivery health.
- meta.lead_forms_listList Lead Ads forms
List the Facebook Lead Ads forms discovered for this workspace, optionally narrowed to one Page or searched by form name, including questions, field mappings, capture status, and latest sync health. This only reads the saved form catalog and does not contact leads.
- meta.lead_forms_discoverDiscover forms
Read the connected Facebook Pages' Lead Ads form catalogs and questions, then refresh the tenant-scoped form list. Newly discovered forms stay paused until a workspace manager enables them. This makes Graph API reads but does not create ads, spend money, or contact anyone.
- meta.lead_form_updateSave form settings
Turn CRM capture on or off for one connected Facebook Lead Ads form and map each submitted question into a contact field. Pausing a form causes future submissions from it to be retained as ignored webhook events instead of contacts.
- meta.leads_sync_recentSync recent leadsconfirm
Import up to 25 recent real submissions from one connected Facebook Lead Ads form. This creates or enriches real CRM contacts; every newly created contact immediately enters the workspace's contact-created automations, which may send real SMS, email, calls, or other configured follow-up and incur the workspace's provider charges.
- meta.page_automations_listList Page automation
List each Facebook Page's current Messenger, linked Instagram Direct, and public-comment autoresponder settings, including which AI agents are assigned. This does not send any replies.
- meta.page_activity_getPage activity
Report recent Messenger, linked Instagram Direct, and public-comment activity for each connected Facebook Page: how many messages and visitor comments arrived, how many an AI agent answered on its own, how many a person answered, how many threads are still waiting on a human, and how many comment replies were skipped or failed. Read-only — it sends nothing and spends nothing.
- meta.page_automation_updateSave automationconfirm
Set how one connected Facebook Page handles incoming Messenger messages, linked Instagram Direct messages, and public post comments. Enabling a channel causes the assigned AI agent to send REAL REPLIES TO REAL PEOPLE automatically using the business identity and the workspace's OpenRouter account. Public comment replies are visible to everyone who can see the post.
- meta.ad_accounts_listList Meta ad accounts
List the workspace's connected Meta ad accounts, their currency, Business Manager owner, account status, and minimum daily budget. This does not spend money.
- meta.social_events_listList Facebook activity
List recent Facebook and Instagram comments, mentions, feed changes, and message reactions captured for this workspace. This is read-only.
- meta.page_resubscribeRe-subscribe
Reinstall the webhook subscription on one Facebook Page. For Instagram accounts connected through Facebook Login, that Page installation is also the delivery link. This changes Meta configuration but does not publish content or send a message.
- meta.connection_disconnectDisconnect Facebookconfirm
Disconnect Facebook and Instagram from this workspace, uninstall Chirply from its Pages, revoke the Meta user grant, and remove locally stored Page/ad assets. Existing CRM contacts and messages remain. Reconnecting is required to restore service.
- meta.comment_dm_getView comment-to-DM
Show the comment-to-DM settings for one connected Instagram account: whether it is on, the keyword a comment must contain, the message that gets sent, and whether each commenter is messaged only once.
- meta.comment_dm_updateSave comment-to-DMconfirm
Set whether an Instagram direct message is sent automatically to people who comment on this account's posts. Turning this on causes REAL DIRECT MESSAGES TO BE SENT AUTOMATICALLY TO REAL PEOPLE who have not messaged the business first, every time a matching comment is posted, with no further human approval. Instagram permits one such message per comment, within 7 days of it being posted.
- meta.comment_posts_listList posts you can automate
List the recent posts from one connected Facebook Page and its linked Instagram account, with the id each one is identified by. Use it to find the post id a per-post comment rule targets.
- meta.comment_rules_listView comment rules
List the comment automation rules on one connected Facebook Page: what each one matches (every post or one post, which keywords), what it replies publicly, and whether it sends a private message or starts a flow. Rules cover Messenger and the Page's linked Instagram account together.
- meta.comment_rule_saveSave comment ruleconfirm
Create or update one comment automation rule on a connected Facebook Page. An active rule causes REAL PUBLIC COMMENT REPLIES and REAL PRIVATE MESSAGES TO BE SENT AUTOMATICALLY TO REAL PEOPLE, from the workspace's own connected account, every time a matching comment is posted and with no further human approval. Meta permits one private reply per comment within 7 days, so a rule either sends a fixed message or starts a flow, never both. When several rules match, the most specific one runs: a rule for one post beats a rule for every post, and a keyword rule beats a catch-all.
- meta.comment_rule_deleteDelete comment ruleconfirm
Permanently delete one comment automation rule. It stops running immediately and cannot be recovered; comments it used to handle fall through to the next matching rule, or to the Page's own comment settings.
- meta.sample_threads_seedAdd sample conversations
Add a small set of clearly-labelled example Instagram or WhatsApp conversations to this workspace's inbox so the messaging features can be demonstrated before real customers write in. The threads are fictional, are shown with a “Sample” badge, and replies sent to them are recorded but never delivered to anyone. It also creates matching CRM contacts marked as sample data. Running this again replaces the existing samples for that channel.
- meta.sample_threads_clearRemove sample conversationsconfirm
Delete this workspace's seeded sample conversations and the sample contacts created alongside them. Only ever removes demonstration data — real customer conversations and contacts are untouched. Omit the channel to clear both Instagram and WhatsApp samples.
- meta.campaigns_listList Meta campaigns
List campaigns in one connected Meta ad account. This reads campaign configuration and does not spend money.
- meta.campaign_createCreate paused campaignconfirm
Create a real campaign in Meta Ads Manager in PAUSED state. It cannot spend until separately activated, but it permanently creates an external advertising object.
- meta.campaign_activateActivate campaign — can spendconfirm
Activate a real Meta campaign. If its ad sets and ads are eligible, this may immediately begin spending the connected organization's money according to their budgets.
- meta.campaign_pausePause campaign
Pause a real Meta campaign, stopping new delivery and spend as Meta applies the status change. The campaign and its configuration remain available to reactivate later.
- meta.ad_set_createCreate paused ad setconfirm
Create a real PAUSED Meta ad set with a daily budget and targeting. It cannot spend until activated, but its budget becomes live if its campaign and delivery are later activated.
- meta.creative_create_linkCreate link ad creativeconfirm
Create a real unpublished Meta link-ad creative for a connected Facebook Page. This does not deliver or spend until attached to an active ad.
- meta.ad_sets_listList Meta ad sets
List ad sets, budgets, optimization goals, schedules, and delivery status in one connected Meta ad account. This does not spend money.
- meta.ad_set_activateActivate ad set — can spendconfirm
Activate a real Meta ad set. If its campaign and ads are also active, it may immediately begin spending the connected organization's money up to its configured budget.
- meta.ad_set_pausePause ad set
Pause a real Meta ad set, stopping new delivery and spend as Meta applies the change while retaining its budget, targeting, and ads.
- meta.ad_createCreate paused adconfirm
Create a real Meta ad in PAUSED state from an existing ad set and creative. It cannot deliver or spend until separately activated in Meta.
- meta.ads_listList Meta ads
List ads, delivery status, parent campaign/ad set, and creative in one connected Meta ad account. This does not spend money.
- meta.ad_activateActivate ad — can spendconfirm
Activate a real Meta ad. If its campaign and ad set are also active, it may immediately deliver to real people and spend the connected organization's money.
- meta.ad_pausePause ad
Pause a real Meta ad, stopping new delivery and spend as Meta applies the change while retaining the ad and creative.
- meta.messenger_profile_getView Messenger setup
Read the live localized greetings, Get Started action, ice breakers, persistent menus and composer state, commands, account-linking URL, complete webview settings, and whitelisted domains for one connected Facebook Page. This reads Meta's live Messenger Profile configuration and sends no message.
- meta.messenger_profile_updatePublish Messenger setupconfirm
Immediately publishes current Messenger Profile controls on a real connected Facebook Page: localized greetings, Get Started or ice breakers, localized persistent menus and composer state, URL webview behavior, commands, HTTPS account linking, and optionally the domain whitelist. Chirply postbacks are bound to exact Page Bot Flows. Get Started takes display priority over ice breakers. This changes a live customer-facing surface but sends no message by itself.
- meta.messenger_profile_deleteRemove from Messengerconfirm
Immediately removes Get Started, greetings, ice breakers, persistent menus, commands, and account linking from a real connected Facebook Page. Existing Chirply Bot Flows remain. Messenger Extensions domains stay saved unless remove_whitelisted_domains is explicitly true.
- meta.messenger_capabilitiesList Messenger capabilities
List Meta's current Facebook Messenger Platform features and Chirply's exact implementation state for each one, including supported controls, permission-gated products, Meta previews, policy limits, known build gaps, and retired legacy features. This is read-only and sends no message.
- meta.messenger_bot_flow_link_generateGet Messenger link
Get the permanent m.me URL, QR value, and current Page delivery readiness for one saved Messenger-compatible Bot Flow. Opening the URL does not immediately send a message; after a person enters Messenger and taps Get Started when required, the link starts exactly that live flow and its real outbound messages. Draft flows remain inert until published.
- meta.messenger_referral_link_generateGenerate Messenger link
Generate a Page-scoped m.me Messenger link for a connected Facebook Page, optionally carrying a Bot Flow referral and attribution value. This is read-only, creates no Meta object, and sends no message.
- meta.messenger_message_us_embed_generateGenerate Message Us embed
Generate Meta's Page-scoped Message Us JavaScript SDK embed for a connected Facebook Page. This only returns website code; it creates no Meta object and sends no message.
- meta.messenger_login_connect_status_getCheck Login Connect setup
Check the connected Page, pages_messaging grant, Page messaging task, messaging_optins webhook subscription, and Chirply server configuration for Meta Login Connect with Messenger. Meta exposes no API for App Review or its Login Connect App Dashboard toggle, so those remain explicitly manual; this sends no message.
- meta.messenger_login_connect_setup_generateGenerate Login Connect setup
Generate Meta's documented Login Connect OAuth URL and JavaScript SDK call for a connected Facebook Page. This returns setup code only; it creates no Meta object, sends no message, never exposes the server-only Meta client token, and cannot replace App Review or the manual App Dashboard Page toggle.
- meta.messenger_login_connect_identity_preparePrepare Login Connect contact
Derive Meta's Page-specific Login Connect login_id on Chirply's server and attach the website's authenticated app-scoped user id to one CRM contact. This stores no PSID and sends no message; the identity remains unable to send until Meta's signed user_messenger_contact opt-in webhook is captured.
- meta.messenger_login_connect_optin_attachAttach Login Connect opt-in
Attach a previously captured, signed messaging_optins.login_id webhook to one authenticated CRM contact through Chirply's opaque identity handle. Meta's login_id remains server-only; this does not accept an unverified opt-in, does not create a PSID, and sends no message.
- meta.messenger_login_connect_initial_message_sendSend Login Connect messageconfirm
Immediately sends one real Facebook Messenger message to the CRM contact through Meta recipient.login_id. Chirply requires a signed user_messenger_contact opt-in for the same Page and contact, enforces Meta's 24-hour first-message deadline, records the consent basis, and permanently fences duplicate or indeterminate sends.
- meta.messenger_personas_listList Messenger personas
List the named human and bot personas currently available on one connected Facebook Page. This reads Meta's live Persona API, changes nothing, and sends no message.
- meta.messenger_personas_createCreate Messenger persona
Create a named human or bot identity on a real connected Facebook Page using a public profile-picture URL. Creating it does not send a message, but it becomes available for future Page messages and is stored by Meta.
- meta.messenger_personas_deleteRemove Messenger personaconfirm
Soft-delete one persona from a real connected Facebook Page. It can no longer send new messages, while historical messages retain their attribution; this cannot be undone in Chirply.
- meta.messenger_sticker_packs_listList Messenger sticker packs
Browse Meta's public free first-party Messenger sticker packs with localized names, descriptions, previews, and counts. This is read-only, does not include custom/paid/avatar/GIF catalogs, and sends no sticker.
- meta.messenger_stickers_listList stickers in pack
List every public free first-party Messenger sticker in one Meta sticker pack, including preview image, dimensions, and animation state. This is read-only and sends no sticker.
- meta.messenger_stickers_searchSearch Messenger stickers
Search Meta's public free first-party Messenger sticker catalog by a localized keyword of at least two characters. This is read-only and sends no sticker; custom, paid, avatar stickers and GIF search are not available through this API.
- meta.messenger_attachment_upload_infoView reusable attachment upload
Check whether one connected Facebook Page is ready to create reusable Messenger image, video, audio, and file attachment ids. This reads Page readiness and Meta's provider limits, sends no message, returns no previous attachment ids, and makes no change. Meta's Attachment Upload API does not provide supported list or delete operations, and reusable ids expire after 90 days.
- meta.messenger_attachment_createCreate reusable attachmentconfirm
Ask Meta to fetch one public HTTPS image, video, audio, or file and create a real reusable attachment id owned by a connected Facebook Page. This sends no message and Chirply persists neither the source URL nor returned id, but the external Meta asset cannot be listed or deleted through the supported Attachment Upload API; copy the returned id immediately and expect it to expire after 90 days.
- meta.messenger_routing_getView Messenger routing
Read Meta's live Conversation Routing feature status for one connected Facebook Page, Chirply's own Meta app id, the granted pages_messaging permission, Page messaging task, and required routing webhook subscriptions. This does not reveal a selected default-app id or a live per-thread owner because Meta's current status API does not return either; it sends no message and changes nothing.
- meta.messenger_thread_owner_getView Messenger thread owner
Read Meta's current owner for one workspace-owned Facebook Messenger conversation, including the owning app id, expiration, idle state, and whether Chirply owns it. This sends no message and changes nothing.
- meta.messenger_routing_passSend and pass Messenger conversationconfirm
Send one real customer-visible Facebook Messenger RESPONSE message, then pass that conversation to a specified connected Meta app or the Page's default app. This immediately pauses Chirply automation for the local thread so a bot cannot speak after control leaves; Meta may reject the send unless pages_messaging has Advanced Access through App Review, the authorizing person retains Page messaging access, Chirply currently owns the thread, or the Page explicitly allows Chirply to take control.
- meta.messenger_routing_releaseSend and release Messenger conversationconfirm
Send one real customer-visible Facebook Messenger RESPONSE message, then release the conversation to the Page's default app without asking Meta to notify that app. This immediately pauses Chirply automation for the local thread so a bot cannot speak after release; Meta may reject the send unless pages_messaging has Advanced Access through App Review, the authorizing person retains Page messaging access, Chirply currently owns the thread, or the Page explicitly allows Chirply to take control.
- meta.messenger_thread_control_passPass Messenger thread controlconfirm
Immediately pass one workspace-owned Facebook Messenger conversation to a specified connected Meta app without sending a customer message. Chirply automation is paused before the provider call so it cannot speak after ownership leaves, and Meta emits a messaging_handovers webhook to subscribed apps.
- meta.messenger_thread_control_releaseRelease Messenger thread controlconfirm
Immediately release one workspace-owned Facebook Messenger conversation to idle/default routing without sending a customer message or notifying another app. Chirply automation is paused before the provider call so it cannot speak after ownership leaves.
- meta.messenger_thread_control_takeTake Messenger thread controlconfirm
Immediately take control of one workspace-owned Facebook Messenger conversation for Chirply without sending a customer message. This changes the live owner at Meta and resumes Chirply automation after Meta confirms success.
- meta.messenger_thread_control_requestRequest Messenger thread controlconfirm
Ask the current Meta app owner to pass one workspace-owned Facebook Messenger conversation to Chirply without sending a customer message. This request is supported when Chirply is the Page's default receiver; ownership and automation do not change until Meta later delivers the handover.
- meta.messenger_thread_control_extendExtend Messenger thread controlconfirm
Extend Chirply's current control of one workspace-owned Facebook Messenger conversation by a chosen duration without sending a customer message. Meta allows at most 604,800 seconds (7 days); this changes live routing but does not alter the local automation pause state.
- meta.messenger_people_searchSearch Messenger people
Search known Facebook Messenger people and Page conversations in this workspace by name, email, business name, or Page-scoped person id. This is read-only, sends no message, and never returns Login Connect login ids.
- meta.messenger_user_menu_getView person menu
Reads Meta's live person-specific persistent-menu override and the inherited Page-level menu for one known Facebook Messenger PSID. It sends no message and does not support Instagram or WhatsApp identities.
- meta.messenger_user_menu_publishPublish person menuconfirm
Immediately replaces the live persistent menu for one real Facebook Messenger person on one connected Page, including localized composer state and complete webview behavior. Chirply postbacks are bound to exact Page Bot Flows. This changes a customer-facing surface but sends no message.
- meta.messenger_user_menu_removeRemove person menuconfirm
Immediately deletes one real Facebook Messenger person's custom persistent-menu override. The person falls back to the Page-level menu; existing Bot Flows remain and no message is sent.
- meta.messenger_custom_labels_listList Messenger custom labels
Read the live custom-label catalog for one connected Facebook Page. This changes nothing and sends no message.
- meta.messenger_custom_label_getView Messenger custom label
Read one Page-owned Messenger custom label after verifying that the label belongs to the connected Facebook Page. This changes nothing and sends no message.
- meta.messenger_custom_label_createCreate Messenger custom labelconfirm
Immediately creates a real Page-owned Messenger custom label in Meta. It sends no message, but the new label becomes available to every app and teammate managing that Page.
- meta.messenger_custom_label_deleteDelete Messenger custom labelconfirm
Immediately deletes a real Page-owned Messenger custom label from Meta, removing it from every person who has it. This cannot be undone and sends no message.
- meta.messenger_conversation_labels_listView conversation labels
Read the live custom labels attached to one Page-scoped Messenger person on one connected Facebook Page. This changes nothing and sends no message.
- meta.messenger_conversation_label_assignAssign Messenger custom labelconfirm
Immediately assigns one real Page-owned Messenger custom label to one known Page-scoped person in Meta. This changes shared inbox organization but sends no message.
- meta.messenger_conversation_label_removeRemove Messenger custom labelconfirm
Immediately removes one real Page-owned Messenger custom label from one known Page-scoped person in Meta. This changes shared inbox organization but sends no message.
- meta.messenger_conversation_blockBlock Messenger personconfirm
Immediately applies Meta's block_user moderation action to one real Page-scoped Messenger person on one connected Facebook Page. This changes the live Page conversation and may prevent further interaction; it sends no message. Meta exposes no current moderation-state read, so verify the intended person and Page before confirming.
- meta.messenger_conversation_unblockUnblock Messenger personconfirm
Immediately applies Meta's unblock_user moderation action to one real Page-scoped Messenger person on one connected Facebook Page. This changes the live Page conversation and may allow interaction again; it sends no message. Meta exposes no current moderation-state read, so verify the intended person and Page before confirming.
- meta.messenger_conversation_banBan Messenger personconfirm
Immediately applies Meta's ban_user moderation action to one real Page-scoped Messenger person on one connected Facebook Page. This changes the live Page conversation and may prevent further interaction; it sends no message. Meta exposes no current moderation-state read, so verify the intended person and Page before confirming.
- meta.messenger_conversation_unbanUnban Messenger personconfirm
Immediately applies Meta's unban_user moderation action to one real Page-scoped Messenger person on one connected Facebook Page. This changes the live Page conversation and may allow interaction again; it sends no message. Meta exposes no current moderation-state read, so verify the intended person and Page before confirming.
- meta.messenger_conversation_move_to_spamMove Messenger conversation to spamconfirm
Immediately applies Meta's move_to_spam action to one real Page-scoped Messenger person's conversation, moving it to Spam in Meta Business Suite Inbox. This changes a live external inbox and sends no message. Meta documents no restore-from-spam action on this API, so confirm the intended person and Page first.
- meta.messenger_reviewed_messaging_statusView reviewed messaging setup
Read one connected Facebook Page's live permission and webhook readiness for Utility Messages, the non-inspectable App Review gate for HUMAN_AGENT, and the separate paid limited-beta onboarding status for Marketing Messages. This sends no message, spends no money, and explicitly identifies retired Messenger products that Chirply will not call.
- meta.messenger_reviewed_conversationsList reviewed-message recipients
List recent workspace-owned Facebook Messenger conversations for one connected Page and show which are inside Meta's 24-hour standard window or seven-day HUMAN_AGENT window. This reads local inbox state only, exposes no Page access token or PSID, and sends no message.
- meta.messenger_utility_templates_listList utility templates
Read Meta's live Page-owned Utility Message template library, including language, review status, components, and whether a template was cloned from Meta's library. This requires page_utility_messaging, changes nothing, sends no message, and incurs no Meta messaging charge.
- meta.messenger_utility_library_searchSearch Meta utility templates
Search Meta's current prebuilt Utility Message template library by name or content and language for one connected Page. This is read-only, sends no message, creates no template, and incurs no Meta messaging charge.
- meta.messenger_utility_template_createCreate utility templateconfirm
Submit a Page-owned transactional Utility Message template to Meta for real review. This creates external Page configuration but sends no customer message and incurs no send charge. Marketing or promotional content is prohibited; Meta can reject or later disable the template, and Utility Messages currently require page_utility_messaging plus supported Page and recipient geography.
- meta.messenger_utility_template_cloneClone Meta utility templateconfirm
Clone one current prebuilt Meta Utility Message template into a real Facebook Page's reviewed library, with optional documented body and URL-button inputs. This changes external Page configuration but sends no customer message and incurs no send charge; Meta still controls review status and availability.
- meta.messenger_utility_sendSend utility messageconfirm
SENDS A REAL FACEBOOK MESSENGER MESSAGE outside or inside the standard reply window using one live Meta-approved UTILITY template. Delivery reaches the selected workspace-owned conversation immediately with no undo and may be billed or rate-limited by Meta. The message must be a transactional order, account, appointment, or event update—not marketing—and Meta enforces page_utility_messaging plus Page and recipient geography.
- meta.messenger_human_agent_sendSend human support replyconfirm
SENDS A REAL FACEBOOK MESSENGER HUMAN_AGENT REPLY. This is only for a literal support reply written and approved by a real person within seven days of that person's last Page message; it pauses automation on the conversation, reaches the recipient immediately with no undo, and must never carry automated, unrelated, or marketing content. Chirply requires an explicit human-authored attestation, while Meta separately requires HUMAN_AGENT App Review approval.
- meta.messenger_calling_getView Messenger Calling
Read one connected Facebook Page's live Meta Calling eligibility, current audio/video/icon/hours/routing settings, webhook and permission readiness, known Messenger people, and recent call events. This sends no message and changes nothing; Meta's messenger_api_calling status remains the authoritative review/availability gate.
- meta.messenger_calling_update_settingsUpdate Messenger Callingconfirm
Immediately replace the connected Facebook Page's live Messenger Calling audio, video, call-icon, weekly-hours, timezone, and ring-target settings. META rings Meta's native surface; PARTNERS sends real inbound calls to Chirply's browser softphone and requires the calls webhooks. Meta rejects this operation until the Page and app pass its Calling access/review gate.
- meta.messenger_calling_permission_getCheck Messenger call permission
Read Meta's live outbound-calling permission and per-action limits for one known person in a connected Facebook Page's Messenger thread. This sends no message and does not infer permission from Chirply data.
- meta.messenger_calling_permission_requestRequest Messenger call permissionconfirm
Send a real Messenger calling_optin template to one known person, asking them to approve outbound calls from the connected Facebook Page. Meta allows at most two permission requests per thread in 24 hours and grants expire after seven days; the person may reject the request.
- meta.messenger_calling_prompt_sendSend Messenger call promptconfirm
Send a real Messenger call_prompt template that lets one known person call the connected Facebook Page for one to seven days, including when its persistent call icon is hidden. This immediately messages a real person and Meta rejects it until Calling is enabled.
- meta.messenger_calling_connectStart Messenger callconfirm
Immediately place a real outbound Messenger audio/video call from the connected Facebook Page to one known person using the supplied WebRTC SDP offer. The person must have live call permission and Meta must report start_call is allowed; the calling client must remain connected to carry the media.
- meta.messenger_calling_acceptAccept Messenger callconfirm
Accept a real inbound Messenger call to the connected Facebook Page using the provider call id and supplied WebRTC SDP offer. Meta requires acceptance within 60 seconds, and the calling client must remain connected to carry the media.
- meta.messenger_calling_rejectDecline Messenger callconfirm
Immediately decline a real inbound Messenger call to the connected Facebook Page. The caller's ringing attempt ends and this action cannot be undone.
- meta.messenger_calling_terminateEnd Messenger callconfirm
Immediately terminate a real active Messenger call on the connected Facebook Page. The other participant is disconnected and this action cannot be undone.
- meta.messenger_calling_dtmf_preparePrepare Messenger DTMF toneconfirm
Validate and prepare one RFC4733 touch tone for a real active Messenger call. The returned browser command always uses Meta's required 500 ms duration and 100 ms inter-tone gap; it does not falsely claim that the server injected RTP because the live WebRTC sender exists only in the open softphone, which must consume the command. Meta emits no DTMF webhook.
- meta.messenger_calling_media_updateUpdate Messenger call mediaconfirm
Renegotiate the live audio/video tracks of a real active Messenger call using increasing media versions, the actual browser MediaStreamTrack ids, and a new WebRTC SDP offer containing those ids. This can immediately mute, unmute, enable, or disable camera media for both participants' live call experience.
- meta.messenger_calling_screen_share_updateUpdate Messenger screen shareconfirm
Immediately start, stop, or restore screen-share video in a real active Messenger call by sending Meta's current media_update contract with increasing versions, a browser-created SDP offer, and the exact getDisplayMedia or restored-camera MediaStreamTrack ids. The browser must obtain the person's screen-sharing permission and keep the live WebRTC peer connected; this operation changes what the other participant sees.
- meta.messenger_calling_metrics_submitSubmit Messenger call metricsconfirm
Submit the finished real Messenger call's end reason and optional browser audio-quality counters to Meta. Meta accepts this once per call, only after the call ends, and only within 24 hours; it changes provider analytics rather than contacting the person.
- meta.messenger_measurement_getView Messenger performance
Read one connected Facebook Page's Bot Flow entries, completions, node drop-offs, human handoffs, entry attribution, message delivery/read receipts, customer-shared native cart events, and current Meta Messaging Insights. This sends no message, records no conversion, and changes nothing; Meta Insights can remain unavailable until the Page grants ANALYZE plus the documented permissions and Advanced Access.
- meta.messenger_app_event_logReport conversion to Metaconfirm
Immediately sends a real Messenger App Event to Meta for one Page-scoped person, including the Page id, that person's PSID, the event name, optional purchase value and currency, and the two supplied tracking declarations. It sends no Messenger message and charges no money, but the signal can affect Meta analytics, attribution, optimization, and advertising, so a workspace owner or admin must confirm it.
- meta.messenger_marketing_statusCheck Marketing Messages
Inspect one connected Facebook Page's current paid Marketing Messages readiness: required Meta permissions, access-token lifetime, spendable ad accounts, subscriber API probe, required delivery webhooks, documented business and recipient geographies, and honest boundaries for One-Time Notification, NPI news, Sponsored Messages, and legacy Recurring Notifications. This sends nothing and spends nothing; Meta does not expose Tech Provider review, Terms acceptance, or geography as a single inspectable flag.
- meta.messenger_marketing_webhooks_enableEnable Marketing delivery trackingconfirm
After Meta proves this exact Facebook Page is onboarded for Marketing Messages, add the five current paid-product delivery, failure, echo, read, and click webhook fields to both the app and Page subscriptions while preserving all existing Page fields. This changes external Meta webhook configuration but sends no person a message and creates no paid delivery; Chirply deliberately keeps these gated fields out of the base subscription so an ineligible Page cannot break its working webhooks.
- meta.messenger_marketing_audiences_listList customer-list audiences
List the live Messenger Marketing Messages Custom Audiences for one connected Page and ad account using Meta's exact subtype-1010 filter. This requires a non-expiring Flow 1/3 system-business access token; it sends nothing, uploads no customer data, and spends nothing.
- meta.messenger_marketing_audience_createCreate customer-list audienceconfirm
Create a real external Meta Custom Audience dedicated to Messenger Marketing Messages for one Page and ad account. This requires a non-expiring Flow 1/3 system-business access token and changes external ad-account configuration, but uploads no customer data, sends no message, and spends nothing.
- meta.messenger_marketing_audience_users_addUpload audience customersconfirm
UPLOAD CUSTOMER IDENTIFIERS TO META for matching into one real Messenger Marketing Custom Audience. Chirply normalizes and SHA-256 hashes every email/phone before transmission, sends at most 10,000 rows per confirmed request, and Meta may take up to 24 hours to match them; only matched people with valid marketing consent should be included, and subscription tokens remain hidden until Meta's 100-match privacy threshold is met. This sends no Messenger message and creates no paid delivery.
- meta.messenger_marketing_audience_users_removeRemove audience customersconfirm
REMOVE SHA-256-HASHED CUSTOMER IDENTIFIERS FROM ONE REAL META CUSTOM AUDIENCE. This changes external audience membership after a confirmed request, but does not unsubscribe the people at Page level or remove them from other audiences; call the Page-level unsubscribe operation when marketing permission itself must end. It sends no Messenger message and spends nothing.
- meta.messenger_marketing_audience_unsubscribeUnsubscribe Marketing audienceconfirm
UNSUBSCRIBE REAL PEOPLE FROM THIS PAGE'S ENTIRE MESSENGER MARKETING MESSAGES AUDIENCE using Meta's current Page-level unsubscribe API. Each row must use phone/email, PSID, or an opaque subscriber handle; this ends Page-level marketing eligibility rather than merely removing one Custom Audience membership. It sends no Messenger message and spends nothing, but the person must opt in again before another paid Marketing Message.
- meta.messenger_marketing_subscribers_listList marketing subscribers
Read up to 1,000 current Marketing Message subscription records for one connected Facebook Page directly from Meta, deduplicating recipients and optionally filtering Custom Audiences. Raw subscription tokens are encrypted into opaque Chirply handles before being returned; this sends nothing and incurs no delivery charge, but the result contains sensitive subscriber status and eligibility data.
- meta.messenger_marketing_subscriber_getView marketing subscriber
Read one live Marketing Message subscriber token's status, expiration, next eligible send time, re-opt-in state, timezone, Page-scoped recipient id when Meta exposes it, and matched Custom Audiences. The input and output use an opaque encrypted handle rather than the raw Meta send token; this sends nothing and spends nothing.
- meta.messenger_marketing_campaigns_listList Marketing campaigns
Read the live direct Marketing Message campaigns owned by one connected Facebook Page and billed through one connected Meta ad account, including the underlying campaign, message-set, and message statuses, budget, schedule, and Pixel attribution. This sends nothing and spends nothing.
- meta.messenger_marketing_campaign_getView Marketing campaign
Read one live direct Marketing Message campaign after proving its Page and billed ad-account ownership, including all three Meta delivery objects, budget, schedule, status, and Pixel attribution. This sends nothing and spends nothing.
- meta.messenger_marketing_campaign_createCreate Marketing campaignconfirm
Create a real paid Marketing Messages campaign for one connected Facebook Page and billed Meta ad account. Creating it does not send a message or create a charge, but it creates external ad infrastructure and establishes a real daily, lifetime, or Meta-estimated spend cap; the campaign remains inert until explicitly resumed and the send API is called.
- meta.messenger_marketing_campaign_updateUpdate Marketing campaignconfirm
Update the name, same-mode budget, or schedule of a real direct Marketing Message campaign after proving its Page and ad-account ownership. This changes external paid-campaign configuration and can raise how much a later approved send may spend, but it does not itself send a message; Meta does not document switching a live campaign between daily and lifetime budget modes, so Chirply refuses that guess.
- meta.messenger_marketing_campaign_pausePause Marketing campaignconfirm
Pause the real message, message set, and campaign behind one direct Messenger Marketing campaign, stopping future paid sends from being accepted. This changes external Meta state but does not recall messages already delivered.
- meta.messenger_marketing_campaign_resumeResume Marketing campaignconfirm
Activate the real campaign, message set, and message behind one direct Messenger Marketing campaign. Activation does not itself send, but it enables later approved sends that message real subscribers and charge the billed ad account; Meta requires about 10 minutes after activation before sending.
- meta.messenger_marketing_campaign_deleteDelete Marketing campaignconfirm
Permanently delete one real direct Marketing Message campaign after proving its connected Page and billed ad-account ownership. This cannot be undone, removes external campaign history/configuration, and does not recall messages already delivered.
- meta.messenger_marketing_previewPreview Marketing message
Ask Meta to render a safe hosted preview for one documented rich Marketing Message against a connected Page and ad account. This sends nothing to a subscriber, changes no campaign, and incurs no delivery charge; Chirply returns only Meta's verified facebook.com preview URL, never executable iframe HTML.
- meta.messenger_marketing_delivery_estimateEstimate Marketing delivery
Ask Meta for the estimated lower and upper number of paid Messenger Marketing send calls supported by one daily or lifetime budget for a connected Page/ad account. This is an estimate, not a guarantee; it sends nothing, changes no budget, and incurs no delivery charge.
- meta.messenger_marketing_sendSend paid Marketing messageconfirm
SENDS A REAL PAID FACEBOOK MESSENGER MARKETING MESSAGE immediately to one explicitly opted-in subscriber using an active direct campaign. Meta bills the selected ad account for billable delivery. Chirply re-reads live token eligibility, enforces the 12-hour cooldown and 10-minute activation delay, validates current rich-message formats, requires geography/opt-in/payment attestations, and writes a fenced durable receipt before calling Meta so a crash cannot silently duplicate the send.
- meta.messenger_marketing_insightsView Marketing performance
Read Meta's live paid Messenger Marketing delivered count, reads, link clicks, spend, cost per delivery/click, and attributed Pixel or Conversions API actions, values, and purchase ROAS for one Page-owned campaign. Metrics may be estimated or region-excluded exactly as Meta documents; this sends nothing and spends nothing.
- meta.messenger_marketing_optin_requestAsk for marketing opt-inconfirm
SEND A REAL IN-THREAD FACEBOOK MESSENGER OPT-IN REQUEST to a person who recently started a Page conversation. The notification_messages template asks for explicit Marketing Messages consent; it does not itself grant consent, spend paid-delivery budget, or authorize a later send until Meta returns a subscription token. Chirply enforces the current 24-hour thread window and writes a durable receipt first.
- meta.messenger_news_sendSend NPI news messageconfirm
SENDS A REAL FACEBOOK MESSENGER NEWS MESSAGE outside the 24-hour window using Meta's NON_PROMOTIONAL_SUBSCRIPTION tag. Only a Page currently registered in Meta's News Page Index may use it, and the content must be strictly non-promotional news—no subscription offer, deal, coupon, discount, branded content, affiliate promotion, or third-party promotion. Chirply requires both attestations and writes a fenced durable receipt before sending.
mobile
- mobile.statusMobile apps
Report where each Chirply mobile app is up to — Android on Google Play and the iPhone app on the App Store — and every request this workspace has made to be let into one. An app in 'closed_test' is not publicly installable: it only appears for store accounts a person has added to the tester list, so an install link is useless until a request comes back 'added'. Reads only; changes nothing and sends nothing.
- mobile.request_test_accessRequest mobile app accessconfirm
Ask for an email address to be added to a Chirply mobile app's store tester list, and alert the Chirply team that it is waiting. This does NOT install anything or grant access: a person has to type the address into Google Play Console (or App Store Connect) by hand, usually within a day, and the requester is emailed once that happens. The address must be the one the device's store account uses — a Google account for Android, an Apple ID for iPhone — which is often not the address they sign in to Chirply with; the app will not appear for any other account. Submitting the same address again bumps the existing request rather than queueing it twice. Costs nothing.
- mobile.withdraw_test_accessWithdraw mobile app request
Take one of this workspace's tester requests back off the queue, so nobody adds that address to the store's tester list. The record is kept (marked withdrawn) rather than deleted, and the same address can be requested again later. It does not remove anyone the store has ALREADY added — that has to be undone in Play Console or App Store Connect.
notifications
- notifications.list_devicesBrowsers you're notified on
List the browsers registered to receive desktop notifications. A signed-in caller sees only their OWN browsers, since notification permission is granted per browser profile; an API key acts for the workspace and sees every registration in it. Each entry reports which topics it wants, whether it is still reachable, and when it was last seen. The push endpoint itself is never returned.
- notifications.set_topicsChoose what a browser tells you about
Replace the list of notification topics one browser receives. This is the whole list, not a patch — topics left out are switched off, and an empty list silences that browser without removing it. Valid topics: "message" (new messages and leads), "call" (calls you missed), "approval" (ai employees waiting on you), "payment" (payments), "reminder" (reminders).
- notifications.remove_deviceRemove a browserconfirm
Stop notifying one browser and forget its registration. The browser keeps its operating-system permission, so it will re-register the next time somebody signs in on it and turns notifications on again — this is not a permanent block.
- notifications.send_testSend a test
Fire a single test notification at one registered browser and report whether the push service accepted it. Nothing is charged and nobody outside the workspace sees it. This is the way to tell 'notifications are misconfigured' apart from 'nothing has happened yet'.
- notifications.sendNotify the team
Show a desktop notification on every browser in this workspace that has opted into the given topic. This reaches STAFF ONLY — it is not a way to message a contact or a customer, it sends no SMS or email, and it costs nothing. Anyone at a desk sees it immediately, so use it for things that genuinely warrant interrupting someone. Topics: "message" (new messages and leads), "call" (calls you missed), "approval" (ai employees waiting on you), "payment" (payments), "reminder" (reminders).
partnerships
- partnerships.list_offersPartnerships available to you
List the partnerships this workspace can buy right now, with the monthly price of each, what it includes, and who bills it. These appear only in a workspace created by a white-label agency that has switched them on — the agency's client is buying a partnership with Chirply, the software company behind the platform, not with the agency: Chirply takes the payment on its own checkout and the agency is paid a referral commission. Returns an empty list, and no error, wherever there is nothing on offer. Reads only: charges nothing and changes nothing.
- partnerships.start_purchaseStart a partnership purchaseconfirm
Open a purchase of one of the partnerships from 'partnerships.list_offers' and get back the amount and the Stripe publishable key the card form needs. NOTHING IS CHARGED AND NOTHING IS CREATED IN STRIPE by this call - it records the intent and reserves the price, which the server computes itself. The price is NOT an argument: White-Label is $497 per month and Reseller is $297 per month, billed by Chirply on Chirply's own account, recurring until cancelled. Note what completing it does to this workspace: it is promoted out of the agency's client list into an independent agency of its own, keeping all of its data, and the agency that used to run it loses access. Only an owner or admin of the workspace who is NOT one of the agency's own people may do this. Pair it with 'partnerships.complete_purchase', which is the call that actually takes the money.
- partnerships.complete_purchasePay for the partnershipconfirm
CHARGES THE CARD and, when it succeeds, makes the buyer a partner: a real recurring monthly subscription on Chirply's Stripe at the price reserved by 'partnerships.start_purchase', the paid entitlement applied, and THIS WORKSPACE PROMOTED out of its agency's client list into an independent agency of its own - it keeps every contact, funnel and campaign in it, gains the ability to brand itself and open client workspaces, and the agency that used to run it loses its access and is emailed to say so. Pass the Stripe ConfirmationToken the card form produced. If the bank asks for 3-D Secure the call returns 'requires_action' with a client secret, and the same capability is called again with no token once the browser has cleared it. Safe to call more than once — every step after the charge is idempotent.
- partnerships.get_selling_settingsPartnerships you offer your clients
Report whether this white-label agency offers its client workspaces a Reseller or a White-Label partnership, what each costs the client, and the deal the agency earns on one — the commission percentage, whether it pays on every renewal or only the first payment, and how many months it runs for (0 meaning it never stops). All of it is read from the live rate table rather than fixed numbers. Both offers are off until an agency deliberately turns them on. Reads only: changes nothing and shows nothing to anybody.
- partnerships.set_selling_settingsSave partnerships you offerconfirm
Decide whether this white-label agency's CLIENT workspaces are shown the option to buy a Reseller or a White-Label partnership. THIS IS OUTWARD-FACING: switching one on puts an offer in front of the agency's own clients and tells those clients that Chirply exists and that they would be paying Chirply rather than the agency. It charges the agency nothing and earns them affiliate commission on every purchase. A client who buys is permanently credited to this agency — the attribution cannot be moved afterwards, and the commission is paid on every renewal for as long as that client keeps paying, whether or not they still use anything the agency sells them. It also MOVES THAT CLIENT OUT OF THE AGENCY: their workspace is promoted to an agency of its own, keeps all of its data, stops counting against the agency's sub-account limit, and the agency's access to it ends. The agency is emailed when that happens, and any subscription they were running for that client on their own Stripe is left untouched for them to cancel. Switching an offer back off hides it everywhere but never cancels, downgrades, or refunds a partnership somebody already bought, and commission already earned keeps paying.
pipelines
- pipelines.listList pipelines
List the organization's deal pipelines in board order, with the default one flagged. Call this first when you need a pipeline_id.
- pipelines.getOpen a pipeline
Fetch one pipeline together with its stages, in board order. Omit the id to get the org's default pipeline.
- pipelines.boardView the pipeline board
Summarize a pipeline board the way the Pipeline page and the dashboard's Pipeline widget do: every stage with its deal count and total value, plus the board's split into in-progress (open), won, and lost deals with the money in each. 'Lost' counts both lost and abandoned deals — closed without a win. All values are integer cents. Read-only.
- pipelines.createCreate a pipeline
Create a new deal pipeline and seed it with the starter stages (New → Qualified → Proposal → Won → Lost) so its board is usable immediately. It is added after the existing pipelines and does not become the default.
- pipelines.seed_defaultCreate the default pipeline
Set up the starter 'Sales' pipeline (New → Qualified → Proposal → Won → Lost) for an org that has no pipeline yet, and mark it default. Does nothing if the org already has at least one pipeline.
- pipelines.renameRename a pipeline
Change a pipeline's name. Stages and deals are untouched — this is the name field in the pipeline settings dialog.
- pipelines.set_defaultMake a pipeline the default
Mark one pipeline as the org's default. The default is what the Pipeline page opens on and what a deal created without a pipeline_id lands in; any other pipeline loses the flag.
- pipelines.set_value_trackingMoney or a process
Switch a board between a sales pipeline and a process board. With tracks_value true each card carries a value and the columns total it; false hides the value field and the totals, which is what you want for onboarding, fulfilment, hiring or any board where the cards aren't sales. This is a display decision only — values already saved on the deals are kept, so switching back restores them.
- pipelines.reorderReorder pipelines
Set the left-to-right order of the pipelines in the switcher. Pass every pipeline id in the order you want; any pipeline you leave out keeps its current position.
- pipelines.deleteDelete a pipelineconfirm
Permanently delete a pipeline. DESTRUCTIVE: the database cascades, so every stage in the pipeline AND every deal on its board is deleted with it — deals are not moved anywhere. Cannot be undone.
plans
- plans.getMy plan & limits
The plan this workspace is on and every limit it carries — caps like phone numbers and seats, and on/off features like ringless voicemail or the MCP server — with where the plan came from (bought by the owner, pinned by a platform admin, or inherited from a parent agency) and any limit bent for this workspace specifically. Read-only; nothing is charged.
- plans.usagePlan usage
How much of each countable limit this workspace has used — phone numbers, seats, funnels, workflows, AI agents and so on — with the cap beside it and whether there's room for another. Use this before creating something to know whether it will be refused. Read-only.
predictive_dialer
- predictive_dialer.startStart the predictive dialerconfirm
Open a predictive dialing session on a call queue. Once a rep takes a seat in the console, the server begins placing REAL outbound calls — several per free rep — from the workspace's own Twilio number and billed to its own Twilio account. It rings people it screens and drops (a small share hear a recorded apology and nothing else), so it is a live outbound campaign, not a draft. A session with nobody seated dials nobody, so this is safe to call ahead of a shift. One session per queue: called again on a queue that already has one, it returns the existing session rather than starting a second. Settings default to the queue's own saved dialer settings; anything passed here overrides them for this session only.
- predictive_dialer.getPredictive session status
Read one predictive dialing session: how many lines are up, how many people have been dialed, picked up, been connected to a rep or been dropped, the live dropped-call rate, and who is seated.
- predictive_dialer.find_for_queuePredictive session on a queue
Find the predictive dialing session currently open on a call queue, if there is one. A queue can only have one at a time, so this is how to tell whether dialing is already under way before starting it.
- predictive_dialer.listList predictive sessions
List this workspace's predictive dialing sessions, newest first — the open ones and, optionally, the finished ones with their final numbers.
- predictive_dialer.pausePause dialing
Stop a predictive session from placing any NEW calls. Calls already up are left alone — nobody mid-conversation is cut off, and phones already ringing keep ringing. Seats stay open, so resuming picks straight back up.
- predictive_dialer.resumeResume dialingconfirm
Put a paused predictive session back to work. Real outbound calls start again within seconds for every rep who is free, billed to the workspace's own Twilio account.
- predictive_dialer.stopStop dialingconfirm
End a predictive session for good. Every phone still ringing is HUNG UP mid-ring, every seat is closed, and the people who were being dialed go back to waiting on the queue. Conversations already in progress are not interrupted. This cannot be undone — a new session has to be started to carry on.
- predictive_dialer.set_pacingChange how hard it dials
Change a running session's pacing. Raising lines_per_agent reaches more people per hour and drops more calls; lowering it does the reverse. Takes effect on the next pass, within a couple of seconds. Setting lines_per_agent by hand also switches pacing to 'fixed', so the automatic throttle stops overriding the number you chose — pass pacing_mode 'adaptive' to hand it back.
- predictive_dialer.list_callsCalls from a predictive session
List the individual call attempts a predictive session has made, newest first, with how each one ended — connected to a rep, answering machine, no answer, dropped because no rep was free, or blocked because the number is on the do-not-contact list.
- predictive_dialer.record_outcomeSave how a predictive call went
Record the outcome of one predictive call: its disposition and a note. Writes the call log, stamps the person's queue entry as called, runs whatever actions the disposition is wired to, and puts the rep who took it back in rotation. This is what the console's wrap-up form does — use it when something else took the notes.
preview
- preview.list_packsDesign packs
List the design packs available for previews — one per trade — with the choices, stackable layers and product styles each offers. Call this first: the ids returned here are what preview.render expects, and they differ per pack. Requires the AI Project Preview app (a purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- preview.renderGenerate previewconfirm
Edit a photo of a customer's property to show the finished work on THAT building — the point being that it is their house, not a stock example. SPENDS REAL MONEY: every call runs an image model on this workspace's own OpenRouter key and is billed to them directly, with no caching, and requesting several product styles bills once per style. Takes 20-60 seconds. The photo goes in as a base64 data URL; the result is archived to the media library and returned as a permanent URL. Requires the AI Project Preview app (a purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- preview.list_rendersPreview history
List previews already generated, newest first, with the permanent image URLs. Every render is kept — they cost money to produce — so this is the archive to pull from when attaching a before/after to a proposal or a follow-up message. Requires the AI Project Preview app (a purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- preview.delete_renderDelete previewconfirm
Permanently remove one preview from the archive. The image itself stays in the media library, so anything already attached to a proposal or sent to a customer keeps working — this removes the archive entry only. Previews cost money to generate and cannot be recreated identically, so deleting is rarely the right move. Requires the AI Project Preview app (a purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- preview.get_settingsPreview settings
The workspace's preview configuration: which design packs are switched on, and whether the public lead-capture page is live — its address, its wording, and the monthly render cap that limits what the public can spend. Requires the AI Project Preview app (a purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- preview.save_settingsSave preview settingsconfirm
Update which design packs are offered and configure the public lead-capture page. TURNING THE PUBLIC PAGE ON PUBLISHES A URL ANYONE CAN USE, and every submission spends this workspace's own AI credit — which is what the monthly cap is for. Set the cap to what you are willing to spend on strangers in a month, because that is exactly what it controls. Requires the AI Project Preview app (a purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
privacy
- privacy.get_settingsPrivacy mode
Read the user's screen-share privacy settings: whether privacy mode is on, which classes of information it is hiding, and whether redaction is `partial` (a short readable prefix survives on names, phone numbers and emails) or `full` (nothing survives). Also returns the full catalog of categories, marking the three that are always hidden (contact details, conversations, and keys) and the six the user can choose.
- privacy.enableTurn on privacy mode
Turn on privacy mode so the user can share their screen, demo, or record a walkthrough safely. Sensitive values are blurred on screen immediately: contact names and details, conversation and call content, and API keys are ALWAYS hidden, plus whichever optional categories the user has chosen. Nothing is deleted and no permissions change — this only affects what is legible on screen. Optionally pass `also_hide` to switch extra categories on at the same time.
- privacy.disableTurn off privacy modeconfirm
Turn privacy mode off, making every hidden value readable again across the whole app — contact names, phone numbers, email addresses, message content and API keys included. Only do this when the user has confirmed they are no longer sharing their screen or recording; if they are, this puts their customers' personal information straight back on the stream.
- privacy.set_strengthHow much to hideconfirm
Choose how much of a redacted identity stays readable. `partial` (the default) leaves the first few characters of names, phone numbers, emails and addresses legible — 'Sar…', '+1 (415) …' — so the user can still tell rows apart and talk about one while demoing, without anyone being identifiable. `full` blurs them completely, for a recording that will be published. Either way this only affects IDENTITIES: money and counts are always hidden whole, because the leading digits of a figure give the figure away.
- privacy.hideHide more in privacy mode
Switch additional optional categories on, so privacy mode also hides them. Use this for the user's own commercially sensitive figures — their revenue, what individual customers have paid, their subscription, their teammates' names, their volumes, or their own name and workspace. Takes effect immediately if privacy mode is on, and is remembered for next time if it isn't. Contact details, conversations and keys don't need to be listed here: they are always hidden.
- privacy.revealStop hiding a categoryconfirm
Stop hiding one or more optional categories, making those values readable again even while privacy mode stays on — for example to show revenue during a sales demo. This exposes real figures on screen, so confirm the user actually wants them visible. It cannot unhide contact details, conversation content or API keys: those stay hidden whenever privacy mode is on.
profile
- profile.getView profile
Read the acting person's profile. For a workspace API key, reads the workspace owner's profile. Founder-only fields are included when that person is a founder.
- profile.updateSave profile
Update the acting person's real name, chat handle, and photo across the app. If the person is a founder, also updates their founder-wall biography, links, and public-or-anonymous visibility. For a workspace API key, updates the workspace owner's profile. This sends no messages and costs nothing.
- profile.security_statusSign-in & security
Read how the acting person's sign-in is protected: whether they use an authenticator app, how many passkeys they have registered, how many single-use recovery codes are left in case they lose the authenticator, and how many browsers are set to skip the second step. For a workspace API key, reads the workspace owner's status. Read-only, and it never returns a passkey or a recovery code — only whether they exist. Enrolling an authenticator, adding a passkey, generating recovery codes and spending one to get back in can only be done by the person themselves, because each needs a live code, a live browser ceremony, or a sign-in session; see the note at the foot of this module.
- profile.forget_trusted_browsersForget all browsersconfirm
Remove every browser that was set to skip the two-factor step for this account, so the next sign-in on each one asks for a second factor again. Use this when a laptop or phone is lost or stolen. It signs nobody out and removes no sign-in method — it only withdraws the ‘don’t ask again on this browser’ permission. It cannot be undone except by ticking that box again on each device.
proposals
- proposals.list_price_bookPrice book
List the workspace's reusable services and materials with their unit prices — the catalog the proposal builder's pickers draw from and the AI drafter is grounded in. Prices are returned in integer cents. Requires the AI Quotes & Proposals app (a purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- proposals.save_price_book_itemSave price book item
Create or update one reusable service or material in the price book. Pass an id to update an existing item, omit it to create a new one. Item names are unique within the workspace, so saving under an existing name is rejected rather than silently creating a duplicate the builder's picker would show twice. Requires the AI Quotes & Proposals app (a purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- proposals.delete_price_book_itemDelete price book itemconfirm
Permanently remove one item from the price book. Proposals that already used it keep their lines — a proposal's lines are copies, not references — so this affects only what appears in the builder's pickers from now on. Prefer setting is_active to false if you may want it back. Requires the AI Quotes & Proposals app (a purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- proposals.list_financingFinancing products
List the financing products this workspace offers, with their APR, term and qualifying amount range. Chirply only DISPLAYS the monthly payment on a proposal — it does not originate, underwrite or service any loan; the lender's own approval happens off-platform. Requires the AI Quotes & Proposals app (a purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- proposals.save_financingSave financing product
Create or update a financing product customers can be offered on a proposal. The monthly payment Chirply shows is a standard amortized calculation from the APR and term you set here — set them to match what your lender actually approves, because the number a customer sees on the proposal is the number they will expect. Requires the AI Quotes & Proposals app (a purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- proposals.listProposals
List proposals with their status, customer and price. Each returns a `from` price — the cheapest option — because a proposal offers several, and its accepted total once the customer has chosen. Requires the AI Quotes & Proposals app (a purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- proposals.getOpen proposal
Fetch one proposal in full — every option with all its lines and add-ons, the attachments, the customer-facing link, and the activity trail showing when it was sent, opened, and decided. Requires the AI Quotes & Proposals app (a purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- proposals.saveSave proposal
Create or update a proposal and its options. Pass an id to update an existing one, omit it to create a draft. Every option total is recalculated from its lines here — subtotal minus discount plus surcharge, then tax — so a total you send is ignored. This only writes the document; it does not notify the customer (use proposals.send). Requires the AI Quotes & Proposals app (a purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- proposals.draft_with_aiDraft with AI
Describe a job in plain language and get back a priced Good/Better/Best draft, grounded in this workspace's own price book. The AI asks a clarifying question instead of guessing whenever a quantity that drives the price is missing — footage, fixture count, storeys — so a reply may be a question rather than a draft. Nothing is saved or sent: the returned draft is a suggestion to review, edit and then save with proposals.save. Uses this workspace's own OpenRouter key and is billed to it. Requires the AI Quotes & Proposals app (a purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- proposals.sendSend proposalconfirm
Mark a proposal as sent and return the customer-facing link. Sending is what makes the link acceptable — until then it renders as a preview the customer cannot act on. This does not itself deliver an email or text; pass the returned url to communications.send_email or communications.send_sms, or copy it to the customer yourself. Requires the AI Quotes & Proposals app (a purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- proposals.record_decisionRecord decisionconfirm
Record that the customer accepted or declined, for the times they tell you over the phone or in person instead of clicking the link. Accepting freezes the chosen option and its add-ons at today's prices, exactly as the customer-facing page does. Only use this for a decision a real customer actually gave you. Requires the AI Quotes & Proposals app (a purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- proposals.activityProposal activity
The trail of what happened to a proposal and when — created, sent, first opened by the customer, accepted or declined. This answers the question every contractor asks before following up: have they even looked at it yet? Requires the AI Quotes & Proposals app (a purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- proposals.deleteDelete proposalconfirm
Permanently delete a proposal and its activity trail. The customer-facing link stops working immediately, so anyone still holding it sees a not-found page. An accepted proposal is the record of what somebody agreed to buy and cannot be deleted. Requires the AI Quotes & Proposals app (a purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- proposals.get_settingsProposal settings
The workspace's proposal defaults: sales-tax rate applied to new options, how many days a proposal stays valid, the terms shown under the total, the wording above the accept button, and whether accepting creates an invoice automatically. Requires the AI Quotes & Proposals app (a purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
- proposals.save_settingsSave proposal settings
Update the workspace's proposal defaults. Changing the default tax rate affects new options only — proposals already built keep the rate they were priced at, so an already-sent quote never changes underneath the customer looking at it. Requires the AI Quotes & Proposals app (a purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required.
providers
- providers.catalogConnectable API catalog
List every third-party API this workspace's own agents can reach through Chirply once the account is connected — each provider's id, the exact hosts it may be called on, a link to its API reference, and how to build a correct path. Also names the providers that are connectable but NOT reachable by passthrough, with the reason. Read this before calling providers.request so you use the right provider id and path shape. Prefer a curated capability (meta.*, telephony.*, campaigns.*) whenever one exists: those validate input, respect plan limits, and write results back into the CRM, which passthrough does not. Read-only and costs nothing.
- providers.connectedConnected provider APIs
Report which third-party accounts THIS workspace has actually connected, and for each one whether its API can be reached through passthrough right now — including why not, when it cannot (the provider isn't wired for passthrough, or its credentials come from the parent agency's pooled account rather than this workspace's own). Never returns any credential, only which ones exist. Use it to find out what an agent can do here before trying a call. Read-only and costs nothing.
- providers.readRead from a connected API
Make a read-only (GET or HEAD) call to a third-party API using the credentials this workspace already connected to Chirply — Meta Graph, Twilio, Stripe, Supabase, Mailgun, Klaviyo, Cloudflare and the rest. The credential is attached server-side and is never returned. Use this to pull data an agent needs: ad account performance, a Stripe customer, Twilio call logs, a Supabase project list. Nothing is written and nothing is charged beyond whatever the provider bills for a read (most bill nothing; Outscraper, fal.ai, Replicate and OpenRouter charge per request even for reads). Only hosts on the provider's allowlist can be reached, and redirects are never followed.
- providers.requestCall a connected APIconfirm
Make any HTTP call — including POST, PUT, PATCH and DELETE — to a third-party API using the credentials this workspace already connected to Chirply. This acts AS the workspace on its own accounts, so it can do anything those credentials can: publish a Facebook ad and start it spending, send an SMS billed to the org's Twilio account, charge a card on its Stripe account, or delete records in its Supabase project. There is no undo, no confirmation from the provider, and no Chirply-side spend limit — the cost lands on the workspace's own provider bills. Prefer a curated capability (meta.*, telephony.*, campaigns.*) when one exists; those validate input, respect plan limits, and write results back into the CRM. Only hosts on the provider's allowlist can be reached, credentials are attached server-side and never returned, and redirects are never followed. Every call is written to the workspace's audit log.
rcs
- rcs.list_sendersList RCS senders
List the RCS senders (branded agents) registered for this organization, with the review status of each and which carriers have approved it. Read-only and free. An empty list is the normal state until someone registers a sender on the organization's own Twilio account.
- rcs.register_senderRegister an RCS sender
Record an RCS sender that already exists on the organization's own Twilio account, so Chirply routes that Messaging Service's traffic over RCS once it is approved. THIS DOES NOT CREATE THE SENDER — Twilio only onboards RCS senders through its Console, after which Google and each US carrier verify the brand separately, which Twilio says takes four to six weeks. Register it here as 'submitted', then set the status to 'approved' when Twilio's console shows a carrier has approved it. Nothing routes over RCS until the status is 'approved'.
- rcs.update_senderUpdate an RCS sender
Update a registered RCS sender — most often to move its status along as Twilio's console reports carrier approvals, or to record per-carrier verdicts. Setting status to 'approved' is what makes Chirply start routing that Messaging Service's messages over RCS; setting it to anything else stops that immediately and sends fall back to plain SMS.
- rcs.delete_senderRemove an RCS senderconfirm
Remove a registered RCS sender from Chirply. Messages on that Messaging Service immediately go back to plain SMS. This does NOT delete anything on Twilio — the sender and its carrier approvals stay on the organization's Twilio account, and re-registering it here restores RCS routing without any new review.
- rcs.list_templatesList rich message templates
List the organization's rich RCS message templates — cards, carousels, media and text — with each one's sync state and its Twilio Content SID once synced. Read-only and free.
- rcs.create_templateCreate a rich message template
Create a rich RCS message — a card with buttons, a carousel, an image, or plain text. Creating it here does not put it on Twilio; run rcs.sync_template to do that, which is what gives it the Content SID needed to send it. The `body` field is the plain-text fallback delivered as an SMS to any handset that can't take RCS, so write it to stand on its own.
- rcs.update_templateEdit a rich message template
Edit a rich RCS template. Any change puts it back into 'draft' — Twilio's Content Templates are immutable, so an edited template has to be pushed again before the new version can be sent. Messages already sent are unaffected.
- rcs.sync_templatePush a template to Twilioconfirm
Push a rich template to the organization's own Twilio account as a Content Template and store the resulting Content SID, which is what lets it be sent. Twilio's Content Templates are immutable, so syncing an edited template creates a new one on Twilio and the old one is left behind unreferenced. Uses the organization's own Twilio credentials.
- rcs.delete_templateDelete a rich message templateconfirm
Delete a rich RCS template from Chirply and, if it was synced, remove the matching Content Template from the organization's Twilio account. Messages already sent from it are unaffected. This cannot be undone.
- rcs.send_templateSend a rich messageconfirm
Send a synced rich template into an existing conversation. THIS SENDS A REAL MESSAGE TO A REAL PERSON, billed to the organization's own Twilio account. It goes out over RCS — branded, with the card or carousel rendered — only when the sending number's Messaging Service has an approved RCS sender AND the recipient's handset supports RCS; otherwise Twilio automatically falls back to SMS carrying the template's plain-text body, which the recipient receives instead. The thread records which of the two actually happened.
readiness
- readiness.listFeature setup checklist
List every feature that needs setup before it will work (AI calling, business texting, custom domains, email, Facebook/Instagram), with each prerequisite and whether this workspace satisfies it yet — including the external steps only the user can do (accepting Twilio's AI addendum, verifying DNS, registering A2P, granting Meta permissions). Read-only.
- readiness.statusFeature setup status
Show the setup prerequisites and their satisfied/outstanding state for one feature. "feature" is one of: ai_calling, sms_texting, custom_domains, email_sending, meta_social, ai_studio, affiliate_payouts, restaurant, review_ingestion, contact_enrichment, tenant_subscriptions, white_label, directories. Read-only.
- readiness.acknowledgeConfirm a setup step
Mark an external setup step that can't be verified automatically (e.g. accepting Twilio's AI addendum, verifying DNS, registering A2P) as done for this workspace, or clear it with done=false. This only updates the advisory checklist — it does NOT perform the step itself. Steps that are detected automatically (a provider being connected) cannot be set here.
releases
- releases.listView release notes
List every successful production deployment newest first, including its exact build identifier, live time, and only the commits included since the previous deployed version.
reports
- reports.describe_catalogBrowse report options
List the report catalog: every dataset the report builder can query, with its available dimensions, metrics, and filters. Read this first to build a valid reports.run call.
- reports.runRun report
Run a custom report over one of the curated datasets — contacts (Contacts), deals (Deals), calls (Calls), messages (Messages), appointments (Appointments), revenue (Revenue) — bucketed by day/week/month or as totals, optionally split by one dimension, with 1–3 metrics and the dataset's filters. Returns table rows and a chart-ready series. Read-only; money figures are integer cents.
- reports.list_savedSaved reports
List the saved report definitions this caller can see: reports shared with the workspace plus their own. Workspace admins (and API keys) see every saved report in the organization.
- reports.saveSave report
Save a report definition so it can be re-run later, or update an existing saved report when an id is given. Setting shared=true makes it visible to every member of the workspace. The definition is validated against the report catalog before it is stored. Only the report's creator or a workspace admin can update one.
- reports.delete_savedDelete saved reportconfirm
Permanently delete a saved report definition. If it was shared, it disappears for the whole workspace. The underlying data is untouched — only the saved definition is removed — but this cannot be undone. Only the report's creator or a workspace admin can delete one.
- reports.list_schedulesReport email schedules
List the standing email schedules for saved reports: which saved report, its daily/weekly/monthly cadence, the recipient emails, whether it's on, when it last sent, and any delivery error. Members see the schedules they created; workspace admins (and API keys) see every schedule. Read-only.
- reports.set_scheduleEmail this report on a schedule
Create or update the ONE standing email schedule for a saved report: cadence ('daily' sends every day (UTC); 'weekly' sends every Monday (UTC); 'monthly' sends on the 1st of each month (UTC) — all UTC), the addresses it goes to, and whether it's on. Enabling it means the report's CURRENT results are emailed to those addresses automatically — real email through the workspace's own connected email account (Mailgun/Resend, on the org's bill) — until it is paused. No email is sent by this call itself. Only the schedule's creator or a workspace admin can change an existing one.
- reports.send_scheduled_nowEmail report nowconfirm
Immediately runs one saved report and emails its current results — a REAL email to the given addresses, sent through the workspace's own connected email account (Mailgun/Resend, on the org's bill). Up to 20 addresses per send. Only works on a saved report the caller can see: one shared with the workspace, one they saved themselves, or any of them for a workspace admin. Omit recipients to use the ones saved on the report's email schedule. Does not move the schedule's clock: the next scheduled send still happens on time.
reputation
- reputation.list_locationsList reputation locations
List the workspace's business locations and their Google review-link readiness. This only reads data and does not call Google.
- reputation.create_locationAdd location
Add a private business location for reputation tracking. This does not create or modify a Google Business Profile and costs nothing.
- reputation.update_locationSave location
Update a reputation location's name, address, Google review link, or active state. This only changes local records and does not modify Google.
- reputation.list_reviewsList reviews
List imported and manually recorded reviews with ratings and response workflow state. This only reads local records and does not call Google.
- reputation.add_reviewAdd review
Record a review manually for monitoring and response preparation. This does not publish anything or claim the review exists on Google.
- reputation.save_response_draftSave response draft
Save a private response draft for a review. Nothing is posted to Google or shown to the reviewer.
- reputation.list_requestsList review requests
List trackable review-request links and their opened/clicked status. This only reads data and sends no messages.
- reputation.create_requestCreate review link
Create a private trackable review-request link for one location. This does not email or text anyone and costs nothing; use reputation.send_request to actually deliver one by SMS or email.
- reputation.send_requestSend review requestconfirm
Create a trackable review link for a contact and immediately deliver it as a real SMS or email — sent through and billed to the org's own Twilio or Mailgun/Resend account, and logged in the contact's conversation thread. Opted-out addresses are refused; happy customers are routed to the location's public review sites while low ratings are caught as private feedback.
- reputation.list_destinationsList review sites
List the public Google, Facebook, Yelp, Trustpilot, BBB, Tripadvisor, and custom review destinations attached to workspace locations. This only reads data.
- reputation.add_destinationAttach review site
Attach a public review destination to a reputation location. It becomes an optional choice shown to every customer after private feedback, regardless of rating; this sends nothing and costs nothing.
- reputation.list_feedbackList private feedback
List private first-party experience feedback, customer contact requests, and service-recovery status. This data is not published to review sites.
- reputation.find_google_placeFind your business on Googleconfirm
Search Google Maps for a business so a reputation location can be connected to its Google listing, via the organization's OWN Outscraper account. THIS SPENDS THE TENANT'S MONEY: Outscraper bills per place returned (a few records per search, roughly $3 per 1,000 Maps records on standard tiers). Returns candidate listings with their Google place ids; pass the right one to reputation.set_location_place. Nothing is stored by this call.
- reputation.set_location_placeConnect Google listing
Attach a Google Maps place id to a reputation location so review syncs know which business to pull. Also fills the location's public 'write a review' link from the place id when one isn't set yet. This only updates local records — it costs nothing and does not modify anything on Google.
- reputation.sync_reviewsSync Google reviewsconfirm
Pull the newest Google reviews for one location (which must be connected to its Google listing first) and store them in the review inbox, via the organization's OWN Outscraper account. THIS SPENDS THE TENANT'S MONEY: Outscraper bills per review record returned, capped at 100 per sync (roughly $3 per 1,000 records on standard tiers). Re-syncing never duplicates — reviews are matched on Google's own review id and refreshed in place, and saved reply drafts are preserved. If Outscraper queues the job, this returns pending:true; call it again shortly to collect the finished results at no extra cost.
- reputation.draft_replyDraft a review reply
Write an owner's reply to one review with AI, grounded on the chosen slice of the workspace's Knowledge Brain, and save it as a private response draft on the review. Runs on the organization's own OpenRouter key (a small AI charge billed to their OpenRouter account). NOTHING IS POSTED ANYWHERE — Google publishing isn't available yet, so a human copies the draft and posts it on Google themselves.
- reputation.list_widgetsList review widgets
List the workspace's embeddable review-display widgets with their keys, star-rating thresholds, and status. This only reads data; the key in each row is what the public embed snippet uses.
- reputation.create_widgetCreate review widget
Create an embeddable public widget that shows one location's best reviews (star display, newest first) on any website via a one-line script tag. Only reviews at or above the star threshold are shown — the default is 4 stars and up. Creating a widget costs nothing and publishes nothing until the tenant pastes the returned snippet onto their site.
- reputation.update_widgetSave review widget
Change a review widget's name, star threshold, layout, colors, or status. Setting status to 'disabled' makes every copy of the embed on the tenant's websites render nothing until re-published; the embed snippet itself never changes.
- reputation.delete_widgetDelete review widgetconfirm
Permanently delete a review widget. Every copy of its embed snippet on the tenant's websites immediately renders nothing, and the widget key cannot be restored — a replacement widget gets a new key that has to be re-pasted onto the site. The reviews themselves are not touched.
- reputation.update_recoverySave recovery status
Update the internal service-recovery status, assignee, and private notes for customer feedback. This does not contact the customer or publish anything.
reseller
- reseller.list_plansYour plans
List the plans this agency sells to its clients — the reseller's own packages, with the price the reseller charges and which underlying platform tier each one unlocks. These are NOT the platform's own retail plans; they're what this agency resells under its own brand.
- reseller.create_planCreate plan
Create a plan this agency sells to its clients: a name the client sees on their invoice, the price the agency charges, which underlying platform tier it unlocks, and optional per-limit tweaks on top of that tier. Creating a plan costs nothing and charges nobody — it only defines a package. Clients are put on it separately with reseller.assign_plan.
- reseller.update_planEdit planconfirm
Change a plan's name, price, billing interval, which underlying platform tier it unlocks, or its per-limit tweaks. This edits a package the agency SELLS: every client already on the plan immediately follows its new tier and limits, so lowering a tier or a limit can take a working feature away from a paying customer, and the new price is what the next client to sign up pays. Their existing Stripe subscription is NOT re-priced — changing what a live customer pays has to be done deliberately by cancelling and re-starting their billing.
- reseller.archive_planRetire planconfirm
Retire a plan so it can no longer be assigned or sold. Clients already on it keep it and keep working — nothing is deleted and nobody's billing changes — but the plan disappears from the pickers. There is no un-retire; recreate it if needed.
- reseller.list_clientsClients
List every client workspace (sub-account) under this agency, with the plan each one is on, how many people are in it, and whether the agency is billing them. Includes soft-deleted clients — those have status 'canceled' and can be brought back with reseller.restore_client. Read-only.
- reseller.get_clientOpen a client
Everything about one client workspace: its plan, the people in it, outstanding invitations, and its billing state on the agency's own Stripe. Read-only.
- reseller.create_clientNew sub-account
Create a new client workspace under this agency, optionally putting it straight onto one of the agency's plans. Counts against the agency's paid sub-account allowance and fails once that's used up. The workspace starts empty with nobody in it — invite the client with reseller.invite_client_user. Assigning a plan here pins their tier and limits immediately but charges nobody; billing is started separately.
- reseller.rename_clientRename client
Rename a client workspace. Cosmetic — the URL slug and everything inside it are untouched.
- reseller.set_client_statusSuspend or reactivate clientconfirm
Suspend a client workspace so nobody in it can sign in — how an agency handles a client who has stopped paying — or reactivate a suspended one. Nothing is deleted and it is fully reversible, but suspending locks real people out of their workspace immediately.
- reseller.delete_clientDelete clientconfirm
Delete a client workspace. This is a soft delete: the workspace is closed and everyone in it is locked out immediately, but nothing is destroyed — it can be restored later with reseller.restore_client. It stops counting against your paid sub-account allowance, so deleting a client frees a slot to create another. It does NOT cancel any billing you have running for them on your Stripe — stop that separately with reseller.cancel_client_billing.
- reseller.restore_clientRestore client
Bring a deleted (cancelled) client workspace back to active, with all its data intact. Fails if you're already at your paid sub-account allowance — free a slot or upgrade first, since a restored client counts again.
- reseller.assign_planAssign planconfirm
Put a client workspace on one of the agency's plans, or clear it. This changes what a paying third-party business can actually do straight away, because the plan pins their underlying platform tier and limits — moving them down a tier removes features and can push them over a limit they are currently using. It does NOT charge them or change an existing subscription — billing is started separately with reseller.start_client_billing — so the money and the entitlements can end up out of step until someone reconciles them.
- reseller.list_client_usersClient's people
List who can sign in to a client workspace, plus any invitations that haven't been accepted yet. Read-only.
- reseller.invite_client_userInvite to client workspaceconfirm
Invite someone into a client's workspace — normally the client themselves, so they can log in and use the platform. Creates a pending invitation they accept to join; it grants them access to that one workspace only, never to the agency. Sends a real invitation to a real email address.
- reseller.revoke_client_inviteWithdraw invitation
Withdraw a pending invitation into a client workspace. Their invite link stops working. Anyone who already accepted is unaffected.
- reseller.get_billing_statusBilling setup
Whether this agency has connected its own Stripe account, which is required before it can charge any client. Read-only.
- reseller.start_client_billingStart billing a clientconfirm
SPENDS THE CLIENT'S MONEY. Subscribes a client to one of the agency's plans on the AGENCY'S OWN Stripe account and has Stripe email them an invoice immediately, due in 7 days, recurring at the plan's price and interval. The money goes to the agency; the platform neither holds it nor takes a cut. Requires the agency to have connected Stripe. Fails if the client already has a live subscription.
- reseller.cancel_client_billingStop billing a clientconfirm
Cancel a client's subscription on the agency's Stripe. By default it ends when the period they've already paid for runs out; `immediately` cuts it off now, which forfeits the rest of a period they have already been charged for and is not reversible. Does not suspend or delete their workspace.
- reseller.sync_client_billingRefresh billing from Stripe
Re-read a client's subscription from the agency's Stripe account and update the status shown here. This platform receives no webhooks from a tenant's own Stripe, so a payment that failed over there is only noticed when this runs. Read-only as far as Stripe is concerned — it charges nothing.
- client_reports.rollupClient performance rollup
Read each active client's new contacts, calls, connect rate, messages, appointments, reviews, and collected net revenue for a UTC period. Failed sources return null metrics and unavailable section names, never zero. Revenue totals disclose client coverage. Read-only; sends no messages.
- client_reports.list_schedulesList client report schedules
List every scheduled client report this agency has configured: which client, weekly or monthly, the recipient emails, whether it's enabled, when it last sent, and any delivery error. Read-only.
- client_reports.set_scheduleSet a client's report schedule
Create or update the standing report for one client workspace: weekly or monthly cadence, the client emails it goes to, and whether it's on. Enabling it means a real branded report email is sent to those addresses automatically after each period ends, through this agency's own connected email account. No email is sent by this call itself.
- client_reports.previewPreview a client report
Build a branded client report for the latest completed UTC week or month, returning subject, numbers, fetch time, and HTML without sending email. Unavailable source sections are null and visibly labeled; incomplete reports cannot be sent.
- client_reports.send_nowSend a client report nowconfirm
Assemble and email a client's branded performance report for the latest completed UTC week or month — a REAL email through the agency's connected Mailgun/Resend account, billed to the agency. Delivery is held if any source section is unavailable. Use preview to inspect without sending.
- reseller.list_pooled_credentialsPooled credentials
For one client workspace, show which providers it's using YOUR pooled credentials for versus its own. Each row reports the provider, whether pooling is on, its status (off / provisioning / active / error), and any isolation detail (the Twilio subaccount SID, the Mailgun subdomain and its DNS state). Read-only.
- reseller.enable_pooled_credentialPool a credential to a clientconfirm
Let a client workspace use YOUR provider account instead of connecting its own. For Twilio this creates an isolated Twilio subaccount under your master account — the client's calls and texts run on it and Twilio bills YOU for them. For Mailgun it provisions an isolated sending subdomain (which needs its DNS verified before it goes live). For ElevenLabs / OpenRouter / Outscraper / Firecrawl it uses your API key directly, effective immediately. The client's usage becomes your real cost — meter and rebill it with the credit system. Reversible at any time.
- reseller.disable_pooled_credentialStop pooling a credentialconfirm
Turn off a pooled provider for a client. By default this is a reversible soft off — the client goes back to using its own connection and the isolation artifacts are kept, so re-enabling is instant. Set teardown to also SUSPEND the Twilio subaccount (reversible, keeps the client's numbers) or DELETE the Mailgun subdomain.
- reseller.verify_pooled_credentialVerify pooled Mailgun DNS
Re-check a pooled Mailgun subdomain's DNS with Mailgun and flip it live once the records verify. Only applies to Mailgun; the other providers need no verification. Charges nothing.
- reseller.get_stripe_connectClient payment setup (Stripe Connect)
For one client workspace, show whether it accepts payments through YOUR Stripe (a connected account under your platform) or its own. Reports the status (own Stripe / onboarding / restricted / live), whether charges and payouts are enabled, and the application fee you take on its transactions (the per-client override or the plan default). Read-only.
- reseller.enable_stripe_connectLet a client take payments through youconfirm
Turn on Stripe Connect for a client: create (or reuse) an Express connected account under YOUR Stripe platform so the client can accept card payments through you. Money settles to the client and they stay merchant of record; you take an application fee on each transaction (set separately). This does NOT finish setup — the client must still complete Stripe's identity + bank verification (a link, from get_stripe_connect_link) before they can accept a payment. Requires your own Stripe to be connected and have Connect enabled. Reversible.
- reseller.get_stripe_connect_linkGet a client's payment-setup link
Mint a fresh Stripe onboarding link for a client's connected account — the URL the client opens to complete identity + bank verification. The link is single-use and expires within minutes, so generate it when you're about to send it. Connect must already be enabled for the client.
- reseller.refresh_stripe_connectRefresh a client's payment status
Re-check a client's connected account with Stripe and update whether it can accept charges and receive payouts. Use after the client finishes onboarding to confirm they're live. Charges nothing.
- reseller.set_stripe_connect_feeSet a client's payment feeconfirm
Set the application fee you take on this client's transactions, in basis points (100 = 1%, max 10000 = 100%). This is money taken off the top of another business's card revenue, and at 10000 it takes all of it — so confirm the number with a human before saving. It is a per-client override; pass null to clear it and inherit the client's plan default instead. Applies to future charges once Connect charge routing is live.
- reseller.disable_stripe_connectStop a client taking payments through youconfirm
Turn off Stripe Connect for a client. Reversible: the client goes back to selling on its own Stripe, and the connected account is kept so you can switch it back on instantly. Does not move or refund any money already collected.
- reseller.get_rate_cardRate card
Read the per-item prices a reseller charges. With a client_id, returns that client's effective prices (override → plan default → agency default) plus its own overrides. Otherwise returns the default prices for a plan (or the agency-wide default when no plan_id is given). Each price also reports `markup_percent`: the percentage on cost it was written as, or null when it is a fixed dollar amount. Read-only, costs the caller nothing.
- reseller.set_rate_cardSet prices
Set what a reseller charges per metered unit, as a fixed price or as a percentage mark-up on what the unit costs them. With a client_id, sets that client's overrides; with a plan_id, the plan's defaults; with neither, the agency-wide defaults. A mark-up price is recomputed from the cost basis (see reseller.get_unit_costs) every time it is charged, so correcting a cost re-prices every unit priced that way. Setting a price changes what the client is billed for future usage on your pooled credentials — it moves no money by itself and bills nobody retroactively. Unknown units are ignored with a warning.
- reseller.get_unit_costsWhat it costs you
Read what each metered unit costs the RESELLER at their own provider — the base every percentage mark-up price is worked out from. Units the reseller has not given a figure for report Chirply's published list price for that provider, flagged with is_custom false. This is the agency's margin data and is never visible to a client. Read-only.
- reseller.set_unit_costsSet your provider costsconfirm
Record what metered units actually cost the reseller at their provider, in dollars per unit (e.g. {"sms": 0.0079}). This is agency-wide and is the base for every price written as a percentage mark-up, so changing a cost immediately changes what EVERY client on a mark-up for that unit is charged for future usage — it moves no money by itself and never re-bills past usage. Units listed in `reset` fall back to Chirply's published list price. Unknown units are ignored with a warning.
- reseller.get_client_creditsClient credits
A client's prepaid credit standing on the reseller's pooled providers: balance (cycle allowance + purchased), whether it's paused for being out of credits, its effective per-unit prices, and its recent credit ledger. Read-only.
- reseller.adjust_client_creditsAdjust creditsconfirm
Manually add or remove credits from a client's balance (a comp, a correction). A positive amount grants credits; a negative amount deducts them. Adding credits lifts a paused client back into service. This changes a real balance the reseller is liable for, so it's confirm-gated.
- reseller.start_client_card_setupStart card setup
Begin saving a payment card for a client's credit top-ups. Creates (or reuses) a customer on the RESELLER'S OWN Stripe account and returns a SetupIntent client_secret plus the reseller's publishable key, for a Stripe Payment Element the CLIENT completes in a browser. No money moves and no card data passes through this call — the card is entered directly into Stripe's form. Finish with reseller.complete_client_card_setup.
- reseller.complete_client_card_setupFinish card setup
Verify a confirmed SetupIntent with the reseller's Stripe and record the resulting payment method as the client's saved card for credit top-ups and auto-recharge. Refuses unless Stripe itself reports the setup succeeded for this client's customer. Moves no money.
- reseller.top_up_client_creditsCharge cardconfirm
Immediately charges the client's SAVED card on the RESELLER'S OWN Stripe account for a credit top-up — real money, billed to the client by the reseller — and adds the amount to the client's credit balance once Stripe confirms the charge settled (which also resumes paused pooled sends). Between $5 and $2,000. Refuses when no card is saved (see reseller.start_client_card_setup).
- reseller.set_client_auto_rechargeAuto-rechargeconfirm
Turn a client's credit auto-recharge on or off and set its rules. While on, whenever a pooled-usage debit drops the client's balance under the threshold, their SAVED card is automatically charged the recharge amount on the RESELLER'S OWN Stripe — unattended, real-money charges, at most one attempt per 15 minutes. Confirm-gated because saving these settings arms future charges nobody clicks on.
restaurant
- restaurant.list_locationsRestaurant settings
List the workspace's restaurant locations with all of their settings — public web address (/eat/<slug>), contact details, opening hours, timezone, currency, sales tax rate, online-ordering/pickup/delivery/reservation toggles, delivery fee and minimum, prep time, tip presets, and public-page branding.
- restaurant.create_locationSet up a restaurant
Create a restaurant location — the same thing the Settings first-run wizard does. Claims a globally unique public web address (/eat/<slug>) where diners can immediately see the menu and, when enabled, order and reserve. Only the name is required; everything else has sensible defaults (open toggles, 20-minute prep, no tax).
- restaurant.update_locationSave restaurant settingsconfirm
Update a restaurant location's settings. CHANGES WHAT DINERS SEE AND WHAT THEY ARE CHARGED: the tax rate and delivery fee alter real checkout totals, the toggles turn public ordering/reservations on or off, hours change when orders are accepted, and changing the slug MOVES the public pages (old /eat/<slug> links stop working). Omitted fields are left alone.
- restaurant.list_stationsKitchen stations
List the kitchen stations (Grill, Fry, Bar…) tickets are sorted onto. Menu items and categories point at a station, and the kitchen display filters by it.
- restaurant.create_stationAdd station
Add a kitchen station to a location — a named screen/printer tickets route to, like Grill or Bar. Station names are unique within a location.
- restaurant.delete_stationDelete stationconfirm
Permanently delete a kitchen station. Menu items and categories routed to it fall back to 'no station' — their tickets keep printing but stop being sorted onto this screen. Cannot be undone.
- restaurant.list_service_periodsReservation services
List a location's service periods — the bookable sittings on the public reservation page (e.g. Dinner: Tue–Sun 5–10pm, 30-minute slots, tables turn in 90 minutes). Days use 0=Sunday…6=Saturday; times are 24-hour local to the location.
- restaurant.set_service_periodsSave reservation services
REPLACE a location's entire reservation schedule with the given service periods. This immediately changes which dates and times diners can book on the public page — services not in the list are removed (existing reservations are kept). Pass the full schedule, not a delta.
- restaurant.list_menusList menus
List the restaurant's menus (Dinner, Brunch, Drinks…) in display order, optionally for one location only. Each menu row includes whether it is live — an inactive menu is hidden from the POS, tablets, online ordering, and the website.
- restaurant.get_menuOpen a menu
Fetch one menu with its full contents: categories in service order, every dish in each category (including 86'd ones, flagged by available=false), and the ids of the modifier groups attached to each dish.
- restaurant.create_menuNew menu
Create a menu (e.g. Dinner, Brunch, Drinks) at one of the restaurant's locations. A live menu appears on the POS, table tablets, online ordering, and the website menu block as soon as it has dishes.
- restaurant.update_menuEdit a menuconfirm
Rename a menu, change its display position, or toggle it live/hidden. Setting active=false immediately pulls the ENTIRE menu — every category and every dish on it — off the POS, table tablets, online ordering, and the website, mid-service if that is when you run it; real diners stop being able to order any of it within seconds. Nothing is deleted, and active=true puts it all back.
- restaurant.delete_menuDelete a menuconfirm
Permanently delete a menu with all of its categories and dishes, removing it from the POS, tablets, online ordering, and the website. This cannot be undone — to take a menu offline temporarily, set active=false instead.
- restaurant.create_menu_categoryNew category
Add a category (Starters, Mains, Desserts…) to a menu. Optionally route its dishes to a kitchen station, so they print on that station's kitchen screen.
- restaurant.update_menu_categoryEdit a category
Rename a category, change its description or display position, or point it at a different kitchen station. Pass station_id: null to clear the station.
- restaurant.delete_menu_categoryDelete a categoryconfirm
Permanently delete a category and every dish in it, removing them from all ordering surfaces. This cannot be undone.
- restaurant.list_menu_itemsList dishes
List the dishes on the menu, in display order. Filter by category or by menu, restrict to available (or 86'd) dishes only, and search names and descriptions. available=false rows are 86'd — hidden from diners but still on the books.
- restaurant.create_menu_itemAdd a dish
Add a dish to a menu category with its price, description, photo, and dietary tags. It becomes orderable on every surface (POS, tablets, online ordering, website menu) immediately unless available=false.
- restaurant.update_menu_itemEdit a dishconfirm
Update a dish — name, price, description, photo, dietary tags, kitchen station, SKU, or which category it sits in. Omitted fields are left alone. Every change is outward-facing: this edits a dish real diners are looking at, and a new price_cents is what the next order charges on the POS, the tablets, online ordering, and the website, within seconds and with no review step. A wrong dietary tag reaches someone with an allergy. To 86 a dish use restaurant.set_item_availability instead.
- restaurant.delete_menu_itemDelete a dishconfirm
Permanently delete a dish from the menu. Past order lines keep their snapshot of it, but it disappears from every ordering surface and cannot be restored. To take it off temporarily, 86 it instead.
- restaurant.set_item_availability86 a dish / bring it backconfirm
Flip a dish's availability. available=false 86's it: the dish disappears from the POS, table tablets, online ordering, and the website menu immediately, so real diners can no longer order it. available=true puts it back on sale everywhere. Nothing is deleted either way.
- restaurant.list_modifier_groupsList modifier groups
List the restaurant's reusable option sets ("Choose a side", "Add-ons"…), each with its options and their price bumps. available=false options are hidden from diners.
- restaurant.create_modifier_groupNew modifier group
Create a reusable option set diners pick from when ordering a dish — e.g. "Choose a side" (required, exactly one) or "Add-ons" (optional, any number). Attach it to dishes with restaurant.attach_modifier_group.
- restaurant.update_modifier_groupEdit a modifier group
Rename a modifier group or change its pick rules (min/max/required). The change applies at once to every dish the group is attached to.
- restaurant.delete_modifier_groupDelete a modifier groupconfirm
Permanently delete a modifier group and all of its options, detaching it from every dish that offered it. Diners lose those choices immediately. This cannot be undone.
- restaurant.create_modifierAdd an option
Add one option to a modifier group — e.g. "Fries" or "Extra shot (+$1.50)". Its price is added on top of the dish's own price whenever a diner picks it.
- restaurant.update_modifierEdit an option
Update one option in a modifier group — rename it, change its extra cost, its display position, or hide/show it (available). Price changes reach diners immediately on every dish offering the group.
- restaurant.delete_modifierDelete an optionconfirm
Permanently delete one option from a modifier group. Diners can no longer pick it on any dish. This cannot be undone — to pull it temporarily, set available=false instead.
- restaurant.attach_modifier_groupAttach a modifier group to a dish
Offer a modifier group's options on one dish. Diners ordering that dish are shown the group (and must pick from it if the group is required) on every ordering surface. Attaching an already-attached group just updates its display position.
- restaurant.detach_modifier_groupDetach a modifier group from a dishconfirm
Stop offering a modifier group on one dish. Diners immediately lose those options when ordering it (a required group's detachment means the dish orders as-is). The group itself and its other attachments are untouched.
- restaurant.list_ordersList orders
List the restaurant's orders (POS, tablet and online), newest first. Filter by status, order type, source channel, location, or an opened-at date range.
- restaurant.get_orderOpen an order
Fetch one order in full: its line items (with modifiers and kitchen status), its checks with computed totals, and every payment taken against them.
- restaurant.create_orderNew order
Open a new restaurant order with its first check. Nothing is cooked or charged yet — add items with restaurant.add_order_items, then fire them to the kitchen with restaurant.fire_order.
- restaurant.add_order_itemsAdd items to order
Add menu items to an open order. Prices, names and station routing are ALWAYS re-read from the menu on the server — pass menu item and modifier ids only. Items land as 'pending' and are not cooked until restaurant.fire_order sends them.
- restaurant.fire_orderSend to kitchenconfirm
Fire an order's pending items to the kitchen — they appear on the kitchen display for REAL cooks to start making, and each item's recipe depletes inventory. Optionally fire only specific item ids (a course). Fired food can't be un-fired, only voided.
- restaurant.split_checkSplit a check
Split a check two ways: mode 'items' moves the listed item groups onto new checks on the same order (groups[0] stays on the original), each paying independently; mode 'even' creates NO new checks — it returns the per-payer share amounts, each of which is then taken as a partial payment (restaurant.record_cash_payment, or a card payment in the app).
- restaurant.record_cash_paymentRecord cash paymentconfirm
Record REAL MONEY taken in cash against a check. The amount (plus any tip) counts toward the check's total, and the check marks itself paid once its payments cover it. An amount below the total is a partial payment — how an even split settles. This is a financial record; get it wrong and the till won't balance.
- restaurant.complete_orderComplete orderconfirm
Close an open order out as completed. One-way and irreversible: completed orders can NOT be reopened — anything else the table wants has to go on a new order, and any check still unpaid stays unpaid on a closed order. Normally done only after every check is paid.
- restaurant.cancel_orderCancel orderconfirm
Cancel an open order. One-way: the order closes as canceled and can't be reopened; its checks and any payments already taken stay on record as the audit trail. Food already fired to the kitchen is NOT recalled automatically.
- restaurant.send_receiptSend receiptconfirm
Email and/or text a REAL diner the itemized receipt for a check, on the org's own connected email sender and Twilio number. NOT SAFE TO RE-SEND: there is no de-duplication, so every call delivers another message to that person's phone and inbox, and every SMS is another Twilio message billed to the org. Send it once. It does not charge the diner's card or change the check — the cost is the messaging, and the harm is texting a customer repeatedly. Optionally captures the guest's email/phone onto the order first.
- restaurant.kitchen_queueKitchen display
Read the live kitchen display: every fired order item that is queued, cooking, or ready, grouped into per-order tickets (oldest fire first) with table/guest, order type, modifiers, notes, seats and courses — plus the 'all day' totals per item name still being cooked. Optionally filter to one location or one kitchen station. Read-only; changes nothing.
- restaurant.bump_itemBump an item
Advance one fired order item through the kitchen: queued → 'in_progress' (start cooking), → 'ready' (up in the window), ready → 'served' (drops off the display). Sending 'in_progress' to an item that is currently 'ready' un-bumps it back to cooking — the fix for a mis-tap. Only legal moves are accepted; the kitchen display updates in real time.
- restaurant.bump_orderBump a whole ticket
Advance every fired item on one order in a single move — the ticket-level bump-all. Only items a step can legally reach are moved: 'in_progress' starts the queued items, 'ready' moves queued and cooking items up, 'served' clears the ready ones off the display. Items already past the target (and pending/voided items) are left alone. Returns how many items moved.
- restaurant.list_tablesTables
List the restaurant's dining tables and their floor-plan geometry: name, seat count, room, shape, position, size, rotation, server section, service state, and QR/tablet token. Filter by location, room, section, or active state, or search by name.
- restaurant.create_tableAdd table
Add a dining table to the restaurant's floor plan. It appears on the Tables screen immediately and gets its own tablet ordering screen (/restaurant/tablet/<id>) that staff can hand to guests. Nothing is sent to anyone.
- restaurant.update_tableEdit table
Update a dining table: rename it, change its seats, shape, floor position, size, rotation, room, server section, location, display order, or service state. Omitted fields are left alone, except moving a table to another location without naming a destination section clears its old section. Taking a table out of service makes its tablet screen refuse new orders.
- restaurant.list_floor_sectionsServer sections
List the restaurant's floor sections, including each section's display color, assigned server, location, and the number of tables currently in the section. This reads scheduling state only and sends nothing.
- restaurant.create_floor_sectionCreate section
Create a color-coded floor section at one restaurant location and optionally assign a workspace teammate as its server. No tables move until they are explicitly assigned, and nothing is sent to the teammate.
- restaurant.update_floor_sectionSave section
Update a floor section's name, color, display order, or assigned server. Changing the server reassigns responsibility for every table already in that section; it does not send a notification or message.
- restaurant.assign_tables_to_sectionAssign selected
Assign one or many dining tables at the same restaurant location to a server section, or clear their section. The section's assigned teammate becomes responsible for the whole selected group; no guest or teammate is messaged.
- restaurant.delete_floor_sectionDelete sectionconfirm
Permanently delete a server section. Its dining tables remain on the floor plan but immediately become unassigned; the lost section name, color, and server assignment cannot be restored automatically. No messages are sent.
- restaurant.get_physical_floor_planPhysical floor plan
Read one restaurant location's measured building plan, uploaded-image reference, rooms, walls, doors, fixed service areas, and exact seat markers. This returns layout data only and sends nothing.
- restaurant.save_physical_floor_planSave building plan
Create or update the real-world width, depth, measurement unit, and uploaded-plan visibility for one restaurant location. Existing uploaded images, rooms, fixtures, seats, tables, orders, and reservations stay in place.
- restaurant.remove_floor_plan_imageRemove plan imageconfirm
Permanently delete the private uploaded architectural-plan image from one restaurant location. Every traced room, wall, fixture, seat, operational table, order, reservation, and server section stays in place, but the image itself cannot be restored automatically.
- restaurant.upload_floor_plan_imageUpload plan imageconfirm
Upload or replace the private architectural-plan image underneath one restaurant floor. The image is stored permanently in the workspace's Supabase Storage (up to 10 MB, which can incur storage and egress cost); traced rooms, fixtures, seats, and tables remain unchanged.
- restaurant.create_floor_featureAdd floor feature
Add one measured physical feature—room, wall, door, bar, kitchen, checkout, restroom, patio, fixture, or exact seat—to a restaurant building plan. A seat can be linked to its operational dining table; nothing is sent or charged.
- restaurant.update_floor_featureSave floor feature
Update a traced room, wall, door, fixed service area, fixture, or exact seat on the physical restaurant plan. This changes only the drawing; linked tables, orders, reservations, and server assignments remain intact.
- restaurant.delete_floor_featureDelete floor featureconfirm
Permanently delete one traced room, wall, door, service area, fixture, or exact seat marker from the physical restaurant plan. Linked operational tables, orders, reservations, and server sections stay intact, but the deleted drawing cannot be restored automatically.
- restaurant.delete_tableDelete tableconfirm
Permanently delete a dining table from the floor plan. Past orders keep their history (they just lose the table link), but the table's tablet ordering screen and QR token stop working immediately. This cannot be undone — prefer taking the table out of service (restaurant.update_table with active=false) if it might come back.
- restaurant.table_statusTable status
The live floor view: for each table, whether it currently has an open order (with order number, when it opened, and covers) and its next upcoming reservation inside the look-ahead window. This is exactly what the Tables screen shows staff.
- restaurant.list_inventoryInventory
List the restaurant's inventory items with what's on hand, the unit each is counted in, its par (reorder) level, unit cost in cents, and SKU. Optionally filter to one location or search by name/SKU. Costs nothing to run. Requires the Restaurant app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required and no data.
- restaurant.create_inventory_itemNew item
Create an inventory item — something the restaurant keeps on the shelf and wants tracked (an ingredient, a bottle, packaging). An opening on-hand amount, when given, is recorded as a 'count' movement so the ledger starts complete. Link the item to menu items with restaurant.set_recipe to have sales deplete it automatically. Requires the Restaurant app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required and no data.
- restaurant.update_inventory_itemEdit item
Edit an inventory item's name, unit, par level, unit cost, SKU, or location. Omitted fields are left alone. The on-hand amount is deliberately NOT editable here — change it with restaurant.adjust_stock so the ledger records why. Requires the Restaurant app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required and no data.
- restaurant.adjust_stockAdjustconfirm
Change an inventory item's on-hand amount by a signed delta and write the matching row in the stock-movement ledger. This rewrites what the shelf says the restaurant owns — deliveries, waste, corrections, and physical counts all go through here — so the number feeds the low-stock report and everything downstream that trusts it. Requires the Restaurant app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required and no data.
- restaurant.list_stock_movementsMovements
Read the stock-movement ledger, newest first: every change to every on-hand count, with its signed delta, reason ('purchase', 'waste', 'adjustment', 'count', or the automatic 'sale' written when the kitchen fires an order), note, the order that consumed the stock for sale rows, and who made it. Optionally filter by item or reason. Costs nothing to run. Requires the Restaurant app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required and no data.
- restaurant.set_recipeSave recipe
Replace a menu item's ingredient lines — how much of which inventory items selling ONE of that dish consumes. This is what makes sales deplete stock automatically: when the kitchen fires the dish, each line writes a 'sale' movement. An empty ingredient list clears the recipe, and the dish stops touching inventory. Quantities are per single sale, in each ingredient's own unit. Requires the Restaurant app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required and no data.
- restaurant.low_stockReorder list
The reorder report: every inventory item at or below its par level, with what's on hand, the par line, unit and unit cost — i.e. what to order before service runs out. Items without a par level never appear. Costs nothing to run. Requires the Restaurant app (a one-time purchase from the Chirply App Marketplace) to be installed in this workspace; without it the call returns 403 app_required and no data.
- restaurant.list_reservationsReservations
List the restaurant's reservations — the same book the host stand shows. Filter to one local calendar day (the 'tonight's book' view), a status, or a location. Rows include guest name/phone/email, party size, start time (UTC instant), assigned table, status, and how the booking came in (online, phone, walk-in, or API).
- restaurant.reservation_availabilityCheck available times
The bookable reservation times for one local calendar day and party size, computed from the location's service periods minus what existing reservations and table capacity already consume. Read-only and free. Each slot is a UTC instant with an 'available' flag; offer only available ones to a guest, and know the time is re-validated again at booking.
- restaurant.create_reservationAdd reservationconfirm
Book a real table: creates a reservation exactly like the host stand's Add-reservation form, after re-validating the requested time against live availability (an unavailable time is refused). Matches or creates a CRM contact for the guest and immediately sends them a confirmation by email and/or SMS through the org's own connected sender and Twilio number — a real message to a real person.
- restaurant.set_reservation_statusUpdate reservation statusconfirm
Move a reservation through its lifecycle, the same buttons the host stand shows: confirm a pending one, seat the party, complete the visit, mark a no-show, or cancel. Canceling or no-showing releases the table, and another guest may book the freed slot immediately — treat those as irreversible in practice. No message is sent to the guest.
revenue
- revenue.summaryRevenue
Gross revenue collected by the workspace across native invoices, sales funnels, and every connected Stripe account for an optional date range, with refunds, transaction counts, and true Stripe MRR from active subscriptions billed every month. Trials, past-due subscriptions, and plans billed quarterly or annually are excluded from MRR. Invoice and funnel charges are removed from the general Stripe bucket so the all-sources total never double-counts them. Also returns what was collected today and yesterday in the workspace's own timezone, independent of the requested date range. Read-only; changes nothing and charges nobody.
revenue_operator
- revenue_operator.get_recovery_outcomesView observed recovery outcomes
Read subsequent inbound contact messages, inbound calls, noncanceled bookings and linked live-mode invoice/funnel/Stripe payments after one confirmed recovery send. The observation window ends at 30 days, the next confirmed recovery for that contact, or now, whichever comes first. Currencies stay separate; invoices use refund date while funnel/Stripe refunds adjust the collection window. These are contact-level observations and current deal status, not causal lift, guaranteed replies to this email or recovered revenue attribution. Makes no provider or AI calls, changes nothing, sends nothing and spends no money.
- revenue_operator.get_recovery_boardOpen Revenue Operator
Read the Revenue Operator's shadow-mode recovery board. It separately examines up to 2,000 recently updated open risks and 2,000 resolved risks with stored deal intelligence, then derives prioritized recovery proposals, coverage KPIs, and a flagged-to-won-or-lost outcome proxy. Partial populations are explicitly marked in the result. It makes no AI model or provider calls, sends nothing, starts nothing, spends nothing, and changes nothing.
- revenue_operator.get_recovery_workspaceReview recovery email
Read one deal's current status, linked email contact, and latest twenty recovery drafts and delivery receipts. The deal's current won/lost status is an outcome observation, not evidence the email caused a sale. Read-only; sends nothing.
- revenue_operator.prepare_emailSave recovery draft
Save immutable subject, body, recipient and deal/contact versions for one reviewed recovery email. Requires an open at-risk or stalled deal with an unblocked email contact. Sends nothing and makes no AI calls. Only one pending recovery per contact is allowed; drafts expire after 24 hours. A separate explicit approval sends it.
- revenue_operator.send_emailApprove and send recovery emailconfirm
Send the saved recovery message to its real recipient through the workspace's connected email provider, at the workspace's provider cost, with normal configured signatures, unsubscribe links and tracking. Consumes approval once, rechecks open deal/risk/contact versions, refuses newer messages, calls, bookings or linked collected payments, honors opt-outs and enforces seven days between recovery emails per contact. An uncertain delivery is held for review and is never automatically resent.
- revenue_operator.cancel_emailDiscard recovery draft
Cancel an unsent recovery draft so a revised message can be prepared. Cannot cancel or reset a send already in progress. Sends nothing and retains the canceled draft in history.
- revenue_operator.check_deliveryCheck recovery delivery receipt
Reconcile an uncertain recovery send against its exact stored message receipt. Marks it sent only when the matching message records provider acceptance or delivery. Missing, failed or merely queued rows remain held because a local failure may follow a provider timeout. Never resets a send claim, sends or retries an email, or spends money.
rvm
- rvm.list_recordingsList voicemail recordings
List the saved voicemail recordings this workspace can drop — uploaded audio, in-app recordings, ElevenLabs-synthesized clips, and Twilio text-to-speech scripts. Newest first.
- rvm.get_recordingOpen a voicemail recording
Fetch one saved voicemail recording by id, including its duration and (for text-to-speech recordings) the script and voice it speaks.
- rvm.add_recording_from_urlAdd a voicemail recording from a URLconfirm
Save an existing audio file as a reusable voicemail recording by fetching it from a public URL (the machine-surface equivalent of the app's upload button — binary uploads can't ride a tool call). TWILIO FORMAT RULE: the file must be MP3, WAV, GSM, µ-law or AIFF; m4a/aac/webm fail the drop with Twilio error 12300, so they're refused here. Voicemails must be 55 seconds or shorter. THIS MAKES AN OUTBOUND REQUEST FROM CHIRPLY'S SERVERS to whatever address is given, so everything in the URL — host, path, query string — is disclosed to whoever operates it. That is why it asks for confirmation.
- rvm.generate_recordingGenerate a voicemail with text-to-speechconfirm
Turn a script into a reusable voicemail recording. Engine 'elevenlabs' synthesizes an MP3 right now through the workspace's OWN ElevenLabs account and SPENDS ITS TTS CREDITS; engine 'twilio' stores only the script and has Twilio speak it live at drop time (no synthesis charge, billed as part of the call). Scripts longer than about 55 spoken seconds are refused.
- rvm.list_campaignsList voicemail campaigns
List ringless-voicemail campaigns with their status and per-recipient tallies (dropped, failed, skipped), newest first.
- rvm.get_campaignOpen a voicemail campaign
Fetch one voicemail campaign with live per-status drop counts — how many are pending, in flight, dropped, failed or skipped.
- rvm.list_dropsList voicemail drops
List the individual recipients of a voicemail campaign and what happened to each one — pending, filtering, dropping, dropped, failed (with the error) or skipped (e.g. do-not-contact).
- rvm.create_campaignSend a ringless voicemail campaignconfirm
Create and send a ringless-voicemail campaign to contacts and/or saved lists. THIS DROPS REAL VOICEMAILS: each recipient costs two outbound Twilio calls (a filter call plus the voicemail leg) billed to the workspace, and 'now' fans out immediately. Needs a saved recording (rvm.generate_recording or rvm.add_recording_from_url) and TWO DIFFERENT active numbers — a filter number and a from number. Do-not-contact is always enforced; quiet hours only when scheduled and explicitly asked for.
- rvm.drop_to_contactDrop a voicemail to one contactconfirm
Drop a ringless voicemail to a single contact right now — the same one-click action as the contact page's 'Drop voicemail' dialog. THIS PLACES REAL CALLS and bills the workspace two Twilio legs. Needs a saved recording and two different active numbers. Do-not-contact is enforced before dialing.
- rvm.cancel_campaignCancel a voicemail campaignconfirm
Stop a voicemail campaign: any drop that hasn't gone out yet is skipped and the campaign is marked canceled. Voicemails already delivered can't be recalled, and a canceled campaign can't be resumed.
search
- search.globalSearch
Search this workspace's contacts, deals, companies, and pipelines at once. Returns matching records with direct links and changes nothing.
security
- security.getWorkspace security
Read this workspace's security posture: whether two-factor authentication is required for every member. Changes nothing.
- security.set_require_mfaRequire two-factor for everyoneconfirm
Turn the workspace-wide two-factor requirement on or off. Turning it ON locks every teammate without a verified authenticator app out of the workspace until they enroll — on their next navigation they are taken to a mandatory set-up screen and can do nothing else there but enroll or sign out. It is refused unless the acting person (for an API key, the workspace owner) already has a verified authenticator, so the requirement can never lock out the person who could undo it. Turning it OFF simply stops requiring enrollment; existing authenticators are untouched.
segments
- segments.fieldsList Smart Segment fields
List every Smart Segment scalar field and correlated relationship, its supported operators and filters, the workspace's custom contact fields, and current tenant-scoped choices such as tags, lists, pipelines, team members, forms, Stripe accounts, and Stripe products. Read-only and performs no provider calls.
- segments.listList Smart Segments
List the organization's saved Smart Segments, including their complete validated rule definitions and last successful display-only count metadata. This does not evaluate membership.
- segments.getView Smart Segment
Get one saved Smart Segment and its complete rule definition. This reads the saved configuration and does not evaluate contacts.
- segments.previewPreview audience
Validate unsaved Smart Segment conditions and start a tenant-scoped, exact background evaluation. Large workspaces return a preparing status and durable evaluation id for segments.get_evaluation; a final count and up to five sample contact ids appear only after concurrent CRM changes are reconciled. Reads only and sends nothing.
- segments.get_evaluationCheck audience preview
Read the durable progress or exact final result of a Smart Segment evaluation started by segments.preview or segments.refresh. Preparing results never expose a partial count as final; ready results return the exact count and at most five deterministic sample contact ids. Reads only and sends nothing.
- segments.refreshRefresh Smart Segment count
Start or deduplicate an exact background evaluation of one saved Smart Segment's current definition. The saved display count is published only if the definition and tenant configuration remain unchanged through final reconciliation. Sends nothing and does not alter contacts.
- segments.createCreate Smart Segment
Save a reusable dynamic audience definition. Exact previews are timestamped background snapshots, while an outbound campaign resolves its own guarded audience from the then-current definition. Saving sends nothing and does not copy contacts.
- segments.updateSave changes
Replace a Smart Segment's name, description, matching mode, and complete validated rule set. Prior preview counts become historical, future evaluations use the new definition, and already-enrolled campaign recipients do not change.
- segments.deleteDelete Smart Segmentconfirm
Permanently delete a saved Smart Segment. Contacts are never deleted. The database refuses deletion while any campaign still includes or excludes the segment so a draft audience cannot silently expand.
settings
- zapier.list_recipesZapier business recipes
Read three practical Zapier build guides: assigning new inquiries, maintaining a booking ledger and handing won deals to delivery. Each includes field mapping, deduplication and a test-before-enable checklist. These are instructions, not installed Zaps. Reading them creates nothing, enables nothing, sends nothing and costs nothing; executing configured actions later may trigger workspace workflows or send data to other apps.
- zapier.get_client_settingZapier for your clients
Read the one Zapier decision an agency makes for the client workspaces it creates, and what it currently resolves to: off (no Zapier link on their screens), Chirply's own invite link, or the agency's white-labelled integration on the $50/month self-serve tier or the $100/month plus one-time $999 done-for-you tier. Also reports whether Chirply still owes a done-for-you setup and what the agency's own invite URL is. Reads configuration only — it changes nothing, charges nothing, and does not affect any Zap that is already running.
- zapier.set_client_modeSave Zapier choiceconfirm
Set the single Zapier choice an agency makes for the client workspaces it creates. THIS SPENDS REAL MONEY on two of the three values. 'off' is free and shows no Zapier link on any client workspace's screens — it hides a link and nothing more: it does not revoke anything, does not disconnect existing Zaps, and anyone who already has the invite link can still use the integration. 'self_serve' COMMITS THE AGENCY TO $50 PER MONTH to white-label the integration and do all of the setup themselves in their own Zapier developer account, with Chirply providing instructions and no other help. 'done_for_you' COMMITS THEM TO $100 PER MONTH PLUS A ONE-TIME $999 SETUP FEE — $1,099 on the first charge — for Chirply to build the integration in their Zapier developer account and maintain it. The Stripe products for both tiers are not created yet, so this records the decision, marks it awaiting billing, and files a support ticket for a human to complete the sale; it does not charge a card by itself.
- zapier.set_branded_appSave your Zapier invite link
Record the agency's OWN Zapier integration — its name and the public-invite URL from their own Zapier developer account — so their client workspaces are shown that link instead of Chirply's. Only accepted on a paid Zapier white-label tier, because on the free modes there is no integration of the agency's for the link to point at. Costs nothing and sends nothing; it changes which URL Chirply prints on a screen. The URL must be a zapier.com public-invite link, which is what Zapier's own Sharing screen gives you.
- zapier.setup_guideZapier setup instructions
Return the step-by-step instructions for building a white-labelled Zapier integration in the agency's OWN Zapier developer account: the account to create, the OAuth endpoints and scopes to configure against Chirply, where the catalog of triggers, actions and searches comes from, the branding steps, and where to paste the resulting invite link back. This is what the $50/month self-serve tier buys — Chirply supplies the information and does none of the work, does not submit the integration for review, does not maintain it, and does not support it. Reading it costs nothing and changes nothing.
- org.getWorkspace settings
Read this workspace's profile: its name, URL slug, status, what kind of account it is (a plain Account, or a White-Label / Reseller / Agency Partner once those upgrades are bought), its entitlements, its branding, and its seat usage — members, owners, and pending invites.
- org.listWorkspaces
List every workspace the signed-in person can switch into — what the workspace picker in the sidebar shows — with each one's name, kind (a top-level workspace or a sub-account under one), the caller's role in it, and which one is currently active. org.get only ever describes the ACTIVE workspace, so this is the only way to find out what else exists. Soft-deleted (cancelled) workspaces are left out, exactly as the picker leaves them out. Read-only.
- org.switchSwitch workspace
Move the signed-in person into a different workspace, the way picking one from the sidebar's workspace switcher does. Everything afterwards — records, settings, billing, every later action — belongs to the new workspace, so this changes what all subsequent work operates on. Only workspaces the person is actually a member of are accepted, and a superadmin who was viewing a tenant stops doing so. THE APP MUST BE RELOADED for the change to show: the switch is stored in a cookie, but pages already open still hold the previous workspace's data in memory. Changes no records in either workspace.
- dispositions.listList call dispositions
List the call outcomes agents pick after a call — “Connected”, “Left voicemail”, “Not interested”, and any custom ones — in display order, with the actions each one fires.
- dispositions.createAdd a disposition
Add a call outcome to the end of the list. Attach actions from the shared Actions registry to have picking it fire them automatically — sending an SMS, tagging the contact, dropping a voicemail, enrolling in a campaign, and so on. Those actions send real messages and spend the workspace's own Twilio/Mailgun credit every time an agent picks the outcome, so an outcome with a send attached is a recurring cost, not a label. Created switched on unless is_active is false.
- dispositions.updateEdit a disposition
Rename a disposition, recolor it, switch it on or off for the dialer, or replace the actions it fires. Omitted fields are left alone; supplying `actions` REPLACES the whole list.
- dispositions.deleteDelete a dispositionconfirm
Permanently delete a call outcome and the actions attached to it. Calls already logged against it keep their record; the outcome just stops being offered.
- integrations.listList integrations
List every provider this workspace can connect — telephony (Twilio), email (Mailgun, Resend), AI (OpenRouter, ElevenLabs, fal.ai, Replicate, Firecrawl), commerce and marketing (the tenant's own Stripe, Shopify, Klaviyo, BookFunnel, GoHighLevel, Outscraper, PayPal, Facebook & Instagram), and infrastructure (Cloudflare for automatic DNS, Supabase app backends) — with whether it's connected, whether it's fully configured, its last status, and the callback URLs it needs. Stored credentials are NEVER returned — secrets are reported only as set/not set.
- integrations.getOpen an integration
Fetch one provider's connection status plus the exact fields its connect form takes — use this before integrations.connect so you know which keys to send. Never returns a stored credential, only whether each secret is set. When manageOnly is set, integrations.connect will refuse this provider and manageHref names the screen that owns its credentials: agency-supplied connections, providers with named or multiple connections, and providers whose keys are verified with the provider before they are stored (the tenant's own Stripe).
- integrations.get_zapier_inviteGet the Zapier invite
Return the Zapier invite link this workspace is offered — Chirply's own, or the agency's white-labelled integration where one has been set up — plus how many triggers, actions and searches it exposes. Some workspaces are offered no link at all: an agency decides whether the client workspaces it creates are shown Zapier, and when that decision is off this reports so instead of returning a URL. That is a decision about what Chirply displays, NOT an access control — it revokes nothing and disconnects nothing, and anyone already holding the link keeps using the integration normally. Reading this costs nothing, connects nothing and grants nobody access on its own: after accepting an invite, an owner or admin still has to approve the connection to a specific workspace from inside Zapier, and Zaps then run against the REST API, which is plan-gated.
- integrations.request_accessRequest accessconfirm
Ask the Chirply team to switch on an integration that isn't open to everyone yet — the “Request access” button on a limited-availability tile. Meta (Facebook / Instagram) is the case this exists for: until Facebook finishes reviewing Chirply it only works for accounts we have added to our developer list, so connecting it first requires a human at Chirply to add you. This FILES A REAL SUPPORT TICKET on behalf of the signed-in user AND EMAILS THE CHIRPLY TEAM, then answers back through Support. Asking twice is harmless: while a ticket for the same provider is still open it reports that and files nothing new — which is exactly why this exists rather than support.file_bug, whose free-text title would defeat that de-duplication and leave the team with a pile of identical requests.
- integrations.connectConnect an integration
Save this workspace's own credentials for a provider's single canonical connection, creating the connection or updating it. Named, multi-account, agency-managed, and verify-before-store providers (the tenant's own Stripe) are rejected here and must use their dedicated manager, so the exact connection is explicit and its keys are checked with the provider before anything is written. Secret fields are encrypted with AES-256-GCM before storage and can never be read back; OMIT a secret to keep the one already stored. Connecting Twilio also auto-creates the API Key + Voice app the softphone needs, connecting Mailgun reconciles the inbound route and delivery webhooks in the tenant's Mailgun account, and connecting Cloudflare verifies the token and turns on automatic DNS for custom domains. Charges from these providers bill to the tenant's own account.
- integrations.testTest an integration
Verify this workspace's single canonical stored connection against the provider with a FREE, read-only call (account metadata, a domain list, a token check — it never sends a message, generates content, or spends the account's balance), then record the outcome on that connection (connected / error). Named, multi-account, agency-managed, and verify-before-store providers (the tenant's own Stripe) are rejected here and must be tested in their dedicated manager. Testing Mailgun also REPAIRS inbound email — it re-creates the missing route or webhook and re-checks the domain's MX. A dead Facebook grant is stamped 'needs_reauth' so the UI offers Reconnect. BookFunnel is webhook-only with no credential to check, so its result reports when the last reader event arrived rather than claiming verification.
- integrations.diagnose_emailDiagnose incoming email
Check, end to end, why replies to this workspace's email are or aren't arriving in Conversations, and report each link in the chain: the Mailgun connection, whether receiving is switched on, whether the domain's MX actually delivers to Mailgun, whether the inbound route exists, whether another route in the Mailgun account outranks it and calls stop() (which silently swallows every reply), whether the webhook signing key is stored, and whether mail Mailgun recently accepted for the domain actually matched that inbound route. Read-only — it inspects the Mailgun account and changes nothing. Costs nothing and sends nothing. Run integrations.test on Mailgun afterwards to repair whatever this finds.
- integrations.disconnectDisconnect an integrationconfirm
Delete this workspace's single canonical connection to a provider, including its stored credentials. Named, multi-account, and agency-managed providers are rejected here and must be disconnected in their dedicated manager so the target is explicit. Everything that runs on a removed connection stops immediately — disconnecting Twilio kills calling and SMS, Mailgun kills email, OpenRouter kills the AI features.
setup
- setup.save_launch_followthroughSave launch follow-up
Assign an existing agency teammate, record an internal next action and due date, or snooze a client setup task for up to 30 days. Rejects stale revisions and records each change in history. Completion always comes from live setup evidence; this never marks a client ready, grants access, notifies anyone, sends a message, or spends money.
- setup.launch_followthrough_historyView launch follow-up history
Read the latest twenty recorded owner, next-action, due-date and snooze changes for one client setup task, with the total change count. Restricted to managers of its current parent agency. Internal read only; sends nothing, changes nothing and spends no money.
- setup.get_start_bannerGet getting-started banner preference
Read whether the calling person dismissed the optional getting-started banner in this workspace. This personal preference is independent of the shared first-win goal and welcome tour; a saved goal can also hide the banner. The setup guide stays available through Help & setup. Read-only: changes nothing, sends nothing, and spends no money. Requires a calling user and cannot read another person's preference.
- setup.set_start_bannerDismiss getting started
Hide or restore the optional getting-started banner for the calling person in this workspace, across their devices. Set dismissed to true to hide it or false to restore it when no first-win goal is saved. Changes no teammate's preference, workspace goal, welcome-tour progress, or setup evidence; the guide stays available through Help & setup. Sends nothing and spends no money. Requires a calling user and cannot change another person's preference.
- setup.set_goalChoose my first win
Save the workspace's first-win focus: organize contacts, capture leads, book appointments, set up follow-up, or launch agency clients. Changes guidance only; creates no customer records, sends no messages, activates no workflows, and spends no money. The choice is shared across this workspace and can be changed later.
- setup.create_lead_formBuild my lead form
Create one reusable contact-form starter in this workspace, with lead fields and a thank-you message ready to edit. It stays a private draft until you publish it separately. Repeating this action opens the same form and preserves its edits and current publication state. Existing form plan limits apply. Sends no messages, starts no workflows, publishes nothing, and spends no money.
- setup.client_launch_queueClient launch queue
Inspect ten active client workspaces under this agency, with live setup progress, missing requirements, first-outcome evidence, assigned owners, due dates, next actions and snoozes. Includes up to 500 current agency teammates for assignment; larger team inventories are reported unavailable. Completion comes from evidence and regressions reopen attention. Exceptions sort first within the inspected page. Failed checks stay unavailable. Read-only; sends no messages or provider requests that incur charges.
- setup.set_client_profileSave client launch path
Choose a local-business or sales-team setup checklist for one client belonging to this agency. Changes checklist priorities only; installs no assets, enables no workflows, sends no messages, and spends no money.
- setup.get_overviewGet setup overview
Read the workspace's available first-win goals, saved focus, practical steps, personalized setup path, required progress, next best action, and feature prerequisites. This is read-only and recomputes provider health, published content, and customer-activity evidence live; it never changes setup or starts customer work.
- setup.set_profileSave setup path
Set whether this workspace is launching as an agency, local business, or sales team. This changes setup-checklist prioritization only; it sends nothing, starts no workflow or customer action, and spends no money.
- onboarding.get_statusGet first-run tour status
Read whether a person has been through the first-run product tour — the short welcome sequence covering the AI key, the assistant, the setup guide and the community — how far they got, and which step they are on. Read-only: it changes nothing, shows nobody anything, and touches no customer data.
- onboarding.set_stepSave first-run tour progress
Move a person's bookmark in the first-run tour so their next visit resumes at that step. The step index only ever moves FORWARD — to send someone back to the beginning use onboarding.restart. Affects nothing but which screen of a five-screen overlay they next see; it sends nothing and spends nothing.
- onboarding.completeFinish the first-run tour
Mark the first-run tour as done for a person, so the welcome overlay stops appearing when they sign in. Use this to stop showing it to someone who already knows the product. It is fully reversible with onboarding.restart, and it changes nothing else — no setup-guide item is ticked off, because that guide reads live evidence from the workspace rather than anyone's say-so.
- onboarding.restartRestart the first-run tour
Show the first-run tour again from the beginning next time this person opens the app — the usual reason being a new teammate on an existing seat, or someone who skipped it and wants it back. It is an overlay they can close at any time, so the worst case is one extra click; nothing is sent, spent, or reset in the workspace itself.
shopify
- shopify.sync_storeSync Shopify storeconfirm
Fetch the next resumable batch of customers, products, orders, and abandoned checkouts from one connected Shopify store into this workspace. It reads from Shopify and changes nothing in the store, but IT WRITES TO THIS CRM AND CAN TEXT OR EMAIL REAL SHOPPERS. Every Shopify customer it sees is created as a CRM contact, or, when one already matches by email or phone, that existing contact's name, email, phone and lifecycle are OVERWRITTEN with Shopify's values. Once the first full pass has finished, every subsequent call reconciles recently abandoned checkouts and runs this workspace's 'Shopify checkout abandoned' automations for each newly abandoned cart — so any cart-recovery workflow sends real SMS/email to real shoppers, billed to the workspace's own Twilio/Mailgun account. Check those automations before running this on a store you have not synced before.
- shopify.list_storesList Shopify stores
List Shopify stores connected to this workspace, including connection, import, and webhook health. This only reads stored connection metadata and does not call Shopify or spend money.
- shopify.list_customersList Shopify customers
List Shopify customer commerce profiles synchronized into this workspace, including CRM contact linkage, order count, spend, tags, and marketing-consent states. This reads protected customer data but does not contact anyone.
- shopify.list_productsList Shopify products
List products synchronized from connected Shopify stores with their status, vendor, type, tags, and storefront URL. This does not change Shopify inventory or product listings.
- shopify.list_collectionsList Shopify collections
List Shopify collections synchronized into this workspace for catalog segmentation and automation filtering. This is read-only and does not change collection membership in Shopify.
- shopify.list_ordersList Shopify orders
List orders synchronized from connected Shopify stores, including customer linkage, totals, refunds, payment state, fulfillment state, and attribution fields. This does not modify, fulfill, cancel, or refund any order.
- shopify.list_abandoned_checkoutsList abandoned Shopify checkouts
List synchronized Shopify checkouts that were abandoned or later recovered, including customer linkage, cart value, recovery URL, and timestamps. This reads protected customer commerce data and does not send recovery messages.
- shopify.list_refundsList Shopify refunds
List full and partial Shopify refunds synchronized into this workspace, including order and contact linkage and refunded value. This is read-only and never issues or changes a refund.
- shopify.list_privacy_requestsList Shopify privacy requests
List mandatory Shopify customer data access and erasure requests with their due dates and completion states. Protected payloads are not returned. This is read-only and restricted to workspace administrators.
signage
- signage.get_accountView Signage plan
Show this workspace's Signage trial, renewal date, private Scale-offer status, and installed Signage app. This is read-only and charges nothing.
- signage.accept_scale_offerUnlock Scale for $47/monthconfirm
Immediately charges the saved card $47, starts the complete Scale subscription at $47/month, includes the installed $9.99/month Signage app free, and cancels the separate Signage trial subscription so it will not renew. This private offer can only be accepted before its deadline.
- signage.decline_scale_offerKeep my Signage trialconfirm
Permanently declines the one-time $47/month Scale bundle. The existing Signage trial remains unchanged and will renew at $9.99/month after day 30 unless canceled. No money is charged by this action.
snapshots
- snapshots.listSnapshots
Lists the configuration snapshots this workspace owns, with their status and how many times each has been installed.
- snapshots.getSnapshot details
One snapshot with every published version, including what each version contains and which outside servers its configuration would contact.
- snapshots.inventoryWhat can go in a snapshot
Everything in this workspace that can be packaged into a snapshot, grouped by type. Reads configuration only — it never touches contacts, conversations, calls or any other customer data, because none of that can be put in a snapshot.
- snapshots.createNew snapshot
Creates a draft snapshot from a selection of this workspace's configuration. A draft is private and cannot be installed or shared until it is published.
- snapshots.updateEdit snapshot
Renames a snapshot or changes what its next version will contain. Versions already published are frozen and are not affected.
- snapshots.publish_versionPublish versionconfirm
Freezes the current selection as a new, permanent version and copies every recording and image it uses into the snapshot. From now on anyone holding a share link or licence installs this version. Published versions can never be edited — publish again to ship a change.
- snapshots.preview_installPreview install
Dry-runs an install and reports exactly what would be created, what would be reused because a name already matches, which outside servers the configuration would contact, and what setup it still needs. Changes absolutely nothing.
- snapshots.installInstall snapshotconfirm
Writes a snapshot's configuration into a workspace — funnels, automations, templates, phone menus and settings. Everything arrives switched OFF: no automation runs, no message is sent, no page is published until a human turns it on. Nothing existing is overwritten or deleted. Reversible for 14 days.
- snapshots.push_to_subaccountsPush to sub-accountsconfirm
Installs a snapshot into several sub-accounts at once. Each account is installed independently, so one failing does not stop the others. As with any install, everything arrives switched off and nothing existing is overwritten.
- snapshots.list_installsInstall history
Every snapshot install into this workspace, with what it created, its status, and any setup steps still outstanding.
- snapshots.deleteDelete snapshotconfirm
Permanently deletes a snapshot, every published version of it, and the frozen copies of its recordings and images. Anything anyone already installed from it stays exactly where it is — an install is a copy, so this does not un-install anything.
- snapshots.create_linkCreate install linkconfirm
Mints a public URL for a published snapshot. ANYONE HOLDING THE LINK can install this configuration into a workspace they manage, until it is revoked or expires. Optionally cap the number of uses or lock it to one recipient's email address.
- snapshots.list_linksInstall links
Every share link for a snapshot, with how many times each has been used, when it expires, and whether it has been revoked.
- snapshots.revoke_linkRevoke install linkconfirm
Kills a share link immediately, so it can no longer be used to install. Installs already completed from it are unaffected — an install is a copy, so this does not un-install anything.
- snapshots.checklistFinish setup
The outstanding setup steps for an install — the phone numbers, domains and teammates a snapshot could not carry, plus anything that needs a look. Also reports what the install actually wrote: items created, items matched to what the workspace already had, and the number of detail rows written inside those items (pages, automation steps, knowledge items, invoice lines), broken down by kind so the figures can be checked against the snapshot.
- snapshots.resolve_slotFill a setup stepconfirm
Supplies one of the things a snapshot could not carry — typically a phone number id. The value is written everywhere the snapshot used it, including inside phone menus and automation steps, and anything that was held back waiting on it is created at that point.
- snapshots.undo_installUndo installconfirm
DELETES everything a snapshot install created in this workspace and restores anything it replaced. Items you have since attached real data to — a funnel that has taken orders, an invoice that has been paid — are kept rather than deleted. Only available for 14 days after the install.
- snapshots.list_grantsWho can install this
Every workspace that has been granted or has bought the right to install a snapshot, and whether that access is still active.
- snapshots.revoke_grantRevoke accessconfirm
Stops a workspace installing this snapshot again. It does NOT remove anything they have already installed — that is their own configuration now, and there is no remote kill switch.
- snapshots.typesSnapshot item types
The kinds of configuration a snapshot can carry, with the label and category of each. Useful for building a selection before calling snapshots.create.
social
- social.list_calendarView content calendar
List the workspace's drafted, scheduled, published, failed, and manual-handoff social posts with the result for every destination.
- social.get_postOpen a social post
Fetch one content-calendar post and every Page or Group delivery result.
- social.list_destinationsList publishing destinations
List every place this workspace can put a social post: connected Facebook Pages and Instagram Professional accounts that publish automatically, and saved Facebook Groups that require a manual handoff. Each destination reports its kind (page, instagram, group) and whether it can currently publish.
- social.generate_bulkGenerate postsconfirm
Use the workspace's OpenRouter account to write a reviewable batch of social posts. This consumes paid AI tokens but does not save or publish anything.
- social.save_bulkSave content batchconfirm
Save 2–12 social posts as drafts, or schedule the series across the selected destinations. Scheduled posts create real public posts at their publishing times. Every post in a series shares one set of destinations and none of them carry an image, so an Instagram destination is rejected outright here — schedule Instagram posts one at a time with social.schedule.
- social.save_draftSave draft
Save social content and its destinations without publishing or scheduling anything. Content is still validated against every selected network, so an Instagram destination without an image is rejected now rather than at publishing time. Facebook Groups remain manual handoffs.
- social.scheduleSchedule postconfirm
Schedule this content to publish to every selected Facebook Page and Instagram account at the specified time. Real public posts will be created automatically on the org's own connected Meta assets. Selected Groups become manual handoffs because Meta removed Group publishing from its API.
- social.publish_nowPublish nowconfirm
Immediately creates REAL public posts on every selected Facebook Page and Instagram account. There is no undo on Facebook or Instagram. Group destinations become ready-to-copy manual handoffs and are not posted automatically.
- social.retryRetry failed destinationsconfirm
Retries every failed destination immediately, creating REAL public Facebook or Instagram posts where the retry succeeds. Destinations already published are not duplicated.
- social.cancelCancel post
Cancel a draft or scheduled social post before any remaining destinations publish. Public Page posts that already succeeded are not deleted.
- social.mark_group_postedMark Group posted
Record that a person copied this scheduled content into its Facebook Group. This does not call Facebook or create a post itself.
- social.add_groupAdd Group handoff
Save a Facebook Group as a manual content-calendar destination. Meta no longer permits apps to publish to Groups, so scheduled content is prepared for a person to copy and post.
- social.remove_groupRemove Group handoff
Remove a saved Group from future destination pickers. Existing scheduled posts keep their destination snapshot.
stages
- stages.listList pipeline stages
List a pipeline's stages in board order, with each stage's win probability and whether it counts as won or lost. Omit pipeline_id for the default pipeline.
- stages.createAdd a stage
Add a stage to the end of a pipeline. A stage with outcome 'won' or 'lost' closes any deal moved into it (stamping closed_at); 'in_progress' keeps deals open.
- stages.updateEdit a stage
Rename a stage or change its win probability or outcome. Omitted fields are left alone. Changing the outcome does NOT restate deals already sitting in the stage — it applies the next time a deal moves in.
- stages.reorderReorder stages
Set the left-to-right column order of a pipeline's stages. Pass the stage ids in the order you want them; every id must belong to the given pipeline. Deals stay in their stages.
- stages.deleteDelete a stageconfirm
Permanently delete a stage from its pipeline. Deals in that column are NOT deleted — their stage is cleared, so they drop off the board until they're moved into another stage. Cannot be undone.
stripe
- stripe.list_accountsStripe accounts
List every Stripe account this workspace has connected, with how many customers, payments and subscriptions have been imported from each, when it last synced, and which one collects money for invoices and funnels. Read-only; returns no keys.
- stripe.payment_accountsChoose a Stripe account
List the connected Stripe accounts that a workspace member can select when taking a mobile payment. Returns display and availability fields only; no secret or publishable keys are exposed.
- stripe.search_payment_recipientsSearch payment contacts
Search every CRM contact by first name, last name, combined full name, business, email, or phone for the mobile point of sale, with matching imported Stripe customers included when available. Read-only; returns safe identifiers scoped to the selected workspace and Stripe account.
- stripe.create_mobile_paymentEnter card securelyconfirm
Create a Stripe PaymentIntent in the selected connected account for secure in-person card entry. Confirming the Stripe payment form can charge the customer's real card in live mode and incurs that account's Stripe processing fees; the card number and security code are never received or stored here. Also CREATES A STRIPE CUSTOMER in that connected account when the payer has no Stripe customer record yet — a permanent record carrying their name, email and phone — and links it to the CRM contact for reuse on later payments.
- stripe.prepare_mobile_card_entryOpen secure card form
Return the selected connected Stripe account's publishable configuration so the mobile app can display Stripe's encrypted card form. This does not create a PaymentIntent, charge anyone, or leave an incomplete transaction; the intent is created only after the customer enters a payment method and confirms Pay.
- stripe.create_mobile_payment_linkCreate payment linkconfirm
Create a one-time Stripe-hosted checkout link in the selected connected account. Anyone with the link can complete the payment; a successful live checkout charges real money and incurs that account's Stripe processing fees.
- stripe.list_mobile_payment_linksRecent payment links
List durable one-time Stripe Checkout links created from the mobile POS, newest first, including their amount, selected Stripe account, optional CRM customer, mode and current saved status. Read-only and does not charge anyone.
- stripe.prepare_mobile_terminal_paymentPrepare Tap to Pay paymentconfirm
Create a Stripe Customer for the selected CRM contact when needed — a permanent record in that connected account carrying their name, email and phone — then create the card-present PaymentIntent that the authenticated mobile app will collect. The later native confirmation can charge the customer's real card in live mode and incurs the connected account's Stripe Terminal fees; this preparation call alone does not collect a card.
- stripe.verify_mobile_paymentVerify mobile payment
Retrieve one mobile PaymentIntent directly from its connected Stripe account and return Stripe's authoritative current status, amount, customer and receipt details. This is read-only and does not create, capture, cancel, refund or otherwise change the payment.
- stripe.prepare_tap_to_payStart Tap to Payconfirm
Create a short-lived Stripe Terminal connection token for the selected connected account and list that account's reader locations. If that account has no Terminal location yet, this also CREATES ONE in Stripe from the workspace's saved business address — a permanent Terminal Location record on the connected account, named after the workspace — or, when the address is incomplete, returns setup_required instead of creating anything. A later confirmation on a supported NFC phone can collect real money in live mode and incurs Stripe Terminal fees; this call alone does not charge anything.
- stripe.cancel_mobile_terminal_paymentCancel failed Tap to Pay attemptconfirm
Cancel a failed or abandoned Tap to Pay PaymentIntent in the selected connected Stripe account. If the card was authorized, cancellation releases the authorization instead of collecting money; succeeded payments are never canceled by this action.
- stripe.connect_accountConnect a Stripe accountconfirm
Connect another Stripe account by its secret key. The key is verified against Stripe and stored encrypted, and an import of that account's customers, charges and subscriptions starts immediately — a large account continues in the background. Customers are matched to existing contacts by email address; new ones are added as contacts but are NOT subscribed to marketing unless subscribe_to_marketing is set. Handles credentials that can charge money.
- stripe.update_accountChange a Stripe account's settingsconfirm
Rename a connected Stripe account, turn its import on or off, change whether it creates contacts or lets them receive marketing email, or replace or REMOVE its live/test keys. Every account remains available on invoices, products, funnels, and websites. Replacement keys are verified before they overwrite working ones. Removing a publishable key (send an empty string) stops card payments on that account until one is added back, and changes nothing else — the secret key, webhook and the other mode's credentials are untouched and importing carries on. Omitted fields are never touched.
- stripe.disconnect_accountDisconnect a Stripe accountconfirm
Delete a connected Stripe account's stored keys and remove every customer, payment and subscription imported from it. Contacts are KEPT — they just lose the revenue that came from this account. Nothing changes inside Stripe itself. Irreversible short of reconnecting and re-importing.
- stripe.sync_accountSync a Stripe account
Import one connected Stripe account again, picking up where the last run left off. Reads only — nothing is created or charged in Stripe. May create CRM contacts for payers who aren't contacts yet (subject to the account's settings), and those contacts are excluded from marketing unless the account allows it. A large account may not finish in one call; check `done` in the result and call again.
- stripe.enable_realtime_syncTurn on real-time syncconfirm
Register the platform's webhook inside this connected Stripe account so payments, refunds and subscription changes land on the contact the instant they happen, instead of waiting up to an hour for the next scheduled import. Uses the account's stored secret key to create the endpoint — no dashboard steps for the user. If that key is a restricted one that can't manage webhooks, this reports why and the account stays on the hourly import (paste a signing secret by hand as the fallback). Safe to run repeatedly; it re-registers rather than duplicating.
- stripe.sync_all_accountsSync all Stripe accounts
Import every connected Stripe account that has syncing switched on. Same read-only behaviour as stripe.sync_account, run across all of them with the time budget shared between accounts.
- stripe.list_customersStripe customers
List the Stripe customer records imported into this workspace, newest first. One human can own several of these — one per Stripe account they've bought from — and `contact_id` is the single CRM contact they were all matched onto. Filter by contact to see every Stripe identity behind one person.
- stripe.list_paymentsStripe payments
List charges imported from the connected Stripe accounts, newest first. Amounts are in minor units (cents) in the charge's own currency. `status` is the outcome of the ATTEMPT — a succeeded payment that was later refunded is still `succeeded`, with `amount_refunded_cents` set — so never total money without filtering to succeeded.
- stripe.list_subscriptionsStripe subscriptions
List subscriptions imported from the connected Stripe accounts, in every status so cancelled plans are still visible as history. Amounts are per interval, in minor units.
- stripe.contact_revenueWhat this contact has paid
One contact's complete Stripe picture, unified across EVERY connected account: lifetime value net of refunds, number of payments, refunds (count in `refunds_count` and total value in `refunded_cents`), disputes (count in `disputed_count` and total value in `disputed_cents`), active subscriptions, monthly run rate, first and last payment, plus their recent payments and live subscriptions. This is the number to quote for a person — `stripe.list_customers` shows the separate Stripe records it was assembled from. Amounts are in minor units; check `currencies` before trusting a total on a multi-currency account.
- stripe.list_sync_runsStripe import history
Every import attempt against the connected Stripe accounts, newest first, with what it pulled, how many contacts it matched versus created, and the error if it failed. This is what answers “why isn't this customer here?”.
subaccounts
- subaccounts.listList sub-accounts
List the client workspaces under this agency, with how many of the plan's sub-account allowance are used.
- subaccounts.createCreate a sub-account
Create a client workspace under this agency. The URL slug is derived from the name and de-duplicated automatically. Fails once the reseller plan's sub-account allowance is used up.
- subaccounts.updateEdit a sub-account
Rename one of this agency's client workspaces, or suspend/reactivate it. Suspending locks the workspace for everyone in it.
supabase
- supabase.get_connectionCheck Supabase connection
Checks whether this workspace has connected its own Supabase account (pasted personal access token or OAuth grant) and, when connected, verifies it live and lists the Supabase organizations the credential can see — the organization ids create_project needs. Never returns the credential itself.
- supabase.list_projectsList Supabase projects
Lists the Supabase projects this workspace knows about — provisioned from here or imported from the connected account — with status, region, API URL, the public (anon) key, and what each one is linked to as a backend. Reads the local mirror; run supabase.sync_projects first for a fresh pull from the account.
- supabase.sync_projectsSync projects from Supabase
Pulls the live project list from the connected Supabase account and mirrors it here: refreshes names/regions/statuses, imports projects that already existed in the account, and marks projects deleted upstream. Changes nothing in the Supabase account itself.
- supabase.create_projectCreate projectconfirm
Creates a REAL Supabase project (Postgres database + user auth + file storage) in the workspace's own Supabase account. Supabase bills that account's organization directly for the project's compute — real money on the tenant's own Supabase invoice, starting immediately (their free tier covers two small projects). The project comes up asynchronously over one to three minutes; poll supabase.get_project until status is ACTIVE_HEALTHY.
- supabase.get_projectView project
One Supabase project's current state: refreshes status from the live account, fetches its API keys once available, and returns the project with its links. Safe to poll after create_project.
- supabase.get_project_keysReveal project credentialsconfirm
Returns a project's connection credentials: API URL and publishable (anon) key always, plus — only when include_service_role is true — the service-role key, which bypasses every row-level security rule on the tenant's project, and the generated database password for Chirply-provisioned projects. Handing out the service-role key is equivalent to handing out the whole database, which is why this call requires confirmation.
- supabase.run_sqlRun SQL on projectconfirm
Executes arbitrary SQL against one of the workspace's own Supabase projects with full database privileges — creating tables, migrating schemas, reading or DELETING any data in that project. This is the tool for setting up a linked backend's schema. It runs on the tenant's project, not on Chirply, but a destructive statement is still irreversible.
- supabase.link_projectLink project as backend
Points a Chirply-built thing at a Supabase project as its backend: one site/funnel, one installed app, or the workspace-wide default. The target then boots with that project's API URL and publishable key. A target's existing backend link is replaced, not duplicated.
- supabase.unlink_projectUnlink backend
Removes one backend link (from supabase.list_projects). The Supabase project itself is untouched; whatever it backed simply no longer has a backend (or falls back to the workspace default).
- supabase.delete_projectDelete projectconfirm
PERMANENTLY deletes a Supabase project from the workspace's own Supabase account — the database and every row, user, and file in it are destroyed with no undo, and anything using it as a backend stops working. Supabase stops billing for it. Use supabase.forget_project to merely remove a project from this workspace without touching the real project.
- supabase.forget_projectRemove project from workspace
Forgets a project locally — removes its row and backend links from this workspace only. The real Supabase project keeps running, untouched, in the tenant's account; supabase.sync_projects would re-import it.
- supabase.disconnectDisconnect Supabaseconfirm
Removes the workspace's Supabase account connection (the stored token or OAuth grant). Refuses while any backend link still exists, so live sites and apps aren't silently stranded — unlink them first. Project mirror rows are kept; the real projects in the Supabase account are untouched.
support
- support.list_bug_reportsList bug reports
List bug reports newest first, with reply and attachment counts. Workspace owners and admins see every report filed by their team; members see only reports they filed. Optionally filter by status, keep only tickets awaiting a reply from the platform team, or search titles and descriptions.
- support.get_bug_reportOpen a bug report
Fetch one bug report with its full reply thread. Workspace owners and admins may open any report filed by their team; members may open only their own. Internal platform-team notes are included only for platform admins.
- support.file_bugReport a bug
File a bug report with the platform team on behalf of the signed-in user. Emails the team immediately and starts a thread the reporter can be replied to on. Screenshots and screen recordings can only be attached from the app.
- support.reply_to_bugReply on a bug report
Post a reply on a bug report's thread. Workspace owners and admins may reply to any report filed by their team; members may reply only to their own. A platform admin's public reply emails the reporter; a reporter's reply emails the platform team and, if the ticket was resolved or closed, automatically reopens it. Set internal=true (platform admins only) to leave a note the reporter never sees.
- support.delete_bug_reportDelete a bug reportconfirm
Permanently delete a bug report, its whole thread, and its uploaded screenshots. Only the person who filed it, or the platform team, can do this.
- support.list_feature_requestsList feature requests
List the feature-request board this workspace is on, with vote and comment counts. Which board that is depends on the workspace: a client of a white-label agency is on that agency's own private board, and everyone else is on the platform-wide public board, which is shared by every workspace on purpose and carries no customer data — only a poster's display name. A white-label agency can additionally pass board=clients to read the board its own clients post to. Never returns another agency's board.
- support.get_feature_requestOpen a feature request
Fetch one feature request with its vote count and its comment thread. Only requests on a board this workspace is on are readable — its own public-board posts, or the private board of the white-label agency it belongs to; anything else reports as not found. Internal platform-team comments are included only for platform admins.
- support.request_featureRequest a feature
Post a feature request on behalf of the signed-in user, and upvote it for them. It lands on whichever board this workspace posts to — the platform-wide public board, where every user on the platform can see and vote on it, or, for a client of a white-label agency, that agency's own private board. The team that answers that board is emailed.
- support.comment_on_featureComment on a feature request
Add a public comment to a feature request and email its author. Only requests on a board this workspace is on can be commented on; anything else reports as not found. Set internal=true (platform admins only) for a note nobody else sees.
- support.vote_featureUpvote a feature request
Upvote a feature request, or take your vote back. Only requests on a board this workspace is on can be voted on; anything else reports as not found. Votes are one per person and drive that board's “most wanted” ranking, which is how the team behind it decides what to build next.
- support.delete_feature_requestDelete a feature requestconfirm
Permanently delete a feature request, its comments, its votes, and its attachments. Only the person who posted it, or the platform team, can do this.
- support.delete_feature_commentDelete a feature commentconfirm
Permanently delete one comment from a feature request, along with anything attached to it. Only its author, or the platform team, can do this.
- support.list_client_reportsClient reports
List the bug reports filed by this agency's client workspaces — the tickets the agency answers itself, because its clients bought a white-labelled product and have never heard of Chirply. Shows each ticket's title, status, severity, which client workspace it came from, who is waiting on whom, and whether it has already been raised with Chirply. Reads only. This is the agency's own desk; it never returns tickets belonging to another agency, or the agency's own tickets with Chirply.
- support.get_client_reportRead a client report
Read one bug report filed by a client of this agency, in full, with every reply on the thread. Internal notes the agency's own staff left are included; the client cannot see those. Reads only. Refuses any ticket that is not on this agency's desk.
- support.reply_to_client_reportReply to a client reportconfirm
Post a reply on a client's bug report. A public reply is SENT TO A REAL PERSON — the client who filed it gets an email, branded as this agency, and sees the message in their own workspace. An internal note is visible only to this agency's staff and is never shown to the client. Refuses any ticket that is not on this agency's desk.
- support.set_client_report_statusUpdate a client reportconfirm
Change the status and/or severity of a client's bug report on this agency's desk. CHANGING THE STATUS EMAILS THE CLIENT who filed it, branded as this agency — marking something resolved tells a real person their problem is fixed, so do not use it to tidy a queue. Refuses any ticket that is not on this agency's desk.
- support.escalate_client_reportRaise a client report with Chirplyconfirm
Raise a client's bug report with Chirply as a genuine platform fault. This opens a SEPARATE ticket between this agency and Chirply, quoting what the client wrote, and Chirply's team answers the agency on it. The client is not told, is never contacted by Chirply, and sees nothing change on their own ticket — the agency stays the only company they deal with. One escalation per client ticket; asking twice is refused. Refuses any ticket that is not on this agency's desk.
- support.list_client_ideasClient ideas
List the feature requests this agency's client workspaces have posted to the agency's OWN board — what their clients are asking THEM to build, sorted by how many clients voted for it. A white-label agency's clients post here rather than to Chirply's public board, so nobody outside the agency's own workspaces can see these. Reads only. Not to be confused with support.list_feature_requests, which is the board this agency posts to as Chirply's customer.
- support.promote_client_ideaSend a client idea to Chirplyconfirm
Send one of this agency's client ideas up to Chirply as a request for the platform itself. Opens a SEPARATE request on Chirply's public board, authored by this agency, quoting what the client wrote and carrying the number of client workspaces that voted for it — that vote count is the argument, so it travels with the request. The client is not told, is never contacted by Chirply, and their own post is unchanged. One promotion per idea; asking twice is refused. Refuses any request that is not on this agency's board.
team
- team.list_membersList team members
List everyone with access to this workspace — name, email, role (owner/admin/member), and when they joined.
- team.list_invitesList pending invitations
List invitations to this workspace that haven't been accepted or revoked yet, including which have passed their 14-day expiry.
- team.get_invite_linkCopy link
Get the acceptance link for a pending invitation — the “Copy link” button on the team page. Use it when the invitation email didn't arrive, or to hand someone their link over chat instead. ANYONE HOLDING THIS LINK CAN JOIN THE WORKSPACE at the invitation's role until it is accepted, expires (14 days), or is revoked with team.revoke_invite, so treat it like a password: send it to the invited person and nobody else. Reading it changes nothing and sends no email.
- team.inviteInvite a teammateconfirm
Creates an invitation and sends a real email inviting someone to join this workspace as a member or an admin — a real message to a real person, billed to the workspace's own email provider. The email contains a 14-day acceptance link; owners can only be made by changing an existing member's role. The invitation is created FIRST and always survives: if the email cannot be sent (the workspace has no verified sending domain, or the provider failed), the invitation still exists and stays pending, `email_sent` comes back false with the reason in `delivery_status`, and the link can be handed over with team.get_invite_link or the email retried with team.resend_invite.
- team.resend_inviteResendconfirm
Sends the invitation email again for an invitation that is still pending — a real email to a real person, billed to the workspace's own email provider. Use it after a mail outage, or once the workspace has connected a verified sending domain and wants invitations created before that to actually go out. It does NOT create a new invitation and does not change the existing link, its role, or its 14-day expiry, so an invitation that was already emailed will simply be emailed a second time. If the send fails again the invitation is left untouched and still pending.
- team.change_roleChange a member's roleconfirm
Change what a teammate can do in this workspace. Promoting to owner grants full control including billing, so only an owner (or the platform team) can hand the owner role out or take it away — an admin cannot. Nobody can change their own role, and demoting the last remaining owner is refused, since an organization must always keep one.
- team.remove_memberRemove a teammateconfirm
Remove someone's access to this workspace. They lose the workspace immediately; their records (contacts, notes, calls) stay. Removing an owner takes an owner (or the platform team) — an admin cannot remove one — though anyone may remove themselves to leave. The last owner can never be removed.
- team.revoke_inviteRevoke an invitation
Cancel a pending invitation so its link stops working. The person can be invited again afterwards.
team_chat
- team_chat.list_peopleList teammates for chat
List everyone in this workspace who can be messaged in team chat — their user id, display name, email and avatar. These are STAFF, not CRM contacts; use contacts.list for customers.
- team_chat.list_channelsList chat channels
List the internal chat rooms available to the caller: channels they have joined, open channels they could join, and their direct messages. Each row carries the room's last message, its member count, and the caller's own unread count. An API key sees open channels only — private rooms and DMs belong to a person.
- team_chat.list_messagesRead a chat conversation
Read the most recent messages in one channel or direct message, oldest first. Page backwards with `before`. Pass `parent_id` to read one thread's replies instead of the channel's top level. Refuses a room the caller cannot see.
- team_chat.searchSearch team chat
Search the text of team-chat messages across every room the caller can see. Deliberately never reaches a private channel the caller is not a member of.
- team_chat.send_messageSend a chat messageconfirm
Post a message into a team-chat channel or direct message. It appears immediately for everyone in the room and pushes a desktop notification to each member whose notification setting allows it — real people are interrupted, so treat it like speaking in the room. Costs nothing: this is internal staff chat, never an SMS or email to a customer. A message can be edited or deleted afterwards, but not unsent. NOTE: @mentioning an AI here does NOT make it answer — that would let one AI wake another in a loop. Use team_chat.ask_ai, which is explicit about the cost.
- team_chat.edit_messageEdit a chat message
Rewrite the text of a message you sent. It is marked as edited for everyone in the room. You can only edit your own messages — not a colleague's, whatever your role.
- team_chat.delete_messageDelete a chat messageconfirm
Remove a message from the conversation. Its text and any attachments stop being readable immediately and cannot be recovered. You can delete your own messages; workspace owners and admins can delete anyone's.
- team_chat.reactReact to a chat message
Add an emoji reaction to a message, or take yours off if it is already there — the same toggle as clicking the emoji in the app. Acts as the signed-in person, so an API key cannot use it.
- team_chat.create_channelCreate a chat channel
Open a new internal chat channel and add teammates to it. A private channel is invisible to everyone outside its member list and cannot be opened up later, so choose deliberately. Channel names are unique within the workspace.
- team_chat.update_channelUpdate a chat channel
Change a channel's name, its one-line description, or its standing meeting link. Only the channel's owner, its creator, or a workspace owner/admin can. Making a public channel private is possible and permanent; the reverse is not, because it would retroactively expose a transcript people wrote in private.
- team_chat.archive_channelArchive a chat channelconfirm
Archive a channel so it drops out of everyone's sidebar and takes no new messages. The transcript is kept and the channel can be unarchived with `archived: false`. Only the channel's owner, its creator, or a workspace owner/admin can.
- team_chat.open_direct_messageOpen a direct message
Find or start the private conversation between you and one or more teammates, and return its channel id so you can post into it. Calling it twice never creates a second thread. Acts as the signed-in person, so an API key cannot use it.
- team_chat.join_channelJoin a chat channel
Join an open channel, so it appears in your sidebar and you start getting its messages. A short 'joined the channel' line is posted. Private channels cannot be joined — someone in them has to add you.
- team_chat.leave_channelLeave a chat channelconfirm
Leave a channel. It drops out of your sidebar and stops notifying you; the transcript stays and you can rejoin an open channel later. Leaving a PRIVATE channel means you can no longer see it at all unless someone adds you back.
- team_chat.add_membersAdd people to a chat channelconfirm
Add teammates to a channel. For a private channel this grants them its ENTIRE past transcript, not just what is said from now on. Any member of the channel can do this; you must be in the channel yourself.
- team_chat.remove_memberRemove someone from a chat channelconfirm
Take a teammate out of a channel. In a private channel they immediately lose access to the whole conversation, including its history and files. Only the channel's owner, its creator, or a workspace owner/admin can.
- team_chat.list_aiList AI available for chat
List every AI in this workspace that can be put into a team-chat room — AI employees (which can take actions) and AI agents (which answer as themselves). Shows whether each is currently active; a paused employee or a switched-off agent stays silent in chat.
- team_chat.add_aiAdd AI to a chat channelconfirm
Put an AI employee or AI agent into a team-chat room so people can @mention it there. It will be able to read everything said in that room from then on — including, in a PRIVATE channel, the whole history. An AI employee added this way acts under the permissions of whichever person asks it something, never its own. Only the channel's owner, its creator, or a workspace owner/admin can.
- team_chat.remove_aiRemove AI from a chat channelconfirm
Take an AI employee or agent out of a room. It stops answering there and stops seeing what is said. Anything it already did is not undone. Only the channel's owner, its creator, or a workspace owner/admin can.
- team_chat.open_ai_direct_messageOpen a direct message with an AI
Find or start your own private one-to-one conversation with an AI employee or agent, and return its channel id. Nobody else in the workspace can see it. In a one-to-one the AI answers every message without being @mentioned. Calling it twice never creates a second conversation. Acts as the signed-in person, so an API key cannot use it.
- team_chat.ask_aiAsk an AI in a chat channelconfirm
Post a question into a team-chat room addressed to an AI that is already in it, wait for that AI to answer, and return its reply. Everyone in the room sees both the question and the answer. This SPENDS MODEL CREDITS on the workspace's own AI key, and an AI employee may take real actions while answering — under the permissions of the person asking, with anything risky stopping in the approvals inbox. Slow by nature: a turn with tool calls can take tens of seconds. Acts as the signed-in person, so an API key cannot use it.
- team_chat.set_notificationsSet a channel's notifications
Choose what one channel sends you — every message, only mentions, or nothing — and whether it is starred to the top of your sidebar. Personal to you and invisible to everyone else in the room. Acts as the signed-in person, so an API key cannot use it.
- team_chat.mark_readMark a conversation read
Clear your unread badge for one channel or direct message, exactly as opening it in the app does. Personal to you. Acts as the signed-in person, so an API key cannot use it.
telephony
- numbers.listList phone numbers
List the phone numbers this workspace uses, with each one's inbound destination, recording preferences, missed-call text-back settings and status. Read-only — costs nothing.
- numbers.getOpen a phone number
Fetch one phone number with every setting on its settings page: label, inbound destination and its target, call recording, transcription, forwarding preferences and missed-call text back.
- numbers.search_availableSearch available numbers
Search the workspace's own Twilio account for local, toll-free, or mobile numbers available to buy. Filter by country, beginning prefix, locality, digit/keypad-letter pattern, and required voice/SMS/MMS capabilities; use the returned cursor to load every matching page. This only searches — nothing is purchased and nothing is billed.
- numbers.buyBuy a phone numberconfirm
PURCHASE a phone number on the workspace's own Twilio account. This SPENDS REAL MONEY — Twilio bills the tenant an upfront and a monthly fee for the number immediately, and it can only be undone by releasing it. By default the number is also routed into this workspace (voice + SMS webhooks) and added to the dialer.
- numbers.releaseRelease a phone numberconfirm
PERMANENTLY release a phone number back to Twilio and remove it from this workspace. This CANNOT BE UNDONE — the number is gone from the account, anyone who calls it reaches nobody, and it may not be re-purchasable. Billing for it stops. Use numbers.archive instead to simply hide a number you want to keep.
- numbers.update_settingsSave a number's settings
Update any setting on one phone number's settings page: its internal label, where incoming calls go (team simulring, direct voicemail, an AI receptionist, an IVR phone menu, a blind forward, or a conference room), how long the team rings and where unanswered team calls go, destination targets, call recording, transcription and recording announcements, transparent-forward caller ID, and whether it is the workspace's default outbound caller ID. Omitted fields are left alone. Does not touch Twilio's own webhook routing — use numbers.configure_routing for that.
- numbers.get_scheduleOpen a number's call & text schedule
Fetch the ordered timezone-aware windows that decide how one number handles incoming calls and texts. Read-only; the normal number settings remain the fallback outside every matching window.
- numbers.set_sms_routingSave inbound text routingconfirm
Choose what one number does with incoming SMS outside scheduled windows. Inbox stores the message without replying; static immediately sends fixed text; autoresponder runs one attached keyword rule (or all active rules when no id is supplied); ai_agent automatically replies from the chosen agent using the thread and its Brain. Static, responder and AI modes SEND REAL SMS from the tenant's own Twilio account and incur carrier charges when a message arrives.
- numbers.set_missed_call_text_backSave missed-call text backconfirm
Enable, disable, or edit one number's missed-call text back. When enabled, every unanswered inbound call immediately sends one REAL SMS from that same number, billed to the workspace's own Twilio account. Callers who opted out of SMS are skipped, and webhook retries never send a duplicate for the same call. Two optional gates narrow when it sends: only outside the workspace's business hours (from the Business profile), and only to callers who aren't already a contact.
- numbers.set_scheduleSave call & text scheduleconfirm
Replace one number's complete ordered schedule, interpreted in the workspace timezone from Business profile. The first local-time window that matches controls both incoming calls and texts; outside it, the normal number settings apply. Enabling static, responder or AI text behavior causes REAL automatic SMS, billed to the tenant's Twilio account, whenever matching messages arrive. Call destinations take effect on the next inbound call.
- numbers.configure_routingSet where a number's calls arrive
Point one Twilio number's inbound voice and/or SMS webhooks at this workspace, at a custom https URL, or turn the channel off. This changes configuration on the workspace's Twilio account and takes effect on the next call. Addressed by the Twilio number SID, so it also works for numbers that aren't in the dialer yet.
- numbers.use_in_chirplyUse a Twilio number in this workspace
One click: route a Twilio number's voice AND SMS into this workspace and add it to the dialer so it can place calls and send messages. Addressed by the Twilio number SID. Does not buy anything.
- numbers.stop_using_in_chirplyStop using a number in this workspace
Reverse of 'use in this workspace': clear this workspace's voice + SMS routing on Twilio and remove the number from the dialer. The number stays on the Twilio account and keeps billing — this does not release it. Clears the default-outbound preference if it pointed here.
- numbers.archiveArchive a phone number
Hide a number from the dialer and every from-number picker WITHOUT releasing it on Twilio or changing its routing — for a line you run elsewhere but want out of the way. Billing continues. Clears the default-outbound preference if it pointed here. Reverse it with numbers.restore.
- numbers.restoreRestore an archived number
Bring an archived number back into the dialer and the numbers console.
- numbers.set_default_outboundSet the default outbound number
Set (or clear) the workspace's default outbound caller ID and SMS sender. The number must already be active in this workspace.
- numbers.get_line_typeLook up known line types
Read the cached line type (mobile / landline / VoIP / toll-free) and carrier for one or more phone numbers. Reads the platform's shared lookup cache only, so it is instant and costs nothing; numbers nobody has ever paid to look up simply come back unknown. Use numbers.queue_line_type_lookup to pay for the unknown ones.
- numbers.queue_line_type_lookupLook up line types (paid)confirm
Queue phone numbers for a Twilio Lookup line-type check. This SPENDS REAL MONEY — each number not already in the shared cache is billed to the workspace's own Twilio account (roughly $0.008 each). Numbers already known, or already queued, are skipped for free. Nothing is queued at all when the workspace has switched automatic lookup off (numbers.set_auto_line_type_lookup) or has no Twilio connected. Results land asynchronously; read them back with numbers.get_line_type.
- numbers.scan_line_typesScan the database for line typesconfirm
One-time sweep: queue EVERY not-yet-known phone number across this workspace's contacts and staged leads for a line-type lookup. This SPENDS REAL MONEY — each unknown number is billed to the workspace's own Twilio (roughly $0.008 each), and a large database can mean thousands of lookups.
- numbers.set_auto_line_type_lookupToggle automatic line-type lookup
Turn automatic line-type lookup on or off for this workspace. When on, every new phone number that enters the CRM is looked up on the workspace's own Twilio account (a small per-number charge) unless the platform already knows it. Stored alongside the Twilio credentials it spends.
- telephony.get_connectionCheck the Twilio connection
Report whether this workspace has connected its own Twilio account, and the connection's current status. Never returns the auth token or API key secret — those are stored encrypted and are not readable.
- telephony.connect_twilioSave Twilio credentialsconfirm
Connect (or update) the workspace's own Twilio account. Every call, message and number purchase made here is billed to these credentials, so pointing them at a different account changes who pays. The auth token and API-key secret are encrypted before storage; leaving either blank on an update keeps the stored value.
- telephony.test_connectionTest the Twilio connection
Verify the stored Twilio credentials by fetching the account from Twilio, and update the connection's status to reflect the result.
- calls.listList calls
List the call log, newest first — inbound and outbound, with duration, disposition, recording state and transcript availability. Filter by direction, status, contact, line, voicemails, or whether a recording was kept.
- calls.getOpen a call
Fetch one call from the log with its full detail: both numbers, duration, disposition, notes, recording URL (when the audio is still stored) and transcript.
- calls.placePlace a callconfirm
PLACE A REAL OUTBOUND PHONE CALL from one of the workspace's numbers. This DIALS A REAL PERSON immediately and bills the workspace's own Twilio account for the minutes. The call is logged, and when a contact is named the call also lands on that contact's timeline. If the from-number has recording switched on, the call is recorded.
- calls.start_softphoneStart in-app callconfirm
Prepare a real outbound VoIP call from the signed-in mobile softphone. The mobile app immediately connects the caller to the recipient through the workspace's Twilio account, so the recipient's phone rings and Twilio bills the workspace for call minutes.
- calls.set_dispositionSet a call's outcomeconfirm
Record a call's disposition (outcome) and note, the same as picking one in the power dialer or the call log. Setting a disposition FIRES ITS ATTACHED ACTIONS against the call's contact — which can send real SMS or email, enrol them in a campaign, or move a deal — and runs any automation set to fire when a call outcome is recorded. It is not a passive edit.
- calls.list_needing_dispositionList calls with no outcome
List calls that have ended but still have no outcome recorded — the review queue. This is where calls taken on a desk phone or the mobile app land, since no browser was open to ask at the time. Read-only; it changes nothing.
- calls.outcomes_reportCalls by outcome
Roll up the call log by recorded outcome for a date window: how many calls landed on each outcome (with percentages), total calls, how many were answered (connect rate), how many have an outcome recorded versus still missing one, and a per-teammate breakdown with each person's connect rate and commonest outcome. Spam and voicemail drops are excluded, matching the Calls page. Read-only — it counts existing calls and changes nothing.
- calls.suggest_dispositionSuggest a call's outcome
Read a call's transcript and suggest which of the workspace's own call outcomes fits, with a confidence score and the reason. It only suggests — nothing is saved and no actions fire; pass the answer to calls.set_disposition to apply it. Requires a transcript, so the number must have transcription switched on. Uses and bills the workspace's own OpenRouter account.
- calls.delete_recording_audioDelete a recording's audioconfirm
Permanently delete ONLY a call's recorded audio from storage, keeping the call log entry and its transcript. This cannot be undone — the audio is gone.
- calls.deleteDelete a callconfirm
Permanently delete a call from the log, along with its recorded audio. This cannot be undone.
- calls.set_voicemail_greetingSet the voicemail greetingconfirm
Set one phone number's voicemail greeting. Twilio speaks built-in voices live; ElevenLabs generates and stores finished audio immediately using the workspace's OWN account and SPENDS ITS TTS CREDITS. Recording or uploading a clip is available in the app.
- calls.set_muteMute
Mute or unmute THIS side of a call that is happening right now — the workspace's own leg, exactly like the Mute button on the in-call bar. The other party stays connected and keeps talking; they simply stop hearing you. Escalates the call into a conference if it isn't one already (a brief, silent transition), because muting one participant is a conference operation. Trivially reversible by calling again with muted=false.
- calls.set_holdHold
Put the other party on hold on a call that is happening right now, or take them off it. On hold they hear hold music instead of this side and cannot hear anything said here. Escalates the call into a conference if it isn't one already (a brief, silent transition). NOTE: the app has no hold button today — this is the same participant-hold the warm-transfer flow uses internally, and for now it is a machine-only control. Trivially reversible by calling again with on_hold=false.
- calls.send_digitsKeypadconfirm
Press keypad digits on a call that is happening right now — the in-call keypad. The tones are played down the line TO THE OTHER PARTY, so this is how you drive somebody else's phone menu (“press 2 for accounts”) or enter an extension, an account number or a PIN. IRREVERSIBLE ONCE SENT: a wrong digit can commit the far end's IVR to a selection you cannot take back, and digits are audible to whoever is on the line, so never send anything secret this way. Escalates the call into a conference if it isn't one already, and the other party leaves the room for the moment the tones play, which both sides hear as a short silence.
- calls.declineDeclineconfirm
Decline a still-ringing INBOUND call without answering it, so it falls through to whatever the line does next — voicemail, or the number's fallback. The caller is not hung up on and never hears a rejection. Because a machine caller has no browser leg of its own, this declines the ring for the WHOLE workspace rather than for one person's softphone: everybody's phone stops, and the caller moves on. That cannot be taken back for this call. Refused once somebody has picked up — end an answered call with calls.hangup instead. To choose the destination yourself, use calls.send_to_voicemail or calls.forward_incoming.
- calls.set_presenceAvailable for calls
Set whether a member is available to take inbound calls on the browser phone — the softphone's Available/Away switch. A line whose inbound routing is set to the team rings everyone currently available, so switching someone off stops inbound calls reaching them, and switching everybody off means nobody's phone rings and callers fall through to voicemail. Availability lapses on its own about a minute after the browser stops heartbeating, so this is a way to take somebody OFF the rota, not a way to keep them on it.
- calls.list_presenceWho's available
List who in this workspace is currently able to answer an inbound call, and who is not. Covers both the browser phone and registered mobile apps, with each person's name and email and when they were last seen. This is the answer to “why is nobody picking up the main line?” — a number whose inbound routing is set to the team only rings people who are available here, so an empty list means every caller goes to voicemail. Read-only.
- calls.add_partyAdd someone to a live callconfirm
DIAL A THIRD PERSON into a call that is happening right now, escalating it to a conference so all three can talk. This RINGS A REAL PHONE and bills the workspace's own Twilio for the extra leg.
- calls.transferTransfer a live callconfirm
Blind-transfer a call that is happening right now: DIAL the target and hand the other party straight over, dropping this side. This RINGS A REAL PHONE, bills the workspace's own Twilio, and cannot be taken back once the transfer lands. Use calls.warm_transfer_start to consult first.
- calls.warm_transfer_startStart a warm transferconfirm
Step one of a consultative transfer on a live call: DIAL the target, put the other party on hold, and let this side speak to the target privately. RINGS A REAL PHONE and bills the workspace's own Twilio. Finish with calls.warm_transfer_complete or back out with calls.warm_transfer_cancel.
- calls.warm_transfer_completeComplete a warm transferconfirm
Finish a consultative transfer: take the caller off hold, connect them to the target, and drop this side out of the call. Cannot be taken back.
- calls.warm_transfer_cancelCancel a warm transferconfirm
Back out of a consultative transfer: drop the target's leg and take the caller off hold so this side keeps the call.
- calls.hangupEnd a live callconfirm
END a call that is happening right now, dropping every remaining party. This disconnects real people mid-conversation and cannot be undone.
- calls.send_to_voicemailSend a ringing call to voicemailconfirm
Send a still-ringing INBOUND call straight to voicemail without answering it. The caller hears the workspace's greeting and can leave a message.
- calls.forward_incomingForward a ringing callconfirm
Forward a still-ringing INBOUND call to another number without answering it. This RINGS A REAL PHONE and bills the workspace's own Twilio for the forwarded leg. When the receiving line opts into transparent forwarding, the original caller's number is presented.
- ivr.listList IVR phone menus
List the workspace's IVR flows (phone menus) built in the visual builder, with whether each is live and which lines it answers.
- ivr.getOpen an IVR phone menu
Fetch one IVR flow: its full node/edge graph as drawn in the builder, whether it is live, any validation issues that would stop it answering a real line, and every phone number currently pointing at it.
- ivr.createCreate an IVR phone menu
Create a new, empty IVR flow and return its id. Deliberately off air and unattached — pointing a live number at a flow with no steps would answer real callers with silence. Draw it with ivr.update, then publish and attach it.
- ivr.updateSave an IVR phone menu
Rename an IVR flow and/or replace its node/edge graph — the same graph the visual builder saves, and the one the live call runtime walks for both inbound calls and outbound voice campaigns. The flat greeting/options summary is recompiled automatically. A flow that is already LIVE is refused if the new graph would misroute a real caller.
- ivr.upload_prompt_audioUpload audio
Store a recorded clip for one step of a phone menu to play, and get back the permanent URL to put on that step with ivr.update. Send the bytes base64-encoded, up to 10 MB. IT MUST BE PLAYABLE ON A PHONE CALL: WAV, MP3, AIFF, GSM or u-law only — Twilio rejects anything else outright and the caller hears dead air, so a clip in another format is refused here rather than stored. Best results come from 8 kHz mono WAV, which is what the builder's own recorder produces. Costs nothing and calls nobody; it stores one file.
- ivr.generate_prompt_audioGenerate with AI voiceconfirm
Speak a line of a phone menu in an ElevenLabs voice and store it, returning the permanent URL to put on that step with ivr.update. SPENDS REAL MONEY: every call renders the text on the workspace's OWN ElevenLabs account and consumes its credits, so re-rendering the same line ten times costs ten times — and there is no cached preview to fall back on. Needed because Twilio's built-in <Say> voices cannot speak ElevenLabs: choosing a cloned or premium voice for a menu prompt means rendering it up front and playing the file on the call. Twilio's own voices (Polly, Google) are spoken live and never come through here. Nothing is dialed and no caller hears anything until the URL is saved onto a step and the menu is published.
- ivr.publishPublish an IVR phone menuconfirm
Put an IVR flow LIVE so it can answer real callers, or take it off air. Publishing is refused when the flow has issues that would misroute a caller (a dead hand-off, a missing or paused AI agent, an unconnected key). TAKING IT OFF AIR CHANGES WHERE CALLS GO: an off-air menu cannot answer, so every line pointing at it is sent back to the team simulring and named in the result.
- ivr.attach_numberAttach an IVR to a number
Point one of the workspace's phone numbers at this IVR flow, so incoming calls to that line walk the menu. The flow must be live. A line already answering with an AI receptionist, a forward, a conference room or a different IVR is refused rather than silently repointed.
- ivr.detach_numberDetach an IVR from a number
Stop a phone number answering with this IVR flow and send it back to the team simulring. Omit the number to release every line pointing at the flow.
- ivr.deleteDelete an IVR phone menuconfirm
Permanently delete an IVR flow. This cannot be undone. Any line answering with it is first sent back to the team simulring and named in the result. A flow that ANOTHER flow hands calls to is refused, because deleting it would leave that other flow hanging up on real callers.
- sales_bridges.listList sales bridges
List the workspace's sales bridges — the press-1 simulring connectors that ring a pool of agents for one hot lead.
- sales_bridges.getOpen a sales bridge
Fetch one sales bridge with its whisper script, caller-ID line, dial timeout, recording preferences and its full agent pool in ring order.
- sales_bridges.createCreate a sales bridge
Create a sales bridge: a name, the line to call from, an optional whisper played to the agent who answers, an optional SMS sent to each agent on dispatch, and the pool of agent phones to ring. Creating it dials nobody — use sales_bridges.start_run for that.
- sales_bridges.updateEdit a sales bridge
Update a sales bridge's settings. Omitted fields are left alone. Supplying `agents` REPLACES the whole pool in the order given.
- sales_bridges.deleteDelete a sales bridgeconfirm
Permanently delete a sales bridge and its agent pool. This cannot be undone. Past runs are removed with it.
- sales_bridges.start_runStart a sales bridgeconfirm
Dispatch a sales bridge for one lead RIGHT NOW: it RINGS EVERY AGENT IN THE POOL simultaneously and, on the first press of 1, bridges that agent to the lead. This places multiple REAL CALLS and bills the workspace's own Twilio for every leg, plus an SMS per agent when the bridge has one. Numbers on the do-not-contact list are refused.
- sales_bridges.list_runsList sales bridge runs
List recent sales-bridge dispatches with their outcome — ringing, claimed, bridged, completed, no answer, failed or canceled — and which agent won each one.
- sales_bridges.get_runOpen a sales bridge run
Fetch one sales-bridge run with every agent leg it dialed and how each leg ended, plus the call-log row for the bridged conversation.
- voice_campaigns.listList voice campaigns
List the workspace's outbound voice campaigns (call blasts and outbound IVR) with their status, schedule and per-recipient tallies.
- voice_campaigns.getOpen a voice campaign
Fetch one voice campaign with its content (IVR menu, spoken message, recorded audio or AI agent), schedule, quiet-hours and concurrency settings, and its recipient tallies.
- voice_campaigns.list_recipientsList campaign recipients
List a voice campaign's recipients with each one's dial status, attempt count, answering-machine verdict and last keypad selection.
- voice_campaigns.createCreate a voice campaignconfirm
Create and LAUNCH an outbound voice campaign. This CALLS REAL PEOPLE — every contact in the chosen audience is dialed from the workspace's own Twilio account and billed to it, starting immediately unless a future start time is given. Content is either an IVR phone menu (the live runtime walks the same flow the builder draws), a spoken message, or an AI agent. Every answered call also offers a keypad opt-out (“press 9 to be removed”) unless opt_out_enabled is false — that is the only way somebody being dialed can stop the calls, so leave it on. Uploading pre-recorded broadcast audio is only possible in the app.
- voice_campaigns.set_opt_outSave opt-out settingsconfirm
Change how somebody being dialed by a running voice campaign can get off the list — whether the “press a key to be removed” offer is made at all, which key, what pressing it stops (calls, voicemail drops, texts, emails), and the exact sentence read to them. This is deliberately editable WHILE the campaign dials, because “I set the wrong key and it's calling people right now” needs fixing in the next thirty seconds. Changes apply to calls placed from now on; anyone already dialed keeps whatever they heard. TURNING IT OFF REMOVES THE ONLY WAY A CALLEE CAN STOP THE CALLS — the campaign itself keeps running. Campaigns that play an IVR menu opt people out through an Unsubscribe action node in the menu instead, and are refused here.
- voice_campaigns.set_statusStart, pause or cancel a campaignconfirm
Change a voice campaign's status. Setting it to 'running' STARTS OR RESUMES DIALING REAL PEOPLE and billing the workspace's own Twilio. 'paused' holds the queue; 'canceled' stops it for good and marks every not-yet-dialed recipient as skipped, which cannot be undone.
- voice_campaigns.add_recipientsAdd contacts to a campaignconfirm
Enqueue more contacts into an existing voice campaign. On a RUNNING campaign they WILL BE CALLED — real calls billed to the workspace's own Twilio — as soon as the dispatcher reaches them. Contacts already on the campaign, and contacts without a dialable number, are skipped.
- voice_campaigns.call_contactCall one contact with a menu or messageconfirm
Place a single outbound IVR (or spoken-message) call to one contact. This CALLS A REAL PERSON and bills the workspace's own Twilio. Implemented as a one-recipient campaign so it goes through the same dispatcher — quiet hours, do-not-contact and pacing all apply, and the same IVR runtime walks the flow.
- voice_campaigns.deleteDelete a voice campaignconfirm
Permanently delete a voice campaign, its recipient queue and its stored broadcast audio. This cannot be undone. A running campaign stops.
- regions.list_callingCountries you can call
Lists every country with whether this workspace's own Twilio account is currently allowed to call it. A brand new Twilio account can only call its own country, and calls to anywhere switched off are refused before they are placed — this is what that setting says right now. Reads the workspace's Twilio account and costs nothing. Note this is the CALLING list only; Twilio keeps texting permissions on a separate switch it publishes no API for, so use regions.texting_status for that.
- regions.get_callingCheck calling to a country
Answers whether this workspace's Twilio account is allowed to call one particular country, named either by ISO code or by giving any phone number in it. Reading is free; when a phone number is given its country is resolved through a Twilio Lookup requested without any billable data packages, so that is free as well.
- regions.enable_callingTurn on calling to this countryconfirm
SWITCHES ON INTERNATIONAL CALLING to one country on the workspace's own Twilio account, immediately and for every user in the workspace. Calls placed after this are billed by Twilio at that country's international rates, which can be many times the domestic rate. Premium-rate and known toll-fraud number ranges stay OFF unless they are asked for by name — those are the ranges that turn a compromised account into a very large bill, so enabling them is a separate, deliberate decision.
- regions.disable_callingTurn off calling to this countryconfirm
SWITCHES OFF calling to one country on the workspace's own Twilio account. Every call placed to it after this is refused before it is dialed, for every user in the workspace — including calls made by dialers, campaigns and AI agents. Use it to close down a destination that is being abused or is not needed; it takes effect immediately.
- regions.texting_statusWhy texting this country is blocked
Explains a blocked international TEXT (Twilio error 21408) and says exactly who can unblock it and where. Unlike calling, Twilio publishes no API for messaging geo-permissions — it states they cannot be changed programmatically, for security reasons — so neither Chirply nor any agent can switch a country on, and there is no endpoint to read the current setting from either. This returns the country the number belongs to, the Twilio console page that owns the setting, and the account SID that page has to be opened against, so a human can finish it in about thirty seconds. Costs nothing.
- call_scripts.listList call scripts
List this workspace's call scripts, with how many steps and objection handlers each one has. Read-only.
- call_scripts.getRead a call script
Read one call script in full — its ordered steps and its objection handlers. Read-only.
- call_scripts.for_callOpen the script for this call
Answer “which script is this live call on?” in one hop: the script the call opens with by default (taken from the call queue it was dialed from, then from the line it is on), every other script a rep could switch to, and the latest AI assist reading already taken on the call. This is exactly what the in-call script panel paints itself from. Read-only — it never speaks to the customer, changes nothing about the call, and unlike call_scripts.assist_reading it takes no NEW reading, so it costs nothing.
- call_scripts.createCreate a call script
Create an empty call script. Add its steps afterwards with call_scripts.add_step. Nothing is shown to a rep until the script has at least one step.
- call_scripts.updateEdit a call script
Rename a call script, change what it's for, switch it on or off for calls, or turn the AI assist on or off. Turning assist ON means the AI reads the live transcript of every call using this script and spends this workspace's own OpenRouter credit doing so; it never speaks to the customer. Omitted fields are left alone.
- call_scripts.deleteDelete a call scriptconfirm
Permanently delete a call script and every step and objection handler in it. This cannot be undone. Any call queue or phone number pointing at it simply stops offering a script.
- call_scripts.add_stepAdd a step or objection handler
Add one entry to a call script. A 'step' is appended to the end of the ordered path through the call; an 'objection' is a handler and must name which objection it answers.
- call_scripts.update_stepEdit a step or objection handler
Rewrite one entry of a call script in place. Supplying a field replaces it; the entry keeps its position.
- call_scripts.delete_stepDelete a step or objection handlerconfirm
Permanently delete one entry from a call script. This cannot be undone. The remaining entries keep their order.
- call_scripts.reorder_stepsReorder a call script
Set the order of a script's steps (or of its objection handlers) by giving their ids in the order you want. Pass every id of that kind — any left out keeps a stale position.
- call_scripts.assist_readingRead where a live call is in its script
Take one reading of a call in progress: which step of its script the conversation appears to be in, and whether the customer just raised an objection the script has a handler for. Reads the call's transcript only — it never speaks to the customer and changes nothing about the call. Requires the script to have assist switched on and the number to have transcription on. Uses and bills the workspace's own OpenRouter account.
templates
- templates.listList message templates
List the organization's reusable message templates — SMS bodies, email subject+body pairs, and ringless-voicemail scripts or recordings. These are what campaigns, automations, dispositions and bulk sends pick from.
- templates.getOpen a message template
Fetch one message template with its subject, body and — for a recorded voicemail — the audio URL.
- templates.createCreate a message template
Create a reusable SMS, email or ringless-voicemail template. Bodies may contain merge tokens like {{first_name}} or {{company.name}}, resolved per recipient at send time — call templates.list_merge_tokens for the full set. Creating a template sends nothing.
- templates.updateEdit a message template
Update a message template's name, subject, body, voice or recording. Editing a template changes what every campaign, automation and disposition using it will send from now on; messages already sent are unaffected. Omitted fields are left alone.
- templates.deleteDelete a message templateconfirm
PERMANENTLY DESTROY a message template. This cannot be undone, and any campaign, automation or disposition still pointing at it loses its message content.
- templates.list_merge_tokensList merge tokens
List every merge token a template body can use — the built-in contact, company and address fields plus one entry per contact custom field this organization has defined. Use these exact spellings; an unknown {{token}} renders as an empty string.
- templates.previewPreview a template
Render a template's subject and body with merge tokens resolved, either for a real contact or with the tokens left empty. Sends nothing — this is the preview shown in the template editor.
tracking
- tracking.overviewWebsite tracking
The Website tracking landing screen's headline numbers, for the WHOLE workspace rather than one site: total visitors ever seen across every tracked website, how many of them are matched to a CRM contact, and how many are on the sites right now. Also lists each tracked website with its own lifetime and live counts, split into the tenant's own installed sites and the system-owned 'Chirply pages' source. These are LIFETIME totals with no date window — tracking.stats answers a single site over the last 1–90 days and cannot reproduce these numbers, and tracking.live_visitors only ever answers 'in the last few minutes'. Read-only.
- tracking.list_sitesList tracked websites
List the organization's tracking sources, newest first. A source with hosted_pages=true is the system-owned 'Chirply pages' source for platform-hosted websites, funnels, invoices, payment pages, and receipts; it is tracked automatically and never needs an install snippet. Other sources are external websites that require the tracking script. Returns no visitor data.
- tracking.get_siteOpen a tracked website
Fetch one tracking source by id. External websites include their allowed origins and exact install snippet. The system-owned 'Chirply pages' source has hosted_pages=true and covers platform-hosted websites, funnels, invoices, payment pages, and receipts automatically; no snippet or allowed-origin setup is required.
- tracking.install_snippetCopy install snippet
For an EXTERNAL website, return the one-line <script> tag shown by the Copy button. Never instruct a user to install this on the system-owned 'Chirply pages' source: platform-hosted websites, funnels, invoices, payment pages, and receipts are tracked automatically and require no snippet.
- tracking.create_siteAdd a website
Register a website for tracking and mint its public embed key. Collection FAILS CLOSED: until at least one allowed origin is added (pass `origin`, or call tracking.add_origin), the script records nothing. Creating a site costs nothing and sends nothing.
- tracking.update_siteEdit tracking settingsconfirm
Rename a tracked website, pause or resume collection, or change how it behaves. Omitted fields are left alone. Setting status to 'paused' stops all collection immediately without the tenant editing their website's HTML. This covers every control on the site's settings screen EXCEPT session replay — screen recording is turned on and off through tracking.set_replay, which requires an explicit human approval.
- tracking.set_replayRecord sessions I can watch backconfirm
TURN SESSION RECORDING ON OR OFF for a tracked website. When on, the script captures what real visitors do on the tenant's pages — mouse movement, clicks, scrolling and DOM changes — and stores it so anyone on the team can replay the visit at tracking.get_replay. Everything a visitor types is masked in their browser before it is ever sent, and pages listed in the site's excluded paths are never recorded, but this is still the most privacy-consequential switch in the product: it is OFF BY DEFAULT and deliberately opt-in, and whoever turns it on is taking on whatever their own privacy policy and local law require them to disclose. Recordings are deleted automatically once they reach retention_days old (default 30). Turning it off stops new recordings immediately; recordings already captured are kept until they age out — delete those with tracking.delete_replay. It is split out of tracking.update_site precisely so it cannot be flipped as a side effect of editing some other setting.
- tracking.delete_siteDelete a websiteconfirm
Permanently delete a tracked website, along with every visitor and event recorded for it. The embed key stops working, so the script left on the site becomes a no-op. Contacts and their timeline entries are NOT deleted. This cannot be undone.
- tracking.list_originsList allowed websites
List the website addresses a tracking site is allowed to report from. An empty list means the script records nothing at all.
- tracking.add_originAdd allowed websiteconfirm
Allow a website address to report tracking data against this site. THIS GRANTS THAT DOMAIN WRITE ACCESS TO THIS ORGANIZATION'S CRM: anything served from that origin can create page views and, while 'Match form fills to contacts' and 'Add new people as contacts' are on, create real contact records. Add only hosts the tenant actually controls — a typo'd or attacker-supplied origin is a standing injection route into the CRM, and nothing else re-checks it. Add every host the script legitimately runs on: apex, www, and staging. Removing it again is tracking.remove_origin.
- tracking.remove_originRemove allowed websiteconfirm
Stop accepting tracking data from one website address. Takes effect on the next page view; already-recorded data is kept. Removing the last origin stops collection entirely.
- tracking.list_visitorsList browsers seen
List individual BROWSERS seen on a tracked website — the rows the app labels "Browsers seen" — most recently active first, with their page-view counts and first-touch attribution (first referrer, landing page, and campaign parameters). One row is one browser, so the same human on a laptop and a phone appears twice; for one row per resolved person use tracking.list_people, which is what the app's own Visitors list shows. Filter to a single contact, to one resolved person, or to only browsers that have been identified. Read-only.
- tracking.get_visitorOpen a browser seen
Fetch ONE BROWSER with its attribution and, optionally, its 50 most recent events — the 'what did this browser read?' view behind a contact record. This is a single browser, not the whole human: for everything one person has ever done across all their devices, use tracking.get_person, which is what the app's Visitor page shows. Read-only.
- tracking.list_eventsList website activity
List recorded website activity — page views, form fills, identifications, and custom events — newest first. Filter by site, visitor, contact, kind, or URL path.
- tracking.statsWebsite tracking stats
Headline numbers for a tracked website over a recent window: page views, unique visitors seen, how many were identified as contacts, and the busiest pages. Read-only.
- tracking.check_installTest installation
Check whether a tracked website's script is actually working, and explain why not if it isn't. Reports whether any beacon has ever arrived, when the last one did, and the specific blocker when one exists (collection paused, or no allowed website addresses so everything is being ignored). Read-only — it inspects what has already been received rather than fetching the site.
- tracking.live_visitorsLive visitors
Who is on the organization's tracked websites right now, plus the most recent visitors: the page each person is reading, how long that page and the current session have been open, how many pages are in the current session, when activity last arrived, and which visitors are known contacts. Returns everyone with a live open-tab lease followed by the most-recently-seen visitors up to the limit, regardless of how long ago they left. Read-only.
- tracking.list_peopleList visitors
List website visitors grouped as PEOPLE rather than browsers — one entry per resolved human, folding together every device and anonymous session stitched to them. Newest activity first, with each person's visit count, device count, and a link to their contact when identified. Read-only.
- tracking.get_personOpen a visitor
Fetch one resolved visitor (person) with their whole story across every device: totals, first-touch attribution, the individual browsers folded into them, known IP addresses, and their most recent page views and form fills. This is the 'everything this person has ever done on our sites' view behind a visitor row.
- tracking.merge_peopleMerge visitorsconfirm
Merge two resolved visitors (people) into one, for when they are really the same human seen as two and the automatic stitching missed it. Every browser, page view and form fill from the second is moved onto the first, their counts are recombined, and the second visitor is deleted. Contacts and their timelines are untouched. This cannot be undone.
- tracking.list_heatmap_pagesList heat maps
List the pages that have a click/scroll heat map, busiest first, with view and click counts, rage-click totals, and how far down the page half of visitors reached. Heat is kept separately per screen size (mobile/tablet/desktop) because a phone and a desktop render different layouts — filter by `device` to compare like with like. Read-only, and free.
- tracking.get_heatmapOpen a heat map
Fetch one page's heat map: which elements get clicked and how often (keyed by CSS selector, so it stays correct across screen sizes), the scroll-depth curve in 5% bands, and optionally the raw click density grid. Aggregate counts only — a heat map cannot be traced back to an individual visitor. Read-only, and free.
- tracking.reset_heatmapStart a heat map overconfirm
PERMANENTLY delete one page's heat map — every click position, element count and scroll sample for it. There is no per-click history behind these totals, so this cannot be undone and the data cannot be rebuilt. The honest use is after a redesign, when the old clicks describe a layout that no longer exists. Collection continues from zero on the next visit.
- tracking.list_replaysList session recordings
List session recordings — replayable captures of what a visitor did on a tracked page — newest first, with who it was (when identified), which page, how long, and whether the recording was cut off. Filter to one site, contact, or person. Recording is opt-in per site and off by default. Read-only, and free.
- tracking.get_replayOpen a session recording
Fetch one session recording's details — who, which page, how long, how big, and when it expires — plus the link to watch it. Does NOT return the recorded events themselves: a recording is megabytes of DOM mutations that only the player can render, and it is not something a model can usefully read. Read-only, and free.
- tracking.delete_replayDelete a session recordingconfirm
PERMANENTLY delete one session recording and the stored video-like event data behind it. Cannot be undone. Use it to honour a visitor's erasure request, or to drop a recording that captured something it shouldn't have.
white-label
- white_label.get_onboardingWhite-label setup checklist
List every step a white-label agency has to complete before its clients see nothing of Chirply — their own sign-in domain, a verified sending address of their own, the plans they sell their clients, connecting their own Stripe, the Zapier decision, the App Marketplace decision, their brand, and the app-reselling offer — with each step's live done/outstanding state, the wording a person reads on the same screen, and where to go to finish it. Also reports which banner the app shell is showing in this workspace right now — the setup reminder, the app-reselling offer, or neither; it is never both. Reads only: it changes nothing, costs nothing, and marks nothing done.
- white_label.get_reseller_apps_offerApp reseller offer
Read the offer that lets a white-label agency resell every app Chirply has published in the App Marketplace — unlimited, to as many client workspaces as they like, under their own brand. It costs $497 per month, or $250 per month for an agency that takes it inside its 7-day introductory window; an agency that took the introductory rate keeps it permanently. Returns the price THIS agency would pay, whether that is the introductory or the list price, how long their window has left, the live list of apps the licence covers today, and whether they have already answered. Reads only — nothing is bought and nothing is charged.
- white_label.buy_reseller_appsTake the app reseller offerconfirm
Take the app-reselling offer for this agency. THIS SPENDS REAL MONEY: it immediately starts a recurring subscription on the card saved to the owner's Chirply billing account at $497 per month, or $250 per month if the agency is still inside its 7-day introductory window — whichever applies is decided by the server, never by the caller, and the price is then locked to this agency permanently so it does not rise when the window closes for everybody else. In return the agency may resell every app Chirply has published in the marketplace, unlimited, to as many client workspaces as it likes, and any app Chirply publishes later is included. If there is no saved card, nothing is charged or activated and the owner can add one in Billing and retry without Chirply staff. Owner only.
- white_label.decline_reseller_appsPass on the app reseller offer
Record that this agency does not want the app-reselling offer for now. Costs nothing, charges nothing, and is not final — the offer stays available and can be taken later, though the introductory price is only held for the length of the agency's introductory window. Its purpose is to complete the optional 'decide about reselling our apps' step in the white-label setup, so an agency that does not want it stops being asked.
- white_label.get_managed_supportManaged support status
Report whether Chirply is answering this agency's clients' support tickets, and what that costs. By default a white-label agency answers its own clients' bug reports — they arrive in the agency's 'Client reports' inbox and never reach Chirply. This add-on hands the answering back to Chirply, who reply inside the client's workspace under the agency's brand. Returns the current status, the monthly price this agency pays (or would pay), whether the introductory rate still applies and when that window closes. Reads only: it changes nothing and charges nothing.
- white_label.start_managed_supportHave Chirply answer client supportconfirm
Hand this agency's client support over to Chirply. THIS SPENDS REAL MONEY: it starts a recurring subscription charged to the card already on the agency's Chirply account, at $497 per month, or $250 per month inside the 7-day introductory window — the server decides which applies and then locks that price to this agency permanently, so it does not rise when the window closes for everyone else. From then on, bug reports filed by the agency's client workspaces go to Chirply's support team instead of the agency's own inbox, and are answered inside the client's workspace under the agency's brand. Any of the agency's client tickets that are still open move across immediately; closed ones stay where they were answered. Owner only.
- white_label.stop_managed_supportStop Chirply-managed supportconfirm
Stop paying Chirply to answer this agency's clients, and take the support desk back. Cancels the recurring subscription immediately, so billing stops, and every still-open ticket filed by this agency's client workspaces moves back into the agency's own 'Client reports' inbox for them to answer; tickets Chirply already closed stay closed where they are. This is not free to undo: restarting later is a new purchase at whatever the price is on that day, and the introductory rate is NOT held. Owner only.
- white_label.list_reseller_app_pricesYour prices
List what this agency charges its own client workspaces for each Chirply-published app its reselling licence covers. An app with no price is INCLUDED — the licence already paid for it, so the agency's clients install it free. Prices set here are collected on the agency's own Stripe account; Chirply takes no cut of them. Reads only — nothing is charged.
- white_label.set_reseller_app_priceSave
Set what this agency's own client workspaces pay for one Chirply-published app that its reselling licence covers. The client is billed on the AGENCY's own Stripe account — Chirply neither collects the money nor takes a cut of it. This does not charge anybody by itself: it changes the price the agency's clients see in the marketplace from that moment on, and a client already on a subscription keeps the price they were sold at. Set 0 to list the app as free, or use white_label.clear_reseller_app_price to make it included again. Owners and admins only.
- white_label.clear_reseller_app_priceInclude it
Stop charging this agency's clients for one Chirply-published app, so it goes back to being included with their workspace at no cost — which is the reselling licence's default. Does not cancel a subscription a client is already on; it only stops the app being sold to new clients. Owners and admins only.
- white_label.give_app_to_clientInstall for a client
Install a Chirply-published app into one of this agency's own client workspaces at no charge, bypassing whatever price the agency's rate card sets for it. This is the other half of the reselling licence: a covered app may be sold to a client or simply handed to them. Nobody is billed and no Stripe account is touched — the client can use the app immediately. To charge for it instead, use white_label.sell_app_to_client.
- white_label.sell_app_to_clientBill a client for an appconfirm
Bill one of this agency's own client workspaces for a Chirply-published app at the price on the agency's rate card. THIS SPENDS THE CLIENT'S MONEY: Stripe immediately emails them a real invoice from the AGENCY's own Stripe account, and a recurring price starts a real subscription that bills them every period until somebody cancels it. The money goes to the agency and Chirply takes no cut. The amount is read from the agency's rate card and can NOT be set here. The client owns the app once the invoice is paid, not before. To hand the app over free instead, use white_label.give_app_to_client.
- white_label.getWhite-label settings
Read the agency's white-label configuration: brand display name, logo URL, favicon URL, primary/accent colors, every connected custom domain with its SSL and verification state, and the verified default identity used for branded authentication email.
- white_label.update_brandingSave brand
Set the agency brand applied across the app for this workspace and every sub-account under it — display name, logo, favicon, and primary/accent colors. The logo and favicon are given here as hosted https:// URLs (in the app you can instead upload an image file and it becomes such a URL). Any field left out is cleared; on a custom domain the organization name and a neutral favicon are used instead of exposing the platform brand.
- white_label.get_appBranded app
Read the agency's installable app: its name, home-screen label, icon, splash colors, and whether it is switched on. Clients install it from the agency's own custom domain — it carries the agency's branding and involves no app store, no developer account and no review. It is only offered on a custom app domain; the platform's own host never presents one.
- white_label.update_appSave branded app
Configure the installable app the agency's clients add to their home screen or dock from the agency's custom domain. Every field is optional and falls back to the agency's existing brand — the app is named after the white-label display name, and painted in the primary color, unless overridden here. Setting `enabled` to false withdraws the app: existing installs keep working but stop being offered. Fields left out are cleared and fall back to those defaults.
- white_label.get_app_marketplaceApp Marketplace for your clients
Read whether the client sub-accounts under this agency are offered the App Marketplace. Returns the effective answer, whether it was set deliberately or is still the default, and the default itself. The agency's OWN workspace always has the marketplace and is not affected by this setting. Read-only; changes nothing.
- white_label.set_app_marketplaceLet client sub-accounts use the App Marketplaceconfirm
Decide whether the client sub-accounts under this agency can browse and install apps from the App Marketplace. It is OFF for them by default, because the marketplace is the PLATFORM's storefront rather than the agency's: listings are published by other agencies, some apps carry the platform vendor's name, and the purchase and developer pages use the vendor's wording. TURNING IT ON MAY THEREFORE EXPOSE THE PLATFORM'S BRAND TO THE AGENCY'S OWN CLIENTS — which is the thing a white label exists to prevent — so treat it as a branding decision, not a feature flag. It changes every client workspace under this agency at once. The agency's own workspace always keeps the marketplace and is unaffected either way. Switching it OFF hides the storefront only: apps a client already installed keep running, keep their access tokens, and are never uninstalled or refunded.
- white_label.list_domainsList custom domains
List the custom hostnames this agency serves the app from, with Cloudflare provisioning status, SSL state, and the CNAME target their DNS must point at. DEPRECATED: domains.list returns all of the workspace's domains, including these.
- white_label.connect_domainConnect a custom domainconfirm
Point a custom hostname at this workspace. Registers a Cloudflare custom hostname and issues an SSL certificate; the domain stays 'pending'/'verifying' until DNS is CNAMEd at the returned target. This changes where a real, public hostname resolves.
- white_label.remove_domainRemove a custom domainconfirm
Disconnect a custom hostname and delete its Cloudflare custom hostname. Anyone still visiting that domain stops reaching the app.
widgets
- widgets.listList click-to-call widgets
List the organization's embeddable click-to-call widgets — the floating button that rings agents and bridges a website visitor without exposing anyone's number.
- widgets.getOpen a widget
Fetch one CLICK-TO-CALL widget with everything its builder shows: behavior and appearance settings, business hours, the agents it rings, the sites it's allowed to appear on, and the embed snippet. Click-to-call only — a live-chat widget's id returns not-found here, because its settings are a different shape entirely; read those with live_chat.get_widget.
- widgets.createCreate a widget
Create a draft click-to-call widget with default copy and weekday 9–5 availability, and mint its public embed key. It renders nowhere until you add allowed sites and publish it.
- widgets.updateEdit a widget
Update a widget's name, dialing behavior, which devices it shows on, and its appearance/copy settings. If the widget is published these changes are visible on the tenant's live site immediately. Omitted fields are left alone.
- widgets.publishPublish a widgetconfirm
MAKE THIS WIDGET LIVE ON THE TENANT'S WEBSITE. Once published, the embed snippet renders the floating button to real visitors on every allowed origin, and a visitor pressing it will originate real calls or texts to the configured agents. Add allowed sites first — an unpublished or origin-less widget renders nowhere.
- widgets.unpublishUnpublish a widgetconfirm
Take a widget off the tenant's website. The button stops rendering for visitors and the public config API stops responding for it, even where the embed snippet is still installed. Configuration is kept — publish again to restore it.
- widgets.deleteDelete a widgetconfirm
PERMANENTLY DESTROY a widget along with its schedule, agent routing, allowed origins and visitor session history. Any embed snippet still installed on the tenant's site stops working. This cannot be undone.
- widgets.embed_snippetGet the embed snippet
Get the one-line script tag that installs a CLICK-TO-CALL widget — paste it into the site's HTML just before </body> — plus the widget's standalone iframe URL. The button only appears on origins added with widgets.add_origin. This returns the click-to-call loader (/embed/w.js and /w/<key>); a live-chat widget needs a DIFFERENT loader and would be inert with this one, so a chat widget's id returns not-found here — use live_chat.embed_snippet for those.
- widgets.save_scheduleSet widget business hours
Set the timezone and per-day availability window for a widget. Outside these hours the widget offers a scheduled callback instead of ringing agents.
- widgets.add_originAllow a site
Allow a website to embed a widget. The widget only renders (and its public config API only responds) on origins in this list, so add every host it should appear on — apex, www and staging.
- widgets.remove_originRemove an allowed siteconfirm
Stop a widget from rendering on a site. THIS BREAKS A WORKING INSTALL: the button vanishes from that website for real visitors on the loader's next refresh — the embed snippet is still in their HTML, so it looks installed and simply does nothing — and its public config API stops answering for that origin. Visitors on that site can no longer request a call or text. Re-adding the origin with widgets.add_origin restores it.
- widgets.add_agentAdd a widget agent
Add someone for a widget to ring: either a team member (rung through their browser softphone) or an arbitrary phone number. Agents are rung in the order they were added when the dial strategy is sequential.
- widgets.remove_agentRemove a widget agent
Stop a widget ringing a particular team member or number. Removing the last agent leaves the widget with nobody to connect visitors to.
wordpress_sites
- wordpress_sites.catalogView WordPress hosting options
Lists live DigitalOcean regions, the WordPress plans with current monthly server prices, and every domain visible through the workspace's connected Cloudflare account.
- wordpress_sites.check_domainCheck WordPress domain
Checks whether a hostname belongs to the workspace's connected Cloudflare account and lists existing A, AAAA, or CNAME records that a WordPress launch would replace. Makes no DNS changes.
- wordpress_sites.listList WordPress sites
Lists this workspace's DigitalOcean WordPress sites, including server, DNS, HTTPS, provisioning, and cost status. Never returns passwords or OAuth tokens.
- wordpress_sites.getOpen WordPress site
Fetches one DigitalOcean WordPress site's safe operational status and provisioning history. Passwords and provider credentials are never returned.
- wordpress_sites.launchLaunch WordPress siteconfirm
CREATES BILLABLE INFRASTRUCTURE in the workspace's own DigitalOcean account. Creates a Droplet, paid backups when enabled, a cloud firewall, hardened WordPress, database, Redis, automatic updates, DNS records when authorized, and HTTPS. DigitalOcean bills the connected team until the site is destroyed.
- wordpress_sites.retryRetry setupconfirm
Resume a stalled or failed WordPress launch. CAN CREATE BILLABLE INFRASTRUCTURE: if the launch never got as far as creating its server, this CREATES THE DIGITALOCEAN DROPLET (plus paid backups when the site was configured with them), which DigitalOcean bills the connected team for until the site is destroyed. Only when the site already has a server does it merely re-run the bootstrap, DNS and HTTPS checks, adding no new charge — and in that case it may still write the site's DNS records at the configured hostname. Check wordpress_sites.get first: a site with no server id is the billable case.
- wordpress_sites.destroyDestroy siteconfirm
PERMANENTLY DELETES the WordPress Droplet and its managed DigitalOcean firewall, stopping future server and backup charges. The website, database, and files on that server cannot be recovered unless an independent snapshot exists.
workspace
- workspace.exportExport workspace dataconfirm
Produces a downloadable ZIP archive of this workspace's own business data — contacts, companies, deals, tasks, appointments, conversations and full message bodies, call metadata and transcripts, invoices and orders, forms and their responses, custom objects, the activity log and the unsubscribe/do-not-call lists — as one CSV per dataset plus a manifest.json listing row counts and everything deliberately left out. Returns a signed download URL that expires in 15 minutes, NOT the rows themselves; fetch the URL to get the file. Nothing is changed or deleted. The archive never contains provider credentials, API keys, encrypted columns, customer-facing bearer tokens, or any other workspace's data.
- workspace.list_export_datasetsList exportable datasets
Lists every dataset a workspace export can contain — the key to pass to workspace.export, the file name it lands under, its columns, and a plain-language description — together with the list of things deliberately excluded from any export and why. Reads nothing from the database and changes nothing.
- workspace.request_deletionRequest deletion of this workspaceconfirm
Asks Chirply to delete this entire workspace and everything in it — every contact, conversation, call recording, campaign, funnel and file — after a 90-day waiting period, as described at chirply.io/legal/data-deletion. This does NOT delete anything now: it records the request, starts the clock, and can be withdrawn at any time before the window closes using workspace.cancel_deletion_request. When the window closes the deletion is permanent and there is no recycle bin. Records Chirply must keep to meet a legal, tax or accounting obligation are retained or anonymized rather than deleted — invoices, payments, payouts, signed agreements and do-not-contact entries. Export anything you want to keep first. Owner only.
- workspace.deletion_request_statusCheck the workspace deletion request
Reports whether this workspace has an open deletion request, when it was raised, whether Chirply has confirmed it, and the earliest date the workspace could be deleted. Reads only — it changes nothing and starts no countdown.
- workspace.cancel_deletion_requestCancel the workspace deletion requestconfirm
Withdraws an open request to delete this workspace, so nothing will be deleted. Safe to call at any point before the deletion actually happens; after that there is nothing to cancel. Any provider integrations that were revoked when the request was raised stay revoked and have to be reconnected — reconnecting is a normal action in Settings → Integrations. Owner only.

Chirply