Obsidian plugin · 4.12.3

TaskNotes Webhooks

TaskNotes webhooks send HTTP POST requests when selected events occur.

TaskNotes webhooks send HTTP POST requests when selected events occur.

Prerequisites#

  1. Enable HTTP API in Settings -> TaskNotes -> Integrations -> HTTP API.
  2. Create at least one webhook (settings UI or API).
  3. Subscribe it to one or more events.

Event Types#

Task events:

  • task.created
  • task.updated
  • task.deleted
  • task.completed
  • task.archived
  • task.unarchived

Time events:

  • time.started
  • time.stopped

Pomodoro events:

  • pomodoro.started
  • pomodoro.completed
  • pomodoro.interrupted

Recurring events:

  • recurring.instance.completed
  • recurring.instance.skipped

Completing a materialized occurrence note emits recurring.instance.completed with the reconciled parent task plus the completed occurrence and date in the event data. Skipping virtual recurring instances emits recurring.instance.skipped; materialized occurrence skip currently reconciles the parent and occurrence note without emitting a separate skip webhook.

Reminder events:

  • reminder.triggered

Payload Shape#

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.

Register and Manage Webhooks via API#

Create#

POST /api/webhooks

Required fields:

  • url (string)
  • events (non-empty array)

Optional fields:

  • id
  • secret (auto-generated if omitted)
  • active (default true)
  • transformFile
  • corsHeaders (default true)

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"
  }'

List#

GET /api/webhooks

Returns registered hooks. Secrets are not returned.

Delete#

DELETE /api/webhooks/:id

Delivery History#

GET /api/webhooks/deliveries

Returns last 100 in-memory deliveries.

Delivery Behavior#

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: 3 retries after initial attempt (up to 4 attempts total).
  • If cumulative failureCount for a webhook exceeds 10, webhook is auto-disabled.
  • No explicit request timeout is set in delivery fetch.

Because retries and duplicates are possible, handlers should be idempotent.

Signature Verification#

When corsHeaders is enabled (default), delivery includes:

  • X-TaskNotes-Event
  • X-TaskNotes-Signature
  • X-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;
}

Payload Transformations#

A webhook can specify transformFile pointing to a file in your vault.

Supported file types:

  • .json: event template map with variable interpolation

If transform execution fails, TaskNotes falls back to original payload.

JavaScript transform files are no longer supported.

JSON Transform Files#

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.

CORS and Headers#

Webhook delivery requests are outbound server-side requests from TaskNotes.

corsHeaders behavior:

  • true (default): sends TaskNotes custom headers including signature.
  • false: only sends Content-Type: application/json.

Disable custom headers for endpoints that reject non-standard headers.

Testing#

Local Test Server#

Use repository script:

node test-webhook.js

Optional custom port:

node test-webhook.js 8080

Default server values:

  • URL: http://localhost:3000/webhook
  • Test secret: test-secret-key-for-tasknotes-webhooks

External Inspection#

For quick inspection, use a request-bin tool such as webhook.site.

Troubleshooting#

No deliveries#

  1. Confirm HTTP API is enabled.
  2. Confirm webhook is active.
  3. Confirm event is subscribed.

Signature mismatch#

  1. Confirm secret matches webhook config.
  2. Verify your verifier hashes the exact JSON body.
  3. Verify hex digest comparison.

Webhook disabled automatically#

If failures continue and failureCount exceeds 10, webhook is disabled.

  1. Fix endpoint availability or response handling.
  2. Re-enable webhook in TaskNotes settings, or recreate it via POST /api/webhooks.

Transform errors#

  1. Confirm transformFile exists in vault.
  2. Confirm the file is a .json transform template.
  3. Check Obsidian console logs for transform exceptions.
Source: tasknotes.dev:content/obsidian/webhooks.mdView source