Use case · Compliance & AML

Ongoing Due Diligence API

Turn the AMLR's ongoing-monitoring duty into a scheduled job: re-verify each business customer on a risk-based cycle, watch for trigger events daily, and re-screen sanctions lists automatically — with an audit trail you can show a regulator.

Published 15 September 2026

The problem

Compliance and MLRO teams at fintechs, payment providers, CASPs, lenders and marketplaces already run KYB at signup: resolve the company, screen it, decide. The gap is what happens after approval. The EU AMLR (Regulation (EU) 2024/1624), applying from 10 July 2027, is explicit that a business customer's file must stay current: re-verified on a risk-based cycle, updated when something changes. Today's AMLD5/6 transpositions already point the same way, as does the UK MLR 2017 for UK-regulated firms.

In practice, most teams treat re-verification as an annual spreadsheet exercise: export the list, re-check each company by hand, hope nothing moved in between. A director resigns or a customer enters insolvency eight months into the cycle, and nobody notices until the next scheduled review — or until a regulator asks for the audit trail and there isn't one.

This page describes what the obligation asks for in general terms, not legal advice; see the AMLR compliance timeline for the dates and thresholds.

The pattern

Run the same checks used at onboarding, on a schedule instead of once. Four endpoints do the work, plus one thing kept on your own side:

  • 1. Calendar-driven re-verification. Re-resolve each customer with POST /companies/lookup (up to 100 items per call) and diff status, dissolution_date, address, legal_form and previous_names against what was stored at onboarding — annually for low-risk customers, quarterly for high-risk ones.
  • 2. Trigger-based monitoring. POST /monitor pushes HMAC-SHA256-signed webhooks (X-Prometiam-Signature) for customers in every live country except France and Chile: Spain defaults to corporate_event, status_change and sanctions_match; Ireland and Poland (company-level) default to status_change, dissolution and sanctions_match. For Spanish, French and British customers, a daily GET /company-events/search?date_from= sweep catches dissolution, insolvency, director, capital, name and address changes instead.
  • 3. Sanctions re-screening. GET /sanctions/changes?since= reports what was listed, delisted or updated since the last run; POST /sanctions/screen then re-checks the book due this cycle, up to 50 names per call.
  • 4. Insolvency. POST /insolvency/check covers corporate insolvency across sixteen markets (FR, DE, GB, AT, CH, NO, FI, US, NL, DK, HR, SE, ES, LV, EE, SG) in one batch call, up to 5 notices per item. DE, AT, US and NL have no company registry behind these notices, so this is the only signal available there. Finland, Denmark, Croatia, Sweden and Switzerland have company records (company records only: with register-change events and monitoring, but no officers and no registry-compliance signal) and their notices are linked by business ID, CVR number, MBS, organisationsnummer and UID (Switzerland, where the notice names the debtor's UID). Slovakia and the United States are company records only too (with register-change events and monitoring), but have no insolvency notices: their bankruptcy shows in the company status. Estonia, Latvia and Spain have insolvency notices as well, linked by registry code, registration number and NIF.
  • 5. Audit trail. Store each response with its match_score, the threshold used, and the review date — the record a regulator will ask to see.
# 1. Re-resolve up to 100 customers per call and diff against the stored record
curl -X POST 'https://api.prometiam.com/functions/v1/risk-api/companies/lookup' \
  -H 'Authorization: Bearer rk_live_...' -H 'Content-Type: application/json' \
  -d '{"items":[{"country":"ES","company_number":"A28015865"},{"country":"FR","siren":"552032534"}]}'

# 2. What moved on the sanctions lists since the last run?
curl 'https://api.prometiam.com/functions/v1/risk-api/sanctions/changes?since=2026-08-15' \
  -H 'Authorization: Bearer rk_live_...'

# 3. Corporate insolvency across the sixteen covered markets, same batch shape
curl -X POST 'https://api.prometiam.com/functions/v1/risk-api/insolvency/check' \
  -H 'Authorization: Bearer rk_live_...' -H 'Content-Type: application/json' \
  -d '{"items":[{"country":"FR","company_number":"552032534"},{"country":"GB","name":"Example Ltd"}]}'

Two quirks worth knowing. French records can come back with status: non_diffusible when INSEE has restricted publication of that entity's identity — treat it as unknown, not a status change. And GET /sanctions/changes is a cue, not a lookup: it reports what moved on the lists, not which of your customers it affects, so the practical pattern is re-screening the book due this cycle whenever the feed shows activity. PEP screening stays beta, Spain only, with no relatives or close associates, so a clear PEP result is not a FATF determination by itself. For the mechanics of screening a large list in one call, see batch screening.

A real example

The review job: batch-resolve, diff, conditionally re-screen, check insolvency, and log every result. A scheduler — cron, a Cloudflare Cron Trigger, or a queue worker — calls review() once a day with the customers due under the calendar.

import os, requests
from datetime import date

API = 'https://api.prometiam.com/functions/v1/risk-api'
HEAD = {'Authorization': f'Bearer {os.environ["PROMETIAM_API_KEY"]}'}
TRACKED = ('status', 'dissolution_date', 'address', 'legal_form', 'previous_names')
TWELVE_MARKETS = {'FR', 'DE', 'GB', 'AT', 'CH', 'NO', 'FI', 'US', 'NL', 'DK', 'HR', 'SE'}

def review(customers, last_run):
    # 1. Re-resolve up to 100 at a time; diff the tracked fields against the stored row
    for i in range(0, len(customers), 100):
        chunk = customers[i:i + 100]
        items = [{'country': c['country'], 'company_number': c['company_number']} for c in chunk]
        r = requests.post(f'{API}/companies/lookup', json={'items': items}, headers=HEAD).json()
        for stored, result in zip(chunk, r['data']):
            company = result.get('company') or {}
            changed = {f: company.get(f) for f in TRACKED if company.get(f) != stored.get(f)}
            audit(stored, 'lookup', result, changed or None)

    # 2. Only re-screen if a sanctions list actually moved since the last run
    changes = requests.get(f'{API}/sanctions/changes', params={'since': last_run}, headers=HEAD).json()
    if changes.get('data'):
        names = [c['legal_name'] for c in customers]
        batch = requests.post(f'{API}/sanctions/screen', headers=HEAD,
                               json={'names': names, 'threshold': 85, 'group': 'entity'}).json()
        for stored, hit in zip(customers, batch['data']):
            audit(stored, 'sanctions', hit, threshold=85)

    # 3. Corporate insolvency, sixteen markets only
    watched = [c for c in customers if c['country'] in TWELVE_MARKETS]
    if watched:
        items = [{'country': c['country'], 'company_number': c['company_number']} for c in watched]
        r = requests.post(f'{API}/insolvency/check', json={'items': items}, headers=HEAD).json()
        for stored, result in zip(watched, r['data']):
            if result['status'] == 'found':
                audit(stored, 'insolvency', result)

def audit(customer, check, response, changed=None, threshold=None):
    # 4. Append-only: the raw response, the score/threshold used, and today's date
    db.review_log.insert({'customer_id': customer['id'], 'check': check, 'response': response,
                           'match_score': response.get('best_match_score'), 'threshold': threshold,
                           'changed_fields': changed, 'reviewed_on': date.today().isoformat()})

42 lines including imports. db and the customer rows are your own store — Prometiam has no concept of "your book"; it resolves, screens and checks whatever is sent to it.

What comes back

Each check returns a status to branch on, plus the detail an audit trail should keep:

  • POST /companies/lookup — status (found / not_found / error / timeout) and, when found, the usual company object: status, dissolution_date, address, legal_form, previous_names, and a match_score normally null here since the call resolves by identifier.
  • POST /sanctions/screen — status (match / clear / error / timeout) per name, hits[], hit_count, distinct_entities, and a best_match_score — log this next to the threshold that was set.
  • POST /insolvency/check — status (found / none / error / timeout), up to 5 notices[] newest first, and latest_filing_date. none means no corporate notice in the sixteen covered markets — not proof of solvency.
  • POST /monitor webhooks — alert_type (corporate_event / status_change / dissolution / sanctions_match), event_date, a detail object, and webhook_status.
Customer dashboard screenshot: a list of monitored companiesCustomer dashboard screenshot: a list of monitored companies
POST /monitor in the dashboard: the watchlist and what each company is watched for. Monitoring covers every live country except France and Chile; a sanctions match is beta. Shown with a demo account. Open it in the dashboard

Pricing fit

Take a 5,000-customer book. Annual re-verification is 5,000 POST /companies/lookup items a year — negligible against a monthly quota. Quarterly re-screening of the same book is 5,000 × 4 = 20,000 POST /sanctions/screen items a year, which can land as a burst of up to 5,000 in one month. The real driver is the daily trigger sweep: 1,500 Spanish, French and British names polled once a day through GET /company-events/search runs to about 45,000 calls a month, or 90,000 credits at 2 credits a call. Stack a quarter-end screening burst (up to 5,000 names at 3 credits, 15,000) on top and the busiest month sits near 105,000 credits — beyond Scale (€99.99/month, 50,000 credits a month, 300 requests/minute), so negotiate an Enterprise allowance or put the sweep on webhooks. The same shape at a 500-customer book is about 9,000 credits for the sweep plus a 1,500-credit screening burst, 10,500 in the busiest month: the edge of Professional (€29.99/month, 10,000 credits a month, 120 requests/minute), with Scale comfortably above it.

One more limit to plan around: POST /monitor caps active subscriptions by tier (Free 3, Starter 25, Professional 250, Scale 2,500). A 5,000-customer book with every name in a monitored country (every live country except France and Chile) under webhook monitoring is beyond even Scale (€99.99/month)'s cap; put the highest-risk subset on webhooks and leave the rest to the daily sweep or a scheduled GET /search pass.

Scope limits, stated plainly

  • Insolvency notices are corporate only, across sixteen markets (FR, DE, GB, AT, CH, NO, FI, US, NL, DK, HR, SE, ES, LV, EE, SG). Spanish concursal notices are on /insolvency/search and also on GET /search.
  • Webhook monitoring (POST /monitor) covers every live country except France and Chile, with a per-tier cap on active subscriptions.
  • Sanctions screening is beta. PEP screening is beta, Spain only, and holds no relatives or close associates — a clear result is not a FATF PEP determination by itself.
  • Ireland is company-level: there are no officers to re-verify yet, so the periodic diff is limited to the company record.
  • Norway has register-change events only, from 14 Oct 2026 (no gazette) and sits outside the countries POST /monitor covers, so Norwegian customers get the calendar-driven lookup diff plus sanctions and insolvency checks, not the daily sweep or webhooks.
  • None of this is legal advice. It describes what the API returns; whether a program satisfies the AMLR, AMLD5/6 or UK MLR 2017 is a question for your compliance function — see the AMLR compliance timeline.

FAQ

What does "ongoing due diligence" mean under the AMLR?
The EU AMLR (Regulation (EU) 2024/1624, from 10 July 2027) treats a business customer's file as something to keep current, not a one-time gate: re-verify on a risk-based cycle and act on trigger events such as a dissolution or a new sanctions match. This describes the obligation in general terms, not legal advice — see the AMLR compliance timeline.

How often should I re-verify a business customer?
A common risk-based cadence is annual for low-risk customers and quarterly for high-risk ones, re-running the same checks used at onboarding. The exact cadence is a decision for your compliance function.

Can sanctions re-screening be automated?
Yes: GET /sanctions/changes?since= reports what was listed, delisted or updated since the last run, and POST /sanctions/screen re-checks up to 50 names per call. Sanctions screening is beta; PEP screening is beta, Spain only, and holds no relatives or close associates, so a clear result is not a FATF determination by itself.

Does ongoing monitoring work the same way in every country?
No. POST /monitor webhooks cover every live country except France and Chile. GET /company-events/search covers Spain, France and the United Kingdom, plus register-change events for every other live country except Chile (none before 14 Oct 2026 for Ireland, Poland and Norway; Argentina is read monthly, none before 28 Oct 2026; Mexico covers its LEI-registered entities only). Norway has register-change events only, from 14 Oct 2026 (no gazette), plus monitoring, so Norwegian customers also get status, dissolution and sanctions alerts. Ireland is company-level, with no officers to re-verify yet.

What happens if a review batch is bigger than my remaining quota?
POST /companies/lookup, POST /sanctions/screen and POST /insolvency/check count every item as one request. A batch larger than what is left on your plan is refused up front with a 429 (batch_exceeds_quota) and max_items_now, so the call can be resized instead of losing partial results.

Related