Webhooks

Keep your own lists of companies to monitor (for example your customers) and receive their changes as they are found: board members and owners, status, tax debt, court cases and enforcement, official notices, annual reports and credit rating. Changes are pushed to your server as signed HTTPS requests, or can be polled from GET /monitoring/events.

Monitoring through the API needs the API plan. The monitoring endpoints are free; each monitored company costs the same as on the Monitoring page, once per organisation however many lists it is on. The lists are separate from the Monitoring page's list in the dashboard.

Set up

Lists, companies and the webhook
# 1. A list
curl -X POST https://api.regia.ee/monitoring/lists \
  -H 'X-API-Key: YOUR_API_KEY' -H 'Content-Type: application/json' \
  -d '{"name":"Accounting clients"}'

# 2. Its companies (up to 2,000 per request; replace: true syncs the whole list)
curl -X POST https://api.regia.ee/monitoring/lists/lst_…/companies \
  -H 'X-API-Key: YOUR_API_KEY' -H 'Content-Type: application/json' \
  -d '{"add":[{"registry_code":"10421629","external_ref":"client-42"}]}'

# 3. Where to send changes (the response has signing_secret once: store it)
curl -X PUT https://api.regia.ee/webhook \
  -H 'X-API-Key: YOUR_API_KEY' -H 'Content-Type: application/json' \
  -d '{"url":"https://your-app.com/webhooks/regia","enabled":true}'

Each company can carry your own external_ref, which comes back in every event. A list, or a single company, can follow only some categories: registry, people, tax, court, announcements, reports, rating and assets. Monitoring starts when a company is added; earlier history is not sent.

What arrives

One POST per change. A company on several of your lists gives one event that names all of them. Changes are detected twice a day, in the morning and in the evening, and sent within minutes.

Headers
POST /webhooks/regia HTTP/1.1
Content-Type: application/json
User-Agent: Regia-Webhooks/1.0
X-Regia-Event-Id: evt_48213
X-Regia-Timestamp: 1790661912
X-Regia-Signature: v1=5f1c…
Body
{
  "id": "evt_48213",
  "type": "monitoring.change",
  "created_at": "2026-09-29T06:05:12.000Z",
  "data": {
    "company": {
      "registry_code": "10421629",
      "name": "Näide OÜ"
    },
    "lists": [
      {
        "id": "lst_Q2x9aVb3KpT1mZr0",
        "name": "Accounting clients",
        "external_ref": "client-42"
      }
    ],
    "category": "tax",
    "kind": "tax_debt",
    "tone": "bad",
    "details": {
      "old": 0,
      "new": 1250.4
    },
    "detected_at": "2026-09-29T06:05:12.000Z"
  }
}

tone says whether the change is bad, good or neutral for the company; details depends on the kind and often has old and new. The same objects come from GET /monitoring/events.

Saved searches

Save company filters and the same webhook receives, every morning after the data is refreshed, the companies that started to match: new companies in your field, companies that got tax debt, fell to a lower rating or grew past a size. What matches when you create a search is the starting point and is not sent. Up to 500 companies go in one search.matches event; a bigger day comes in parts. Without a webhook, poll GET /searches/{search}/matches.

A saved search
curl -X POST https://api.regia.ee/searches \
  -H 'X-API-Key: YOUR_API_KEY' -H 'Content-Type: application/json' \
  -d '{"name":"New builders in Harju","filters":{"activity":["41","43"],"county":["Harju"],"registered_within_days":30}}'
search.matches
{
  "id": "evt_srch_Hq3Lk8PzXw1Ab7Tc_20260930_1",
  "type": "search.matches",
  "created_at": "2026-09-30T06:10:00.000Z",
  "data": {
    "search": {
      "id": "srch_Hq3Lk8PzXw1Ab7Tc",
      "name": "New builders in Harju"
    },
    "date": "2026-09-30",
    "part": 1,
    "parts": 1,
    "total": 1,
    "companies": [
      {
        "registry_code": "17012345",
        "name": "Näide Ehitus OÜ",
        "legal_form": "Osaühing",
        "status": "R",
        "registered_on": "2026-09-29",
        "address": "Harju maakond, Tallinn, Näite tn 1",
        "activity_code": "41201",
        "employees": null,
        "revenue": null,
        "tax_debt": null,
        "rating": null
      }
    ]
  }
}

Verify the signature

X-Regia-Signature is v1= followed by the hex HMAC-SHA256 of timestamp + "." + body, keyed with your signing secret. Reject requests older than five minutes.

Node.js
import { createHmac, timingSafeEqual } from "node:crypto";

// rawBody: the request body exactly as received, before JSON parsing.
function verifyRegia(rawBody, headers, secret) {
  const timestamp = headers.get("x-regia-timestamp") ?? "";
  const signature = headers.get("x-regia-signature") ?? "";
  if (!/^\d+$/.test(timestamp) || !/^v1=[a-f0-9]{64}$/.test(signature)) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false; // replayed
  const expected = createHmac("sha256", secret).update(timestamp + "." + rawBody).digest();
  return timingSafeEqual(expected, Buffer.from(signature.slice(3), "hex"));
}

// Verify, store the event and skip ids you already have, then answer 2xx.
// Do slow work in your own background job.

Delivery and retries

Answer with any 2xx status within 10 seconds. Anything else, a timeout or a redirect counts as a failure and is retried after 1 minute, 5 minutes, 15 minutes, 1, 3, 6 and 12 hours: 8 attempts in all. The same event can arrive twice, so use id to skip duplicates. After the last attempt the delivery is marked failed; retry it from the dashboard (API page) or with POST /webhook/deliveries/{id}/retry, or catch up with GET /monitoring/events.

The URL must be public HTTPS on port 443. Changing it, rotating the secret or turning delivery off cancels what is still queued, so data never goes to a replaced endpoint. Turning delivery on starts from that moment.

Test your endpoint with POST /webhook/test or the Send test button on the API page: it sends a webhook.test event signed the same way.

Cookies

We use the cookies the portal needs to work and, with your consent, Google Analytics to understand how the portal is used. Learn more