Scheduling & Automation
Schedule posts for optimal times and set up recurring AI-powered content generation.
Scheduling Posts
You can schedule any post to publish at a specific date and time. When creating a post — whether through Chat, Compose, or by editing a draft — choose Schedule and pick your desired time.
Scheduled posts appear on your Calendar with a visual timeline. You can drag to reschedule, click to edit, or ask the AI assistant to reschedule by describing the change (e.g., "move my Monday post to Wednesday at 3 PM").
Calendar View
The Calendar page shows all your content at a glance:
- Scheduled posts shown with their target platforms
- Published posts with engagement stats
- Draft posts that still need attention
Switch between month, week, and day views. Click any post to see full details or make edits.
Automation Rules
Automation lets you set up recurring AI-powered content generation on a schedule. This is available on the Business plan.
To create an automation:
- Go to Automation in the sidebar
- Click Create Schedule
- Set the frequency (daily, weekdays, weekly, or custom cron expression)
- Write an AI prompt describing the content to generate (e.g., "Create an engaging Instagram post about our latest product features")
- Choose the target platforms and accounts
- Enable the automation
When the schedule triggers, the AI generates a post using your prompt, applies your brand voice, and publishes it automatically.
Require Human Review
Enable the Require human review toggle to add a human approval step before automated posts go live. When enabled:
- The AI generates the post as usual
- Instead of publishing immediately, the post enters a Pending Review state
- You receive an email notification that a post is ready for review
- Go to the Posts page and open the Review tab to see all posts awaiting approval
- Approve the post to schedule it for immediate publishing, or Reject it to move it to Drafts for editing
This is ideal for brand-sensitive accounts where you want AI assistance but want to maintain editorial control. You can also approve or reject posts via the AI assistant (e.g., "approve post [id]" or "show posts pending review").
Media Mode
Each automation can be configured with a media mode that controls whether AI-generated images or videos are attached to automated posts:
- Auto (default) — The AI analyzes the prompt and generated text to decide whether an image, video, or no media is appropriate
- Image — Always generate an AI image for the post
- Video — Generate a hero image and animate it into a short video clip
- None — Text-only posts, no media generation
Set the media mode when creating or editing an automation, either from the Automation page or via the AI assistant (e.g., "create a daily automation with image mode").
Background Music
When an automation generates video posts, you can optionally add AI-generated instrumental background music:
- None (default) — Videos are published without background music
- Auto — The AI decides whether background music would enhance the video based on the content
- Always — Instrumental background music is always generated and merged into the video
The music is generated using AI (royalty-free, instrumental only) and automatically mixed into the video at 30% volume with a fade-out at the end. This ensures the music enhances rather than overpowers the content.
You can also add music to videos manually from the Compose page (AI Generate → Music tab) or via the AI assistant (e.g., "generate upbeat background music for this video").
Content Recycling (Evergreen Posts)
Recycle your best-performing posts with AI-generated variations. Instead of generating new content from scratch, SonetHub rephrases an existing post each time it runs — varying the opening hook, restructuring paragraphs, and changing the call-to-action while keeping the same core message.
How to Set Up
From a post: View any published post → click Recycle → choose a frequency → done.
From the Automation page: Click New Schedule → select "Recycle existing post" → search and select one or more published posts → set frequency.
From the AI chat: Say "recycle my best post every Monday" or "set up evergreen posting for this post."
How Variations Work
Each run, the AI generates a unique variation. You control the variation intensity:
| Style | What changes | Best for |
|---|---|---|
| Subtle | Opening hook and CTA only | Posts where the exact wording matters |
| Moderate (default) | Restructured paragraphs, new hook, varied CTA | Most evergreen content |
| Creative | Full rewrite keeping core message | High-frequency recycling where variety is important |
Media from the original post is reused — no media regeneration needed.
Recycling Queue
You can add multiple posts to a single recycling automation. The system picks a different post each run (never the same one twice in a row) and generates a fresh variation.
Staleness Protection
After 20 variations, the automation auto-pauses to prevent quality degradation. You'll be notified so you can refresh the queue with new content.
Recommended Frequency
| Platform | Safe recycling gap |
|---|---|
| X / Twitter | Every 7-14 days |
| Threads | Every 14-30 days |
| Every 30-60 days | |
| Every 30-45 days | |
| Every 45-90 days |
Managing Automations
View all your automations on the Automation page. Click any automation to see its detail page with generated posts, engagement metrics, upcoming schedule, and settings.
Each automation shows its mode (Generate or Recycle), frequency, next run time, and status. You can:
- Toggle automations on/off
- Edit the prompt, schedule, or target accounts from the detail page
- Delete automations you no longer need
You can also manage automations through the AI assistant — just say "show my automations" or "create a daily posting schedule."
Automations
Automations react to events on your connected social accounts — incoming comments, @mentions, or DMs — and run a sequence of actions in response: reply, classify, tag, notify, hide, delete, or branch into multi-step flows. One concept, two source surfaces.
Every automation has:
- A trigger source —
comment(comments + mentions) ordm(direct messages). Set once at creation; immutable afterwards. - A trigger type — what specifically inside that source the automation fires on (keyword, sentiment, author, custom AI condition, etc.).
- An optional audience predicate — tag-based filtering on the contact (only fire on VIPs, skip if already a customer).
- A step tree — what to run when the trigger matches. A single-step tree is the simple case; multi-step trees chain actions with delays, branches, and waits.
The trigger + audience predicates live on the automation itself; everything that happens after a match lives in the step tree.
Account targeting
Each automation can target specific connected accounts in your workspace, or leave the selection empty to apply across every connected account.
For comment-source automations: accounts from platforms without comment management support (regular TikTok, Pinterest) can't be selected.
For DM-source automations: applies to every account in the workspace that supports DM sync (Instagram, Facebook, X/Twitter, LinkedIn, TikTok Business, Bluesky).
Comment-source scope (Apply To)
Comment-source automations can target one of four scopes:
- Comments — applies only to comments on your own posts
- Mentions — applies only to @mentions of your account on others' posts
- Comments + Mentions — applies to both (the historical default; "Both" in the API)
- Story replies — applies to DMs that reply to one of your Instagram stories (see Story replies below). Treated as comment-source because a reply to your story is engagement on your content, even though it arrives as a DM.
The canvas trigger picker disables any pill whose underlying platform capability isn't supported by the accounts you've selected. Mentions, for instance, requires at least one account on a platform that tracks @mentions — see the Platform capability matrix for the full table.
Destructive actions (delete, hide) only work on comments on your own posts. When an automation with a delete or hide action matches a mention on someone else's post, the action is skipped for that item.
For Story replies automations, the target-posts picker filters to imported stories only — feed posts and reels can't have story replies, so the validator rejects mismatched targeting at save time. Likewise for Comments / Mentions / Both the picker excludes stories (stories don't have comments).
Trigger types
Shared across both sources
- Keyword match — the item contains (or does not contain) specific words or phrases
- Author criteria — based on the author's follower count or verification status
- Custom (AI) — define a natural language condition (e.g., "the comment is about a collaboration or partnership") and the AI evaluates each item against it. No credit cost — uses a fast classification model, gated by per-cycle evaluation limits.
Comment-source only
- Sentiment — the item's sentiment is positive, negative, or neutral (detected by AI classification)
- All comments — catch-all trigger that matches every incoming comment/mention
- First-time commenter / Nth-time commenter — fires only on the first (or Nth) comment from a given contact on this workspace × platform
DM-source only
- All messages — catch-all trigger that matches every incoming DM
- Category — match against the AI-classified message category (collaboration, support, spam, urgent)
- First-time DM / Nth-time DM — fires only on the first (or Nth) DM from a given contact on this workspace × platform
Action types
Shared across both sources
- Add tag / Remove tag — modify the contact's tag list
- Set field — store a typed value on the contact (country, loyalty score, last purchase date…)
- Unsubscribe contact — flip the contact's opt-in status to unsubscribed; subsequent reply / reply_dm / auto_reply automations skip them workspace-wide
- Notify team — email every workspace member who opted into notifications
Comment-source only
- Delete — permanently remove the comment. Supported on: Instagram, Facebook, X/Twitter, LinkedIn, Threads, YouTube, TikTok Business, Bluesky
- Hide — hide the comment so only the author can see it. Supported on: Instagram, Facebook, X/Twitter, Threads, TikTok Business
- Like — like the item on behalf of your account. Supported on: Facebook, X/Twitter, LinkedIn, TikTok Business, Bluesky
- Reply (template) — public reply with a predefined message. Supports
{{author}}and{{platform}}placeholders. - Reply (AI) — public reply generated using your brand voice. See AI-generated replies below.
- Reply privately (DM) — send a private message to the comment author instead of a public reply. On Instagram, this uses Meta's Private Replies API (sends a DM based on the comment ID — no need for the commenter's user ID). Also available on Facebook, X/Twitter, and Bluesky. Works within 7 days of the comment on Instagram/Facebook. This enables ManyChat-style "comment keyword → auto-DM" flows.
- Flag as important — mark the item for manual review in SonetHub
DM-source only
- Classify important — mark the DM as important in SonetHub (no outbound action)
- Auto-reply — AI-generated reply based on guidance you provide and your brand voice
- Skip — explicitly suppress downstream automation evaluation for this event
If a selected account's platform doesn't support the chosen action, that account is silently skipped during execution.
AI-generated replies
When using Reply (AI) or Auto-reply, the reply is generated using your workspace's brand voice and memories. The AI considers the comment/message text, sentiment, category, and your brand guidelines to produce a natural response. Each AI-generated reply consumes 1 credit (fast tier — single-shot reply without tools).
For comment-source AI replies you can provide additional guidance beyond your brand voice — for example, "don't mention pricing" or "keep the tone casual and friendly." This guidance is applied on top of your brand voice settings.
For DM-source auto-replies, the reply instructions field is required — describe what the AI should say and the tone to use.
Spam keyword presets
When creating a keyword-triggered automation, click "Add spam keywords" to pre-fill common spam patterns (e.g., "DM me", "follow for follow", "check my bio", "crypto"). Presets are available in English and Spanish and merge with any keywords you've already added.
Audience predicates
Every automation (any trigger, any action) can be scoped to a segment of your contacts:
- Only fire if tagged — automation only matches when the contact has ALL the listed tags
- Skip if tagged — automation only matches when the contact has NONE of the listed tags
The two work together (AND).
Evaluation order
Automations are evaluated in priority order (higher number = higher priority, evaluated first). The first matching automation wins — once an item matches, no further automations are evaluated for that item.
As a safety measure, each sync cycle applies a maximum of 50 automated actions per workspace. Items beyond this cap are left unprocessed until the next sync.
Per-post automation (comment-source only)
By default, a comment-source automation fires across every post in the workspace. You can scope it to specific posts when you want it to behave like a campaign-bound automation — "comment KEYWORD on this launch post and I'll DM you the link", "auto-flag comments on last week's giveaway while the contest is live."
DM-source automations have no associated post; per-post targeting doesn't apply.
Targeting specific posts
Open the automation's Targeting section and select one or more posts in the picker. The automation fires only when a comment lands on those posts. Leave the picker empty to keep it workspace-wide.
The picker shows your published and imported posts (both can receive comments). You can search by post content.
Activation window
Optionally set an Activate from and/or Activate until datetime. Outside that window, the automation is silent. Useful for:
- Time-boxed contests — "active for 48h after publishing the giveaway"
- Launch windows — "starts when the pre-order opens, ends when it closes"
- Pre-scheduled automations — "fires only during the campaign next month"
Both fields are optional. Setting only "until" makes the automation end at a time; setting only "from" delays the start. The form blocks an inverted window (until earlier than from).
Caps
Two optional caps prevent an automation from spamming on viral posts:
- Max fires per post — a hard cap. Once the automation has fired this many times on a given post, it stops firing on that post. Use for "first 100 commenters get a discount DM" style campaigns. The cap is approximate during very rapid bursts — you may end up a few over the limit on viral posts.
- Fires per hour cap — a rolling rate limit. At most this many fires per post per hour. Use to smooth out viral-post bursts.
Both are per-(automation × post). Different posts each have their own counter.
Multi-step automations
A single-step tree is the simple case ("when X happens, like the comment"). Multi-step trees chain multiple actions together with delays, branches, and waits for replies.
Step kinds
- Outbound — reply (public or DM), auto-reply (AI-generated), or send a new message
- Moderation — delete, hide, or like a comment. Flag as important. Classify as important (DMs only). Skip (suppress downstream automations for this event).
- Audience writes — add or remove tags, set a custom field, unsubscribe the contact
- Team notification — notify workspace members by email
- Control flow:
- Delay — pause for N minutes before the next step. Minimum 1 minute; precision ±60 seconds.
- Wait for reply — park the automation until the contact replies OR the timeout elapses. Optional keyword filter (only replies containing X advance the wait; others fall through to normal automation evaluation).
- If/else — branch on a condition. Children execute on the matched branch only.
Condition expressions
The if/else branch uses a deterministic mini-language (parsed at save time, evaluated in ~1ms — no LLM cost):
{{lastReply.text}} contains "yes"
{{lastReply.text}} matches /^(buy|purchase)/i
{{contact.tags}} includes "vip"
{{contact.field.country}} == "ES"
{{contact.optInStatus}} == "subscribed"
Compose with &&, ||, and parentheses:
{{contact.tags}} includes "vip" && {{lastReply.text}} contains "yes"
Worked example: comment → DM → wait → follow-up
A typical "comment growth" flow: someone comments PRICE on your latest post, you DM them, wait 24 hours, then send a discount if they didn't reply.
- Trigger — keyword
PRICEon the launch post (comment-source) - Step 1 —
reply_dm: "Hi! Here's pricing: …" - Step 2 —
wait_for_replywith timeout 1440 minutes (24h) - Step 3 —
if_elseon{{lastReply.text}} contains "no thanks":- True branch:
add_tagnot-interested - False branch (no reply or anything else):
send_messagewith 10% off code, thenadd_tagdiscount-claimed
- True branch:
Async execution + the 24h Meta window
Steps that take time (delay, wait_for_reply) park the automation in a queue. A background tick wakes it on schedule. The original triggering event opens a 24-hour Meta messaging window; any DM-shaped step that runs after the window closes fails with a clear error rather than attempting an out-of-window send.
If you need to message a contact more than 24h after their last engagement, you'll need a fresh inbound event from them.
Loop guard
The same (automation, contact) pair fires at most once every 30 seconds — matching ManyChat's behaviour. This prevents a rapid burst of comments from the same person triggering multiple DMs.
Story replies
Story replies are SonetHub's hook for the most-used Instagram-creator automation pattern: "when someone replies to my story, send them a DM." On Instagram, a reply to a story is technically a DM (it lands in the recipient's inbox), but it's anchored to a specific story you posted — which is the part that makes it useful as a trigger.
What makes story replies different
A normal DM arrives in /inbox with no inherent context — sender, text, that's it. A story reply arrives with the story it was anchored to:
- The inbox renders a "Replied to your story" quote card above the message bubble, with the story thumbnail and an "Open story" link.
- The post detail page for the story shows a Story Replies panel listing every DM reply with a deep-link to the relevant conversation.
- The automation engine routes the event into the
mentionstable withtype='story_reply'andpostIdlinked to the story post — so trigger conditions can scope to specific stories (the same way comment automations scope to specific posts).
Pre-Slice-2, story replies were just generic DMs and most of this context was discarded. Now every story reply you import or receive carries the link back to the originating story.
Setup: from story to fired automation
The full lifecycle, all wired together:
- Publish (or import) the story. SonetHub-published stories land in
postswithpost_format='story'automatically. Instagram stories created outside SonetHub are imported every 30 minutes (or instantly when you reconnect an account) via the/me/storiespolling pipeline. Each story row has a 24-hourstoryExpiresAt— after expiry, the post is still in the DB but the platform-side story is gone. - Build the automation. On
/automation/canvas/new, pick Story replies as the trigger source. The post picker filters to your stories only (live + archived). Set a keyword filter if you only want the automation to fire on certain reply texts (PRICE,INFO, etc.). - Add steps. Most story-reply flows look the same as comment-growth flows:
send_messagewith text + a Card template, optionalwait_for_reply+if_elsefor follow-ups. Reply paths work because the story reply itself opens Meta's 24-hour conversation window. - Recipient replies to your story. Meta delivers a webhook within ~2 seconds. SonetHub writes two rows: an
inbox_messagesrow (renders in/inbox) and amentionsrow withtype='story_reply'(drives the automation eval). The runner fires your automation.
Worked example: story → DM → wait → follow-up
You post a story teasing a new product, with the prompt "DM me 'LAUNCH' for the link." Followers reply to the story with "LAUNCH" in the text.
- Trigger — keyword
LAUNCH, applyTostory_replies, optionally scoped to that specific story via the post picker - Step 1 —
send_message: "Thanks! Here's the link: …" (uses a Card template with the product image) - Step 2 —
wait_for_replywith timeout 1440 minutes (24h) - Step 3 —
if_elseon{{lastReply.text}} contains "buy":- True:
add_taghot-lead+ send a discount code - False: send a soft follow-up,
add_tagcold-lead
- True:
Identical shape to the comment growth flow above. The only difference is the trigger source.
Platform support
Story replies are Instagram and Facebook Pages only. Both deliver story-reply DMs via Meta's webhook pipeline.
- Instagram — both auth paths (Instagram Login + Facebook Login) deliver story replies. SonetHub-published stories AND
/me/stories-imported stories both trigger. - Facebook Pages — story replies on Pages deliver too, but Pages story import is constrained by Meta's API (no read endpoint for own-page stories). Slice 2 only fully implements IG story import; FB Pages story-reply triggers fire correctly for stories captured via inbound-reply lazy-lookup.
X / Bluesky / LinkedIn / Threads / TikTok / YouTube / Pinterest don't have a story concept that maps onto this model — the "Story replies" pill in the canvas is disabled when none of your in-scope accounts are IG/FB.
The 24-hour window applies here too
Story-reply automations interact with Meta's 24-hour messaging window like every other DM automation: the story reply itself opens the window, so the first send_message step always lands within it. If your flow chains a delay or wait_for_reply past 24 hours and then tries another DM, the runner rejects it unless you configured a message tag on the step.
Inbox-side coverage
The inbox surfaces the same story chip whether or not an automation fires. Even with no automation set up, every story reply renders with the originating-story context in /inbox. This is documented separately in the Inbox & DMs guide — the chip behavior + the lazy-fetch endpoint that hydrates the thumbnail.
Rich DMs — media, quick replies, templates
Any outbound DM step (send_message, reply_dm, the DM half of reply_comment) can carry more than plain text. The drawer surfaces the rich fields as optional toggles, off by default — a one-line DM stays a one-line DM.
Attach media
Drop in an image, video, audio, or document from your media library (same picker used by the composer). The drawer re-signs the storage path at send time so the URL is always fresh, even months after you built the automation.
Per-platform behaviour when an automation runs across mixed account types:
- Instagram, Facebook Pages — full delivery (image, video, audio, document). Image attachments cached server-side so the same 100 commenters don't re-upload your hero image 100 times.
- Bluesky — media is dropped silently and the run log records
degraded: 'unsupported_by_adapter'. The drawer shows a banner when only Bluesky is in-scope. - X / Twitter — text only today. Media attachments are dropped and the run log records
degraded: 'unsupported_by_adapter'. (The v1.1 chunked-upload path that v2 DM requires for media is a future slice.)
Quick replies
Up to 3 tappable chips below the message. Each chip is { label ≤20 chars, payload ≤256 chars }. When the recipient taps a chip, the chip's payload arrives as the next inbound message — perfect for wait_for_reply + if_else flows ("PRICE → DM with [Yes please] [No thanks] → wait → branch on the tapped payload").
- Instagram, Facebook Pages — delivered as native quick-reply chips.
- Bluesky — synthesized as a numbered text suffix ("1. Yes please\n2. No thanks"). Recipients reply with
1/2or the label text and the wait advances. - X / Twitter — quick replies are not supported on the v2 DM API and are dropped at the adapter boundary.
DM templates — buttons or single card
For ManyChat-style "message box with persistent buttons", use the Template toggle in the drawer. Two shapes:
- Buttons — a text body plus up to 3 buttons. Each button is either a
url(opens a webview) orpostback(sends a payload back, same shape as quick-reply taps). - Card — image + title + optional subtitle + optional tap-the-card URL + up to 3 buttons. The richer "swipeable card" look in the chat.
Templates replace the message body when set — the step's regular text field can be empty because the template carries the content. Templates only deliver as native rich UI on Instagram and Facebook Pages. On Bluesky / X the structured template is dropped: recipients see the body text on a Buttons template, or the title (and subtitle, on its own line) on a Card. Buttons inside a template are not delivered on those platforms — if you need tappable choices on Bluesky, use the Quick replies field instead, which IS synthesized as a numbered text suffix.
Message tag — outside the 24h window
When an automation could fire outside the 24-hour Meta messaging window (long delays, scheduled re-engagement), pick a Message tag in the drawer. After 2026-04-27 only HUMAN_AGENT remains valid; the legacy tags (CONFIRMED_EVENT_UPDATE, ACCOUNT_UPDATE, POST_PURCHASE_UPDATE) are deprecated and the drawer surfaces a warning when you pick one. Without a tag set, a step that runs outside the window fails-fast with conversation_window_expired in the run log rather than attempting a forbidden send.
Managing automations
Create and manage automations from the Automation page, or through the AI assistant. The same agent tool covers both comment-source and DM-source:
"Create an automation to auto-hide comments containing spam keywords" "Set up an automation to like all positive comments on my Instagram" "Create an automation to auto-reply to collaboration DMs with AI" "When someone comments PRICE on my launch post, DM them the pricing link, wait 24h, and if they don't reply send a 10% off code."
The assistant builds the step tree directly. Review it before activating.
Plan limits
The automation budget is a single shared cap across both comment-source and DM-source — you decide how to spend it across surfaces.
| Plan | Automations (total) | Steps per automation |
|---|---|---|
| Free | 0 | — |
| Starter | 6 | 1 (single-step only) |
| Growth | 20 | 5 |
| Pro | 50 | Unlimited |
| Business | Unlimited | Unlimited |
The engine ceiling on per-tree size is 200 steps regardless of tier. When you hit the tier's step cap, the validator returns PLAN_STEP_LIMIT_EXCEEDED with an upgrade hint pointing at the next tier's allowance.
Platform capability matrix
What each connected social platform's API actually supports. The canvas grays out trigger pills, step kinds, and rich-DM toggles whose underlying capability isn't available on any in-scope account, so you can't accidentally build a never-firing automation. The agent (manage_automations tool) does the same check server-side.
Source of truth: src/lib/social/platform-capabilities.ts. If this table ever disagrees with the canvas, the file wins — flag a bug.
Comments & mentions
| Platform | Public reply | Like | Hide | Delete | Mention tracking | DM-from-comment |
|---|---|---|---|---|---|---|
| ✓ | — | ✓ | ✓ | ✓ | ✓ | |
| Facebook Pages | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| X / Twitter | ✓ | ✓ | ✓ | ✓ | ✓ | — |
| Bluesky | ✓ | ✓ | — | ✓ | ✓ | ✓ |
| ✓ | ✓ | — | ✓ | — | — | |
| Threads | ✓ | — | ✓ | ✓ | — | — |
| YouTube | ✓ | — | — | ✓ | — | — |
| TikTok Business | ✓ | ✓ | ✓ | ✓ | ✓ | — |
| — | — | — | — | — | — | |
| TikTok (consumer) | — | — | — | — | — | — |
Notes:
- Mention tracking means SonetHub captures @-mentions of your handle in OTHER users' posts. Without it, the "Mentions" trigger pill is greyed for that platform.
- DM-from-comment = "Private Replies" on Meta. Open the 24h messaging window from a single comment without the recipient having to DM you first. X/LinkedIn/Threads/YouTube/TikTok don't have an equivalent — the runner falls back to a regular DM send, which only works if the recipient's account allows it.
- YouTube has a "moderate" workflow (approve/reject pending comments) we surface as the
deleteaction — it's the YouTube equivalent.
Direct messages
| Platform | Send DM | Media attachments | Quick replies | Button template | Card template | Outside-24h tag |
|---|---|---|---|---|---|---|
| ✓ | ✓ | ✓ native | ✓ | ✓ | ✓ | |
| Facebook Pages | ✓ | ✓ | ✓ native | ✓ | ✓ | ✓ |
| X / Twitter | ✓ paid tier | — | — | — | — | — |
| Bluesky | ✓ | — | ✓ as numbered text | — | — | — |
| — | — | — | — | — | — | |
| Threads | — | — | — | — | — | — |
| YouTube | — | — | — | — | — | — |
| TikTok Business | — | — | — | — | — | — |
| — | — | — | — | — | — | |
| TikTok (consumer) | — | — | — | — | — | — |
Notes:
- X / Twitter DMs require a paid API tier (Basic+) and currently support text only — no media, no quick replies, no templates. Free-tier accounts won't deliver DM-source automations at all.
- Bluesky quick replies are synthesized as a numbered text suffix ("1. Yes\n2. No") because the
chat.bsky.convolexicon has no native button primitive. Functionally equivalent forwait_for_reply+if_elseflows. - Templates are Meta-only (Button = text + 3 buttons; Card = image + title + subtitle + buttons). On Bluesky/X the template's text body is sent as plain text; buttons inside the template are dropped.
- Message tag unlocks outside-the-24h-window sends. After 2026-04-27 only
HUMAN_AGENTis valid; the legacy tags (CONFIRMED_EVENT_UPDATE, ACCOUNT_UPDATE, POST_PURCHASE_UPDATE) are deprecated.
Trigger events
| Platform | Comment received | @mention received | Story reply received | DM received |
|---|---|---|---|---|
| ✓ webhook | ✓ webhook | ✓ webhook | ✓ webhook | |
| Facebook Pages | ✓ webhook | ✓ webhook | ✓ webhook | ✓ webhook |
| X / Twitter | ⏱ poll (1h) | ⏱ poll (1h) | — | ⏱ poll (1h, paid tier) |
| Bluesky | ⏱ poll (30m) | ⏱ poll (30m) | — | ⏱ poll (30m) |
| ⏱ poll (1h) | — | — | — | |
| Threads | ⏱ poll (1h) | — | — | — |
| YouTube | ⏱ poll (1h) | — | — | — |
| TikTok Business | ⏱ poll (1h) | ⏱ poll (1h) | — | — |
| — | — | — | — | |
| TikTok (consumer) | — | — | — | — |
Notes:
- Webhook = real-time delivery (~1-2 sec latency). Only Meta gives us this on free tiers.
- Poll (Xh) = cron-based capture at the listed cadence. X paid-tier accounts with the Filtered Stream API could theoretically be near-real-time but we don't implement that today; standard polling latency is ~1 hour for X.
- Story reply is the only platform-specific trigger event we expose — it routes through the
mentionstable withtype='story_reply', scoped to stories the workspace owns.
Contacts & Tags
A contact is anyone who's engaged with one of your connected social accounts — by commenting on your posts, mentioning you, or sending you a DM. SonetHub keeps a record per platform per person so you can tag them, write rules that depend on past behaviour (e.g., "first-time commenter"), and look back at every conversation across all your accounts in one place.
What contacts are NOT:
- Not your followers. Following you isn't an engagement; the platform doesn't tell us about silent followers.
- Not customers. Purchases aren't tracked unless you add a custom field manually.
- Not unified across platforms. Twitter @foo and Instagram @foo are two contacts, even if it's the same human — there's no reliable identifier across platforms. (You can manually link them by giving both rows the same
emailcustom field.)
Scoping contract
Contacts are keyed (workspace × platform × external user):
- Multiple accounts of the same workspace on the same platform (e.g., two Instagram pages) share one row per contact. The contact is the platform user, not the page. Tags applied via either account stick to the same record.
- Engagements (comments, mentions, DMs) keep their originating account ID, so analytics can still filter "VIPs who engaged with the Spanish brand account."
Where to find them
The /contacts page (sidebar entry next to Inbox) is the audience surface:
- List view — search by handle/name, filter by account, opt-in status, or tag. Each row shows the platform-coded edge, follower count, last-seen time, and current tags as chips.
- Detail drawer — click any contact for the full profile: tags + custom fields + a unified timeline of every comment and DM across the workspace.
- Bulk actions — select multiple contacts to tag them or unsubscribe them in one click. The bulk-action bar slides up when you have selections; it disappears when you don't.
Tags + custom fields
Tags are sticky labels per contact: vip, course-buyer, discount-claimed. You apply them manually from the page, automatically from rules (add_tag action), or via the AI assistant ("tag everyone who commented PRICING as pricing-interest"). Tags carry across rules and flows, so a rule can say "skip if this contact is tagged customer."
Custom fields are typed key/value pairs: country = ES, loyalty_score = 84, last_purchase = 2026-04-12. Use them when a tag isn't enough — when you need to remember a value, not just a yes/no.
Opt-in + GDPR
Every contact has an opt-in status: subscribed (default — they reached out, that's their consent), unsubscribed, or paused. Setting a contact to unsubscribed makes every workspace-wide auto-reply rule skip them. The contact-detail drawer also offers full deletion (cascade-removes tags, fields, and the workspace's link to that contact).
Automating tags + fields from automations
Automations can read and write the contact's audience layer:
- Add tag — apply tag(s) to the contact when the rule matches
- Remove tag — clear tag(s)
- Set custom field — store a typed value on the contact
- Unsubscribe contact — flip the contact's opt-in status to unsubscribed; subsequent auto-reply rules skip them
These actions are available alongside the existing reply/hide/delete actions. You can configure them through the rule editor or the AI assistant ("when someone DMs me about a collaboration, tag them as collab-inquiry").
Audience triggers
Beyond keyword and sentiment, rules can fire on the contact's engagement history:
- First-time commenter / first-time DM — fires only on the very first comment or DM from a given contact on this workspace + platform. Great for welcome flows and onboarding tags.
- Nth-time commenter / Nth-time DM — fires on the Nth event from a given contact (you choose N). Use for loyalty patterns ("tag as
engagedafter the 5th comment") or escalation ("flag for review after the 3rd unanswered DM").
Audience filters on every automation
Every automation (any trigger, any action) can be scoped to a segment of your contacts:
- Only fire if tagged — automation only matches when the contact has ALL the listed tags
- Skip if tagged — automation only matches when the contact has NONE of the listed tags
The two work together (AND). Examples:
- "Only DM the VIPs back" → keyword trigger +
Only fire if tagged: vip - "Skip the welcome message for already-tagged customers" → first-time-commenter trigger +
Skip if tagged: customer - "Send the discount only to people on the newsletter who haven't already claimed one" → keyword trigger +
Only fire if tagged: newsletter+Skip if tagged: discount-claimed
Audience filters compose with the trigger — they constrain who an automation applies to, on top of the existing when.
Plan Limits — contacts
| Plan | Tags | Custom fields per contact |
|---|---|---|
| Free | — | — |
| Starter | Unlimited | 5 |
| Growth | Unlimited | 10 |
| Pro | Unlimited | Unlimited |
| Business | Unlimited | Unlimited |