Step 10 — Scheduled work and email¶
About 25 minutes. Branch: step-10.
The one new idea: a cron job is an HTTP request to your own URL.
Cron, and the thing to unlearn¶
cron:
- name: weekly-digest
schedule: "0 7 * * 1"
url: /api/cron/digest
service: web
method: POST
timeout: 60
There is no command: here, and nowhere to put one
If you have used cron anywhere else, you expect to name a command. On this platform you name a URL. The platform makes an HTTP request to your own running service on a schedule; your app does the work behind the endpoint; the response is the outcome.
All six fields, and what each is really for:
| Field | |
|---|---|
name |
how you refer to it in mecca cron |
schedule |
standard five-field cron, in UTC |
url |
a path on one of your services. Must start with / |
service |
which service to call. Required once an app has more than one — and yours has two, so leaving it out is an error rather than a guess |
method |
GET or POST. POST is the default and right for anything that changes state |
timeout |
seconds, 5–300, default 30 |
timeout is the one that shapes your design. If the work cannot finish
inside it, it does not belong in the endpoint — put the item on the worker's
queue and return immediately. You already have a worker for exactly that.
And since the endpoint is reachable, treat it as reachable: make it safe to call twice, because one day it will be.
Try it without waiting a week¶
history gives you what happened and what the endpoint replied.
Email¶
One line, and three variables arrive: a relay URL, a token scoped to this app and this branch, and the address you are allowed to send as.
httpx.post(
url,
headers={"Authorization": f"Bearer {os.environ['APPMECCA_MAILER_TOKEN']}"},
json={"to": [to], "subject": subject, "text": text},
timeout=10.0,
)
You do not choose the From address
The relay stamps it. That is what stops one app sending as another, and it is why there is no mail credential here for you to leak.
There is a quota — a rolling 24 hours, per app per branch — and the relay tells you where you stand in the response headers rather than making you guess.
Notifications fail softly, deliberately¶
app/mailer.py returns False and logs. It never raises.
That is a design decision worth stealing: a roadmap board that cannot send a notification should still accept the idea. The submission is the user's work; the email is yours. Do not lose theirs over yours.
events is the log of what was sent, delivered, bounced or complained about.
What you should see¶
mecca cron triggerruns immediately andhistoryrecords the reply.- A submission produces an email, if you set an owner address.
mecca email eventslists it.
If it didn't work¶
| Symptom | Try | Usually |
|---|---|---|
| Cron never runs | mecca cron list |
Disabled, or service: missing on a multi-service app |
| It runs and fails | mecca cron history |
The endpoint 500s, or exceeded timeout |
| No email | mecca email status |
Not enabled, or the address is not verified |
429 in the logs |
mecca email status |
Quota. The reset time is in the response |