callumalpass_tasknotes/docs/webhooks.md
2026-05-31 21:48:10 +10:00

241 lines
5.1 KiB
Markdown

# 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`
## Payload Shape
All webhook payloads use the same top-level envelope:
```json
{
"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:
```bash
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:
```javascript
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:
```json
{
"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:
```bash
node test-webhook.js
```
Optional custom port:
```bash
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.