TaskNotes webhooks send HTTP POST requests when selected events occur.
- Enable HTTP API in
Settings -> TaskNotes -> Integrations -> HTTP API. - Create at least one webhook (settings UI or API).
- Subscribe it to one or more events.
Task events:
task.createdtask.updatedtask.deletedtask.completedtask.archivedtask.unarchived
Time events:
time.startedtime.stopped
Pomodoro events:
pomodoro.startedpomodoro.completedpomodoro.interrupted
Recurring events:
recurring.instance.completedrecurring.instance.skipped
Reminder events:
reminder.triggered
All webhook payloads use the same top-level envelope:
{
"event": "task.created",
"timestamp": "2026-02-21T10:30:00.000Z",
"vault": {
"name": "My Vault",
"path": "/path/to/vault"
},
"data": {}
}data is event-specific.
POST /api/webhooks
Required fields:
url(string)events(non-empty array)
Optional fields:
idsecret(auto-generated if omitted)active(defaulttrue)transformFilecorsHeaders(defaulttrue)
Example:
curl -X POST http://localhost:8080/api/webhooks \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/tasknotes",
"events": ["task.completed", "task.created"],
"transformFile": "TaskNotes/webhooks/slack.json"
}'GET /api/webhooks
Returns registered hooks. Secrets are not returned.
DELETE /api/webhooks/:id
GET /api/webhooks/deliveries
Returns last 100 in-memory deliveries.
Current implementation behavior:
- Webhook processing is asynchronous (fire-and-forget from task operation).
- Each delivery starts with one attempt.
- Failed deliveries retry with exponential backoff:
1s,2s,4s. - Maximum retries:
3retries after initial attempt (up to 4 attempts total). - If cumulative
failureCountfor a webhook exceeds10, webhook is auto-disabled. - No explicit request timeout is set in delivery fetch.
Because retries and duplicates are possible, handlers should be idempotent.
When corsHeaders is enabled (default), delivery includes:
X-TaskNotes-EventX-TaskNotes-SignatureX-TaskNotes-Delivery-ID
Signature is HMAC-SHA256 over JSON.stringify(payload) using webhook secret.
Node.js example:
const crypto = require("crypto");
function verifyWebhook(payload, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(JSON.stringify(payload))
.digest("hex");
return signature === expected;
}A webhook can specify transformFile pointing to a file in your vault.
Supported file types:
.js: JavaScript transform function.json: event template map with variable interpolation
If transform execution fails, TaskNotes falls back to original payload.
Expected format:
function transform(payload) {
return payload;
}Execution model:
- File is read from the vault.
- Code is executed with
new Function(...). - Returned value becomes the outgoing request body.
Important behavior:
- Returning
nulldoes not skip delivery. Payload body becomes JSONnull. - Returning an array does not fan out to multiple URLs. Array is posted to the configured single URL.
console.logis available because code runs in plugin runtime context.
Example:
function transform(payload) {
if (payload.event === "task.completed") {
return {
text: `Completed: ${payload.data.task.title}`,
event: payload.event,
at: payload.timestamp,
};
}
return payload;
}Structure:
{
"task.completed": {
"text": "Task completed: ${data.task.title}",
"vault": "${vault.name}"
},
"default": {
"text": "TaskNotes event: ${event}"
}
}Rules:
- Matching order: exact event key, then
default. - Variables use
${path.to.value}. - Missing variables are left unchanged.
Webhook delivery requests are outbound server-side requests from TaskNotes.
corsHeaders behavior:
true(default): sends TaskNotes custom headers including signature.false: only sendsContent-Type: application/json.
Disable custom headers for endpoints that reject non-standard headers.
Use repository script:
node test-webhook.jsOptional custom port:
node test-webhook.js 8080Default server values:
- URL:
http://localhost:3000/webhook - Test secret:
test-secret-key-for-tasknotes-webhooks
For quick inspection, use a request-bin tool such as webhook.site.
- Confirm HTTP API is enabled.
- Confirm webhook is active.
- Confirm event is subscribed.
- Confirm secret matches webhook config.
- Verify your verifier hashes the exact JSON body.
- Verify hex digest comparison.
If failures continue and failureCount exceeds 10, webhook is disabled.
- Fix endpoint availability or response handling.
- Re-enable webhook in TaskNotes settings, or recreate it via
POST /api/webhooks.
- Confirm
transformFileexists in vault. - For JS, ensure
transformfunction is defined. - Check Obsidian console logs for transform exceptions.