Create schedule rules whose notification is written fresh at every send from a short brief.
The brief
Set contentMode: "ai" on a schedule rule and send a brief instead of a title and body.
| Field | Required | Limit | Notes |
|---|---|---|---|
appDescription | yes | 2,000 characters | What the app is and what to write about. Include every fact you want mentioned. |
instructions | no | 2,000 characters | Standing rules: tone, topics to avoid, calls to action. |
language | no | 60 characters | Default "English". |
examples | no | 10 examples, 300 characters each | Strings, or one string with one example per line. They set the voice and are never copied. |
A weekly 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/apps/:appId/schedules, an account route like the rest of Scheduling; everything else works the same. An AI rule has title: null and body: null with its brief under ai, and each run records the copy that went out.
Preview a sample
POST https://app.nativenotify.com/api/apps/:appId/schedules/ai-sample
Writes one sample from a brief. Nothing is saved or sent. Send the brief fields, or the ruleId of an existing AI rule: its stored brief fills the fields you leave out, and its recent sends are the no-repeat history.
{
"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"
}
{
"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 retry was needed. Each sample is a real model call: 20 a minute and 300 a day per account user, and the daily count resets at midnight UTC.
Errors
| Status | Code | Meaning |
|---|---|---|
400 | bad_brief | appDescription is missing, or a field is over its limit (field names it). |
400 | bad_rule_id | ruleId isn't a positive integer. |
404 | app_not_found, rule_not_found | The app isn't one this account can access, or the rule isn't on it. |
429 | limit_reached | The per-minute or per-day sample limit is used up. |
502 | llm_failed, invalid_output | The model couldn't be reached, or returned no usable copy after the retry. |
503 | llm_not_configured | AI writing isn't configured on the server. |
As an MCP tool the sample is generate_ai_schedule_sample, and create_schedule_rule and update_schedule_rule take content_mode: "ai" with the brief fields.
What happens at each send
- The runner claims the occurrence, at most once.
- The model writes one title and body from the brief and the rule's 10 most recent sent notifications, so the wording varies.
- The copy 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. A failed check gets one corrective retry.
- Usable copy is sent and saved on the run (
title,body), which is the history step 2 reads next time. - With no usable copy nothing is sent. The run is recorded
failedwith the reason, and the rule tries again at its next occurrence. There is never a blank or fallback push.
Writing rules
Whatever the brief says, notifications stay modest and fit for the whole family: nothing crude, suggestive, violent, hateful or mocking, and nothing about gambling, alcohol, drugs, tobacco, the occult or party culture. They use no fear, guilt or pressure, and invent no facts such as prices, discounts, dates, deadlines or promises the brief doesn't state. Each is plain text with one idea and at most one emoji. Your instructions apply unless they conflict with these rules.
Saving and editing
contentMode: "ai"needsappDescription(or a stored brief, on an update). A missing or oversized field is400 bad_schedule, and the error'sfieldnames it.titleandbodyare ignored for an AI rule. Switching a fixed rule to AI clears them, so switching back needs them again. The brief stays stored.PATCHchanges only what you send:{ "instructions": null }clears the instructions and{ "examples": [] }clears the examples.