Batch events into one daily email
Fifty notifications a day train people to ignore all fifty. The digest API collects the events you send it and ships one readable summary email on the schedule you choose, so the inbox becomes a signal again instead of noise.
Built for teams drowning in single-event emails
Any system that fires a notification per event — a queue processor, a monitoring agent, a form handler, a fleet of cron jobs — eventually floods someone's inbox. notify.digest exists for the moment a team lead says 'stop emailing me every time, just tell me once a day.' You keep sending events exactly as they happen; we hold them and compose the summary.
How the batching actually works
Each call to POST /notify/digest adds one entry — a title, a short body, an optional severity and timestamp — to a digest bucket you name. When the bucket's schedule triggers (hourly, daily, or a custom cron-like window you configure per bucket), everything queued is rendered into a single HTML and plain-text email, grouped and ordered by the time it arrived, and sent to the recipients on file. If nothing was queued, no empty email goes out; you only get mail when there's something to read.
A quiet return to how email was meant to work
Digest emails are not a new idea — mailing lists and newsletter software have batched updates for decades — but most application notification systems skip that layer entirely and fire one message per event because it's the easiest thing to code. We built this endpoint specifically so you don't have to write your own batching, cron job and templating layer just to get back to the sane default of one email a day.
Fits into pipelines you already run
Because the endpoint is async, your event source fires POST /notify/digest and moves on immediately — task_id comes back in milliseconds and the actual send happens on schedule, decoupled from whatever triggered the event. Pair it with an alert-routing step upstream to decide what deserves a digest versus an immediate ping, or point dozens of independent services at the same bucket to get one unified daily report across your whole stack.
What lands in your inbox
The final email lists each entry with its timestamp and severity marker, grouped chronologically, with a subject line summarizing the count and top severity so you can triage from the inbox list view alone before opening it. Delivery confirmation comes back the same way as any other task here: a signed webhook call, or a signed link valid for 24 hours if you'd rather poll.
What you can do with it
Nightly ops summary
A monitoring agent posts every warning-level event through the day; instead of forty pings, the ops channel gets one 7am email listing them all by severity.
SaaS product digest for admins
Account admins get a single daily email of new signups, cancellations and support tickets instead of a real-time feed nobody reads.
Batch job reporting
A fleet of scheduled ETL jobs each posts its pass/fail status; the team lead reviews one consolidated morning email rather than fifty job logs.
Content moderation queue recap
Flagged items accumulate through the day and land in one digest for the moderation lead to triage in a single sitting.
FAQ
How does the daily digest email API decide when to send?
You set a schedule per bucket — hourly, daily or a custom window — and every entry posted to that bucket in the interval is compiled into one email when the window closes.
Is there a free tier for notify.digest?
There's no free tier — free tiers get abused and slow everyone down. Access runs on a prepaid ForHosting KIT balance: top up from $10.00 (it never expires) and each request is charged at its published price, so a call with no balance returns HTTP 402. No subscription, no tokens, no invented credits, and a failed task is never charged.
What does it cost to send a digest?
It's $0.002 per POST /notify/digest request that queues an entry, plus $0.0035 for each digest email actually sent — you're never billed for a digest window that had nothing to send.
Can I run multiple digests with different schedules?
Yes, buckets are independent, so you can run an hourly digest for on-call severity events and a separate daily digest for general activity at the same time.
What happens if a digest send fails?
A failed task is retried automatically up to three times; if it still fails you get a clear error and are never charged for that send.
How do I get the result — webhook or polling?
You can register a signed webhook to be notified the moment the digest is sent, or fetch a signed link that stays valid for 24 hours if you prefer to poll.
Can I include severity levels or tags in each entry?
Yes, each entry accepts an optional severity and free-form tag so the compiled email can group and highlight the entries that matter most.
Is my event data kept after the digest is sent?
No, entries and the compiled email are deleted after the stated retention window and are never used for training.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, and soon from our app, email and Telegram.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/notify/digest \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"input":"…"}'const res = await fetch("https://api.kit.forhosting.com/notify/digest", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"input": "…"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/notify/digest",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"input": "…"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/notify/digest", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"input":"…"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"input":"…"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/notify/digest", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"input": "…"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "notify.digest",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |