---
name: gentic-sms-marketing
description: "Give your AI agent your Postscript SMS subscriber list. Sync subscribers and keywords into your Brain with bounded, resumable syncs, then analyze them in natural language or with SQL via query_data — and join them against your orders, subscriptions and loyalty tables. Free to sync."
license: MIT
metadata:
  author: gentic
  version: "1.0.0"
---

# Gentic SMS Marketing

Connect any AI agent to your Postscript SMS list. Sync your subscribers and keywords into your organization's Brain, then ask the questions a subscriber list can actually answer: what share of revenue comes from SMS subscribers, how fast the list is growing and what the opt-out rate is, whether SMS opt-in tracks better retention, and which keywords are genuinely growing the list. Bounded, resumable syncs keep it current without re-reading everything.

## When to apply

- User wants to know what share of revenue or AOV comes from SMS subscribers.
- User wants list growth and opt-out rate over time.
- User wants to know whether SMS subscribers retain or resubscribe better than non-subscribers.
- User wants to know which keywords or sign-up sources are actually growing the list.
- User wants to sync their Postscript subscriber list into their Brain for analysis.
- User wants to join SMS opt-in status against orders, subscriptions, loyalty or survey data.

## Tools

| Tool | Description | Cost |
|------|-------------|------|
| `postscript_connection_status` | Check whether this organization has Postscript connected and whether the stored API key still works. Returns `{ connected, shop_id, shop_name, checked_live }`. Use this before `postscript_sync` when a sync has failed, to tell a credentials problem apart from an empty result.  Note that Postscript's API has no campaign or message-performance data at all, so no tool in this domain can report how an SMS send performed — for SMS campaign stats use `klaviyo_report`. FREE. | Free |
| `postscript_sync` | Sync this organization's Postscript SMS subscriber list into its data backend — READ-ONLY, one BOUNDED chunk per call. Uses the org's connected Postscript integration (dashboard → Integrations; no API key is passed here). Warehouses `postscript_subscribers` (id, ps_id, email, shopify_customer_id, per-channel opt-in status, tags, custom properties) and `postscript_keywords`, upserted on their id so re-syncs refresh rows in place. Keywords are never removed from the table, so a keyword deleted in Postscript remains as a historical row.  The subscriber list is the product here: joined to your Shopify orders it gives SMS-subscriber revenue share and AOV versus non-subscribers, joined to subscription or loyalty tables it shows whether SMS opt-in tracks retention, and on its own it gives list growth and opt-out rate over time. Opted-out subscribers are INCLUDED, so the table is the churn record as well as the active list.  Postscript's API exposes no campaign, automation or message-performance data, so there is nothing here about how a send performed — for SMS campaign stats use `klaviyo_report`. Query these tables with `query_data`; that is the analytics surface.  Phone numbers and customer names are never stored: they are removed before anything is written, and the removed field names are returned in `scrubbed_keys` and recorded on each row.  BOUNDED + RESUMABLE: each call pages until `max_pages_per_run` pages OR `max_seconds_per_run` seconds, then returns `has_more` + `next_cursor`. Call again passing that back as `cursor` until `has_more` is false. `total_pages` shows how far the walk has to go. FREE. | Free |

## Workflow

### 1. Connect Postscript once, then sync without credentials

The SMS Marketing server sources the Postscript connection (the Private API key) from the org's connected integration (Gentic dashboard → Integrations → Postscript). You never pass credentials to the tools. Call `postscript_connection_status` first — it's free and returns `{ connected, shop_id, shop_name, checked_live }`. Pass `check_live` when you want it to actually probe Postscript rather than just report that a key is stored; that distinguishes a credentials problem from an empty result, which is the ambiguity worth resolving before you debug a sync.

### 2. ⚠️ Know what this domain does NOT have, before you promise it

Postscript's API exposes **no campaign, automation or message-performance data at all**. No tool here can tell you how a send performed — open rate, click rate, revenue per message, none of it exists to read. If the user asks about SMS campaign performance, say so plainly and point them at `klaviyo_report` on the `/email-marketing` domain. Answering with subscriber-list numbers dressed up as campaign results is worse than saying it isn't available.

### 3. Sync the subscriber list with `postscript_sync`

`postscript_sync` is **free** and READ-ONLY. It warehouses `postscript_subscribers` (id, ps_id, email, shopify_customer_id, per-channel opt-in status, tags, custom properties) and `postscript_keywords`, upserted on their id so a re-sync refreshes rows in place. Two retention details worth knowing before you interpret the tables: opted-out subscribers are **included**, so the table is the churn record as well as the active list; and keywords are never deleted from the table, so a keyword removed in Postscript stays as a historical row.

### 4. Phone numbers and names are scrubbed before anything is written

Numbers and customer names never reach the warehouse — they're removed on ingest, and the removed field names come back in `scrubbed_keys` and are recorded on each row. That's deliberate: the list is analysable without warehousing contact details. It also means this domain cannot be used to retrieve someone's phone number, and a request to do so should be declined rather than worked around.

### 5. Walk the list with bounded, resumable pagination

Each `postscript_sync` call is BOUNDED: it pages until `max_pages_per_run` pages OR `max_seconds_per_run` seconds, then returns `has_more` and a `next_cursor`. Call again passing that back as `cursor` until `has_more` is false; `total_pages` tells you how far the walk still has to go. The result also carries `rate_limited_retries` — how many times the run was rate-limited and retried. It's the field that tells a short run apart from a throttled one: Postscript's page size is fixed at 25 and cannot be raised, so when that count is high the answer is to keep calling, not to raise the bounds. Use `resources` to sync only the table you need, and `updated_since` for an incremental refresh rather than a full backfill.

### 6. Count and aggregate with `query_data`

The SMS tools warehouse data; they don't analyze it. For subscriber counts, list growth, opt-out rate, or SMS-subscriber revenue share, use `query_data` (Gentic Data MCP) over `postscript_subscribers` and `postscript_keywords` — standard SQL GROUP BY / COUNT / SUM. Sync first (or re-sync incrementally) so the numbers are current.

### 7. Join against the rest of the business — this is the point

A subscriber list on its own gives you growth and opt-out rate. Everything more interesting comes from the join, and `shopify_customer_id` is already on the row: against orders for SMS-subscriber revenue share and AOV versus non-subscribers, against subscription tables (`skio_*` / `loop_*`) for whether SMS opt-in tracks retention, against loyalty (`rivo_*`) for whether your SMS list and your VIPs are the same people. Compose it with `query_data`.

### 8. Present results clearly

Don't dump raw JSON. Summarize the numbers — subscriber counts, growth, opt-out rate, revenue share — and cite them. After a sync, say how many rows landed per table and **how far through the walk you are** — `has_more` together with `total_pages`, never `has_more` alone. The boolean reads the same at 1% and at 99%, so “there’s more to page through” sounds like nearly-done when it can mean thousands of pages remain; at a fixed 25 records per page that difference is the whole answer. Also mention `rate_limited_retries` when it's non-zero, because it explains a run that returned less than expected. If the question was about campaign performance, lead with the fact that this data doesn't exist here rather than burying it under numbers that don't answer it.

## Notes

- Organization-scoped: the Postscript connection comes from the org's connected integration (dashboard → Integrations → Postscript). No credential is ever passed to the tools.
- Cost: both `postscript_connection_status` and `postscript_sync` are **free**. You pay only for analytics you run over the synced data (e.g. `query_data`). Pricing is pulled live from the Gentic MCP manifest.
- **No campaign or message-performance data exists in Postscript's API** — nothing here reports how a send performed. For SMS campaign stats use `klaviyo_report` on the `/email-marketing` domain.
- `postscript_sync` is READ-ONLY: it never sends a text, never edits a subscriber, and never writes back to Postscript.
- Phone numbers and customer names are removed before anything is written; the removed field names are returned in `scrubbed_keys` and recorded on each row.
- Opted-out subscribers are included in `postscript_subscribers`, so the table is the churn record as well as the active list.
- Keywords are never removed from `postscript_keywords`: a keyword deleted in Postscript remains as a historical row.
- Syncs are bounded and resumable: each call stops at `max_pages_per_run` / `max_seconds_per_run` and returns `has_more` + `next_cursor`; pass it back as `cursor` until `has_more` is false. `total_pages` shows the remaining distance, `rate_limited_retries` reports how often the run was rate-limited and retried, and `updated_since` gives an incremental refresh instead of a full backfill.
- The two tables — `postscript_subscribers` and `postscript_keywords` — are queried with `query_data` for all counts and aggregations.

## Tool details

- `postscript_connection_status` — Check whether this organization has Postscript connected and whether the stored API key still works. Returns `{ connected, shop_id, shop_name, checked_live }`. Use this before `postscript_sync` when a sync has failed, to tell a credentials problem apart from an empty result.  Note that Postscript's API has no campaign or message-performance data at all, so no tool in this domain can report how an SMS send performed — for SMS campaign stats use `klaviyo_report`. FREE.
  - `check_live` (boolean) — Make one real call to Postscript to verify the key still works (default false). The call reads only the shop's own identity — no subscriber data.
- `postscript_sync` — Sync this organization's Postscript SMS subscriber list into its data backend — READ-ONLY, one BOUNDED chunk per call. Uses the org's connected Postscript integration (dashboard → Integrations; no API key is passed here). Warehouses `postscript_subscribers` (id, ps_id, email, shopify_customer_id, per-channel opt-in status, tags, custom properties) and `postscript_keywords`, upserted on their id so re-syncs refresh rows in place. Keywords are never removed from the table, so a keyword deleted in Postscript remains as a historical row.  The subscriber list is the product here: joined to your Shopify orders it gives SMS-subscriber revenue share and AOV versus non-subscribers, joined to subscription or loyalty tables it shows whether SMS opt-in tracks retention, and on its own it gives list growth and opt-out rate over time. Opted-out subscribers are INCLUDED, so the table is the churn record as well as the active list.  Postscript's API exposes no campaign, automation or message-performance data, so there is nothing here about how a send performed — for SMS campaign stats use `klaviyo_report`. Query these tables with `query_data`; that is the analytics surface.  Phone numbers and customer names are never stored: they are removed before anything is written, and the removed field names are returned in `scrubbed_keys` and recorded on each row.  BOUNDED + RESUMABLE: each call pages until `max_pages_per_run` pages OR `max_seconds_per_run` seconds, then returns `has_more` + `next_cursor`. Call again passing that back as `cursor` until `has_more` is false. `total_pages` shows how far the walk has to go. FREE.
  - `resources` (array of string) — Subset of resources to sync this run: subscribers, keywords. Omit to sync both. Keywords are small and unpaginated; subscribers is the long walk.
  - `updated_since` (string) — Only fetch subscribers updated at or after this time (ISO-8601). Omit for the initial full backfill; on later runs omit it too and the sync derives its own starting point from what is already stored, reaching back 48 hours for safety. Pass an explicit value to override that.
  - `max_pages_per_run` (integer) — Cap on pages fetched this call (default 40, hard max 200). Postscript's page size is fixed at 25 records and cannot be raised. Page more via next_cursor, never via a bigger run.
  - `max_seconds_per_run` (integer) — Wall-clock budget for this call (default 60s, hard max 240s). With a fixed 25-record page this is usually the bound that bites first.
  - `cursor` — Pass back the `next_cursor` from the previous call, exactly as returned. It carries the filter as well as the position — Postscript's own cursor does not, so a hand-edited cursor is refused rather than silently walking a different list.

---

_This SKILL.md is generated from the live Gentic MCP manifest. Tool names, descriptions, and pricing are always current. Connect Gentic SMS Marketing at https://gentic.co/sms-marketing._
