# Supplier Risk Monitor (`mikee368/supplier-risk-monitor`) Actor

Re-check a saved supplier list. Register, VAT, sanctions, ownership, domain expiry and US SAM exclusions are billed per supplier. A change alert is billed only when the risk changes. A failed source is never clear.

- **URL**: https://apify.com/mikee368/supplier-risk-monitor.md
- **Developed by:** [Mike Berckmoes](https://apify.com/mikee368) (community)
- **Categories:** Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 register checks

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Supplier Risk Monitor

Re-check a saved list of suppliers on a schedule. One run reads the company register, VAT, sanctions lists, ownership, domain expiry and US SAM exclusions you turn on. The dataset has one baseline row per supplier the first time that supplier is seen, and after that only a supplier whose material result changed.

Saved state is one snapshot per supplier, in the named key-value store `supplier-risk-<customerId>`. It is not one global watermark. The first run stores that snapshot and does not charge a change alert.

This is a screening aid. It is not legal advice, not a credit rating, and not a clearance to contract. A failed source is never reported as clear.

### What it does

- Calls the existing register, VAT, sanctions, ownership, RDAP and SAM functions in this container. No child Actor is started, so those Actors are not billed again.
- Charges one event per supplier per check that called a source and got an answer. A failed source is not charged. A check that does not apply (no VAT number, no UEI, no domain) is not charged.
- Charges `change-alert` only when a saved snapshot changes. The baseline, an unchanged re-check, and a newly enabled check that comes back clean are not change alerts.
- France also reads BODACC for dissolution and insolvency-style notices as part of the register check (90-day window). The UK profile is read only when you set a Companies House key. The Companies House filing stream is not used.
- Sanctions lists are cached in `supplier-risk-sanctions-cache` for `listMaxAgeHours` (default 24). A refresh sends `If-None-Match` or `If-Modified-Since` when the last response included an ETag or Last-Modified. The cache is never used once it is older than 72 hours, including when the download fails. A list that failed to load is not treated as fresh.
- Does not send e-mail. An optional https webhook posts only when there is a real change. localhost, an IP address, an internal or metadata host, a port other than 443, and an `@` in the host are rejected, and the client does not follow redirects. `generic` posts the alert JSON, `slack` posts `{"text"}`, `discord` posts `{"content"}`. A webhook password is sent only as `Authorization: Bearer` and is not stored.
- The insolvency check is a plug for a later Actor (`my_actor/checks/insolvency.py`). It does not call a source yet. Turning it on is not billed and does not produce a clear result.
- At most 50 suppliers per run (default 25). The run is pinned to 512 MB because a cold sanctions load needs that much.

### Use cases

- Weekly re-check of a procurement supplier list, with a Slack or Discord message only when a supplier's risk changes.
- Register and VAT on a French supplier, with sanctions on the same name.
- Domain expiry on the supplier's website, without an alert every day the countdown moves.
- SAM.gov exclusion check for a US supplier you already know by UEI or CAGE.
- Ownership parent check when you opt in, using the same GLEIF walk as Company Ownership Lookup.

### What you get

| Field | Meaning | Example |
|---|---|---|
| `customerId` | Customer this row belongs to | `acme-procurement` |
| `supplierId` | Id from the input, or a hash of the identifiers | `example-fr` |
| `name` | Name from the input. Null when the input had none | `Example SAS` |
| `country` | ISO 3166-1 alpha-2. Null when the input had none | `FR` |
| `eventType` | `baseline` the first time this supplier is stored, `change` after that. An unchanged supplier is not a dataset row | `baseline` |
| `risk` | `clear`, `review`, `alert`, or `unknown` | `clear` |
| `severity` | `clear`, `unknown`, `low`, `medium`, `high`, or `critical` | `clear` |
| `failClosed` | `true` when an enabled check's source failed. The row is never `clear` in that case | `false` |
| `summary` | One short line per check that ran | `register: Register active.` |
| `changes` | Empty on a baseline. On a change, one object per material field that differed: `check`, `field`, `before`, `after`, `severity` | `[]` |
| `checks` | One object per enabled check. `status` is `skipped_unpriced` when that event has no live price: the source is not called, the check has no data, and it is not charged. See the check fields below | register `clear` |
| `checkedAt` | When this supplier was checked. Filled at run time | ISO 8601 UTC |
| `disclaimer` | Screening aid. Not legal advice and not a clearance | the fixed disclaimer |

Every check object has `status`, `severity`, `detail` and `charged`. `status` is `clear`, `review`, `hit`, `not-found`, `source-failed` or `not-applicable`. `charged` is true only when that check called a source and the event was in the live price list. Extra fields:

| Check | Extra fields |
|---|---|
| `register` | `registerSource`, `registrationNumber`, `registerStatus`, `legalName`, `notice` |
| `vat` | `vatNumber`, `vatExistence`, `vatName` |
| `sanctions` | `hitCount`, `listsPartial`. A hit also has `topScore`, `topList`, `topUid`, and `topEntityType`. A person hit gives the list name and the entry id, not the person's name |
| `ownership` | `lei`, `ultimateParent`, `parentCount`, `pscStatus`, `warnings`. A miss has `ownershipStatus` |
| `domain` | `domain`, `registered`, `registrar`, `expiresAt`, `daysToExpiry` |
| `sam` | `ueiSAM`, `cageCode`, `exclusionFlag`, `exclusionCount`, `registrationStatus` |
| `insolvency` | `provider`. The plug sets this to null |

`RUN_SUMMARY` on the default key-value store has the counts and the state-store name. `ALERT` is written there only when this run had a change. The per-supplier snapshot is `supplier-<supplierId>` in `supplier-risk-<customerId>`.

A row is `clear` only when every applicable check succeeded and is clean. `not-applicable` (no VAT number, no domain, no UEI) does not block `clear`. If every enabled check is `not-applicable`, the risk is `unknown`.

### How to use it

1. Set `customerId` to a stable name and paste the supplier list.
2. Leave the checks you want on. Ownership and insolvency are off unless you turn them on.
3. Schedule the same input (for example weekly). The next run reuses `supplier-risk-<customerId>`. To wipe the snapshots, delete that named key-value store in the Apify Console under Storage → Key-value stores. The next run stores baselines again.
4. Read the dataset for baselines and later changes, and `RUN_SUMMARY` for counts. On a change, read `ALERT` or the webhook.
5. For a UK profile or UK PSC, set `companiesHouseApiKey`. For SAM exclusions, set `samGovApiKey` and a UEI or CAGE.

The first run pushes the baseline and charges the checks that ran. It does not charge `change-alert`. A later run with the same material pushes nothing and still charges the checks that ran.

### Input

| Field | What to enter |
|---|---|
| `customerId` | Stable id. Letters, numbers, `_` and `-`. This selects the state store. |
| `suppliers` | Objects with `supplierId`, `name`, `country`, `registrationNumber`, `vatNumber`, `lei`, `domain`, `ueiSAM`, `cageCode`. An empty identifier row is skipped. |
| `maxSuppliers` | 1 to 50. Default 25. Rows past the cap are not checked and are not stored as clear. |
| `checks` | Booleans. Defaults: register, VAT, sanctions, domain and SAM on. Ownership and insolvency off. |
| `matchMode` | `strict` (default), `balanced`, or `loose`. |
| `minScore` | Sanctions score cutoff. Default 80. Range 50 to 100. |
| `expiryWithinDays` | Domain or SAM expiry inside this many days is medium. Default 30. |
| `listMaxAgeHours` | Sanctions cache age. Default 24. Range 1 to 168. A cache older than 72 hours is never used. |
| `refreshLists` | Download sanctions lists again. Default false. |
| `resetState` | Next rows are baselines again. Default false. |
| `companiesHouseApiKey` | Optional. UK profile and UK PSC. Or set `COMPANIES_HOUSE_API_KEY`. |
| `samGovApiKey` | Optional. Required for a UEI or CAGE check. Or set `SAM_GOV_API_KEY`. |
| `webhookUrl` | Optional public `https://` URL. localhost, an IP address, an internal or metadata host, a port other than 443, and an `@` in the host are rejected. Called only for a real change. Redirects are not followed. |
| `webhookFormat` | `generic`, `slack`, or `discord`. |
| `webhookSecret` | Optional password, sent as a Bearer token. Not stored. |

An empty `customerId`, an empty supplier list, a webhook that is not a public `https://` host, or a password without a URL stops the run before any fetch and before any charge.

```json
{
  "customerId": "acme-procurement",
  "maxSuppliers": 25,
  "checks": {
    "register": true,
    "vat": true,
    "sanctions": true,
    "ownership": false,
    "domain": true,
    "sam": true,
    "insolvency": false
  },
  "suppliers": [
    {
      "supplierId": "example-fr",
      "name": "Example SAS",
      "country": "FR",
      "registrationNumber": "380000000",
      "vatNumber": "FR43380000000",
      "domain": "example.com"
    }
  ]
}
```

### Output example

Offline baseline for the Acme fixture in `tests/fixtures/fr_acme.json` (register active, VAT present, sanctions clear, domain registered). `checkedAt` is filled at run time.

```json
{
  "customerId": "acme",
  "supplierId": "acme-fr",
  "name": "Acme France SAS",
  "country": "FR",
  "eventType": "baseline",
  "severity": "clear",
  "risk": "clear",
  "failClosed": false,
  "summary": "register: Register active. vat: VAT number exists. sanctions: No sanctions hit at the chosen score. domain: Domain is registered and outside the expiry window. sam: SAM exclusions run when a UEI or CAGE is set.",
  "changes": [],
  "checks": {
    "register": {
      "status": "clear",
      "severity": "clear",
      "detail": "Register active.",
      "charged": true,
      "registerSource": "FR_SIRENE",
      "registrationNumber": "380000000",
      "registerStatus": "active",
      "legalName": "Acme France SAS",
      "notice": "none"
    },
    "vat": {
      "status": "clear",
      "severity": "clear",
      "detail": "VAT number exists.",
      "charged": true,
      "vatNumber": "FR43380000000",
      "vatExistence": "exists",
      "vatName": "Acme France SAS"
    },
    "sanctions": {
      "status": "clear",
      "severity": "clear",
      "detail": "No sanctions hit at the chosen score.",
      "charged": true,
      "hitCount": 0,
      "listsPartial": false
    },
    "domain": {
      "status": "clear",
      "severity": "clear",
      "detail": "Domain is registered and outside the expiry window.",
      "charged": true,
      "domain": "acme-test.com",
      "registered": true,
      "registrar": "Example Registrar",
      "expiresAt": "2035-01-01T00:00:00Z",
      "daysToExpiry": 3000
    },
    "sam": {
      "status": "not-applicable",
      "severity": "clear",
      "detail": "SAM exclusions run when a UEI or CAGE is set.",
      "charged": false
    }
  },
  "checkedAt": "filled at run time",
  "disclaimer": "Screening aid only. Not legal advice, not a credit rating, and not a clearance to contract. A failed source is never a clear result."
}
```

`checkedAt` and `daysToExpiry` are filled at run time. The other fields are the ones this fixture writes.

A later inactive register is `eventType: "change"`, `risk: "review"`, `severity: "high"`, with `changes` showing `registerStatus` from `active` to `inactive`.

### Pricing

Pay per event. The live price is `.actor/pricing.json`. One supplier on all seven checks is $0.025. The run reserves one `change-alert`, so one supplier on all checks is $0.028 (`minimalMaxTotalChargeUsd`).

Apify keeps 20% of the event price. Platform compute is $0.20 per compute unit (1 compute unit = 1 GB-hour), from the sanctions check run recorded in `actors/BUILD-NOTES.md` (0.00702 CU, compute line $0.001404). This Actor is pinned to 512 MB, so the developer pays that compute.

| Event | Price | Kept after 20% | Compute this price covers |
|---|---|---|---|
| `register-check` | $0.003 | $0.0024 | 30 s at 512 MB is about $0.00083. The measured status-watch check was 7.22 s. |
| `vat-check` | $0.003 | $0.0024 | Same 30 s floor. |
| `sanctions-check` | $0.006 | $0.0048 | Measured cold run: 50.5 s at 512 MB, about $0.00140. |
| `ownership-check` | $0.004 | $0.0032 | Measured 9.06 s (that run was 256 MB). At 512 MB the same wall time is about $0.00025. |
| `domain-check` | $0.003 | $0.0024 | 30 s floor, about $0.00083. |
| `sam-check` | $0.003 | $0.0024 | 30 s floor, about $0.00083. |
| `insolvency-check` | $0.003 | $0.0024 | Charged only when a future source is actually called. |
| `change-alert` | $0.003 | $0.0024 | Extra, only when the snapshot changed. Not required to cover the re-check. |

A 1-supplier run of any single check above is profitable after the 20% cut, including a sanctions-only cold run and a 60-second run of a $0.003 check (compute about $0.00167, kept $0.0024).

A 1-supplier run with all seven checks, one after another, uses the sum of those times (about 210 s) and about $0.0058 of compute. The seven check events are $0.025 and keep $0.020. With the reserved `change-alert` ($0.003) that is $0.028. An unchanged week still charges the checks and does not charge `change-alert`. A check that flips between clear and `skipped_unpriced` does not charge a change alert, does not call the webhook, and does not replace the saved material.

Not charged: a failed source, a check that does not apply, the unconfigured insolvency plug, the baseline as an alert, an unchanged supplier row, and a dataset row by itself. A requested check with no priced event is skipped, not returned for free. Its status is `skipped_unpriced`: no source call, no data, and no charge, so the supplier is not clear. An unpriced `change-alert` is not charged and does not call the webhook; the change row is still saved. The other priced events for that supplier still run. There is no start fee.

### Integrations

- Schedule the same `customerId` and the same supplier list. Weekly is the intended cadence.
- Export the dataset as JSON, CSV, Excel or HTML.
- Read `ALERT` on the default key-value store when you want one record per run that had a change. It is absent when nothing changed.
- Optional webhook above, for Slack, Discord, or a generic JSON receiver. This Actor does not post one webhook per unchanged supplier.
- A platform webhook on run succeeded still fires every run. Use `ALERT` or the dataset if you only want changes.
- Call it with the Apify API or `apify-client`. Pass the same input the schedule uses.

### FAQ

**Does a failed source come back as clear?** No. That check is `source-failed`, `failClosed` is true, and `risk` is `unknown` unless a real problem (medium or above) was also found.

**What is charged when nothing changed?** The checks that called a source and got an answer. A failed source is not charged. Not a change alert, and no dataset row.

**What happens the first time I enable a new check?** A clean result is stored and is not a change alert. A hit, a failed source, or a medium-or-higher result is a change alert.

**Does the insolvency check do anything yet?** It reserves the check and fails closed when you turn it on. Wire the later Actor in `my_actor/checks/insolvency.py`. Until `worked` is true, it is not billed.

**Does EU Company Status Watch already send this webhook?** No. This Actor posts the optional password-protected webhook itself. The status watch does not.

**Is a sanctions clear a legal clearance?** No. A clear result means this run found no name at or above `minScore` on the lists that loaded. Confirm hits on the official list.

### Limits

- 50 suppliers per run. Raise the cap in the input only up to 50. A longer list is truncated, those suppliers are not checked, and they are not given a clear row. A large list may need two scheduled runs, each with its own slice of suppliers. A run that stops at the charge cap leaves the suppliers it did not save for the next schedule.
- Sanctions lists are downloaded at most once per cache window, and never served from a cache older than 72 hours. A conditional request is used when the source supports ETag or Last-Modified. Natural-person names in that cache are SHA-256 hashes of the folded name. A later run matches an individual only when the full name is exact, so fuzzy, token, and reordered-name matches are lost. The hash is unsalted, so it hides the name in the cache but is not anonymisation. A cold load is the slow part of a 1-supplier run. Later runs in the same day reuse the cache.
- Register lookups and SAM/UEI calls are not cached. Companies House and BODACC can answer `304`, but this Actor does not keep those validators. A repeat run makes the same register and SAM calls as a cold run, and those checks cost the same.
- French notices are the last 90 days of BODACC, not the full archive. A notice older than that is not a current finding.
- UK Companies House is a profile read, not the 60–90 second filing stream. Without your key, a GB company can still match on GLEIF, and UK PSC is skipped.
- SAM exclusions run only for a UEI or CAGE, with your api.data.gov key. A US supplier with only a name is `not-applicable` for SAM.
- Domain alerts are registration, expiry band, expiry date and registrar. A nameserver-only change is not an alert. Some TLDs have no RDAP server in the bundled route and come back `source-failed`.
- The run does not crawl a website and does not send e-mail.

Sources: the company registers and GLEIF used by EU Company Register Lookup, VIES and the national VAT checks used by EU TIN VAT Validator, OFAC, the UK FCDO list, the UN consolidated list and the EU FSF file, GLEIF Level 2 parents (and UK PSC when you supply a key), the IANA RDAP bootstrap, BODACC annonces commerciales, and SAM.gov entity and exclusions. This Actor is not the official product of any of those sources.

# Actor input Schema

## `customerId` (type: `string`):

Stable name for this list. State is kept in the named key-value store supplier-risk-<customerId>. Use the same id on every scheduled run.

## `suppliers` (type: `array`):

The list to re-check. Each object can include supplierId, name, country (ISO2 or ISO3), registrationNumber, vatNumber, lei, domain or website, ueiSAM and cageCode. Maximum 50 per run.

## `maxSuppliers` (type: `integer`):

Stop after this many suppliers. Default 25. Maximum 50. Suppliers past the cap are counted as truncated and are not checked.

## `checks` (type: `object`):

Each true check is billed on its own when it calls a source. Ownership and insolvency are off by default. SAM runs only when a UEI or CAGE is set. Insolvency is a plug: turning it on does not call a source yet and does not produce a clear result.

## `matchMode` (type: `string`):

strict (default) keeps a register name match only at 75 or above.

## `minScore` (type: `integer`):

Hits below this score are ignored. Default 80. Range 50 to 100.

## `expiryWithinDays` (type: `integer`):

Domain expiry or SAM registration expiry inside this window is a medium result. Default 30. A closer day inside the same window is not a new alert.

## `listMaxAgeHours` (type: `integer`):

Reuse the sanctions list cache while it is younger than this. Default 24. Range 1 to 168. The cache is never used once it is older than 72 hours, including when a refresh fails. A failed list is not treated as fresh.

## `refreshLists` (type: `boolean`):

Download the sanctions lists again even when the cache is fresh.

## `resetState` (type: `boolean`):

Treat every supplier as new for this customer. The next rows are baselines, not change alerts.

## `companiesHouseApiKey` (type: `string`):

Free key from developer.company-information.service.gov.uk. Used for a UK register profile and UK PSC. Without it, UK PSC is skipped and a GB company can still match on GLEIF. You can also set COMPANIES_HOUSE_API_KEY.

## `samGovApiKey` (type: `string`):

Free key from api.data.gov. Required for a UEI or CAGE exclusion check. Without it that check is not billed and is not clear. You can also set SAM_GOV_API_KEY.

## `webhookUrl` (type: `string`):

Optional https URL. Called only when this run has a real change alert. generic posts the alert JSON. slack posts {"text"}. discord posts {"content"}. The run does not send e-mail.

## `webhookFormat` (type: `string`):

generic, slack or discord. Used only when Webhook URL is set.

## `webhookSecret` (type: `string`):

Optional password. Sent only as an Authorization: Bearer header. It is not written to the log, the dataset, or the alert record. Requires Webhook URL.

## Actor input object example

```json
{
  "customerId": "acme-procurement",
  "suppliers": [
    {
      "supplierId": "example-fr",
      "name": "Example SAS",
      "country": "FR",
      "registrationNumber": "380000000",
      "vatNumber": "FR43380000000",
      "domain": "example.com"
    }
  ],
  "maxSuppliers": 25,
  "checks": {
    "register": true,
    "vat": true,
    "sanctions": true,
    "ownership": false,
    "domain": true,
    "sam": true,
    "insolvency": false
  },
  "matchMode": "strict",
  "minScore": 80,
  "expiryWithinDays": 30,
  "listMaxAgeHours": 24,
  "refreshLists": false,
  "resetState": false,
  "webhookUrl": "",
  "webhookFormat": "generic"
}
```

# Actor output Schema

## `dataset` (type: `string`):

Baseline rows, then change rows only

## `summary` (type: `string`):

Counts and the state store name

## `alert` (type: `string`):

Present on the default store only when this run had a real change. The same body is posted to the webhook.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "customerId": "acme-procurement",
    "suppliers": [
        {
            "supplierId": "example-fr",
            "name": "Example SAS",
            "country": "FR",
            "registrationNumber": "380000000",
            "vatNumber": "FR43380000000",
            "domain": "example.com"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mikee368/supplier-risk-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "customerId": "acme-procurement",
    "suppliers": [{
            "supplierId": "example-fr",
            "name": "Example SAS",
            "country": "FR",
            "registrationNumber": "380000000",
            "vatNumber": "FR43380000000",
            "domain": "example.com",
        }],
}

# Run the Actor and wait for it to finish
run = client.actor("mikee368/supplier-risk-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "customerId": "acme-procurement",
  "suppliers": [
    {
      "supplierId": "example-fr",
      "name": "Example SAS",
      "country": "FR",
      "registrationNumber": "380000000",
      "vatNumber": "FR43380000000",
      "domain": "example.com"
    }
  ]
}' |
apify call mikee368/supplier-risk-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mikee368/supplier-risk-monitor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/3afEv6K3uMiYE4HvV/builds/ghvVJcDFaK3anw27H/openapi.json
