Integrations and webhooks
Push finished work to any URL you own. Delivery happens on a background thread, so a note is pasted and saved whether or not your endpoint answers, and every attempt is reported in the Activity feed. This page has the configuration, the JSON payload as it arrives, the three ways to authenticate it, and a worked example of verifying an HMAC signature in Node.
Integrations and webhooks
Push finished work to another service. Add a destination to
%USERPROFILE%\.deixis\config.toml:
[[integrations]]
name = "notion" # the handle used everywhere: refusals, Test, "Send to"
url = "https://example.com/deixis" # http(s) only
secret = "paste-it-here" # rewritten as dpapi:<ciphertext> on the next save
auth = "hmac" # hmac (default) | bearer | header
events = ["note", "capture", "translation"] # omit for all three
trigger = "auto" # auto (on finish) | manual (Library card button only)
Or use Settings → Integrations, which has a Test button per destination that posts a sample payload and shows you the status and response.
What arrives is one JSON POST:
{
"v": 1,
"event": "note",
"id": "2026-08-26T09-15-00",
"created_at": "2026-08-26T09:15:00Z",
"duration_ms": 42000,
"app": "",
"title": "…",
"text": "…",
"markdown": "…",
"references": [{ "kind": "image", "path": "C:\\Users\\you\\.deixis\\...png", "text": "", "t": 12.5 }]
}
Authentication, by mode:
hmac(recommended) —X-Deixis-Timestamp: <unix seconds>andX-Deixis-Signature: sha256=<hex>, the signature being HMAC-SHA256 of{timestamp}.{body}with your secret. The secret itself never travels, and the timestamp lets you reject replays.bearer—Authorization: Bearer <secret>.header—X-Deixis-Key: <secret>.
Verifying an HMAC delivery, in Node:
import { createHmac, timingSafeEqual } from "node:crypto";
const raw = await req.text(); // the exact bytes, before JSON.parse
const ts = req.headers.get("x-deixis-timestamp");
const sent = req.headers.get("x-deixis-signature") ?? "";
const mine = "sha256=" + createHmac("sha256", SECRET).update(`${ts}.${raw}`).digest("hex");
const ok = sent.length === mine.length && timingSafeEqual(Buffer.from(sent), Buffer.from(mine));
if (!ok || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return new Response("no", { status: 401 });
Details that matter:
- Delivery never blocks you. It happens on a background thread: three attempts with backoff, a 10-second ceiling each, and every outcome shown in the Activity feed. A finished note is pasted and saved whether or not your endpoint answers.
- Retry by hand from the card in the Library — the same "Send to" button that sends
manualdestinations. - Secrets are encrypted at rest with Windows DPAPI, tied to your Windows account. A
config.tomlcopied to another machine (or another user) keeps everything except the secret, which has to be re-pasted there — that is DPAPI working, not a bug. - Images travel as paths, not bytes: a receiver on this machine can read them, one elsewhere cannot.
Hold a key. Speak. Keep working.
Deixis is free, needs no account, and runs on your own device.
v1.2.2 · 64‑bit Windows 10/11 · macOS 13 on Apple silicon · 51 MB · nothing else to install · also for Windows