Skip to Content
splashify CLIEmail marketing

Email marketing

The email command mirrors the entire email marketing surface across the app — /email, /settings/email-domain, /email/templates, /email/audience, and /email/campaigns. From the terminal you can manage authenticated sender domains (DKIM/SPF/DMARC), template content, audience contacts and segments, and end-to-end campaigns including scheduling, sending, and cancellation.

Backed by /api/v1/app/email/*. Every call uses your stored oc_live_ token.

Quick start

# 1. Verify a sender domain splashify email domain add example.com splashify email domain verify example.com # checks DNS records splashify email domains # see all + status # 2. Build a template splashify email template create \ --name "Spring promo" \ --subject "20% off this week" \ --html-file ./spring-promo.html # 3. Stage an audience splashify email audience contact add --email alice@example.com --name "Alice" splashify email audience segment create --name "Active subscribers" \ --filters '{"conditions":[{"field":"is_subscribed","operator":"equals","value":true}],"logic":"and"}' # 4. Send a campaign splashify email campaign create \ --name "Spring launch" \ --template <template_id> \ --from "promos@example.com" \ --segment <segment_id> splashify email campaign send <campaign_id>

Command tree

email ├── stats / dashboard global metrics ├── domains list authenticated sender domains ├── domain │ ├── add <domain> create + return DNS records to publish │ ├── verify <domain> re-check DNS (DKIM/SPF/DMARC/Return-Path) │ └── delete <domain> remove a domain ├── templates list templates ├── template │ ├── <template_id> show one │ ├── create POST a new template │ ├── update <template_id> PUT changes (RMW) │ ├── delete <template_id> remove │ ├── preview <template_id> render preview (HTML + plain text) │ └── stats <template_id> usage counts ├── audience │ ├── contacts list audience contacts │ ├── contact │ │ ├── add add a contact │ │ ├── update <contact_id> edit │ │ └── delete <contact_id> remove │ ├── segments list audience segments │ └── segment │ ├── create new segment │ ├── update <id> edit │ ├── delete <id> remove │ ├── add-contacts <id> bulk-add contacts to a segment │ └── remove-contacts <id> bulk-remove └── campaigns list campaigns └── campaign ├── <campaign_id> show one ├── create draft a new campaign ├── send <id> send immediately └── cancel <id> cancel a scheduled / running campaign

Domain authentication

Email deliverability hinges on SPF/DKIM/DMARC alignment. Both providers (Meta and the partner backend) refuse to send from unverified domains.

# Add a domain — backend returns DKIM CNAMEs + SPF/DMARC TXT records to publish splashify email domain add example.com # After publishing the DNS records, verify splashify email domain verify example.com # Re-list with verification status splashify email domains | jq '.domains[] | {domain, dkim_verified, spf_verified, dmarc_verified}'
Backed byGET /app/email/domains
POST /app/email/domains {domain}
POST /app/email/domains/:domain/verify
DELETE /app/email/domains/:domain

Verification is idempotent — re-run after each DNS edit; the backend re-resolves each record and updates the row.

Templates

# Inline HTML splashify email template create \ --name "Welcome" --subject "Welcome to our store" \ --html "<h1>Hi {{first_name}}</h1><p>…</p>" # From a file splashify email template create \ --name "Spring promo" --subject "20% off" --html-file ./promo.html # Update (RMW — backend's PUT replaces every column) splashify email template update <template_id> --subject "New subject only" # Render preview (handy for QA before sending) splashify email template preview <template_id>
Backed byGET /app/email/templates
POST /app/email/templates
PUT /app/email/templates/:template_id
DELETE /app/email/templates/:template_id
GET /app/email/templates/:template_id/preview
GET /app/email/templates/:template_id/stats

Variables. Use {{first_name}}, {{email}}, or any contact attribute as a placeholder. The backend renders them per recipient at send time.

Audience

The audience surface is distinct from the messaging surface — these are email-only contacts and segments, scoped to your domain.

# Contacts splashify email audience contacts # list splashify email audience contact add --email alice@example.com --name "Alice" splashify email audience contact update <contact_id> --name "Alice Smith" splashify email audience contact delete <contact_id> # Segments (filter DSL same shape as messaging segments) splashify email audience segments splashify email audience segment create \ --name "Active subscribers" \ --filters '{"conditions":[{"field":"is_subscribed","operator":"equals","value":true}],"logic":"and"}' splashify email audience segment update <id> --name "Refreshed" splashify email audience segment delete <id> # Bulk membership splashify email audience segment add-contacts <id> \ --contact-ids c1,c2,c3 splashify email audience segment remove-contacts <id> \ --contact-ids c1
Backed byGET/POST/PUT/DELETE /app/email/audience/contacts[/:id]
GET/POST/PUT/DELETE /app/email/audience/segments[/:id]
POST /app/email/audience/segments/:id/add-contacts
POST /app/email/audience/segments/:id/remove-contacts

Campaigns

# Draft splashify email campaign create \ --name "Spring launch" \ --template <template_id> \ --from "promos@example.com" \ --segment <segment_id> \ [--schedule 2026-06-01T10:00:00Z] # Lifecycle splashify email campaigns # list splashify email campaign <campaign_id> # detail splashify email campaign send <campaign_id> # send now (skips/overrides schedule) splashify email campaign cancel <campaign_id> # cancel scheduled or in-flight
Backed byGET /app/email/campaigns
POST /app/email/campaigns
GET /app/email/campaigns/:id
POST /app/email/campaigns/:id/send
POST /app/email/campaigns/:id/cancel
FlagRequiredNotes
--nameyesDisplay name
--templateyestemplate_id
--fromyesA verified domain address — must match one of splashify email domains with *_verified: true
--segmentyesAudience segment_id
--schedulenoISO 8601 UTC; omit to send via send

Same preflight as broadcasts — campaigns refuse if no active plan / trial or wallet balance is insufficient for the recipient count × per-email rate.

Stats and reporting

splashify email stats # global dashboard splashify email template stats <template_id> # opens / clicks / bounces per template splashify email campaign <campaign_id> | jq '.stats' # per-campaign delivery
Backed byGET /app/email/stats
GET /app/email/templates/:id/stats

Common workflows

Verify every domain in one go

splashify email domains | jq -r '.domains[].domain' | \ xargs -I{} splashify email domain verify {}

Bulk-import an audience from CSV

# csv: email,name while IFS=, read -r email name; do splashify email audience contact add --email "$email" --name "$name" done < audience.csv

Wait for a scheduled campaign to finish

CID=<campaign_id> while : ; do STATUS=$(splashify email campaign "$CID" | jq -r '.campaign.status // .status') echo "$(date -u +%H:%M:%S) $STATUS" [ "$STATUS" = "completed" ] || [ "$STATUS" = "cancelled" ] && break sleep 30 done

Find templates with low open rates

splashify email templates | jq -r '.templates[].template_id' | \ while read tid; do splashify email template stats "$tid" | \ jq --arg t "$tid" '{template_id: $t, opens: .stats.opens, sends: .stats.sends, open_rate: (.stats.open_rate // 0)}' done | jq -s 'sort_by(.open_rate)'

Re-send a campaign to a fresh segment

# Clone a campaign by copying its config to a new one OLD=$(splashify email campaign <old_id>) TPL=$(echo "$OLD" | jq -r '.campaign.template_id') FROM=$(echo "$OLD" | jq -r '.campaign.from_email') splashify email campaign create --name "Re-run May" \ --template "$TPL" --from "$FROM" --segment <new_segment_id>

Troubleshooting

“Domain not verified” on campaign create — --from references a domain that hasn’t passed all four DNS checks (DKIM, SPF, DMARC, Return-Path). Run splashify email domain verify <domain> after DNS propagates. dig CNAME splashifypro._domainkey.<domain> confirms DKIM locally.

“insufficient_balance” on campaign send — wallet won’t cover the campaign cost (recipients × per-email rate). The error includes the shortfall.

Preview shows raw {{first_name}} — variables only resolve at send time against a real contact. The preview endpoint uses sample values when available, but missing fields print as-is.

Audience contact import is slow — the CLI sends one POST per row. For thousands of contacts, batch via the segment add-contacts endpoint: upload contacts in parallel, collect their IDs, then call add-contacts once with the full list.

See also