Skip to content

Latest commit

 

History

History
241 lines (158 loc) · 5.45 KB

webhooks.md

File metadata and controls

241 lines (158 loc) · 5.45 KB

TaskNotes Webhooks

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 (data.missed is true when the reminder is delivered late, for example after Obsidian was closed)

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).
  • A webhook is auto-disabled after more than 10 consecutive deliveries exhaust all retries. Success resets this streak (consecutiveFailures); cumulative failureCount and successCount remain telemetry.
  • 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 more than 10 consecutive deliveries exhaust all retries, the webhook is disabled. A successful delivery resets the streak; intermittent lifetime failures do not disable a recovered endpoint.

  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.