Every Monday at 09:00 in Tokyo, read last week's figures from your analytics API, write a short report and send it to Slack or email. The agent reads through a connection that allows only GET, so it can look at your numbers but cannot change them.
The run takes about 10 seconds. Most of the cost is compute, billed at $0.0002 per second, so four runs a month cost about a cent. The share of the model depends on the model you choose; read cost_micros on the first runs to see it.
Gather what you need
- An API key with the admin role, because only owners and admins create connections and schedules. See roles and keys.
- Your analytics API over HTTPS, and a read-only key for it. This recipe assumes it has
GET /metrics?period=…, whereperiodislast_weekor an ISO week such as2026-W40. Adjust the paths to your API. - A Slack incoming webhook URL, or an endpoint of yours that sends email.
Create the read-only connection
The connection keeps the API key inside Ephemeral and allows
GETonly. Theinstructionstell the agent what the API offers.curl -X PUT $EPHEMERAL_URL/v1/connections/analytics-api \ -H "Authorization: Bearer $EPHEMERAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "http", "url": "https://analytics.example.com/v2", "auth": { "type": "header", "header": "X-API-Key", "token": "an_live_…" }, "allowed_methods": ["GET"], "instructions": "GET /metrics?period=last_week or period=2026-W40 returns signups, active_users, revenue_cents and churned_accounts for that ISO week, with the same figures for the week before. Amounts are in cents." }'Save the workflow
The prompt reads the period from the run's input. The agent does not have a reliable calendar, so pass a period your API can resolve, such as
last_week, instead of asking the agent to work out dates.curl -X PUT $EPHEMERAL_URL/v1/workflows/weekly-report \ -H "Authorization: Bearer $EPHEMERAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "prompt": "Write the weekly business report for input.period. Call GET /metrics?period=<input.period> on the analytics connection and use only what it returns. Reply in Markdown, at most 150 words: a one-line headline, then signups, active users, revenue in dollars and churned accounts, each with the change from the week before. If the call fails, say so instead of estimating.", "connections": { "analytics": { "ref": "analytics-api", "allowed_methods": ["GET"] } }, "shell": false, "timeout_seconds": 120 }'There is no
websetting, so the run has no internet access at all; its only reach is the analytics connection. A workflow cannot holdinput, a budget or a callback; those come with each run.Schedule it for Monday morning
0 9 * * 1is 09:00 on Mondays, read in the schedule'stimezone. The schedule'sinputis what every run it starts receives, so this is where you set the period. Preview it first.curl -X POST $EPHEMERAL_URL/v1/schedules/preview \ -H "Authorization: Bearer $EPHEMERAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "cron": "0 9 * * 1", "timezone": "Asia/Tokyo" }'The times are in UTC: 00:00 UTC is 09:00 in Tokyo. Create the schedule:
curl -X POST $EPHEMERAL_URL/v1/schedules \ -H "Authorization: Bearer $EPHEMERAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "workflow": "weekly-report", "name": "Monday report", "cron": "0 9 * * 1", "timezone": "Asia/Tokyo", "input": { "period": "last_week" }, "max_cost_micros": 300000 }'The budget of $0.30 is reserved at each start and released when the run settles. Because the schedule owns the input, you can add another schedule on the same workflow with a different input, or change this one later with
PATCH /v1/schedules/{id}and a newinput. To see it work before Monday, start one now withPOST /v1/schedules/{id}/runorephemeral schedules run ID.Deliver the report
Scheduled runs do not send callbacks, so deliver by collecting the result. This script, run every ten minutes from cron or CI, asks for the schedule's newest succeeded run, sends its output once and remembers the run ID. Replace the Slack call with a call to your mail service if you prefer email.
import { readFile, writeFile } from "node:fs/promises"; const { EPHEMERAL_URL, EPHEMERAL_API_KEY, SCHEDULE_ID, SLACK_WEBHOOK_URL } = process.env; const stateFile = "report-state.json"; const { sent } = JSON.parse(await readFile(stateFile, "utf8").catch(() => "{}")); const response = await fetch(`${EPHEMERAL_URL}/v1/runs?schedule=${SCHEDULE_ID}&status=succeeded&limit=1`, { headers: { Authorization: `Bearer ${EPHEMERAL_API_KEY}` }, }); if (!response.ok) throw new Error(`Ephemeral returned ${response.status}`); const [run] = (await response.json()).items; if (!run || run.id === sent) process.exit(0); // nothing new since the last report await fetch(SLACK_WEBHOOK_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ text: run.output }), }); await writeFile(stateFile, JSON.stringify({ sent: run.id }));A run that failed sends nothing, so look at
GET /v1/runs?schedule={id}&status=failed&limit=1too, or watch the schedule'slast_error, so that a missing report does not go unnoticed.Rerun a report for a past week
"Start now" reuses the schedule's input, so for another week start the workflow directly with the period you want. This time you can ask for a signed callback, which delivers the result to your endpoint as soon as it is ready. Use an idempotency key that names the week, so a repeated request never sends a second report.
curl $EPHEMERAL_URL/v1/runs \ -H "Authorization: Bearer $EPHEMERAL_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: weekly-report-2026-W38" \ -d '{ "workflow": "weekly-report", "input": { "period": "2026-W38" }, "max_cost_micros": 300000, "callback": { "url": "https://example.com/hooks/ephemeral", "secret": "'"$EPHEMERAL_CALLBACK_SECRET"'" } }'Your callback endpoint verifies the signature and forwards
run.output; the receiver is in the payment investigation recipe. A run started this way does not carry the schedule's ID, so it does not appear under?schedule=.
Good to know
| Topic | Behavior |
|---|---|
| Time zone | Times are read in the schedule's timezone, so a report set for 09:00 stays at 09:00 local time across daylight saving changes. |
| Missed starts | If Ephemeral was unavailable at 09:00, the report runs once when it is back, then the timetable continues. |
| Overlap | With skip_if_running on, a start is skipped while the previous run is still going. |
| Pausing | ephemeral schedules pause ID before a holiday and resume after. |
GET, and the key never reaches the microVM. The workflow has no web access and no shell, so the run can read your analytics API and nothing else. The budget caps each run at $0.30. Use a read-only analytics key, so that even the connection could not write.