Spoki
Automate your communication AI
- Category
- Sales & CRM
- Primary Subcategory
- Email & SMS Lifecycle Marketing Automation
Integration details
Description
Spoki connects ChatGPT to the Spoki staging account so users can inspect contacts, campaigns, tickets, lists, templates, automations, and CRM records, then perform supported Spoki workflow updates through the MCP tools.
- Integration type
- Plugin
- Verification status
- Not applicable
- Platform
- ChatGPT
- Primary Subcategory
- Email & SMS Lifecycle Marketing Automation
- Secondary Subcategories
- None listed
- Brand
- Spoki
- Access
- Account required
- First tracked
- 2026-07-09
- Tool count
- 123
- Geography
- US
The Primary Subcategory used for this profile’s headline score.
Other Subcategories where the Integration is listed.
Get alerts for Spoki
Get updates when Spoki’s Discoverability Score or category rank changes.
ChatGPT Plugin Discovery Score
ChatGPT Plugin discovery is coming soon
ChatGPT can surface a Plugin when it matches a user's request.Your Plugin Discovery Score measures how often yours appears.
No spam. Unsubscribe any time.
What discovery looks like

Competing in ChatGPT Email & SMS Lifecycle Marketing Automation
View Category123 tools agents can invoke
Activate a paused automation so it can receive new triggers.
Add one of the 6 FE Commerce triggers to an existing automation. kind='order' / 'product' create the inbound-message starters (optional channel_id scopes to a specific channel; resolve via get_account_channels first). kind='paypal' / 'stripe' require `outcome` ('success' or 'error') AND the corresponding integration enabled — if disconnected returns {not_connected: True, platform_name, integration_url} so the agent can surface the Open Integrations navigate chip (same UX as add_integration_trigger). PREFER THIS TOOL over add_integration_trigger for paypal/stripe — only this tool sets `payment_outcome_type` correctly. Audit-stamps the automation.
Add a Condition on Date trigger (FieldConditionStarter) to an automation — mirrors the FE Start Trigger → Condition on Date tab. Fires when a contact's DATE or DATETIME custom field matches a temporal condition (X before BIRTHDAY, on the day of ANNIVERSARY at 9:00, etc.). Operators: '<' Before, '=' Is, '>' After. Period: 'm' (DATETIME only), 'h' (DATETIME only), 'd'. `time` (HH:mm) is required for operator='=' on DATE fields. `ignore_year=True` (default) makes the trigger recurring (birthdays); False ties it to the exact stored date. Pre-flight: resolve `custom_field_id` via `get_custom_fields` and confirm field_type ∈ {2, 3}. For TEXT fields the call is rejected with error_code='field_type_invalid'. If the user wants a new date field, delegate creation to `dynamic_fields_agent` via a confirm_intent chip FIRST. Audit-stamps the automation with last_modified_via=spoki-claw.
Link one or more contacts to an existing deal. Existing linked contacts are not removed — this only adds. To remove a contact use remove_contact_from_deal. Workflow: call get_contacts to get contact_ids, call search_deals to get deal_id if needed. All contact IDs must belong to the same account. Returns the updated deal with the full contact list.
Add a 'Customer Message' (Widget) trigger to an existing automation. Mirrors the FE Start Trigger → Customer Message tab. Pass either `widget_id` to bind an existing widget (from `search_widgets`) OR `text` to create a new QRCode widget with that trigger phrase. If a widget with the same text already exists in the account, it is REUSED (no duplicate created) — `reused_existing_widget=True` in the response. Cross-automation conflict: if the picked/found widget is already bound to a DIFFERENT automation, returns `{conflict: True, conflicting_automation_id, conflicting_automation_name, widget_id}` — agent should surface the existing automation as a navigate chip rather than silently stealing the trigger.
Add a Deal trigger to an automation — mirrors the FE Start Trigger → Deal tab. Event-driven; does NOT require any existing deals. Five events: `event='created'` (OnDealCreateStarter — refused with `error_code='deal_created_already_exists'` if one already exists, matches FE radio disabled state); `event='updated'` (OnDealUpdateStarter with REQUIRED non-empty `fields` from: name, pipeline, stage, owner, amount, currency, close_date, reference); `event='contact_added'` (OnDealContactUpdatedStarter is_contact_added=True — refused on duplicate); `event='contact_removed'` (OnDealContactUpdatedStarter is_contact_added=False — refused on duplicate); `event='stage_updated'` (OnDealStageChangeStarter — REQUIRED `pipeline_id` (UUID), optional `stage_from_id` / `stage_to_id` from the same pipeline; null = any stage). Resolve pipelines + stages via `list_pipelines` first. NOT exclusive — coexists with other starters. Audit-stamps with last_modified_via=spoki-claw.
Add all contacts matching advanced filters to an existing list. Max 50000 contacts per call.
Add a First message trigger (OnFirstMessageStarter) to an automation — mirrors the FE Start Trigger → First message tab. Two modes: `first_message_ever=False` (default, 'First message of the conversation (24h)') fires on inbound after 24h silence with per-conversation dedup; `first_message_ever=True` ('First message ever') fires only on the customer's very first inbound across the account, lifetime dedup per contact. Optional `channel_id` scopes to one AccountChannel (omit for any channel — resolve via `get_account_channels` first if the user names one). HARD RULE: refuses with `error_code='other_starters_exist'` if the automation already has ANY other starter (FE-mirror exclusive-trigger guard across 18 starter types). The refusal includes the existing starters so the agent can list them by name. Audit-stamps the automation with last_modified_via=spoki-claw.
Add a Holidays trigger (OnHolidayHoursStarter) to an automation — mirrors the FE Start Trigger → Holidays tab. Two modes: `mode='account'` saves with `dates=[]` (matches the FE 'Based on account holidays' radio — note: the runtime engine looks at `dates__contains=[today]` so an empty array means the trigger doesn't fire from the engine's perspective; we replicate this FE behavior exactly so chat-created triggers match FE-created ones); `mode='manual'` saves with the provided ISO YYYY-MM-DD dates. NOT exclusive — coexists with other starters. Audit-stamps the automation with last_modified_via=spoki-claw.
Add an Integration trigger to an existing automation. Generic dispatcher over the FE-visible platforms (see `list_integrations`). Pass the platform slug + (only for Meta) an `id_ad`, or (only for Zadarma) an `event`. This tool SELF-GATES — call it directly and react to its response; do NOT pre-refuse based on `is_connected`. Webhook and Zadarma triggers are self-configuring and are created without any prior connection. Only the credential platforms (meta/paypal/stripe) require the AccountIntegration enabled — if not, the tool returns `{not_connected: True, platform_name, integration_url: '/integrations'}` (surface a navigate chip). If platform='google_sheets', it returns `{requires_visual_builder: True, automation_id, ...}` (needs Google OAuth — surface a navigate chip to the builder). Otherwise the appropriate starter is created and bound. Audit-stamps the automation.
Append a `Send Message with Buttons` (WhatsApp Interactive Message) step to an automation. Flat args — the BE composes the nested interactive_message dict and runs the same serializer the FE Action Modal uses. Required: `automation_id`, `body_text` (≤1000), and `buttons` (1-3 unique strings ≤20 chars when sub_type='default'). Optional: `header_text` (≤60), `footer_text` (≤60), `channel` (auto-resolves single WhatsApp), `language`, `position`, `note`. `sub_type` is 'default' only; 'callPermissionRequest' isn't supported via chat yet (build it in the visual editor). Static-media headers (image/video/document) are NOT supported via chat; refuse with a builder chip. ALWAYS pre-flight via `get_account_channels(platform='whatsapp')` so refusals surface with the right chip instead of generic BE errors.
Append a `Send List Message` (WhatsApp list-with-menu) step to an automation. Flat args — the BE composes the nested list_message dict and runs StepListMessageCreateSerializer (the same path the Action Modal uses). TWO MODES, mutually exclusive: • Static — pass `list_button_text` (≤20) and `categories` (≥1 category, each with 1-10 items, item text ≤24, description ≤72, all items unique). • Dynamic — pass BOTH `input_custom_field_code` (TEXT field whose runtime value provides list contents) and `output_custom_field_code` (where the selection is stored). Common args: `body_text` (REQUIRED, ≤1000), `header_text` (optional ≤60, TEXT only — no images), `footer_text` (optional ≤60), `channel` (WhatsApp; auto-picks the lone active one when omitted), `position`, `language`, `note`. ALWAYS run the `get_account_channels(platform='whatsapp')` pre-flight first.
Add a Marketing Acceptance trigger (OnMarketingAcceptanceStatusChangeStarter) to an automation — mirrors the FE Start Trigger → Marketing Acceptance tab. Two modes (one BE class): `mode='unsubscribe'` (FE 'Unsubscribe Button Pressed' radio — fires when the contact presses the Unsubscribe button; refused with `error_code='unsubscribe_already_exists'` on duplicate per FE); `mode='status_change'` (FE 'Acceptance Status Updated' radio — requires BOTH `status_from` and `status_to` from {0:NO_STATUS, 1:SUBSCRIBED, 2:UNSUBSCRIBED}, must differ; refused with `error_code='status_change_already_exists'` if an EXACT same-pair starter already exists). NOT exclusive — coexists with other starters. Audit-stamps with last_modified_via=spoki-claw.
Add a Non-working hours trigger (OnClosingHoursStarter) to an automation — mirrors the FE Start Trigger → Non-working hours tab. Fires when a customer writes for the first time during a configured non-working window. HARD RULE: refuses with `error_code='other_starters_exist'` if the automation already has ANY other starter (matches FE's hasStartingStep guard across 18 starter types). The refusal includes the existing starters so the agent can ask the user to remove them first. SOFT WARN: if the account has no `Opening` rows, the trigger is created anyway but the response sets `opening_hours_configured=False` — the agent must surface a navigate chip to `/brand-registry/settings` so the user can configure them. Audit-stamps the automation with last_modified_via=spoki-claw.
Add an internal note to a contact's conversation. Notes are only visible to operators, not sent to the contact.
Add tags to a contact. Returns which were added, already present on the contact, and which tag IDs were not found. Call get_contact_tags first to know the contact's current tags.
Add a Ticket trigger to an automation — mirrors the FE Start Trigger → Ticket tab. Event-driven; does NOT require any existing tickets. Two events: `event='created'` (OnTicketCreateStarter, no extra config — fires on every new ticket; refused with `error_code='ticket_created_already_exists'` if one already exists, matching the FE radio's disabled state); `event='updated'` (OnTicketStatusChangeStarter with optional `status_from` / `status_to` IDs — null sides = 'any'; multiple update-starters can coexist on the same automation). Status IDs accept built-in enum (1=Open, 2=Pending, 3=WaitingOnCustomer, 4=Resolved, 5=Closed) AND per-account custom TicketStatus IDs — resolve user names via `get_ticket_statuses` first. NOT exclusive — coexists with other starters. Audit-stamps the automation with last_modified_via=spoki-claw.
Attach ONE trigger (starter) to an EXISTING automation — the generic counterpart of add_step. Pass `automation_id` + `starter_type` + the identity fields for that type (same vocabulary as create_automation). Best for the simple/registry-driven triggers: OnContactListMembershipStarter (list add/remove — set `list_id` + `is_added`), OnFirstMessageStarter, FieldConditionStarter, OnOpeningHoursStarter, OnClosingHoursStarter, OnHolidayHoursStarter, MetaAdsConditionStarter. For deal / marketing-acceptance / ticket / commerce triggers use their dedicated add_*_trigger tools, and for webhook / Google Sheets / other integration triggers use add_integration_trigger (they need integration-specific config). Refuses an exact-duplicate trigger already on the automation unless `force=True`. Audit-stamps with last_modified_via=spoki-claw.
Append a `Spoki Voice` step (AI-powered phone or WhatsApp call) to an automation. The call is run by a voice AI agent referenced by `agent_id` — resolve it via `list_agents(channel='voice')`; if the account has none, offer to create one first. Required: `automation_id`, `agent_id`. `call_type` 'pstn' (Spoki Voice, default) or 'whatsapp' (WhatsApp Call). `mode` 'fixed' (default — one `channel`, auto-picked when the account has exactly one active connected voice channel of that call_type) or 'rotation' (`rotation_pool` of ≥2 channel ids + `strategy` 'round_robin'|'random'; WhatsApp rotation also needs `respect_quality_score`). Optional `working_hours` per-day UTC override (GDPR: Mon-Fri 09-20, Sat 10-18, Sunday forbidden) — omit to use account opening hours. ALWAYS pre-flight via `get_account_channels(platform='voice'|'whatsapp_voice')`: if none, refuse with a chip to /channels (the chat cannot activate voice channels — provisioning + Meta tier live in the FE); if rotation requested with <2 channels, offer fixed mode or sending the user to Channels.
Assign a ticket to an operator. Pass owner_id=null to unassign.
Block or unblock a contact. Blocked contacts will not receive messages.
Assign a new owner and/or move multiple deals to a new stage in a single operation. Use when the user wants to reassign or batch-move several deals at once. Workflow: call search_deals to find the deals and collect their IDs, then present the count and names to the user for confirmation. Only set confirmed=True after the user explicitly confirms the bulk action. Returns counts of updated and failed deals.
Remove ALL contacts from a list. The list itself is kept but emptied.
Snapshot the current members of an existing list (typically a dynamic one) into a NEW static list. Use this when the user wants to 'freeze' an auto-updating list as it is right now — the new list is independent, won't auto-refresh, and won't change when the source's rules or members change. Source can be any list type; the destination is always static. Capped at 50000 contacts.
Mark a deal as closed by stamping closed_datetime to the current time. Does NOT move the deal to a Won or Lost stage — use move_deal_stage after this if a stage change is also needed. Use when the user says a deal is 'closed', 'done', 'finished', or 'completed'. Requires deal_id — call search_deals first if you don't have it. Returns the updated deal.
Close a ticket and set its closed timestamp.
Create a new automation with one trigger (starter) and an ordered list of steps. Defaults to draft (is_active=False). If another active automation already has the same trigger+identity, the call returns {conflict: True, existing_automation_id, existing_automation_name} without creating — pass force=True to bypass that check. Resolve every entity id via the corresponding lookup tool before calling this, and pass it under the schema's own field name — `template`, `lists`, `tags`, `custom_field` — not a *_id spelling.
Create a new folder under this account. Idempotent on case-insensitive name — if a folder with the same name already exists, the existing row is returned with `existed=True` (no duplicate created). Use when the user says 'put it in a new folder called X' and `list_automation_folders` shows no match for X. Pair with `set_automation_folders` to actually link the automation.
Clone a `is_suggested=True` template (from `list_automation_models`) into the user's account. The clone keeps the original trigger + all steps pre-wired and arrives as a draft (is_active=False by default) — the user reviews + activates from the builder. Bundle templates (`attributes.parent` set) clone all siblings together (returns N IDs). On conflict — when the account already has a clone of the same parent_bundle — returns {conflict: True, existing_automation_id, existing_automation_name, parent_bundle}; the agent then surfaces the existing automation as a navigate chip rather than re-cloning. Audit-stamps the clone with last_modified_via=spoki-claw.
Create a new scheduled campaign. Provide either template_id (auto-creates automation) or automation_id.
Create a new custom contact field. Fails with CODE_RESERVED or CODE_ALREADY_EXISTS if the code collides.
Create a new deal. Required: name, pipeline_id, stage_id. Optional: amount (display units — e.g. 5000.0 for €5,000), currency (default EUR), owner_id, contact_ids (list of contact UUIDs to link), close_date (YYYY-MM-DD), reference. Workflow: call list_pipelines first to get pipeline_id and stage_id; call get_contacts first to get contact_ids if the user mentions linking a contact. Duplicate guard: if a deal with the same name already exists the tool returns the existing deal(s) and asks to confirm — set force_create=True only after the user explicitly confirms they want a duplicate. Returns the created deal with its id, a direct URL, and contacts_linked status.
Create a bare automation with NO trigger and NO steps — the user will configure everything in the FE visual editor. Mirrors the 'Empty' option on the New Automation modal. Use when the user says 'create an empty automation', 'start from scratch', or chooses to set up the trigger later. The response includes needs_configuration=True; the agent MUST follow up with a navigate chip to /automations/:id so the user can wire the trigger + steps. For the all-in-one create (trigger + steps in one shot) use create_automation instead.
Create a new (empty, static) contact list. If a visible list with the same name already exists, it is NOT duplicated — the existing list is returned with `already_existed: true`; treat that as success and reuse its id (do not tell the user you created a second list).
Create a new contact list from advanced filters. Use search_contacts_advanced first to preview matching contacts. list_type='static' (default): snapshot the contacts matching NOW — membership is frozen (capped at 50000 contacts). list_type='dynamic': membership auto-refreshes whenever contacts start/stop matching the filters (no size cap; refresh runs asynchronously after creation). Supports all filter keys: tag_{id}, list_{id}, language, email, ticket_status, etc.
Create a new sales pipeline with an ordered list of stages. Stage order is assigned automatically from list position (first item = order 0). Rules: always include a Won stage (probability=100) and a Lost stage (probability=0); all other stages represent open/active phases and should have probability between 1 and 99. The operation is atomic — either all stages are created or none are. Returns the created pipeline with its stages and their assigned IDs.
Create a new tag. Defaults to spoki-green. Fails if a tag with the same name already exists.
Create a new tag category. Fails if a category with the same name already exists.
Create a new message template. If a template with the same name exists, returns an error asking to confirm replacement.
Create a new support ticket. Requires a contact_id. Optionally assign to a category and owner.
Deactivate an automation. It will stop receiving new triggers but running instances continue.
Soft-delete an automation (sets is_deleted=True). Matches FE PATCH {is_deleted: true} behavior — auto-terminates any NOT_STARTED/RUNNING/PAUSED ContactInAutomation rows. TWO-STEP confirmation: ALWAYS call with confirmed=False first to get the warning + running_contacts_count. Render the warning + [Yes, delete] [No, cancel] chips. Only after user explicitly confirms in a SEPARATE turn, call again with confirmed=True. NEVER call with confirmed=True in the same turn as confirmed=False.
Soft-delete a custom field: deactivates referencing automation steps, removes per-contact values, and suffix-renames the field so its code is freed for reuse. Call get_custom_field_usage first to preview the impact and confirm with the user before invoking this tool.
Permanently and irreversibly delete a deal. ALWAYS show the deal name and amount to the user and ask for explicit confirmation before calling this tool. Only set confirmed=True after the user has confirmed. Requires deal_id — call search_deals first if you don't have it. Returns {'deleted': True, 'deal_id': '...'}.
Delete a contact list (soft delete).
Soft-delete a step (sets is_active=False). Matches the FE 'delete step' behavior. Hidden from the visual builder and skipped at runtime; row retained for audit + in-flight contact references.
Soft-delete a tag from the account. Returns contacts_affected (number of contacts that had this tag). Warn the user about contacts_affected before calling.
Delete a message template (soft delete).
READ-ONLY: list the account's configured channels (WhatsApp / SMS / Voice / Email / etc.). Use to resolve names like 'the SMS channel' or 'WhatsApp Italia' to a numeric channel_id for use in create_automation (OnFirstMessageStarter channel_id, inbound triggers) and add_step (freemessage / sms / templatemessage / paypal / stripe steps that scope to a channel). If `include_inactive=False` (default), returns only ACTIVE channels — the ones that can actually fire triggers and send messages. The agent CANNOT create or modify channels — those flows live in the FE (`/brand-registry/channels`) because they require purchase, carrier verification, and external onboarding. VOICE / WHATSAPP_VOICE channels additionally carry `voice_connected` (bool — whether the voice channel is actually provisioned/connected, mirrors the FE eligibility filter for the Spoki Voice step) and `quality_score` ('green'|'yellow'|'red'|'undefined' — used for WhatsApp-call rotation eligibility). Use these to pre-flight add_voice_step.
Get comprehensive account details including status, type, WhatsApp configuration, current credit (EUR), and plan info. When `credit_visibility` and `plan_visibility` are `partner_managed`, the partner prices and bills the account, so the response carries no balance and no plan — the client must ask the partner in `partner_name` instead.
How do I improve a ChatGPT Plugin's 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.
What are Spoki alternatives on ChatGPT?
As of 2026-09-28, Spoki competes with ActiveCampaign, Aivie, AWeber, beehiiv, Bizgo, Brevo, Conversion, Customer.io, Flodesk, Intuit Mailchimp, Kanal, Klaviyo, Knock, L Message(エルメ), Life Analytics Mail, Loops, magnews, MailerLite, Mailrith, MailSenpai, Nitrosend - AI Native Email, Notifly, Omnisend, OneSignal, Resend, SAMWAD, Sendly, Sent, subscline, Systeme.io, Textmagic, TrueDialog, VerticalResponse, Yournotify in ChatGPT Email & SMS Lifecycle Marketing Automation, ranked by public Discoverability Score.
Where is this profile measured?
This profile uses the geography attached to the latest public registry snapshot: US. Locale tags are intentionally omitted.