Skip to content

COMPANY WATCH

Company watch

One watchlist per organisation, an event feed, and alerts by webhook or e-mail. The same keys as the other APIs, but its own product with its own plan: the price is per watched company per month, and watching never counts as a lookup.
API version
0.4.0
Endpoints
11
Contents

Endpoints

https://api.approach.no/api/v1/companies · OpenAPI contract

GET

/watchlist

List watched companies.

Parameters

NameTypeBounds and defaultDescription
cursor
query
string—next_cursor from the previous page
limit
query
integer1–1000
default 200
—

Responses and error codes

POST

/watchlist

Watch companies. Adds up to 10 000 organisasjonsnumre per call; the plan decides the total (409 watch_limit_exceeded). Unknown numbers are kept and flagged. Sole proprietorships and other entries that are, or may be, a person are kept, flagged natural_person, and never alerted or billed. Only events detected after a company is added are delivered, plus the last 30 days when include_last_30_days is true.

Parameters

NameTypeBounds and defaultDescription
event_types
body
array<object>—Omit for every type, now and later
include_last_30_days
body
boolean—
default false
—
org_numbers
body · required
array<string>——

Responses and error codes

DELETE

/watchlist

Stop watching companies.

Parameters

NameTypeBounds and defaultDescription
org_numbers
body · required
array<string>——

Responses and error codes

GET

/watchlist/deliveries

Webhook and e-mail delivery status.

Parameters

NameTypeBounds and defaultDescription
status
query
stringpending | sent | dead | cancelled—
limit
query
integer1–500
default 50
—

Responses and error codes

POST

/watchlist/deliveries/{delivery}/retry

Requeue a dead delivery.

Parameters

NameTypeBounds and defaultDescription
delivery
path · required
integer——

Responses and error codes

GET

/watchlist/events

Event feed for watched companies. Oldest first. Keep next_cursor and resume from it; since filters on when the event reached your watchlist.

Parameters

NameTypeBounds and defaultDescription
since
query
stringdate-time—
cursor
query
string——
limit
query
integer1–1000
default 100
—
order
query
stringoldest | newest
default oldest
newest returns the latest events, one page, for display; poll with the default

Responses and error codes

GET

/watchlist/settings

Delivery settings.

Takes no parameters.

Responses and error codes

PUT

/watchlist/settings

Change delivery settings. webhook_url must be https on port 443 or 8443 and resolve to public addresses only; an empty string removes it. email_mode is off, immediate or daily (07:00 Oslo). email_locale is nb or en.

Parameters

NameTypeBounds and defaultDescription
email_locale
body
stringnb | en—
email_mode
body
stringoff | immediate | daily—
email_recipients
body
array<string>——
webhook_url
body
string——

Responses and error codes

GET

/watchlist/webhook/secret

Webhook signing secret: shown once, then metadata. The plaintext secret is returned by the first GET after the watchlist is created, and by every rotation; after that this returns only metadata (secret_hint, rotated_at, previous_secret_valid_until, secret_revealed). Lost it? POST rotates.

Takes no parameters.

Responses and error codes

POST

/watchlist/webhook/secret

Rotate the webhook signing secret. Returns the new secret, once. The previous secret keeps signing for 24 hours: Datakilde-Signature then reads t=<unix>,v1=<new>,v1=<previous>, and a receiver accepts the request if either v1 matches. After the window the header has a single v1.

Takes no parameters.

Responses and error codes

POST

/watchlist/webhook/test

Send a signed sample event to the webhook.

Takes no parameters.

Responses and error codes

Webhook

Alerts are sent as a POST with Content-Type: application/json to the URL in the settings. Any 2xx is a successful delivery. Delivery is at least once: deduplicate on id. Redirects are not followed, and the URL must be https on port 443 or 8443 and resolve to public addresses.

Headers

HeaderCarries
Datakilde-Signaturet=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>" with the watchlist's secret>
Datakilde-TimestampThe same t as in the signature.
Datakilde-Event-IdThe payload's id. Delivery is at least once: deduplicate on it.

Retries

A non-2xx answer, a timeout or a refused connection is retried after 1, 2, 4, 8, 16, 32, 64, 128 minutes. After 9 attempts the delivery is dead; it is listed under /watchlist/deliveries?status=dead and can be requeued.

Event types

company.bankruptcy, company.dissolution, company.announcement, company.registry_change, company.roles_change, company.accounts_filed, company.court_hearing

Verify the signature

Compute the signature on the raw bytes, before parsing JSON, with the secret from /watchlist/webhook/secret. POST /watchlist/webhook/test sends a signed sample.

import hashlib, hmac, time

def verify(secret: str, header: str, body: bytes, tolerance: int = 300) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
    t, v1 = parts.get("t", ""), parts.get("v1", "")
    if not t.isdigit() or abs(time.time() - int(t)) > tolerance:
        return False
    expected = hmac.new(secret.encode(), f"{t}.".encode() + body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, v1)
Example payload

The body the test endpoint sends, generated from the code that sends it. test is true only there.

{
  "created_at": "2026-10-01T09:15:02Z",
  "data": {
    "company_name": "EQUINOR ASA (TEST)",
    "detected_at": "2026-10-01T09:15:02Z",
    "event_id": 0,
    "id": "test",
    "occurred_on": "2026-10-01",
    "org_number": "923609016",
    "source": "brreg_announcements",
    "source_ref": "test",
    "source_url": "https://w2.brreg.no/kunngjoring/",
    "summary": {
      "category": "konkurs",
      "court": "OSLO TINGRETT",
      "register_type": "Konkursåpning (testhendelse)"
    },
    "type": "bankruptcy",
    "via": "live"
  },
  "id": "cw_test",
  "test": true,
  "type": "company.bankruptcy"
}