Webhook automations
Webhooks require a paid plan. Basic: 1 webhook, Pro: 3, Ultra: 10.
Webhooks let you connect TellDone to any external service. When you create a voice note, TellDone processes it and automatically sends the extracted data - notes, tasks, events, and reports - to a URL you specify. Each task and event is sent as a separate delivery, so your automation tool can handle them individually.
Setting up a webhook

- Go to Settings > Integrations > Webhook Automations
- Tap New Automation

- Enter a name (e.g., "My Zapier webhook")
- Paste the webhook URL from your automation service
- Choose which data to send: notes, tasks, events, reports (or any combination)
- Optionally, add an auth header - a token sent as the
Authorizationheader for secure endpoints - Tap Save
- Copy the signing secret - it is shown only once. You will need it if you want to verify webhook authenticity
- Tap Test send to confirm your endpoint receives data
The webhook URL must use HTTPS. HTTP addresses and private network IPs are not accepted.
What gets sent
Every delivery is a JSON object with three fields: event (the event type), timestamp, and data (the actual content). 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:00Z",
"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:00Z"
}
}
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. Enable this with the "Note + Audio" option when creating the automation.
Tasks (task.created)
One delivery per task. A single note with 3 tasks sends 3 separate webhooks.
{
"event": "task.created",
"timestamp": "2026-02-27T14:38:01Z",
"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:00Z",
"tags": ["work"],
"status": "todo",
"created_at": "2026-02-27T10:00:00Z"
}
}
Events (calendar_event.created)
One delivery per calendar event.
{
"event": "calendar_event.created",
"timestamp": "2026-02-27T14:38:02Z",
"data": {
"note_id": "550e8400-...",
"note_title": "Meeting notes",
"event_id": "770a0622-...",
"title": "Team standup",
"description": "Weekly sync",
"start": "2026-03-03T10:00:00+03:00",
"end": "2026-03-03T10:30:00+03:00",
"location": "Zoom",
"is_all_day": false,
"attendees": ["alice@example.com"],
"tags": ["work"],
"created_at": "2026-02-27T10:00:00Z"
}
}
Reports (report.created)
Sent when a daily, weekly, or monthly report is generated.
{
"event": "report.created",
"timestamp": "2026-02-28T00:05:00Z",
"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": "productive",
"tasks_created": 5,
"tasks_completed": 3
},
"created_at": "2026-02-28T00:05:00Z"
}
}
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 (note.created,task.created,calendar_event.created,report.created)X-LP-Delivery-Id- unique delivery ID (useful for deduplication)User-Agent- alwaysTellDone-Webhooks/1.0
The signature headers (X-LP-Signature and X-LP-Timestamp) are sent with every delivery, regardless of other settings.
If you set an auth header in the automation settings, it is sent as the Authorization header with every delivery. If the auth header field is empty, no Authorization header is sent.
The signing secret is shown once when you create the automation. You can rotate it anytime in the automation settings - the old secret stops working immediately.
Only HTTPS URLs are accepted. HTTP, IP addresses, and private network addresses are rejected.
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 X-LP-Signature header.
TellDone serializes JSON with spaces after separators (e.g. {"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");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// 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 raw body or match Python spacing |
| 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 with increasing delays:
| Attempt | Delay |
|---|---|
| 1st retry | 30 seconds |
| 2nd retry | 2 minutes |
| 3rd retry | 15 minutes |
| 4th retry | 1 hour |
| 5th retry | 4 hours |
After 5 failed attempts, the delivery is marked as dead. You can still retry it manually from the delivery history.
How TellDone handles different responses:
- 2xx - delivered successfully
- 4xx (except 429) - marked dead immediately, no retry (your endpoint explicitly rejected the data)
- 429 (Too Many Requests) - retries, respects the
Retry-Afterheader - 5xx or timeout - retries with the schedule above
After 20 consecutive failures across all deliveries, the automation is automatically disabled. Fix the issue, then re-enable it in Settings.
Managing webhooks
- Pause/resume - disable any automation without deleting it, re-enable anytime
- Delivery history - view all deliveries with status, HTTP code, and response time. Filter by Delivered or Errors
- Test send - send a test payload (includes
"test": truein the body) to verify your endpoint. Not counted toward limits - Rotate secret - generate a new signing secret if the old one is compromised
- Edit - update URL, auth header, or data types. Changes take effect immediately
- Delete - permanently removes the automation and all its delivery logs
Delivery limits
| Plan | Webhooks | Deliveries/month | Deliveries/hour |
|---|---|---|---|
| Basic | 1 | 300 | 100 |
| Pro | 3 | 3,000 | 100 |
| Ultra | 10 | 15,000 | 100 |
Ultra plan also supports audio links in note payloads (24-hour expiry).
Monthly counters reset on the 1st of each month. Test sends are not counted.
Filtering test events
When you use 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
Popular use cases
- 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