- Brand
- Unknown
- Category
- Pending
- Primary Subcategory
- Pending
Integration details
Description
Connect ChatGPT to your Chili Piper account to review and manage meetings, lead routing, and scheduling in plain language. Ask questions like "How many meetings did each rep get last week?", "Why didn't this lead book?", or "Why does this rep have no availability?" and get answers from your live Chili Piper data. What you can do: • Meetings: list, export, and inspect meetings, including how each one was booked and routed. Mark no-shows or cancel. • Routing: review Concierge, Distro, and Handoff routers, rules, and distributions. Audit for coverage gaps and adjust round-robin weights. • Scheduling: check rep and team availability, and manage meeting types and scheduling links. • Users and teams: look up users, manage workspace and team membership, and onboard or offboard reps. • Chat AI: review conversation logs and routing outcomes. Includes built-in skills for common admin workflows: meeting diagnostics, routing audits, no-show analysis, availability checks, and user onboarding and offboarding. Skills that make changes show you a plan and ask for confirmation first. Requires a Chili Piper account. Sign in with a Chili Piper Admin login.
- Integration type
- Plugin
- Verification status
- Not applicable
- Platform
- ChatGPT
- Category
- Pending
- Primary Subcategory
- Pending
- Secondary Subcategories
- None listed
- Brand
- Unknown
- Access
- Account required
- First tracked
- 2026-10-31
- Tool count
- 202
- Market
- US
The broad Category that contains the Primary Subcategory.
The Primary Subcategory used for this profile’s headline score.
Other Subcategories where the Integration is listed.
Your score is coming
ChatGPT now suggests Plugins on its own when they match a user's request.Your Plugin Discovery Score measures how often yours appears, and it will show here as soon as it’s ready.
What discovery looks like

Get alerts for Chili Piper
Get updates when Chili Piper’s Discoverability Score or category rank changes.
Competitive lineup
202 tools agents can invoke
Turns a Distro router on (inactive → active) so it starts routing records. This is the step that makes a distro-router-create'd router (published but INACTIVE) live. Idempotent — activating an already-active router is a no-op. → {id, workspaceId, name?, description?, status, trigger: {objectType, eventTypes: [{type, ...}], evaluation?, delay?}, routing: {known, representable, rows, catchAll?, routingSteps}} Note: status is one of {type:"Active"} | {type:"Inactive"} | {type:"Activating"} | {type:"Deactivating"} | {type:"Error", message}; activation completes asynchronously, so status may read Activating until the backend settles it to Active ⚠ activation is REJECTED (422) if the backend validation fails (e.g. the trigger object type is not enabled for the workspace) — the router stays inactive; fix the cause and retry see: distro-router-create (creates a router inactive), distro-router-deactivate (turn it off), distro-router-get (check status)
distro-router-activate
Returns the admin-UI deep link for a meeting's activity page — a URL you can open to inspect the meeting in Chili Piper reporting. → {url: "https://{subdomain}.chilipiper.{tld}/fire/admin/workspaces/{workspaceId}/reporting/meetings-activity/{meetingId}"} see: meeting-get (find a meetingId), crm-activity (activity link by CRM id instead)
meeting-activity
Returns the admin-UI deep link for the Chili Piper meeting linked to a CRM event — the CRM-keyed twin of meeting-activity, for jumping from a Salesforce record straight to the meeting's activity page. Accepts a 15- or 18-char Salesforce EventId or equivalent. → {url: "https://{subdomain}.chilipiper.{tld}/fire/admin/workspaces/{workspaceId}/reporting/meetings-activity/{meetingId}"} see: meeting-activity (if CP meetingId already known)
crm-activity
Adds a prospect to a Salesloft cadence, resolving the prospect and assignee against Salesloft. The prospect is referenced by email and/or CRM record id (email is preferred when both are given); the assignee defaults to the prospect's owner and the cadence step defaults to the cadence's first step. Confirm-first by design — run it after a rep has confirmed the action, since it enrolls the prospect in a live cadence. - cadenceId (required): Salesloft cadence id to add the prospect to - prospectEmail / prospectCrmId (at least one required): who to add; email is preferred when both are supplied - assigneeEmail / assigneeCrmId (opt): the Salesloft user to own the membership; defaults to the prospect's owner - cadenceStepId (opt): step to start on; defaults to the cadence's first step → {prospectId, userId, cadenceId} Note: ids are the Salesloft-resolved prospect, assignee user, and cadence ⚠ writes to Salesloft: this enrolls the prospect in an active cadence and may trigger outreach — use confirm-first ⚠ 409 edge.salesloft-prospect-already-in-cadence if the prospect is already enrolled (nothing changes); 404 edge.salesloft-prospect-not-found if no prospect matches; 422 edge.salesloft-request-rejected if Salesloft rejects the request (e.g. invalid cadence or step, cadence has no steps, assignee does not own the cadence); 422 edge.salesloft-not-connected if Salesloft is not connected see: integration-find-users (per-user integration connection status)
integration-salesloft-add-to-cadence
Adds users to a team so they become routing targets for distributions scoped to it. Already-members are silently skipped. → {id, workspaceId, name, members, metadata} — updated team record see: team-list (find teamId and verify after), user-find (resolve emails to userIds)
team-add-users
Grants users access to a workspace so they can use its teams, links, and distributions. Already-members are silently skipped. → {} see: workspace-list (find workspaceId), user-find (resolve emails to userIds), workspace-list-users (verify after)
workspace-add-users
Nudges a distribution and publishes immediately, MERGING rather than replacing — the preferred adjust path (v1 distribution-adjust lacks assignmentTypeConfig and teamId). `weights` and `manualCalibration` are per-userId DELTAS added to current values; assignees you omit are untouched. Use this to tweak a live distribution without restating its whole config; use distribution-update-v3 to overwrite. `resetDistribution` (default false) zeroes counters first; `teamId` moves the distribution to another team, effective immediately. - assignmentTypeConfig (opt): discriminated by `type` (see distribution-update-v3 for full structure): {type: "Record", handling: {type: "WorkingHours"}|{type: "Strict"}} {type: "Meeting", handling: {type: "Flexible"|"Strict", ...}, limits: {type: "MeetingLimitUnset"}|{type: "MeetingLimitSet", ...}} {type: "Conversation"} → {} ⚠ live immediately see: distribution-list (get distributionId and verify after), team-list (find teamId), distribution-update-v3 (assignmentTypeConfig full structure)
distribution-adjust-v3
Links an existing reminder to a team meeting type so it starts sending it, and returns the updated meeting type. The reminder must live in the same workspace (create one with meeting-type-reminder-create). Idempotent: re-attaching an already-attached reminder is a no-op. → {id, workspaceId, name, description?, inviteTitle, inviteDescription, status, isActive, duration, location: {default: {type, index, host?, value?}, others: [{type, index, host?, value?}]}, buffers: {before?, after?}, meetingLimit?: {limitBy: "Email"|"Domain", timeframe: "Hourly"|"Daily"|"Weekly"|"Monthly"|"Yearly", count}, sharedWith?: {type: "Workspace"}|{type: "Teams", teamIds}, reminders: [{id, workspaceId, channel, trigger: {kind, offset?}, name, title?, body, sendTo?, sendFrom?, smsSendFrom?}], availability?: {minimumNotice, bookingWindow, slotIncrement: {type: "Dynamic"}|{type: "Fixed", every}}, schedule?: {type: "HostWorkingHours"}|{type: "Fixed", workingHours: {monday..sunday: [{start, end}]}, timezone?, forceGuestTimezone, activeFrom?, activeTo?}, assigneesMeetingLimit?: {type: "NoMeetingLimit"}|{type: "SetMeetingLimit", numberOfMeetings, reset: {type: "Daily"|"Weekly"|"Monthly", every, startsOn, timezone, resetLocalTime?, resetDayOfWeek?, resetDay?}}} ⚠ takes effect immediately see: meeting-type-reminder-create (make a reminder first), meeting-type-get (confirm the association), meeting-type-detach-reminder (remove it)
meeting-type-attach-reminder
Assigns multiple concierge router items to a folder, or clears their folder membership when folderId is omitted (null), in one request — last-write-wins. → no payload on success see: concierge-folder-list (find a folderId), concierge-folder-move (assign a single concierge router)
concierge-folder-move-bulk
Assigns multiple meeting type items to a folder, or clears their folder membership when folderId is omitted (null), in one request — last-write-wins. → no payload on success see: meeting-type-folder-list (find a folderId), meeting-type-folder-move (assign a single meeting type)
meeting-type-folder-move-bulk
Assigns multiple rule items to a folder, or clears their folder membership when folderId is omitted (null), in one request — last-write-wins. → no payload on success see: rule-folder-list (find a folderId), rule-folder-move (assign a single rule)
rule-folder-move-bulk
Cancels the Chili Piper meeting linked to a CRM event (v2 POST variant) — the CRM-keyed twin of meeting-cancel-post. Resolves the CRM id (15- or 18-char Salesforce EventId or equivalent) to its meeting, cancels it, and returns the updated meeting record. → {meetingId, meetingStatus: "CANCELLED", ...} ⚠ irreversible; may email attendees; 404 if the CRM id has no linked meeting see: crm-get (verify before/after), meeting-cancel-post (if CP meetingId already known), crm-cancel (v1 GET variant)
crm-cancel-post
Permanently cancels a meeting (v2 POST variant). Preferred for programmatic API consumers over the v1 GET cancel — same effect, but uses POST semantics: no redirect parameters, returns the updated meeting record on success. → {meetingId, meetingStatus: "CANCELLED", ...} ⚠ irreversible; may email attendees see: meeting-get (verify status before/after), meeting-cancel (v1 GET variant for browser redirect flows), crm-cancel-post (cancel by CRM id instead)
meeting-cancel-post
Batch, side-effect-free check of whether each user has configured their availability. A user is "configured" when they have at least one custom schedule, or their default schedule's working hours / timezone differ from the bootstrap default (9-5, timezone synced from calendar). Users with no persisted schedule (and users not present in the request) are returned as false; reading does not create a default schedule for users that lack one. - userIds: the users to check. → [{userId, availabilityConfigured}] see: user-find (resolve userIds), availability-slots-v2
availability-configured
Books a meeting directly — no scheduling-link-init / handoff-init session needed. One host user, one of that host's own personal meeting types, a start time and a primary guest. Sends calendar invites and confirmations like any other booking. - hostUserId (req): the single host; get it from user-find. - meetingTypeId (req): a personal meeting type owned by hostUserId (personal-meeting-type-list with workspaceId = the host's personalWorkspaceId from user-find). Team/admin meeting types are rejected. - startTime (req): ISO-8601. Must be a free slot for the host (check with availability-slots-v2); duration comes from the meeting type. - guestEmail (req), guestFirstName / guestLastName (opt): the primary guest. Names fill the guest form's first/last name fields; personal meeting types require them by default. - additionalGuests (opt): extra invitees, [{email, required?}]. - formData (opt): other guest form answers keyed by data-field reference. - location (opt): overrides the meeting type's default location. - force (opt, default false, HTTP/CLI only): true books over busy time, bypassing ALL availability checks. Needs the scheduling-links.force-schedule permission on the API key. Not available over MCP: the MCP tool always books availability-checked. → Success{meetingId} or Failure{reason} Note: an unavailable slot returns Failure with the reason, not an error. Note: meeting-get can report the new meetingId as not found for a few seconds after Success; retry, do not book again. ⚠ Limits: single host and personal meeting types only. For admin one-on-one, round-robin, group or ownership meeting types use scheduling-link-init + scheduling-link-schedule. Invites are sent immediately — to undo, call meeting-cancel-post. see: availability-slots-v2 (find free times), personal-meeting-type-list (meetingTypeId), user-find (hostUserId), meeting-get (view the booked meeting)
meeting-create
Creates a new, empty team workspace. Names need not be unique — check workspace-list first to avoid duplicates. The new workspace has no members, teams or managers. → {id, name, emoji, logo, metadata} Note: id is the new workspaceId. see: workspace-add-users (add members), team-create (add teams), workspace-list (check for an existing workspace first)
workspace-create
Creates an admin (one-on-one) scheduling link — each booking gets a single fixed host, drawn from the sharedWith scope. - slug: URL slug (lowercase letters, digits, hyphens, underscores). - sharedWith (opt): who can host, {type: "Workspace"} (default) or {type: "Teams", teamIds}. - hostMeetingLimit (opt): defaults to NoMeetingLimit; caps meetings booked through this link per host — {type: "NoMeetingLimit"} or {type: "SetMeetingLimit", maximumMeetingsCount, reset}; reset is one of: {type: "Daily", every, startsOn, resetLocalTime: "00:00", timezone} {type: "Weekly", every, startsOn, resetDayOfWeek: "MONDAY".."SUNDAY", timezone} {type: "Monthly", every, startsOn, resetDay: 1..28, timezone} Note: every is the period length (1 = every day/week/month); startsOn (a date) anchors multi-period cycles. → {workspaceId, linkId, name, slug, meetingTypeIds, bookingUrl (null), bookingUrlTemplate, hostMeetingLimit?: {type: "NoMeetingLimit"}|{type: "SetMeetingLimit", maximumMeetingsCount, reset: {type: "Daily"|"Weekly"|"Monthly", every, startsOn, timezone, resetLocalTime?, resetDayOfWeek?, resetDay?}}} Note: bookingUrl is null — replace {userSlug} in bookingUrlTemplate with a host's slug, or list with hostUserId. see: scheduling-link-list-admin-one-on-one (browse existing)
scheduling-link-create-admin-one-on-one
Creates the column definition (schema) for an assignment table. This must exist before any rows are written (via assignment-table-upsert). Declare the input columns (variables the rule matches on) and output columns (each fixed to a context: Record for distro, Meeting for concierge/handoff, Conversation for chat). Column keys are generated from names; reference them later when writing rows. - workspaceId (req): the workspace to create the table in. - assignmentTableId (req): the id to create the table under (path). - name (req): human-readable table name. - inputs (req): at least one input column — {name, required?, typeHint?}. typeHint is one of {type: "StringColumnType"|"NumberColumnType"|"BooleanColumnType"} or {type: "ArrayColumnType", ofType?}. - outputs (opt): output columns — {name, context, required?}; context is one of "Record"|"Meeting"|"Conversation". → {id, name, inputs, outputs, allKeys, metadata: {revision: 0, ...}} see: assignment-table-upsert (write the rows), assignment-table-definition-replace (change the schema)
assignment-table-definition-create
Creates a Concierge router and publishes it live in one step — there is no unpublished-draft state via the API. workspaceId must be a team workspace of this tenant (400 otherwise). The URL slug is derived from name on publish (there is no separate slug field). Triggers are a PRODUCT of optional kinds — set a webform (either form for a Chili-hosted form OR thirdPartyForm for an external form — mutually exclusive), inAppButton, and routerLink in any combination; supplying NONE auto-generates a minimal email-only Chili webform. A routerLink is what gives the router a shareable Router Link URL. branding and localizations are optional. Creating and publishing a router does NOT require a legacy per-user Concierge product licence — Concierge access is governed by the tenant's GBB tier (e.g. the Experiences tier includes it); to check a caller's entitlement read their tier and concierge/conciergeLive flags via user-read or user-find. - routing (req): ordered rules evaluated top-down, plus an optional catch-all fallback. Each row's rule, when it matches, runs that row's `outcome`; the catch-all `outcome` runs when no row matches: {routes: [{ruleId, outcome}], catchAll?: outcome} outcome (one of): {type: "Schedule", assignment, meetingTypeId, additionalMeetingTypeIds?, timeout?, crmActions?, redirectAfterBooking?} {type: "Redirect", url} Schedule assigns the lead and books a meeting type. assignment is {type: "Distribution", distributionId} (round-robin / ownership / whatever the distribution does) or {type: "User", userId} (a specific host). meetingTypeId is the meeting type booked on this path; additionalMeetingTypeIds (opt) offers further meeting types on the same path (a group of options the guest picks from) — the booked set is meetingTypeId followed by them, in order. timeout (opt) {minutes, onTimeout} sets the no-show fallback; onTimeout is {type: "Landing"} or {type: "Url", url}; omit timeout for the default (10 min → Landing). crmActions (opt) is an ordered post-booking chain (ConvertLead is rejected with 400 — Concierge does not execute it; use a Distro router's convertLead), each {type: "AddToCampaign", campaignId, memberStatus} (add the matched Salesforce lead/contact to a campaign with that member status; find campaignId with campaign-list / campaign-search), {type: "SalesforceUpdateFields", contact:[{object,field,value}], lead:[{field,value}]} (write field values onto the matched Salesforce contact/lead), {type: "HubspotUpdateFields", contact:[{object,field,value}]} (write property values onto the matched HubSpot record), {type: "SalesforceUpsertRecord", settings:{noMatch,whenMatch}} (create/update the matched Salesforce lead/contact), or {type: "HubspotUpsertRecord", settings:{noMatch,whenMatch}} (create/update the matched HubSpot contact), {type: "SalesforceUpdateOwnership", contact:[{object,field}], lead:[{field}]} (set the record owner to the booked host), or {type: "HubspotUpdateOwnership", contact:[{object,field}]} (set the record owner to the booked host), {type: "SalesforceCreateEvent", relatedTo?, meetingCancellationBehavior?, guestsBehavior?} (create a Salesforce Event for the booked meeting; relatedTo links it to a CRM object — {type: "Account"|"Opportunity"|"Case"}, {type: "Campaign", campaignId}, {type: "ExplicitObject", id}, {type: "NoRelationNeeded"} or {type: "RelationDisabled"} — omit for no relation; meetingCancellationBehavior DeleteEvent|DoNothing; guestsBehavior CreateEvents|DoNothing), or {type: "HubspotCreateEngagement", relatedTo?, owner?, meetingCancellationBehavior?} (create a HubSpot meeting engagement; relatedTo {type: "Company"|"Deal"|"Ticket"}; owner Assignee|Booker). At most one action of each type per chain — a duplicated type is rejected (400); put all rows of one kind in a single action. A Slack Notify cannot be written here: the Concierge backend does not allow it after a booking (it may still appear on reads). redirectAfterBooking (opt) {url, forwardFormDataAsQueryParams?, delaySeconds?} sends the visitor to url after they book (the app's "Redirect To" step on the Scheduled path); forwardFormDataAsQueryParams appends the form values as query parameters (default false); delaySeconds is how long the booked page shows first, 0 = at once (default 5). Redirect sends the lead to a URL instead of booking. Every redirect URL (Redirect, timeout onTimeout, redirectAfterBooking) must be http(s) or scheme-less; other schemes such as javascript: are rejected (400). ruleId is required on every row; an always-match route is the catchAll. catchAll is optional — a router with no catch-all simply has no fallback when no row matches. Provide at least one row or a catchAll (an empty matrix with neither is rejected). - form (opt): the Chili-hosted guest form fields the lead fills in; MUST include the email field (PersonEmail) or the create is rejected. Omit for a default email-only form: [{dataField, label, description?, required?, hidden?}] Note: dataField is a CRM data-field reference — a default field name (e.g. PersonEmail, PersonFirstName) or a custom-field UUID; a made-up name is rejected (400). label is the field's display name; required toggles whether the guest must fill it. hidden (opt) is a PREFILLED value (a string) stored on the field but NOT shown to the guest — it is a value, not a show/hide flag; omit it for a normal visible field. - thirdPartyForm (opt): a THIRD-PARTY (external) webform — a form hosted on the customer's own website (e.g. an embedded HubSpot/Marketo form) that posts to Chili Piper; the mapping from each external form field name to a CP data field. MUST include the email field (PersonEmail). Mutually exclusive with form (a router has a single webform): [{formFieldName, dataField, label?}] Note: formFieldName is the external form field's submitted name (e.g. "0-1/email"); dataField is a CRM data-field reference (as for form); label (opt) is a display label for a programmatic field name. A third-party form's enrichment waterfall is not settable via the API (preserved if already configured in the Concierge app). - inAppButton (opt): an in-app-button trigger — the data fields the button collects; MUST include the email field (PersonEmail): [{dataField}] - routerLink (opt): a router-link trigger — gives the router a shareable Router Link URL; the fields its form collects; MUST include the email field (PersonEmail): [{dataField, label, required?, hidden?}] Note: dataField/label/required/hidden are as for form. A router-link field's enrichment waterfall is not settable via the API (preserved if already configured in the Concierge app). - branding (opt): the booking page's cover/heading/language — {coverImage?, headingText?, language?} - localizations (opt): per-language text overrides — {"<lang-tag>": {"<key>": "<text>"}} - routingSteps (opt): ordered PRE-ROUTING steps run before the routes are evaluated — enrichment and spam-check. Each is one of: {type: "Enrichment", waterfalls: [{dataField, waterfallId}], timeoutSeconds?} {type: "SpamCheck", writeSpamScoreToDataField, onSpam} Note: Enrichment runs data waterfalls and writes each result into a CRM dataField (waterfalls must be non-empty); waterfallId is an opaque id passed through unvalidated; timeoutSeconds (opt) is a whole-second cap. SpamCheck scores the lead and, when writeSpamScoreToDataField is true, writes the spam score to the default spam data-field; onSpam is the outcome a spam-flagged lead follows — a route outcome of the same shape as a row's outcome ({type: "Schedule", ...} or {type: "Redirect", url}). Steps run in the given order, before any row. At most one step of each type; when both are present SpamCheck must come first ([SpamCheck, Enrichment]) — a duplicate or the reverse order is rejected (400). → {id, workspaceId, name?, slug?, routing: {known, representable, rows: [{ruleId?, ruleType?, outcome}], catchAll?: {outcome}}, form?, thirdPartyForm?, inAppButton?, routerLink?, branding?, localizations?, routingSteps: [step]} Note: the returned routing is the lossy per-row summary (same shape as concierge-router-get), not the matrix you sent; form/thirdPartyForm/inAppButton/routerLink/branding/localizations/routingSteps echo the created config (same shape as concierge-router-get) ⚠ create is all-or-nothing: if the final publish step rejects the router, the router is deleted again and a typed 422 error with the backend reason is returned — nothing is left behind, so fix the request and retry ⚠ every distributionId the request names must exist in this tenant — an unknown one is rejected (404) before anything is written see: concierge-list-routers (find a workspaceId), rule-list (find ruleIds), distribution-list (find distributionIds), user-find (find userIds), meeting-type-list (find meetingTypeIds), concierge-router-update (replace config afterwards)
concierge-router-create
Creates a concierge router folder (asset group) in the workspace. The folder starts empty; assign concierge router items to it with concierge-folder-move / concierge-folder-move-bulk. → {id, name, displayOrder, itemCount: 0, revision} see: concierge-folder-move (assign a concierge router to the new folder), concierge-folder-list (list folders)
concierge-folder-create
Creates a new custom data field and publishes it in a single call — the underlying draft/publish steps are handled internally. Request body: {label, description?, objectType: "Person"|"Company"|"DeanonymizedCompany"|"DeanonymizedPerson", dataType, mappings?: [...], smartParameters?: ["email", ...]} → {reference, objectType, label, dataType, mappings: [...], smartParameters: [...]} Note: dataType list variants (Picklist/RadioButton/Checkbox) require a non-empty values list. smartParameters are lowercase slugs, each unique across the tenant's fields. see: data-field-update (edit it), data-field-set-smart-parameters (change aliases), data-field-list (find references)
data-field-create
Creates and immediately publishes a distribution — a reusable assignment pool that routing rules and round-robin scheduling links reference. `name` is required: a nameless distribution is a legacy (v1) one that never appears in distribution-list and cannot be used until named. `assignmentTypeConfig` fixes what it distributes (records, meetings, or conversations). Tune weights/caps afterwards with distribution-update-v3 or distribution-adjust-v3. - assignmentTypeConfig (req): discriminated by `type`: {type: "Record", handling: {type: "WorkingHours"}|{type: "Strict"}} {type: "Meeting", handling: {type: "Flexible", reassignmentType: "SameUser"|"AnyTeamMember", allowPickingAssignee: bool} |{type: "Strict", hideAssignee: bool}, limits: {type: "MeetingLimitUnset"} |{type: "MeetingLimitSet", userCaps: [{userId, limit}], resetPeriodicity: <meeting-limit reset>}} {type: "Conversation"} - weights (opt): [{userId, weight}]; manualCalibration (opt): [{userId, count}] - capping (opt): {userCaps: [{userId, limit}], resetPeriodicity: <assignment-cap reset>} - reset shapes: two different models are both named Daily/Weekly/Monthly; do not mix their fields: meeting-limit reset — only for limits.resetPeriodicity of a Meeting config (same shape as availability meeting limits): {type: "Daily", every, startsOn (YYYY-MM-DD), resetLocalTime (HH:MM), timezone} {type: "Weekly", every, startsOn, resetDayOfWeek ("MONDAY".."SUNDAY"), timezone} {type: "Monthly", every, startsOn, resetDay (1-28), timezone} assignment-cap reset — only for capping.resetPeriodicity: {type: "HourlyRolling"}|{type: "Daily", timeZone}|{type: "Weekly", timeZone, dayOfWeek (1-7)}|{type: "Monthly", timeZone} Note: every = reset every N days/weeks/months, counted from startsOn. Casing differs: timezone (meeting limit) vs timeZone (assignment cap). → {distributionId} see: team-list (find teamId), distribution-list (verify after)
distribution-create
Creates a Distro router and publishes it, but leaves it INACTIVE — it routes nothing until you call distro-router-activate. Publish is implicit; activation is a deliberate separate step. Distro routes CRM records (leads), so a router needs a trigger and its routes carry NO meeting type. workspaceId must be a team workspace of this tenant (400 otherwise). - routing (req): the lead-routing matrix — a trigger, ordered rules evaluated top-down, a mandatory catch-all, plus optional pre-distribution routing steps. Each route/catch-all carries an assignee (who) AND at least one of: CRM actions (what), convertLead, or addToCampaign (that minimum is required to publish): {trigger: {objectType, eventTypes: [{type, ...}], evaluation?, delay?}, routes: [{ruleId, assignee: assignee, actions: [action], convertLead?, addToCampaign?}], catchAll: {distributionId, actions: [action], convertLead?, addToCampaign?}, routingSteps?: [routingStep]} assignee (one of): {type: "Distribution", distributionId} | {type: "UseAssignmentTable"} | {type: "Owner"} action (one of): {type: "ReassignRecord"} | {type: "UpdateOwnership", respectWorkingHours?, sendSlackNotification?, sendEmailNotification?, matchBy?} | {type: "UpdateField", field, value?, objectType?} | {type: "UpdateFieldDynamic", field, value: dynamicValue, objectType?} | {type: "SendSlackToAssignee"} | {type: "SendEmailReminderToAssignee"} dynamicValue (one of): {type: "RelativeDate", unit: "Day"|"Week"|"Month"|"Quarter"|"Year", offset?, timeZone?} | {type: "TemplateText", text} | {type: "VariableField", variableId, field, relation?} convertLead: {convertedStatus, accountSettings: {type: "CreateNewAccount"} | {type: "MatchAccount"}, opportunitySettings: {type: "CreateNewOpportunity"} | {type: "DoNotCreateOpportunity"}} addToCampaign: {campaignId, status} routingStep (one of): {type: "Enrichment", id?, fieldMappings: [{variableId, field, waterfallId, overwriteExisting?, relation?}]} | {type: "SpamCheck", id?, salesforceWrite: {type: "Off"} | {type: "Enabled", scoreField?, otherFields: [{objectType, field, value?}]}} trigger.objectType is the CRM object (Lead, Contact, Account, Opportunity, …); trigger.eventTypes is a non-empty list of trigger events (e.g. {type:"NewRecord"}, {type:"UpdateField", expression}, {type:"Signal"}, {type:"Scheduled", schedulerId}). Each route row: if its rule matches, route the record to its assignee and run its actions. A row's assignee is Distribution (round-robin over distributionId), UseAssignmentTable (assignee looked up in the assignment table of the row's own ruleId, which must be an assignment-table rule — see rule-create) or Owner (keep the record's current owner); the legacy `distributionId` field on a row is still accepted as shorthand for a Distribution assignee (if both are sent they must name the same distribution). The catchAll takes only distributionId (Distro allows no other catch-all assignee). ruleId is required on every row; an always-match route is the catchAll (used when no row matches). routes may be empty (then only the catchAll applies). No meeting type anywhere — Distro routes leads, it does not book meetings. trigger.evaluation (opt) is a ruleId used as the router's entry rule (records that fail it are not routed); trigger.delay (opt) delays evaluation — {type:"AfterRecordEval"|"BeforeRecordEval", duration}. The assignee picks WHO gets the record; actions decide WHAT happens to the chosen assignee. A route publishes with at least one of {an action, convertLead, addToCampaign} — a route with none of these fails to publish (so a convert-lead-only route with no actions is valid). ReassignRecord (and UpdateOwnership, with optional working-hours / notification / Id-vs-Email match controls) assign the record's owner to the distributed user; UpdateField sets a CRM field to a static value (objectType defaults to the trigger object, omit value to clear it); UpdateFieldDynamic sets a field to a value resolved at routing time — a relative date ({type:"RelativeDate", unit, offset}), {!variable.Field} template text, or a variable-field reference; SendSlackToAssignee / SendEmailReminderToAssignee notify the chosen assignee. convertLead converts a matched Lead into a Contact when the route fires — convertedStatus is the Salesforce lead status label to set, accountSettings chooses CreateNewAccount vs MatchAccount, opportunitySettings chooses CreateNewOpportunity vs DoNotCreateOpportunity (a new contact is always created). A route or catch-all that sets convertLead REQUIRES trigger.objectType to be Lead (rejected otherwise). addToCampaign adds the routed record to the Salesforce campaign campaignId with campaign-member status. routingSteps (opt, default empty) run before the rows: Enrichment writes data-waterfall output to CRM fields (its variableId / waterfallId reference the router's variables & configured waterfalls — passed through, not validated here); SpamCheck scores the record and, when Enabled, writes the score to scoreField plus any otherFields. Supply a step id to keep it stable; omit it to mint a fresh one. → {id, workspaceId, name?, description?, status, trigger: {objectType, eventTypes: [{type, ...}], evaluation?, delay?}, routing: {known, representable, rows: [{ruleId?, outcome}], catchAll?: outcome, routingSteps: [routingStep]}} Note: the created router's status is Inactive — it does NOT route records until you call distro-router-activate. The returned routing is the per-row summary (same shape as distro-router-get) — representable rows echo back the assignee + actions, and routingSteps echo back, not necessarily the exact matrix object you sent ⚠ create is all-or-nothing: if any step (configure or publish — e.g. an invalid routing matrix) fails, the partially-created router is rolled back (force-deleted) and a typed 422 error is returned — nothing is left behind, so resolve the cause and retry see: distro-router-activate (make the new router live), workspace-list (find a workspaceId), rule-list (find ruleIds), distribution-list (find distributionIds), distro-router-update (replace config afterwards)
distro-router-create
Creates a group scheduling link — a meeting with a fixed host plus additional members. Offered slots are the intersection of the host and required members' availability; optional members are invited but do not gate availability. - slug: URL slug (lowercase letters, digits, hyphens, underscores). - hostUserId: the host user; use user-find. - sharedWith (opt): sharing scope, {type: "Workspace"} (default) or {type: "Teams", teamIds}. - hostMeetingLimit (opt): defaults to NoMeetingLimit; caps meetings booked through this link per host — {type: "NoMeetingLimit"} or {type: "SetMeetingLimit", maximumMeetingsCount, reset}; reset is one of: {type: "Daily", every, startsOn, resetLocalTime: "00:00", timezone} {type: "Weekly", every, startsOn, resetDayOfWeek: "MONDAY".."SUNDAY", timezone} {type: "Monthly", every, startsOn, resetDay: 1..28, timezone} Note: every is the period length (1 = every day/week/month); startsOn (a date) anchors multi-period cycles. → {workspaceId, linkId, name, slug, meetingTypeIds, bookingUrl, hostMeetingLimit?: {type: "NoMeetingLimit"}|{type: "SetMeetingLimit", maximumMeetingsCount, reset: {type: "Daily"|"Weekly"|"Monthly", every, startsOn, timezone, resetLocalTime?, resetDayOfWeek?, resetDay?}}} see: user-find (resolve user ids), scheduling-link-list-group (browse existing)
scheduling-link-create-group
Creates a Handoff router and publishes it live in one step — there is no unpublished-draft state via the API. workspaceId must be a team workspace of this tenant (400 otherwise). The routing matrix is a list of ordered rules evaluated top-down plus an optional catch-all fallback: - routing (req): ordered rules evaluated top-down, plus an optional catch-all fallback. Each row's rule, when it matches, runs that row's `outcome`; the catch-all `outcome` runs when no row matches: {routes: [{ruleId, outcome}], catchAll?: outcome} outcome: {type: "Schedule", assignment, meetingTypeId, crmActions?} Schedule assigns the lead and books a meeting type. assignment is {type: "Distribution", distributionId} (round-robin / ownership / whatever the distribution does), {type: "User", userId} (a specific host), or {type: "UseAssignmentTable", ruleId, columnKey} (the host comes from column columnKey of the assignment-table row that rule ruleId matched; only on a row whose ruleId is that same assignment-table rule, never on the catchAll — find columnKey with assignment-table-definition-get). crmActions (opt) is an ordered post-booking chain (ConvertLead is rejected with 400 — Handoff does not execute it; use a Distro router's convertLead), each {type: "AddToCampaign", campaignId, memberStatus} (add the matched Salesforce lead/contact to a campaign with that member status; find campaignId with campaign-list / campaign-search), {type: "SalesforceUpdateFields", contact:[{object,field,value}], lead:[{field,value}]} (write field values onto the matched Salesforce contact/lead), {type: "HubspotUpdateFields", contact:[{object,field,value}]} (write property values onto the matched HubSpot record), {type: "SalesforceUpdateOwnership", contact:[{object,field}], lead:[{field}]} (set the record owner to the booked host), {type: "HubspotUpdateOwnership", contact:[{object,field}]} (set the record owner to the booked host), {type: "SalesforceCreateEvent", relatedTo?, meetingCancellationBehavior?, guestsBehavior?} (create a Salesforce Event for the booked meeting; relatedTo links it to a CRM object — {type: "Account"|"Opportunity"|"Case"} or {type: "Campaign", campaignId} — omit for no relation; meetingCancellationBehavior DeleteEvent|DoNothing; guestsBehavior CreateEvents|DoNothing), or {type: "HubspotCreateEngagement", relatedTo?, owner?, meetingCancellationBehavior?} (create a HubSpot meeting engagement; relatedTo {type: "Company"|"Deal"|"Ticket"}; owner Assignee|Booker). At most one action of each type per chain — a duplicated type is rejected (400); put all rows of one kind in a single action. ruleId is required on every row; an always-match route is the catchAll. catchAll is optional — a router with no catch-all simply has no fallback when no row matches. Provide at least one row or a catchAll (an empty matrix with neither is rejected). Note: handoff routers support only the Schedule outcome — Redirect outcomes, no-show timeouts, Notify (Slack), and the SalesforceUpsertRecord / HubspotUpsertRecord create-record CRM actions are concierge-only and are not part of the handoff schema (AddToCampaign / SalesforceUpdateFields / HubspotUpdateFields / SalesforceUpdateOwnership / HubspotUpdateOwnership / SalesforceCreateEvent / HubspotCreateEngagement are the only CRM actions here). → {id, workspaceId, name?, routing: {known, representable, rows: [{ruleId?, ruleType?, outcome}], catchAll?: {outcome}}} Note: the returned routing is the lossy per-row summary (same shape as handoff-router-get), not the matrix you sent ⚠ create is all-or-nothing: if the final publish step rejects the router, the router is deleted again and a typed 422 error with the backend reason is returned — nothing is left behind, so fix the request and retry ⚠ every distributionId the request names must exist in this tenant — an unknown one is rejected (404) before anything is written see: handoff-router-list (find a workspaceId), rule-list (find ruleIds), distribution-list (find distributionIds), user-find (find userIds), meeting-type-list (find meetingTypeIds), assignment-table-definition-get (find columnKeys), handoff-router-update (replace config afterwards)
handoff-router-create
Creates a reusable team meeting type; the backend fills in product defaults for anything you omit. Only name, duration ("30 minutes") and the default location are set by the create call itself — description, inviteTitle, inviteDescription, location alternatives, availability, schedule, assigneesMeetingLimit, and the admin scheduling link that carries sharedWith are applied by FURTHER calls immediately after. `description` is internal (never shown to guests); to set the guest-facing calendar invite use inviteTitle/inviteDescription, both of which accept {!CP.Object.Field} merge tags (e.g. {!CP.DataField.FirstName}). `sharedWith` is {type: "Workspace"} or {type: "Teams", teamIds}; supplying it provisions the admin (one-on-one) scheduling link that carries the scope — omit it and sharing can only be set later by managing that link directly. Note: if a follow-up call fails, the new meeting type is deleted (rolled back) and the original error is returned, so you can retry safely. If the rollback also fails, or the meeting type came from an earlier call with the same idempotencyKey (never deleted), the error is edge.meeting-type-create-incomplete: it names the meeting type id and the failed step — delete it with meeting-type-delete, fix it with meeting-type-update, or retry with the same idempotencyKey. - location (opt): {default: <Location>, others: [<Location>]}; omit to keep the workspace default. Each <Location> is one of: {type: "ZoomOneTimeLink"|"TeamsMeetingLink"|"GongLink"|"GoToMeetingLink"|"RingCentralLink"|"WebexLink"|"HostsDefaultConferenceDetails"|"HostsDefaultPhysicalLocation", index, host: "Host"|"AssigneeIfExists"|"BookerIfExists"} {type: "CalendarPlatformConference", index} {type: "AskTheGuest", index} {type: "DefinedInMeetingType", index, value?} - availability (opt): {minimumNotice?, bookingWindow?, slotIncrement?: {type: "Dynamic"}|{type: "Fixed", every}}; durations like "24 hours" or "PT72H"; omitted sub-fields keep the product default. - schedule (opt): {type: "HostWorkingHours"} (each host's own working hours, the default) or {type: "Fixed", workingHours: {monday: [{start: "09:00", end: "17:00"}], ...}, timezone?, forceGuestTimezone?, activeFrom?, activeTo?}; a weekday left empty is not bookable; end "00:00" means midnight; timezone null means each host's timezone. - assigneesMeetingLimit (opt): caps meetings of this type per assignee — {type: "NoMeetingLimit"} or {type: "SetMeetingLimit", numberOfMeetings, reset}; reset is one of: {type: "Daily", every, startsOn, resetLocalTime: "00:00", timezone} {type: "Weekly", every, startsOn, resetDayOfWeek: "MONDAY".."SUNDAY", timezone} {type: "Monthly", every, startsOn, resetDay: 1..28, timezone} Note: every is the period length (1 = every day/week/month); startsOn (a date) anchors multi-period cycles. - idempotencyKey (opt): a UUID (a MeetingTypeId) the caller generates per logical create — a retry with the same value returns the already-created meeting type instead of duplicating it; reusing the same value with a DIFFERENT payload fails with 409. → {id, workspaceId, name, description?, inviteTitle, inviteDescription, status, isActive, duration, location: {default: {type, index, host?, value?}, others: [{type, index, host?, value?}]}, buffers: {before?, after?}, meetingLimit?: {limitBy: "Email"|"Domain", timeframe: "Hourly"|"Daily"|"Weekly"|"Monthly"|"Yearly", count}, sharedWith?: {type: "Workspace"}|{type: "Teams", teamIds}, reminders: [{id, workspaceId, channel, trigger: {kind, offset?}, name, title?, body, sendTo?, sendFrom?, smsSendFrom?}], availability?: {minimumNotice, bookingWindow, slotIncrement: {type: "Dynamic"}|{type: "Fixed", every}}, schedule?: {type: "HostWorkingHours"}|{type: "Fixed", workingHours: {monday..sunday: [{start, end}]}, timezone?, forceGuestTimezone, activeFrom?, activeTo?}, assigneesMeetingLimit?: {type: "NoMeetingLimit"}|{type: "SetMeetingLimit", numberOfMeetings, reset: {type: "Daily"|"Weekly"|"Monthly", every, startsOn, timezone, resetLocalTime?, resetDayOfWeek?, resetDay?}}} see: meeting-type-list (find a workspaceId), meeting-type-update (edit afterwards)
meeting-type-create
Creates a meeting type folder (asset group) in the workspace. The folder starts empty; assign meeting type items to it with meeting-type-folder-move / meeting-type-folder-move-bulk. → {id, name, displayOrder, itemCount: 0, revision} see: meeting-type-folder-move (assign a meeting type to the new folder), meeting-type-folder-list (list folders)
meeting-type-folder-create
Creates a reminder in a workspace; attach it to a meeting type afterwards with meeting-type-attach-reminder. `trigger` is {kind, offset}: kind is "BeforeMeeting"/"BeforeMeetingNoResponse"/"AfterMeeting" and offset (e.g. "1 hour") is required. `title` is the email subject (Email only; ignored for Sms). `sendTo` (recipients) and `sendFrom` (sender identity) are Email-only; `smsSendFrom` (sender number) is Sms-only — each defaults to the backend value (SendToAllGuests / SendFromHost / AnyNumber) when omitted. → {id, workspaceId, channel: "Email"|"Sms", trigger: {kind, offset?}, name, title?, body, sendTo?: "SendToAllGuests"|"SendToMainGuestOnly", sendFrom?: "SendFromHost"|"SendFromBooker"|"SendFromNoReply", smsSendFrom?: "AnyNumber"|"LocalAreaNumber"} Note: other advanced send-restrictions are backend-defaulted and not settable here. see: meeting-type-reminder-list (find a workspaceId), meeting-type-reminder-update (edit afterwards)
meeting-type-reminder-create
Creates an ownership scheduling link — routes each booking to the guest's CRM account owner, with a distribution-backed round-robin fallback when no owner matches. - slug: URL slug (lowercase letters, digits, hyphens, underscores). - ownership: owner-routing config, {ownershipSettings, host, bookerInvitation, alwaysInvitedUsers}. - distribution: round-robin fallback config, {assignments: [{distributionId, required}], host, bookerInvitation, alwaysInvitedUsers}. - pageConfig (opt): {title?}. sharedWith (opt): {type: "Workspace"} (default) or {type: "Teams", teamIds}. Note: the ownership/distribution shapes mirror the scheduling-link-list-ownership response, except assignments here are lean {distributionId, required} — do not send the read-side members field (members are resolved from the distribution by the backend). → {workspaceId, linkId, name, slug, meetingTypeIds, ownership, distribution, pageConfig, sharedWith, bookingUrl} see: scheduling-link-list-ownership (browse existing for the exact ownership/distribution shape), distribution-list (find distributionIds)
scheduling-link-create-ownership
Creates a personal meeting type (a booking page) in ownerUserId's personal workspace; the backend fills in product defaults and generates the booking slug. Only name, duration ("30 minutes"), the default location and syncToCrm are set by the create call itself — description, inviteTitle, inviteDescription and location alternatives are applied by a FURTHER call immediately after. `description` is internal; the guest-facing invite is inviteTitle/inviteDescription ({!CP.Object.Field} merge tags, e.g. {!CP.DataField.FirstName}). Note: if the follow-up call fails, the new meeting type is deleted (rolled back) and the original error is returned, so you can retry safely. If the rollback also fails, or the meeting type came from an earlier call with the same idempotencyKey (never deleted), the error is edge.meeting-type-create-incomplete: it names the meeting type id and the failed step — delete it with personal-meeting-type-delete, fix it with personal-meeting-type-update, or retry with the same idempotencyKey. - ownerUserId (req): the user in whose personal workspace the meeting type is created; must be an existing user of this tenant (unknown id → 404, nothing is created). - location (opt): {default: <Location>, others: [<Location>]}. Each <Location> is one of: {type: "ZoomOneTimeLink"|"TeamsMeetingLink"|"GongLink"|"GoToMeetingLink"|"RingCentralLink"|"WebexLink"|"HostsDefaultConferenceDetails"|"HostsDefaultPhysicalLocation", index, host: "Host"|"AssigneeIfExists"|"BookerIfExists"} {type: "CalendarPlatformConference", index} {type: "AskTheGuest", index} {type: "DefinedInMeetingType", index, value?} - idempotencyKey (opt): a UUID (a MeetingTypeId) the caller generates per logical create — a retry with the same value returns the already-created meeting type; reusing it with a DIFFERENT payload fails with 409. → {id, workspaceId, name, slug, description?, inviteTitle, inviteDescription, status, isActive, duration, location: {default: {type, index, host?, value?}, others: [{type, index, host?, value?}]}, buffers: {before?, after?}, meetingLimit?: {limitBy: "Email"|"Domain", timeframe: "Hourly"|"Daily"|"Weekly"|"Monthly"|"Yearly", count}, syncToCrm, reminders: [{id, workspaceId, channel, trigger: {kind, offset?}, name, title?, body, sendTo?, sendFrom?, smsSendFrom?}]} see: personal-meeting-type-update (edit afterwards)
personal-meeting-type-create
Creates a round-robin scheduling link — bookings are distributed across the backing distributions' members. Each distribution is a required assignee and the first one supplies the host. - slug: URL slug (lowercase letters, digits, hyphens, underscores). - distributionIds: one or more distributions; use distribution-list. - sharedWith (opt): sharing scope, {type: "Workspace"} (default) or {type: "Teams", teamIds}. → {workspaceId, linkId, name, slug, meetingTypeIds, assignments, bookingUrl} see: distribution-list (find distributionIds), scheduling-link-list-round-robin (browse existing)
scheduling-link-create-round-robin
Creates a reusable routing rule so routers can reference it. It is live immediately (revision=0 — the first revision is 0, not 1). Choose the dto variant matching the rule kind — ownership rules (which resolve a record owner) use CreateOwnershipRuleRequest and must carry a teamId; assignment-table rules (which resolve the assignee from an assignment table on match) use CreateAssignmentTableRuleRequest and carry an assignmentTableId; all others use CreateRuleRequest. Omit ruleId to auto-assign a UUID, or supply one to force it. The fastest safe path is to copy conditions (and workspaceId) from a rule-list result, since those already carry the required discriminators. - dto (req): rule definition — choose ONE variant: Non-ownership: {type: "CreateRuleRequest", workspaceId, name, conditions} Ownership: {type: "CreateOwnershipRuleRequest", workspaceId, name, conditions, teamId} Assignment table: {type: "CreateAssignmentTableRuleRequest", workspaceId, name, assignmentTableId, conditions} - conditions: EVERY conditions object requires a "type" discriminator field. Example structure: {type: "ConditionGroup", id: "<any-string>", operator: "and"|"or", conditions: [ {type: "StaticValueCondition", conditionId: "<any-string>", dataReference: {source: "SF"|"DF"|"CP"|"HS"|"MK", object: "<CrmObject>", field: "<FieldName>"}, operator: ">"|"<"|">="|"<="|"equal"|"notEqual"|"isAnyOf"|"isNotAnyOf"|"contains"|"doesNotContain"| "containsAnyOf"|"containsNoneOf"|"startsWith"|"endsWith"|"startsWithAnyOf"|"endsWithAnyOf", value: <any JSON value>} {type: "SingleParameterCondition", conditionId: "<any-string>", dataReference: {source, object, field}, operator: "notEmpty"|"empty"} ]} - product: not settable. The rules backend sets product to "Distro" on every new rule. It is a legacy label only: it does not limit which product (Distro, Concierge, Handoff, Chat or other Web Experiences) can reference the rule. → {type, id, workspaceId, name, conditions, ruleBuilderVersion, product, metadata: {revision: 0, ...}} ⚠ live immediately with no dry-run — a misconfigured rule may misroute leads in every router that references it see: rule-list (get workspaceId and copy conditions from an existing rule), web-experience-create (a RoutingRule conversation node references a rule by id)
rule-create
Creates a rule folder (asset group) in the workspace. The folder starts empty; assign rule items to it with rule-folder-move / rule-folder-move-bulk. → {id, name, displayOrder, itemCount: 0, revision} see: rule-folder-move (assign a rule to the new folder), rule-folder-list (list folders)
rule-folder-create
Creates a team inside a workspace to serve as a routing target for distributions. Optionally seed it with initial members (userIds); add more later with team-add-users. → {id, workspaceId, name, members, metadata} see: workspace-list (resolve workspaceId), user-find (resolve emails to member userIds), team-add-users (add more members later)
team-create
Creates a Web Experience in a workspace from full, typed playbook content and immediately publishes it, so it goes Live (Enabled) and visible to site visitors at once — there is no draft-only create via this API. Request body: {name, widgetType: "Chat"|"Scheduling"|"Offer"|"Message", trigger, conversation, passThrough?, languageSettings?} Note: conversation node rules (the server rejects a playbook that breaks them): - StartLiveChat needs, on every path that reaches it: (1) an earlier email-collecting node, i.e. a FillForm with the PersonEmail data field, or a ScheduleMeeting; else it fails with "Node with id: 'N' does not have an email input before". (2) An earlier visitor action: ReplyButtons, FillForm or PiperAiStrategyNode. (3) Per assignee: Owner needs an earlier RoutingRule or StartLiveChat; Meeting needs an earlier ScheduleMeeting; AssignmentTableColumn needs an earlier RoutingRule with the same ruleId; EveryoneInTeam needs a WaitForRepToJoin after it (on onAssignmentExit). Its onAssignmentExit can pass only through side-effect nodes (CrmAction, notifications, Message) before a WaitForRepToJoin. - NavigateTo is an in-journey jump: its target is another node id in the same conversation and it has no visible effect. It is not a URL redirect; to send the visitor to a URL, use RedirectTo. → {id, workspaceId, name, widgetType, trigger, conversation, passThrough?, languageSettings?, draftCreator, state: "Enabled", version, publisher, enabledCrms?} Note: the response is the created, published playbook view; state is "Enabled" and the published fields (version, publisher) are populated. ⚠ the experience goes Live on the customer's website immediately ⚠ returns 403 edge.web-experience-management-disabled when Web Experience management via API is not enabled for the tenant; nothing is changed ⚠ if the create fails, cleanup of the new draft is best-effort: a draft can remain (e.g. when the backend rejects the create on a business rule) and it can be missing from web-experience-list. Before you retry, look in web-experience-list for an entry with the same name and no state, and delete it with web-experience-delete. If it is not listed, tell the user to delete it in the Chili Piper app see: web-experience-list (find a workspaceId), web-experience-update (rename or pause/resume)
web-experience-create
Subscribes a url to a meeting lifecycle event (MeetingCreated | MeetingUpdated | MeetingDeleted); each firing delivers a signed POST to that absolute https url. The (triggerType, url) pair is the webhook's identity, so creating a duplicate is rejected. Set enabled=false to create it paused (no deliveries until a later update enables it). → the created {triggerType, url, enabled} see: webhook-list (see existing webhooks), webhook-update, webhook-delete
webhook-create
Turns a Distro router off (active → inactive) so it stops routing records. Also the prerequisite for deletion: an active router cannot be deleted, so deactivate and wait for Inactive before distro-router-delete. Idempotent — deactivating an already-inactive router is a no-op. Set force=true to deactivate even when the router's revision is stale. → {id, workspaceId, name?, description?, status, trigger: {objectType, eventTypes: [{type, ...}], evaluation?, delay?}, routing: {known, representable, rows, catchAll?, routingSteps}} Note: deactivation completes asynchronously, so status typically reads {type:"Deactivating"} on return and settles to {type:"Inactive"} shortly after — poll distro-router-get until Inactive before calling distro-router-delete see: distro-router-activate (turn it back on), distro-router-delete (delete once inactive), distro-router-get (check status)
distro-router-deactivate
Deletes (soft-deletes) a team workspace. It is never blocked: all members lose access to it and its Workspace Managers lose that role. Delete or reassign its teams, meeting types and routers first if they are still needed. → {id, name, emoji, logo, metadata} Note: returns the deleted workspace. ⚠ destructive — members and Workspace Managers are removed from the workspace; confirm with the user first see: workspace-list-users (check who is affected first), workspace-list (find workspaceId)
workspace-delete
Deletes the rows of an assignment table at the specified revision ONLY (the DEFINITION is left intact — remove it separately via assignment-table-definition-delete). Uses optimistic concurrency: pass the table's current revision (re-fetch via assignment-table-get right before calling). - workspaceId (req): the workspace that owns the table. - assignmentTableId (req): the table's id. - revision (req): the table's current revision. → empty (204) on success ⚠ irreversible via API; a stale revision causes a 409 conflict — re-fetch and retry see: assignment-table-get (get a fresh revision immediately before calling), assignment-table-definition-delete (remove the schema too)
assignment-table-delete
Deletes the column definition for an assignment table at the specified revision ONLY. Uses optimistic concurrency: pass the current revision (re-fetch via assignment-table-definition-get right before calling). Any rules that reference this table will no longer resolve an assignment. - workspaceId (req): the workspace that owns the table. - assignmentTableId (req): the table's id. - revision (req): the definition's current revision. → empty (204) on success ⚠ irreversible via API; a stale revision causes a 409 conflict — re-fetch and retry see: assignment-table-definition-get (get a fresh revision immediately before calling)
assignment-table-definition-delete
Deletes a concierge router folder. Member concierge router items are not deleted — they are detached (moved to ungrouped). Uses optimistic concurrency: the supplied revision must equal the current one (re-fetch it via concierge-folder-list right before calling). → no payload on success ⚠ revision conflict = re-fetch via concierge-folder-list and retry see: concierge-folder-list (get a fresh folderId+revision immediately before calling)
concierge-folder-delete
Permanently (hard) deletes a distribution. Uses HTTP DELETE on the same path as distribution-update (DELETE /v1/org/distribution/{distributionId}) — there is no separate /delete path. Deletion does NOT cascade: fix referencing routing rules and round-robin scheduling links yourself first, or they dangle. → {} ⚠ irreversible; routing rules referencing this distributionId become dangling — audit and update/delete them first; round-robin scheduling links pointing to it become broken — no cascade cleanup see: distribution-list (confirm id), rule-list (find referencing rules to fix first), scheduling-link-list-round-robin (find affected links)
distribution-delete
Deletes a brief. All its meeting types stop getting Meeting Prep briefs. To disable Meeting Prep for only some meeting types, remove them from `meetingTypeIds` with meeting-prep-brief-upsert instead. → {} Note: an unknown brief id (or one in another workspace) returns a 404. ⚠ irreversible via API see: meeting-prep-brief-list (confirm the briefId and workspaceId before deleting)
meeting-prep-brief-delete
Deletes a meeting type folder. Member meeting type items are not deleted — they are detached (moved to ungrouped). Uses optimistic concurrency: the supplied revision must equal the current one (re-fetch it via meeting-type-folder-list right before calling). → no payload on success ⚠ revision conflict = re-fetch via meeting-type-folder-list and retry see: meeting-type-folder-list (get a fresh folderId+revision immediately before calling)
meeting-type-folder-delete
Deletes a rule folder. Member rule items are not deleted — they are detached (moved to ungrouped). Uses optimistic concurrency: the supplied revision must equal the current one (re-fetch it via rule-folder-list right before calling). → no payload on success ⚠ revision conflict = re-fetch via rule-folder-list and retry see: rule-folder-list (get a fresh folderId+revision immediately before calling)
rule-folder-delete
Permanently deletes a team. Fails while any active distribution still references it — reassign those with distribution-update-v3 first. Members stay in the workspace; only the team grouping is removed. → {id, workspaceId, name, members, metadata} — the deleted team record ⚠ irreversible; does not remove members from the workspace see: team-list (find teamId), distribution-update-v3 (reassign before deleting)
team-delete
Unsubscribes a webhook, matched by its (triggerType, url) identity; deliveries for that subscription stop immediately. → {} ⚠ irreversible — re-create with webhook-create to restore see: webhook-list (verify before and after)
webhook-delete
Deletes a knowledge base entry and its stored content. Chat AI stops using it immediately. Deleting an entry that does not exist succeeds. → {} see: knowledge-base-list (find id)
knowledge-base-delete
Unlinks a reminder from a team meeting type (it stops sending it), and returns the updated meeting type. The reminder itself is NOT deleted — it stays available to re-attach or to use elsewhere; delete it entirely with meeting-type-reminder-delete. Idempotent: detaching a reminder that is not attached is a no-op. → {id, workspaceId, name, description?, inviteTitle, inviteDescription, status, isActive, duration, location: {default: {type, index, host?, value?}, others: [{type, index, host?, value?}]}, buffers: {before?, after?}, meetingLimit?: {limitBy: "Email"|"Domain", timeframe: "Hourly"|"Daily"|"Weekly"|"Monthly"|"Yearly", count}, sharedWith?: {type: "Workspace"}|{type: "Teams", teamIds}, reminders: [{id, workspaceId, channel, trigger: {kind, offset?}, name, title?, body, sendTo?, sendFrom?, smsSendFrom?}], availability?: {minimumNotice, bookingWindow, slotIncrement: {type: "Dynamic"}|{type: "Fixed", every}}, schedule?: {type: "HostWorkingHours"}|{type: "Fixed", workingHours: {monday..sunday: [{start, end}]}, timezone?, forceGuestTimezone, activeFrom?, activeTo?}, assigneesMeetingLimit?: {type: "NoMeetingLimit"}|{type: "SetMeetingLimit", numberOfMeetings, reset: {type: "Daily"|"Weekly"|"Monthly", every, startsOn, timezone, resetLocalTime?, resetDayOfWeek?, resetDay?}}} ⚠ takes effect immediately see: meeting-type-get (confirm), meeting-type-attach-reminder (re-attach), meeting-type-reminder-delete (delete the reminder entirely)
meeting-type-detach-reminder
Downloads an assignment table's rows as CSV text — a header row plus one row per assignment-table row. If the table has no rows yet, only the header row is returned. Round-trips with assignment-table-csv-upload: download, edit, re-upload. - workspaceId (req): the workspace that owns the table. - assignmentTableId (req): the table's id. → raw CSV text on HTTP and CLI (not a JSON wrapper); the MCP tool returns {csv: "<CSV text>"} see: assignment-table-csv-upload (write rows from CSV), assignment-table-get (fetch rows as JSON)
assignment-table-csv-download
Chili Piper ChatGPT Plugin FAQ
How the directory, categories and Discoverability Score work.
Read the methodologyHow do I improve Chili Piper's ChatGPT Plugin discoverability?
The levers are the listing surface agents actually read: names, descriptions, keywords, tool metadata, and registry health. Which lever matters depends on where discovery breaks, which is what continuous measurement shows.
Where is this profile measured?
This profile uses the geography attached to the latest public registry snapshot: US. Locale tags are intentionally omitted.