Skip to content

Compose & send a push

The TwistCue push composer respects the 3/day cap hardcoded on the backend and shows you the number of users who will be dropped before you send. This is the single most important safety net in engagement: respect it.

Engagement → Push (/engagement/push or /push) is the composer + campaign list.

  1. Name: internal-only, for the history log.
  2. Audience: pick a saved segment (Create a segment).
  3. Content:
    • Title (localized EN/AR/ID)
    • Body (localized EN/AR/ID)
    • Deep link: where tapping opens the app (e.g. twistcue://series/{seriesId})
    • Category: drops, marketing, moderation, product (users can opt out per category)
  4. Schedule: Send now, or pick a datetime (UTC).

Click Estimate before sending. The composer runs the segment query and reports:

Targeted: 21,340 users
- Capped: 1,213 users (would exceed 3/day)
- Category opted-out: 2,041 users
Deliverable: 18,086 users

The 3/day cap is enforced server-side in push-scheduler.cron.ts. It counts pushes in the past 24 hours per user, per category-hierarchy. If a push would be the user’s 4th, it’s silently dropped from the send list and logged.

  • Send now: flushes to the delivery queue immediately. Cron picks it up on the next minute.
  • Schedule: PushCampaign.status='scheduled' with scheduled_at. The push-scheduler cron (every minute) picks up scheduled campaigns whose time has passed.
sequenceDiagram
autonumber
participant Cron as push-scheduler cron
participant DB as Postgres
participant Filter as cap filter
participant APNs as APNs (iOS)
participant FCM as FCM (Android)
Cron->>DB: fetch scheduled campaigns due
DB-->>Cron: [campaign_a, ...]
Cron->>DB: resolve segment → user_ids
Cron->>Filter: apply 3/day cap + opt-outs
Filter-->>Cron: deliverable_user_ids
Cron->>DB: fetch device tokens for user_ids
Cron->>APNs: batch push (iOS tokens)
Cron->>FCM: batch push (Android tokens)
APNs-->>Cron: delivery receipts
FCM-->>Cron: delivery receipts
Cron->>DB: update campaign metrics (sent, delivered, opened)

Every campaign shows in the list with:

  • Sent: users the platform accepted the push for.
  • Delivered: carrier ack received.
  • Opened: users who tapped through (attribution window: 4 hours).
  • Open rate: opened / delivered.

Healthy open rates on TwistCue: 8–15% for drops category, 3–8% for marketing.

  • The 3/day cap is per user, not per campaign. A user targeted by three campaigns in the same day will get one push and be silently dropped from the other two.
  • APNs and FCM are stubbed in v2. See Integrations status: the delivery pipeline builds and logs but doesn’t hit real providers until you configure APNs P8 + FCM service account keys.
  • Deep links: must match a mobile-registered scheme (twistcue://…). Bad deep links open the app to the home screen (fallback).
  • Localized content: the delivery layer picks the language matching the user’s device locale. Missing translation falls back to EN.
  • Category opt-outs: users can turn off any category from mobile Settings. Opted-out users are counted as “not deliverable” and don’t consume from their 3/day.
ActionEndpoint
List campaignsGET /v1/admin/push/campaigns
DetailGET /v1/admin/push/campaigns/:id
CreatePOST /v1/admin/push/campaigns
UpdatePUT /v1/admin/push/campaigns/:id
DeleteDELETE /v1/admin/push/campaigns/:id
EstimatePOST /v1/admin/push/campaigns/:id/estimate
Send nowPOST /v1/admin/push/campaigns/:id/send
SchedulePOST /v1/admin/push/campaigns/:id/schedule
UnschedulePOST /v1/admin/push/campaigns/:id/unschedule

Full details in API: Engagement.