AI-Written Notifications
Schedule rules whose copy is written fresh at every send from a short brief — what the app is, standing instructions, language and example notifications — checked before it goes out, never repeating recent sends, and never sent blank. Preview it first with the sample endpoint.
A schedule rule with contentMode: "ai" carries a brief instead of a fixed title and body. At every send, Native Notify writes one new notification from the brief, checks it, and sends it to the rule's audience like any other scheduled send — so a weekly reminder never reads the same twice.
It works for every project: Agent Notify projects (any cadence, audience and environment a rule takes) and classic Expo apps (daily and weekly rules to all devices or a segment). The older classic AI Push schedules are a separate API and keep working unchanged.
The brief
| Field | Required | Limit | Notes |
|---|---|---|---|
appDescription | yes | 2,000 characters | What the app is and what the notifications should be about. Put every fact you want mentioned here — times, places, what is new. |
instructions | no | 2,000 characters | Standing rules for the writer: tone, topics to avoid, calls to action. |
language | no | 60 characters | The language to write in. Default "English". |
examples | no | 10 examples, 300 characters each | Example notifications for the voice — a list of strings, or one string with one example per line. They set the tone and are never copied. |
A weekly AI-written rule for a bookstore, saved paused so you can preview it first:
{
"name": "Saturday story hour",
"enabled": false,
"contentMode": "ai",
"appDescription": "Maple Street Books, a family bookstore. Invite families to Saturday story hour at 10:00 in the children's corner, and mention that new picture books arrive every week.",
"instructions": "Warm and brief. No prices or discounts. Always say story hour starts at 10:00.",
"language": "English",
"examples": [
"Story hour starts at 10 — bring the little ones!",
"New picture books just arrived in the children's corner."
],
"frequency": "weekly",
"dayOfWeek": 5,
"timeOfDay": "17:00",
"timezone": "America/Chicago",
"audienceKind": "all"
}
Send it to POST /api/agent/apps/:appId/schedules — every other field (cadence, audience, environment, pushData, quiet hours) works exactly as on Scheduling. An AI rule stores no title or body: in the rule list it has title: null, body: null and its brief under ai, and each run records the copy that actually went out.
What happens at each send
- The runner claims the occurrence — at most once, like every rule.
- It asks the model for one title and body, handing it the brief and the rule's 10 most recent sent notifications, so it varies the angle and wording instead of repeating itself.
- The answer is checked: a non-empty title of at most 65 characters and a non-empty body of at most 180 (an emoji counts once), with markup, markdown and wrapping quotes removed. An unusable answer gets one corrective retry.
- Usable copy is sent to the rule's audience and saved on the run (
title,body) — which is also the history step 2 reads next time. - No usable copy means nothing is sent. If the model cannot be reached, or both answers are unusable, the run is recorded
failedwith the reason, and the rule tries again at its next occurrence. There is never a blank, placeholder or fallback push.
The writing rules
Every AI-written notification follows the same rules, whatever the brief says:
- Modest, kind and appropriate for the whole family — nothing crude, suggestive, violent, hateful or mocking.
- Nothing about gambling, alcohol, drugs, tobacco, the occult or party culture.
- No fear, guilt or pressure tactics.
- No invented facts — no prices, discounts, dates, deadlines or promises the brief does not state.
- Plain text in the brief's language, one clear idea, at most one emoji.
- The owner's standing
instructionsare followed unless they conflict with these rules.
Preview a sample
POST https://app.nativenotify.com/api/agent/apps/:appId/schedules/ai-sample
Writes one sample from a brief — nothing is saved and nothing is sent. Send the brief fields, or ruleId of an existing AI rule: its stored brief fills the fields you leave out, and its recent sends count as the no-repeat history, exactly as at send time.
{
"appDescription": "Maple Street Books, a family bookstore. Invite families to Saturday story hour at 10:00.",
"instructions": "Warm and brief. No prices or discounts.",
"language": "English"
}
Response — 200:
{
"ok": true,
"sample": {
"title": "Story hour this Saturday",
"body": "Bring the family to the children's corner at 10:00 for stories and brand-new picture books."
},
"model": "~deepseek/deepseek-flash-latest",
"attempts": 1
}
attempts is 2 when the corrective retry was needed. Every sample is a real model call, so each account user can write 20 samples a minute and 300 a day (the daily count resets at midnight UTC).
| Status | Code | Meaning |
|---|---|---|
400 | bad_brief | The brief is invalid — missing appDescription, or a field over its limit (field names it). |
400 | bad_rule_id | ruleId is not a positive integer. |
404 | app_not_found, rule_not_found | The app is not one this account can access, or the rule is not on it. |
429 | limit_reached | The per-minute or per-day sample limit is used up. |
502 | llm_failed, invalid_output | The model could not be reached, or returned no usable copy after the retry. |
503 | llm_not_configured | AI writing is not configured on the server. |
The same call is behind the dashboard's Generate sample button (the rule form) and Sample button (a saved AI rule), and the chat always writes a sample before it saves an AI rule — the Turn on schedule card shows it, and a brief that cannot produce usable copy saves nothing.
Saving and editing
contentMode: "ai"needsappDescription(or a stored brief, on an update); a missing or oversized field answers400 bad_schedule, and the error'sfieldnames it.titleandbodyare ignored for an AI rule. Switching a fixed rule to AI clears its fixed title and body — switching back needs them again. Switching back to fixed keeps the brief stored, so it is still there if you switch to AI later.PATCHchanges only the fields you send:{ "instructions": null }clears the instructions,{ "examples": [] }clears the examples.
For agents
The MCP server has generate_ai_schedule_sample (the sample endpoint — pass app_description, or rule_id), and create_schedule_rule / update_schedule_rule take content_mode: "ai" with app_description, instructions, language and examples. A good agent flow: write a sample, show it to the user, adjust the brief, and only then create the rule with enabled: true — or save it with enabled: false and let the user turn it on.
Related
- Scheduling — cadence, audiences, quiet hours and run statuses.
- Inbox & Audience — what each send reached and who read it.