Webhooks

Webhooks let your integration react to events as they happen inside Aleta. When a new transaction posts, a holding's market value updates, or an account is reconnected, we send a signed HTTP POST to the URL you've registered.

Registering an endpoint

POST /v1/webhooks

curl https://api.aleta.io/v1/webhooks \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/aleta/webhooks",
    "events": ["transaction.created", "account.connected"]
  }'

The response includes a secret — store it securely and use it to verify signatures on incoming requests.

Verifying signatures

Every webhook delivery includes an Aleta-Signature header: a timestamp plus an HMAC-SHA256 of the request body keyed by your webhook secret. Reject any delivery whose signature doesn't verify, or whose timestamp is more than five minutes off.

Express handler

import crypto from "crypto";

app.post("/aleta/webhooks", (req, res) => {
  const [timestamp, signature] = req.header("Aleta-Signature").split(",");
  const expected = crypto
    .createHmac("sha256", process.env.ALETA_WEBHOOK_SECRET)
    .update(`${timestamp}.${JSON.stringify(req.body)}`)
    .digest("hex");

  if (signature !== expected) return res.sendStatus(401);
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return res.sendStatus(401);

  // Handle the event…
  res.sendStatus(200);
});

Return a 2xx response within 10 seconds. We consider anything slower a failure and retry with exponential backoff, up to 24 hours. Idempotency is on you — we may deliver the same event more than once.

Event catalogue

EventWhen it fires
account.connectedA new financial account is linked to an entity.
account.disconnectedAn account's credentials break or it is unlinked.
transaction.createdA new transaction is ingested from a data source.
transaction.updatedAn existing transaction is classified or amended.
holding.valuedA holding receives an updated market value snapshot.