Expo Push (Classic)

Scheduling and AI Push

Schedule one-off sends, daily or weekly rules in their own timezone, quiet hours and AI Push.

Schedule a one-off send

POST https://app.nativenotify.com/api/schedule/notification
{
  "appId": 123,
  "appToken": "yourAppToken",
  "title": "Service this Sunday",
  "body": "Doors open at 9:00.",
  "pushData": { "screen": "events" },
  "timezone": "America/New_York",
  "year": 2026, "month": 9, "day": 27, "hour": 9, "minute": 30,
  "utcYear": 2026, "utcMonth": 8, "utcDay": 27, "utcHour": 13, "utcMinute": 30
}

Send both the wall-clock and the UTC fields:

FieldNotes
title, bodyThe notification. It goes to every device (a mass push).
pushDataOptional object your app reads on tap.
timezoneThe IANA zone the schedule is displayed in.
year, month, day, hour, minuteThe time the user picked, for display. It becomes the delivered notification's inbox date (month-day-year hour:minute), so send month 1-based (9 is September).
utcYear, utcMonth, utcDay, utcHour, utcMinuteThe moment the server fires on. utcMonth is 0-based (8 is September), like Date#getUTCMonth().

UTC fields aren't validated

The server doesn't validate the utc* fields: a past or malformed time still answers 201 and never sends, so send plain unpadded numbers (5, not "05") at least a couple of minutes ahead.

It answers 201 with Notification scheduled successfully!. Native Notify and its in-app agent work out the UTC fields from your timezone; this API doesn't. The MCP server has no one-off tool.

GET /api/scheduled/notifications/:appId lists pending one-offs (201 with the stored rows; a notification leaves the list once sent). It needs a Native Notify dashboard session or an MCP access token, and an app token gets 403.

Recurring rules

Manage rules in the Scheduling tab in Native Notify, in the agent chat, with the MCP schedule-rule tools (list_schedule_rules, create_schedule_rule, …) or over the session API. These routes need a Native Notify dashboard session or an MCP access token, and an app token gets 403.

GET    /api/apps/:appId/schedules                 — rules + quiet-hours settings
GET    /api/apps/:appId/schedules/upcoming        — next fires (rules + pending one-offs)
POST   /api/apps/:appId/schedules                 — create a rule
PATCH  /api/apps/:appId/schedules/:ruleId         — update / pause / resume
DELETE /api/apps/:appId/schedules/:ruleId         — delete a rule
GET    /api/apps/:appId/schedules/:ruleId/runs    — recent fire history
FieldNotes
nameRule label, such as "Weekly digest". Up to 120 characters.
title, bodyThe notification, up to 200 and 4,000 characters.
contentModefixed (default) sends title and body. ai writes fresh copy at every send from a brief, for every app: see AI-written notifications.
enabledPause or resume without deleting.
frequencydaily or weekly. Apps on the push API also take once (a one-off on a date): see push API scheduling.
dayOfWeek0–6 (0 is Sunday), for weekly rules.
timeOfDay24-hour "HH:MM", like "09:00".
timezoneThe IANA zone the rule fires in. It follows its own wall clock, daylight saving included.
audienceKindall or segment (with segmentId). Apps on the push API also take group and sub (with audienceRef), an environment and an image.
skipIfActiveWithinDaysOptional 1–365, for segment audiences only. Subscribers who opened the app within that many days are skipped.
pushDataJSON delivered with the notification, up to 16 KiB. The whole message, title and body included, must fit Expo's 4 KiB limit, or every fire is recorded as blocked.

The runner ticks every minute. It sends an occurrence when the tick lands on it, or within a 15-minute grace window, and never twice. A blocked send (a lapsed trial or the membership gate) answers 201 with an explanation and is recorded as a blocked run, and the rule keeps its schedule. See run statuses.

Quiet hours

Quiet hours hold a send instead of dropping it. They're per app and apply to schedule rules: an occurrence inside the window, read in the rule's own timezone, is held and sent when the window ends. They cover recurring rules and the push API's one-off rules, not the one-off API above.

GET /api/apps/:appId/schedules/settings
PUT /api/apps/:appId/schedules/settings
{ "quietHoursEnabled": true, "quietHoursStart": "22:00", "quietHoursEnd": "07:00" }

Quiet hours are off until you enable them, and the default window is 22:00–07:00. The window may cross midnight, and start and end must differ (a zero-length window never holds).

AI Push

AI Push writes a weekly notification from a description of your app and sends it on the day and time you choose.

POST   /api/schedule/ai/mass/push                        — create a weekly mass schedule
PUT    /api/scheduled/ai/mass/push                       — update one (body: aiPushId + every field)
DELETE /api/scheduled/ai/mass/push                       — delete one (body: appId, appToken, aiPushId)
GET    /api/scheduled/ai/mass/push?appId=…&appToken=…    — list what is configured

The indie (subscriber) variants use /api/schedule/ai/indie/push and /api/scheduled/ai/indie/push, with indie_ai_push_id as the id. The create body:

{
  "appId": 123,
  "appToken": "yourAppToken",
  "app_description": "A neighborhood bakery app with daily specials",
  "specific_instructions": "Friendly and under 80 characters",
  "example_push_notifications": ["Fresh sourdough is out of the oven"],
  "language": "English (US)",
  "timezone": "America/New_York",
  "day_of_week": "Monday",
  "time_of_day": "09:00",
  "day_of_week_utc": "Monday",
  "time_of_day_utc": "13:00"
}

The weekly runner fires on day_of_week_utc and time_of_day_utc (UTC "HH:mm") only. The server stores what you send and never derives them, so a schedule without the UTC pair never sends. An update replaces every field, so send the stored values back with your change.

An expired trial blocks every AI Push, and indie AI Push isn't available on Premium. There are no MCP tools for AI Push, so manage it in Native Notify. For quiet hours and a run history, use a recurring rule with contentMode: "ai" instead.

Best send times

Native Notify shows each rule's next fire next to your app's best send times (weekday and hour, from real opens). Agents read the same data with get_best_send_times.