Webhooks

Notifikácie o udalostiach (faktúra vytvorená, platba prijatá, atď.).

Webhooky ti pošlú HTTP POST notifikáciu na tvoju URL kedykoľvek sa v Monivio stane vybraná udalosť. Subscriptiony spravuješ buď cez REST endpointy nižšie (scope webhooks:write), alebo v UI cez Nastavenia → Webhooks. Plaintext signing secret sa vracia iba pri vytvorení a regenerácii – odlož si ho hneď.

Udalosti

Pri prihlasovaní webhooku môžeš zvoliť konkrétne typy alebo wildcard: * matchne všetko, document.* všetky document udalosti, alebo presný kód.

  • document.created – nový dokument
  • document.updated – nová verzia dokumentu
  • document.sent – dokument odoslaný emailom
  • document.paid – faktúra označená ako uhradená
  • document.overdue – splatnosť prekročená
  • document.cancelled – dokument stornovaný
  • contact.created
  • contact.updated
  • contact.deleted
  • expense.created
  • expense.updated
  • expense.deleted
  • transaction.imported – nová bankové transakcia
  • transaction.matched – transakcia spárovaná s dokumentom
  • transaction.unmatched – párovanie zrušené
  • transaction.skipped – transakcia označená ako preskočená

Endpointy

POST/api/v1/webhookswebhooks:write

Vytvorí webhook subscription. Vracia plaintext secret iba raz – ulož ho hneď, neskôr sa už nedá načítať.

Request
curl -X POST "https://monivio.sk/api/v1/webhooks" \
  -H "Authorization: Bearer mk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Production webhook",
  "url": "https://example.com/hooks/monivio",
  "events": [
    "document.created",
    "document.paid"
  ]
}'
Response 201json
{
  "data": {
    "id": "whe_2hf8pq3rxn4mlkzyt9abwvve",
    "secret": "whsec_4b7f2a8c9d1e3f5a6b8c0d2e4f6a8b0c"
  }
}
GET/api/v1/webhookswebhooks:read

Zoznam webhook subscription-ov organizácie. Secret sa nevracia.

Request
curl "https://monivio.sk/api/v1/webhooks" \
  -H "Authorization: Bearer mk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Response 200json
{
  "data": [
    {
      "id": "whe_2hf8pq3rxn4mlkzyt9abwvve",
      "name": "Production webhook",
      "url": "https://example.com/hooks/monivio",
      "events": [
        "document.created",
        "document.paid"
      ],
      "headers": {},
      "is_active": true,
      "failure_count": 0,
      "disabled_at": null,
      "disabled_reason": null,
      "created_at": "2026-05-07T08:42:00.000Z",
      "updated_at": "2026-05-07T08:42:00.000Z"
    }
  ],
  "meta": {
    "page": 1,
    "limit": 1,
    "total": 1,
    "total_pages": 1
  }
}
GET/api/v1/webhooks/{id}webhooks:read

Detail subscription-u.

Request
curl "https://monivio.sk/api/v1/webhooks/con_2hf8pq3rxn4mlkzyt9abwvve" \
  -H "Authorization: Bearer mk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Response 200json
{
  "data": {
    "id": "whe_2hf8pq3rxn4mlkzyt9abwvve",
    "name": "Production webhook",
    "url": "https://example.com/hooks/monivio",
    "events": [
      "document.created",
      "document.paid"
    ],
    "headers": {},
    "is_active": true,
    "failure_count": 0,
    "disabled_at": null,
    "disabled_reason": null,
    "created_at": "2026-05-07T08:42:00.000Z",
    "updated_at": "2026-05-07T08:42:00.000Z"
  }
}
PATCH/api/v1/webhooks/{id}webhooks:write

Update polí subscription-u (name, url, events, headers, is_active). Aspoň jedno pole je povinné.

Request
curl -X PATCH "https://monivio.sk/api/v1/webhooks/con_2hf8pq3rxn4mlkzyt9abwvve" \
  -H "Authorization: Bearer mk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
  "events": [
    "document.created",
    "document.paid",
    "document.cancelled"
  ]
}'
Response 200json
{
  "data": {
    "id": "whe_2hf8pq3rxn4mlkzyt9abwvve",
    "name": "Production webhook",
    "url": "https://example.com/hooks/monivio",
    "events": [
      "document.created",
      "document.paid",
      "document.cancelled"
    ],
    "headers": {},
    "is_active": true,
    "failure_count": 0,
    "disabled_at": null,
    "disabled_reason": null,
    "created_at": "2026-05-07T08:42:00.000Z",
    "updated_at": "2026-05-07T08:55:00.000Z"
  }
}
POST/api/v1/webhooks/{id}/testwebhooks:write

Pošle synthetic `webhook.test` event na URL subscription-u. Nemení doručené eventy v queue. Vracia HTTP status zacieleného endpointu.

Request
curl -X POST "https://monivio.sk/api/v1/webhooks/con_2hf8pq3rxn4mlkzyt9abwvve/test" \
  -H "Authorization: Bearer mk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Response 200json
{
  "data": {
    "success": true,
    "response_status": 200,
    "duration_ms": 142,
    "error_message": null
  }
}
POST/api/v1/webhooks/{id}/regenerate-secretwebhooks:write

Invaliduje aktuálny signing secret a vráti nový. Predchádzajúce eventy podpísané starým secret-om už nebudú validné. Vracia plaintext secret iba raz.

Request
curl -X POST "https://monivio.sk/api/v1/webhooks/con_2hf8pq3rxn4mlkzyt9abwvve/regenerate-secret" \
  -H "Authorization: Bearer mk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Response 200json
{
  "data": {
    "id": "whe_2hf8pq3rxn4mlkzyt9abwvve",
    "secret": "whsec_4b7f2a8c9d1e3f5a6b8c0d2e4f6a8b0c"
  }
}
DELETE/api/v1/webhooks/{id}webhooks:write

Vymaže subscription. Pending deliveries v queue sa už nepošlú.

Request
curl -X DELETE "https://monivio.sk/api/v1/webhooks/con_2hf8pq3rxn4mlkzyt9abwvve" \
  -H "Authorization: Bearer mk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Payload

POST {your_url}json
{
  "id": "evt_3kw9pmxn7rt2vyzqlhc8bfsj",
  "type": "document.created",
  "created_at": "2026-06-01T10:00:00.000Z",
  "organization_id": "org_2hf8pq3rxn4mlkzyt9abwvve",
  "data": {
    "id": "doc_4kp9mxn8tq3vyz2lwbcr5shg",
    "type": "invoice",
    "number": "FA260042",
    "amount_total": 495,
    "currency": "EUR"
  }
}

Podpis (HMAC SHA-256)

Každá požiadavka je podpísaná HMAC SHA-256 hlavičkou Monivio-Signature. Secret dostaneš pri vytvorení webhooku (cez API alebo UI) a vieš ho rotovať cez POST /webhooks/{id}/regenerate-secret.

Hlavičkyhttp
Monivio-Signature: t=1717689600,v1=8b2f3c9a...
Content-Type: application/json
User-Agent: Monivio-Webhook/1.0

Verifikácia podpisu (TypeScript)

import crypto from "node:crypto";

function verifyMonivioSignature(
  rawBody: string,
  signatureHeader: string,
  secret: string
): boolean {
  const parts = signatureHeader.split(",").reduce<Record<string, string>>(
    (acc, part) => {
      const [k, v] = part.split("=");
      if (k && v) acc[k] = v;
      return acc;
    },
    {}
  );

  const timestamp = parts.t;
  const sig = parts.v1;
  if (!timestamp || !sig) return false;

  // Reject replay attacks older than 5 minutes.
  const age = Math.floor(Date.now() / 1000) - Number(timestamp);
  if (age > 300) return false;

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(sig, "hex"),
    Buffer.from(expected, "hex")
  );
}

Retry logika

  • • Tvoj endpoint musí vrátiť 2xx do 10 sekúnd. Inak considerujeme delivery za neúspešnú.
  • • Pri zlyhaní retryujeme spolu v intervaloch 0 (okamžite), 1 min, 5 min, 30 min, 2 h. Po piatom pokuse je delivery označená ako failed.
  • • Idempotenciu si zabezpeč na svojej strane podľa id v payloade – pri retry posielame rovnaký event id, takže môžeš dedup-núť.
  • • Ak endpoint zlyhá 5× po sebe v rôznych eventoch, automaticky ho deaktivujeme (is_active=false) a počkáme na manuálny re-enable cez PATCH /webhooks/{id} alebo v UI.