Webhook automations
Automations need Basic and up. Basic: 1 automation, Pro: 3, Ultra: 10. On Free, the row shows Upgrade to unlock Automations.
A webhook is a web address that receives data automatically. With Automations, TellDone sends each new note with its tasks and events to that address as soon as the note is ready, and each new report when the report is ready, so tools like Zapier, Make, or n8n can act on them. Each task and event is sent as its own delivery.
Choosing what to send
Each automation has its own settings under What to send. Pick one or any combination:
- Note - the full processed note (title, transcript, summary, type, tags, priority, language, and creation date)
- Note + audio - on the Ultra plan, adds a 24-hour download link to the audio file to each note delivery. Keep Note on: without it, no note is sent
- Tasks - one delivery per extracted task
- Events - one delivery per extracted calendar event
- Reports - one delivery per generated daily, weekly, monthly, or yearly report (the report types your plan includes). Quiet-day placeholders and short quiet-week reports are not sent
You can change these any time from the automation's settings - changes apply to new deliveries only.
Choosing when to send
Under the data choices, four separate switches decide which changes trigger a delivery: Created, Updated, Completed, and Deleted. A trigger only works together with the matching data switch above it. For example, Completed sends completed tasks only if Tasks is on.
Created is sent for notes TellDone makes from a recording or a text note, and for the tasks and events found in them. Tasks and events you add by hand or with an AI assistant, and new items from Add by voice, are not sent as Created. Updated, Completed and Deleted are sent when you change an item in the app or with Add by voice. Changes made by Smart Context, by an AI assistant, or in Todoist, Notion or Microsoft To Do don't send a delivery.
The event field of each delivery names what happened, for example task.created, task.completed, or calendar_event.updated.
Setting up a webhook

- Go to Settings > Integrations > Automations
- Tap New automation

- Enter a name (for example, "My Zapier webhook")
- Paste the webhook URL from your automation service
- Choose which data to send (Note, Note + audio on Ultra, Tasks, Events, Reports) and which triggers to use (Created, Updated, Completed, Deleted)
- Optionally, add an auth header - a token sent as the
Authorizationheader for secure endpoints - Tap Save
- Copy the signing secret - it is shown in full only when you first create the automation or after you rotate it. You need it to check that deliveries really come from TellDone (see Verifying webhook signatures)
- Tap Test send to send a sample payload to your endpoint without changing any of your data. You'll see Success with the HTTP code and response time, or Error with the reason. Test sends don't appear in the delivery log
What gets sent
Every delivery is a JSON object with three fields: event (the event type), timestamp, and data (the actual content). Test sends also include "test": true. Here is what each event type looks like.
Notes (note.created)
Contains the AI-generated title, full transcript, summary, note type, tags, priority, language, and creation date.
{
"event": "note.created",
"timestamp": "2026-02-27T14:38:00.512345+00:00",
"data": {
"note_id": "550e8400-e29b-41d4-a716-446655440000",
"title": "Meeting notes - Project Alpha",
"transcript": "Full transcript text...",
"summary": "Brief AI-generated summary...",
"type": "meeting",
"tags": ["work", "project-alpha"],
"priority": "high",
"language": "en",
"created_at": "2026-02-27T10:00:00.284117+00:00"
}
}
On the Ultra plan, note deliveries can also include a link to the audio recording (audio_url, audio_format, duration_seconds). The link expires after 24 hours. Turn this on with the Note + audio option in the automation.
Tasks (task.created)
One delivery per task. A note with 3 tasks sends 3 separate deliveries.
{
"event": "task.created",
"timestamp": "2026-02-27T14:38:01.204718+00:00",
"data": {
"note_id": "550e8400-...",
"note_title": "Meeting notes - Project Alpha",
"task_id": "660f9511-...",
"title": "Send proposal to client",
"description": "Include pricing for Q2",
"priority": "high",
"due_date": "2026-03-01",
"reminder_at": "2026-02-28T09:00:00+00:00",
"tags": ["work"],
"status": "todo",
"cancel_reason": null,
"cancelled_at": null,
"created_at": "2026-02-27T10:00:00.284117+00:00"
}
}
Events (calendar_event.created)
One delivery per calendar event.
{
"event": "calendar_event.created",
"timestamp": "2026-02-27T14:38:02.093361+00:00",
"data": {
"note_id": "550e8400-...",
"note_title": "Meeting notes",
"event_id": "770a0622-...",
"title": "Team standup",
"description": "Weekly sync",
"start": "2026-03-03T07:00:00+00:00",
"end": "2026-03-03T07:30:00+00:00",
"location": "Zoom",
"is_all_day": false,
"attendees": ["alice@example.com"],
"tags": ["work"],
"created_at": "2026-02-27T10:00:00.284117+00:00",
"timezone": "Europe/Istanbul",
"start_local": "2026-03-03T10:00:00+03:00",
"end_local": "2026-03-03T10:30:00+03:00",
"start_date": "2026-03-03",
"end_date": "2026-03-03"
}
}
start and end are in UTC. start_local, end_local, start_date, and end_date use your time zone (timezone). For an all-day event, end_date is the last day of the event.
Reports (report.created)
Sent when a daily, weekly, monthly, or yearly report is generated.
{
"event": "report.created",
"timestamp": "2026-02-28T00:05:00.731902+00:00",
"data": {
"report_id": "880b1733-...",
"report_type": "daily",
"period_start": "2026-02-27",
"period_end": "2026-02-27",
"content_md": "# Daily Report\n\n...",
"content_json": {
"productivity_score": 72,
"day_type": "planning",
"tasks_created": 5,
"tasks_completed": 3
},
"created_at": "2026-02-28T00:05:00.418253+00:00"
}
}
Security
Every delivery is signed with HMAC-SHA256 using your automation's signing secret. The following headers are included with each delivery:
X-LP-Signature- the HMAC-SHA256 signature (sha256=...)X-LP-Timestamp- Unix timestamp used for signingX-LP-Event- the event type (for examplenote.created,task.completed,calendar_event.updated,report.created)X-LP-Delivery-Id- unique delivery ID (useful for deduplication)User-Agent- alwaysTellDone-Webhooks/1.0
TellDone sends an Authorization header only if you add an auth header in the automation. You can use two formats: Header-Name: value sends a named header, and a plain value sends Authorization: <value>. Your signing secret is never sent in a header.
Each automation has its own signing secret, so rotating one does not affect any of your other webhooks. The secret is shown in full only when you first create the automation or after you rotate it. At all other times, only a preview (whsec_...last4) is shown. To swap it for a new one, tap Rotate secret in the automation settings - this generates a new HMAC key without changing the URL or the auth header. The old secret stops working immediately.
Only HTTPS URLs with a domain name are accepted. HTTP, IP addresses (public or private), URLs with a username or password, and domains that point to private network addresses are rejected. TellDone checks the address again each time it sends. If that check fails, for example because the domain doesn't resolve at that moment, the delivery is stopped and not retried.
Verifying webhook signatures
Every webhook is signed so you can confirm it actually came from TellDone. The signature is computed as:
HMAC-SHA256(signing_secret, timestamp + "." + raw_body)
The result is hex-encoded and prefixed with sha256= in the signature header.
TellDone serializes JSON with spaces after separators (for example {"event": "note.created"}). If you parse and re-serialize the body, the signature won't match. Always verify against the raw bytes of the request body.
Python
import hmac
import hashlib
def verify_signature(raw_body: bytes, signature: str, timestamp: str, secret: str) -> bool:
payload = f"{timestamp}.".encode() + raw_body
expected = "sha256=" + hmac.new(
secret.encode(), payload, hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
# Flask example
@app.route("/webhook/telldone", methods=["POST"])
def telldone_webhook():
raw_body = request.get_data() # raw bytes, NOT request.json
signature = request.headers.get("X-LP-Signature", "")
timestamp = request.headers.get("X-LP-Timestamp", "")
if not verify_signature(raw_body, signature, timestamp, SIGNING_SECRET):
abort(403)
data = request.json
# process webhook...
Node.js
const crypto = require("crypto");
function verifySignature(rawBody, signature, timestamp, secret) {
const payload = `${timestamp}.${rawBody}`;
const expected =
"sha256=" +
crypto.createHmac("sha256", secret).update(payload).digest("hex");
const a = Buffer.from(signature);
const b = Buffer.from(expected);
// timingSafeEqual throws if the lengths differ, e.g. when the header is missing
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
// Express example (use express.raw to get the raw body)
app.post("/webhook/telldone", express.raw({ type: "application/json" }), (req, res) => {
const rawBody = req.body.toString();
const signature = req.headers["x-lp-signature"] || "";
const timestamp = req.headers["x-lp-timestamp"] || "";
if (!verifySignature(rawBody, signature, timestamp, SIGNING_SECRET)) {
return res.status(403).send("Invalid signature");
}
const data = JSON.parse(rawBody);
// process webhook...
});
Replay protection (optional)
To prevent replay attacks, check that the timestamp is recent:
import time
def verify_timestamp(timestamp: str, tolerance_seconds: int = 300) -> bool:
try:
return abs(time.time() - int(timestamp)) < tolerance_seconds
except (ValueError, TypeError):
return False
Common mistakes
| Mistake | Fix |
|---|---|
| Using parsed + re-serialized body | Use the raw request body |
JSON.stringify() in JS (compact, no spaces) | Use the raw request body |
| Forgetting the timestamp prefix | Payload format is timestamp + "." + body |
| Comparing strings directly | Use hmac.compare_digest or timingSafeEqual |
Retry policy
If your endpoint fails, TellDone retries each delivery on this schedule:
| Attempt | Delay |
|---|---|
| 1st retry | 30 seconds |
| 2nd retry | 2 minutes |
| 3rd retry | 15 minutes |
| 4th retry | 1 hour |
| 5th retry | 4 hours |
If the 5th retry also fails, the delivery is marked as failed and isn't retried again. In the app it shows a red cross. You can still resend it yourself from the full delivery log: open All deliveries (the link appears once an automation has more than 5 deliveries) and tap Try again on the failed delivery. The short Recent deliveries list doesn't offer a retry.
How TellDone handles different responses:
- 2xx - delivered successfully
- 4xx (except 429) - marked as failed right away and not retried (your endpoint explicitly rejected the data)
- 429 (Too Many Requests) - retried after the number of seconds in
Retry-After(60 seconds if there is none). These retries are not limited to 5, but each one counts toward the 20 failures that turn the automation off - 5xx or timeout - retries with the schedule above
When deliveries fail repeatedly
If deliveries keep failing, TellDone lets you know and eventually turns the automation off.
After 3 failed attempts: Inbox warning
After 3 failed attempts in a row (retries count too), TellDone sends a message to your Inbox titled Your automation "Name" is failing, with your automation's name in the quotes. You can then open it in Settings > Integrations > Automations, check its deliveries, and decide what to do.
After 20 failed attempts: Auto-disable
If 20 attempts in a row fail for the same automation (retries count too), TellDone turns the automation off so it stops sending to an address that isn't working. Fix the problem in your automation tool, then turn its Active switch back on in Settings > Integrations > Automations. Switching it back on doesn't reset the failure count, so until one delivery succeeds, a single failed attempt turns it off again.
Every failed attempt counts, so one delivery that fails all 5 retries adds 6 to the count. An automation can therefore switch itself off after about 4 failing deliveries. The counter resets to zero on the next successful delivery.
Managing webhooks
- Active - turn this switch off to stop an automation without deleting it, and back on any time. Nothing is sent while it is off: notes and reports from that time are not sent later, and deliveries waiting for a retry are dropped
- Deliveries - Recent deliveries shows the last 5 with status, HTTP code, and response time. Once there are more, All deliveries opens the log of the last 30 days, where you can filter by All, Delivered, or Errors and tap Try again on a failed delivery to resend it without waiting for the next scheduled retry
- Test send - sends a sample note (
note.created, with"test": truein the body), whatever data types you chose, without changing any of your data. Available once the automation is saved. Test sends don't count toward your monthly limit - Rotate secret - generate a fresh HMAC signing secret without changing the URL or auth header
- Edit - update the URL, auth header, data choices (Note / Note + audio / Tasks / Events / Reports), or triggers (Created / Updated / Completed / Deleted). Changes take effect immediately
- Delete automation - permanently removes the automation and all its delivery logs
Each delivery shows its status as an icon:
| Icon | Meaning |
|---|---|
| Green check | Delivered |
| Yellow clock | Waiting or being sent |
| Red triangle | Failed, TellDone will retry |
| Red cross | Failed, no more automatic retries |
You can tap Try again on deliveries with a red triangle or a red cross in the full log.
Delivery limits
There are two limits: a monthly limit that depends on your plan, and an hourly limit of 100 deliveries on every paid plan, which protects against sudden spikes.
| Plan | Automations | Monthly deliveries | Hourly limit |
|---|---|---|---|
| Free | 0 | 0 | - |
| Basic | 1 | 300 | 100 |
| Pro | 3 | 3,000 | 100 |
| Ultra | 10 | 15,000 | 100 |
How the limits behave:
- Monthly limit reached - the delivery waits. It shows as failed ("Monthly webhook limit exceeded"), and TellDone tries again every 5 minutes. It goes out after your limit resets at 00:00 UTC on the 1st, unless it's more than 30 days old by then. Deliveries sent after the reset count toward the new month.
- Hourly limit reached - the delivery is deferred and retried after about 5 minutes, so a short burst won't lose data.
Only successful deliveries count toward the monthly limit. The hourly limit counts every attempt, including retries. Test sends don't count toward either limit.
If you change plans, your automations and their settings are kept. On Free they stop sending and you can't open them until you upgrade. On Basic, automations you already had keep sending within 300 deliveries a month, but you can't add or switch on more than one.
Filtering test events
When you tap Test send, the payload includes "test": true at the top level. In your automation tool, you can check for this field and skip processing when it is present.
Platform guides
For step-by-step setup instructions with your automation tool:
- Zapier - connect TellDone to thousands of apps with Zaps
- Make - build visual automation scenarios (formerly Integromat)
- n8n - use TellDone webhooks in self-hosted or cloud workflows
- Custom endpoints - any service that accepts HTTPS POST requests works with TellDone webhooks
Ideas
- Send tasks to a Google Sheet via Zapier
- Create Slack messages from notes via Make
- Log events to a custom CRM via n8n
- Back up all notes to cloud storage
- Forward reports to a team dashboard
See also
- Inbox and support - if a webhook fails repeatedly, you'll see a message in your Inbox
- Zapier - step-by-step Zapier setup
- Make - step-by-step Make setup
- n8n - step-by-step n8n setup
- Todoist - dedicated two-way task sync (no webhooks needed)
- Notion - dedicated Notion integration
- Email forwarding - receive note data via email