Skip to main content

Webhook automations

Basic and up

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​

Settings automations for Zapier, Make, and n8n

  1. Go to Settings > Integrations > Automations
  2. Tap New automation

New automation webhook setup

  1. Enter a name (for example, "My Zapier webhook")
  2. Paste the webhook URL from your automation service
  3. Choose which data to send (Note, Note + audio on Ultra, Tasks, Events, Reports) and which triggers to use (Created, Updated, Completed, Deleted)
  4. Optionally, add an auth header - a token sent as the Authorization header for secure endpoints
  5. Tap Save
  6. 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)
  7. 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 signing
  • X-LP-Event - the event type (for example note.created, task.completed, calendar_event.updated, report.created)
  • X-LP-Delivery-Id - unique delivery ID (useful for deduplication)
  • User-Agent - always TellDone-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.

Use the raw request body

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​

MistakeFix
Using parsed + re-serialized bodyUse the raw request body
JSON.stringify() in JS (compact, no spaces)Use the raw request body
Forgetting the timestamp prefixPayload format is timestamp + "." + body
Comparing strings directlyUse hmac.compare_digest or timingSafeEqual

Retry policy​

If your endpoint fails, TellDone retries each delivery on this schedule:

AttemptDelay
1st retry30 seconds
2nd retry2 minutes
3rd retry15 minutes
4th retry1 hour
5th retry4 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​

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": true in 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:

IconMeaning
Green checkDelivered
Yellow clockWaiting or being sent
Red triangleFailed, TellDone will retry
Red crossFailed, 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.

PlanAutomationsMonthly deliveriesHourly limit
Free00-
Basic1300100
Pro33,000100
Ultra1015,000100

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