# CBP WRO & Findings Supplier Screening Monitor (`automa-flow/cbp-wro-findings-supplier-monitor`) Actor

Screen suppliers, countries, industries and merchandise against the official CBP Withhold Release Orders (WRO) and Findings dataset, and monitor new, modified and removed actions. Exact-name matching with CBP's own a/k/a aliases, and a no-match result that never claims a supplier is clear.

- **URL**: https://apify.com/automa-flow/cbp-wro-findings-supplier-monitor.md
- **Developed by:** [Vadim Bezrukov](https://apify.com/automa-flow) (community)
- **Categories:** Business, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$10.00 / 1,000 decided watch-item screenings

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## CBP WRO & Findings Supplier Screening Monitor

Screen and monitor supplier, country, industry and merchandise watch criteria
against the current U.S. Customs and Border Protection Withhold Release Orders
(WROs) and Findings.

Give it a watchlist of up to 5,000 items. It downloads one validated snapshot of
the official CBP dataset, screens every item against it, and in monitor mode
returns deterministic change events since your last successful run.

**Independent service. Not affiliated with, endorsed by, or operated by CBP or
DHS.**

### Use from AI agents with MCP

After deployment, connect this Actor directly at
`https://mcp.apify.com?tools=automa-flow/cbp-wro-findings-supplier-monitor`.
Ask your agent to screen a supplier watchlist, explain every match, and retain
the explicit per-item status instead of treating an unavailable source as a
clear result.

### Why this matters

A WRO lets CBP detain covered merchandise at every U.S. port of entry. A Finding
goes further: covered merchandise can be seized unless admissibility is
established. CBP expects importers to exercise reasonable care over their supply
chains, and identifies its current WRO/Finding list as information importers can
monitor.

The dataset changes when actions are issued, modified or removed, on no fixed
schedule.

### What you get

One output row per watch item, always. Each row carries the matched CBP actions,
the exact snapshot it was screened against, and, in monitor mode, what changed.

```json
{
  "input_index": 0,
  "reference": "supplier-001",
  "criteria": {"entity_name": "Hoshine Silicon Industry Co. Ltd.", "country": null,
               "industry": null, "merchandise_keywords": []},
  "status": "SUCCESS",
  "match_status": "DIRECT_MATCH",
  "match_count": 1,
  "matches": [
    {
      "action_source_id": "cbp-c7f842ccb552140ea694054ede5b8c2d",
      "action_source_url": "https://www.cbp.gov/newsroom/national-media-release/...",
      "action_type": "WRO",
      "action_status": "Partially Modified",
      "effective_date": "2021-06-23",
      "country_code": "CN",
      "country": "China",
      "industry": "Industrial and Manufacturing Materials",
      "merchandise": "Silica-based products",
      "entity_name": "Hoshine Silicon Industry Co. Ltd. and Subsidiaries",
      "entity_names": ["Hoshine Silicon Industry Co. Ltd. and Subsidiaries",
                       "Hoshine Silicon Industry Co. Ltd"],
      "match_basis": "ENTITY"
    }
  ],
  "changed": false,
  "source_snapshot": {
    "source_retrieved_at": "2026-09-04T10:00:00Z",
    "source_row_count": 67,
    "source_last_modified": "Mon, 31 Aug 2026 19:05:06 GMT"
  }
}
```

### What NO\_DIRECT\_MATCH does and does not mean

`NO_DIRECT_MATCH` means exactly one thing: **the successfully validated current
CBP dataset does not name your watch item.**

It is **not** a determination that:

- the supplier is free of forced-labour risk;
- the goods are admissible into the United States;
- no country-wide or commodity-wide CBP action covers the shipment;
- the **UFLPA** Entity List does not apply, which is a separate list this Actor
  deliberately does not merge into WRO/Finding semantics;
- DOL ILAB, OFAC, BIS or any other authority does not apply.

There is no `clear`, `safe`, `compliant` or `admissible` field anywhere in the
output, and there never will be. Screening WROs and Findings is one component of
import due diligence. **This Actor produces informational workflow data. It is
not legal advice**, and you remain responsible for reasonable care and for your
own legal review.

Several current CBP actions name a region or a commodity rather than a company,
for example `All Turkmenistan Cotton or products produced in whole or in part
with Turkmenistan cotton`. No supplier name can ever match those. Screen by
`country`, `industry` and `merchandiseKeywords` as well as by name.

### Match statuses

| Status | Meaning |
| --- | --- |
| `DIRECT_MATCH` | A current CBP action names this entity. |
| `CONTEXT_MATCH` | An action satisfies your country / industry / merchandise filters. It does **not** mean your supplier is the subject of that action. |
| `NO_DIRECT_MATCH` | The validated dataset does not name this item. See the section above. |
| `NO_CONTEXT_MATCH` | No current action satisfies the supplied context filters. No entity-name absence or compliance conclusion is implied. |
| `AMBIGUOUS` | The only candidate actions leave a screened field blank in the source, so no decision was reached. Never charged. |
| `UNKNOWN` | The item was not evaluated (invalid input, or the source failed). |

#### Matching is exact, never fuzzy

Names are matched after normalizing case, punctuation and whitespace, and
nothing else. CBP writes several names into one subject cell, so the Actor also
matches the names CBP itself publishes there: `a/k/a` aliases, semicolon-
separated names, the company name inside a `... and Subsidiaries` subject, and
the individually named companies in the compound subject forms observed in the
current dataset. Location qualifiers such as `Kathmandu` are not turned into
entity names.

```text
subject:  "Wulin (or Wuling) Machinery Works, a/k/a Hangzhou Wulin Machinery Plant, a/k/a ..."
matches:  "Wulin (or Wuling) Machinery Works"
          "Hangzhou Wulin Machinery Plant"
          "Hangzhou Wulin Machinery Works"
          "Zhejiang Province No. 4 Prison"
```

`Hoshine Silicon` does **not** match `Hoshine Silicon Industry Co. Ltd.`. Read
`entity_names` on any matched action to see the exact spellings CBP publishes.

### A source failure is never a non-match

If the CBP dataset cannot be downloaded, is truncated, arrives with a changed
schema, or collapses to a suspiciously small number of actions, the run fails
loudly. Every item returns `status: FAILED` with `match_status: UNKNOWN`, no item
is charged, and in monitor mode **the last good state is preserved**, so a bad
run can never manufacture a wave of "action removed" events.

### Monitoring

Set `mode: "monitor"` and a stable `monitorId`. The first successful run is a
silent baseline. Later runs return only real changes.

Monitor state is schema-versioned. After this release, an older stored state is
ignored once and the next successful check becomes a fresh baseline; failed or
ambiguous checks still never replace it. Writes also carry the source retrieval
time, so a normally completing older overlapping run is refused when newer
state is already present.

| Event | Meaning |
| --- | --- |
| `ACTION_NEWLY_MATCHED` | A CBP action now matches this watch item. |
| `ACTION_NO_LONGER_MATCHED` | Still published, but no longer matches your criteria. |
| `ACTION_NO_LONGER_IN_CURRENT_DATASET` | Gone from the current dataset. **Not** reported as a revocation: CBP removes modified and revoked actions after a retention period, and the Actor does not invent the legal reason. |
| `ACTION_STATUS_CHANGED` | The published status changed. |
| `ACTION_MODIFIED` | The new status is a modification, in CBP's own words. |
| `ACTION_REVOKED` | The source explicitly says revoked. |
| `MERCHANDISE_SCOPE_CHANGED` | The action's merchandise scope changed. |
| `INDUSTRY_CHANGED` | The action's industry changed. |
| `ACTION_REMARKS_CHANGED` | CBP's remarks changed, which is where partial modifications are described. |
| `ACTION_BECAME_UNDECIDED` | A previously decided action now has a blank screened source field. |
| `ACTION_NO_LONGER_UNDECIDED` | A previously undecided action can now be matched or excluded. |
| `UNDECIDED_ACTION_DETAILS_CHANGED` | The semantic state of an undecided action changed. |

#### Schedule it

Run daily or weekly from the Apify **Schedules** tab, or via the API:

```bash
curl -X POST "https://api.apify.com/v2/acts/<username>~cbp-wro-findings-supplier-monitor/runs?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
        "watchItems": [
          {"entityName": "Hoshine Silicon Industry Co. Ltd.", "reference": "supplier-001"},
          {"country": "CN", "industry": "Apparel, Footwear and Textiles", "reference": "cn-apparel"}
        ],
        "mode": "monitor",
        "monitorId": "vendor-master",
        "onlyChanges": true
      }'
```

#### Get the changes only

```bash
curl "https://api.apify.com/v2/datasets/<datasetId>/items?view=changes&clean=true&token=$APIFY_TOKEN"
```

#### Webhook

Add a webhook on **Run succeeded** pointing at your compliance system. Each row
is already a usable alert payload: it carries a stable `source_id`, a
`fingerprint`, `scraped_at`, the matched actions and their CBP source links.

### Input

| Field | Type | Notes |
| --- | --- | --- |
| `watchItems` | array, 1-5000 | Required. Each item needs at least one criterion. |
| `watchItems[].entityName` | string | Exact name match. |
| `watchItems[].country` | string | ISO 3166-1 alpha-2 code or the country name CBP publishes. CBP also uses the pseudo-code `FV` for fishing-vessel actions. |
| `watchItems[].industry` | string | A CBP industry label, for example `Base Metals`. |
| `watchItems[].merchandiseKeywords` | string\[] | Any one keyword hitting the merchandise text is enough. |
| `watchItems[].reference` | string | Echoed unchanged. |
| `mode` | `snapshot` | `monitor` | Default `snapshot`. |
| `monitorId` | string | Required in monitor mode. |
| `onlyChanges` | boolean | Monitor mode. Blanks matches, undecided actions and alerts on unchanged rows; the row still exists. |
| `includeSourceDetails` | boolean | Default `true`. Adds CBP `remarks`. |

Entity matching and context screening are independent result sets. A direct
entity hit is always returned even when a supplied context field disagrees;
matching context actions are returned alongside it. Country, industry and
merchandise criteria combine with AND. Keywords inside `merchandiseKeywords`
combine with OR.

### Pricing

Pay per event, charged once per **decided** watch item: a `DIRECT_MATCH`, a
`CONTEXT_MATCH`, a confirmed `NO_DIRECT_MATCH`, or a confirmed
`NO_CONTEXT_MATCH`. A confirmed non-match is charged because producing it
requires the same validated snapshot as a match.

Never charged: invalid input, source failures, `AMBIGUOUS` items, retries, the
dataset download itself, Dataset writes and Key-Value Store operations.

Every run uses one source download, regardless of watchlist size. Dataset,
billing and monitor-state operations are emitted in bounded batches. Hosted
tests at 512 MB covered fully paid baskets of 1, 10 and 100 items and the maximum
5,000-item input path. The 100-item run cost $0.003462 in platform usage; at
$0.01 per check, that left a 79.7% contribution margin after Apify's 20% share.

If the run's maximum charge cannot fund every decided item, the Actor still
returns one row per input: funded items keep their decision, while the remainder
become free `FAILED / MAX_TOTAL_CHARGE_REACHED` rows and never overwrite good
monitor state. Increase the run's maximum charge and retry those rows.

### Source

- Dataset: https://www.cbp.gov/document/stats/withhold-release-orders-findings
- Dashboard: https://www.cbp.gov/newsroom/stats/trade/withhold-release-orders-findings-dashboard
- Data dictionary: https://www.cbp.gov/sites/default/files/2025-07/wro-finding\_dashboard\_data\_dictionary.pdf

The CSV filename changes with each publication, so the Actor resolves the current
file from the stable CBP document page on every run rather than hardcoding a URL.
Public data only: no login, no CAPTCHA, no browser, no proxy, and nothing behind
an access control. The dataset contains no personal data, and the Actor adds
none.

# Actor input Schema

## `watchItems` (type: `array`):

One row per watch item. Entity matching and context screening are independent: an exact entity-name hit is always returned, while supplied country, industry and merchandise criteria select additional context actions. Context criteria combine with AND; merchandise keywords combine with OR. A CONTEXT\_MATCH does not mean the supplier itself is the subject of that action.

## `mode` (type: `string`):

snapshot screens the watchlist against the current dataset. monitor also compares each item with its last successful observation and returns deterministic change events.

## `monitorId` (type: `string`):

Required in monitor mode. Names the stored baseline so several watchlists can be monitored independently. 1-64 letters, digits, dots, underscores or hyphens.

## `onlyChanges` (type: `boolean`):

Monitor mode only. Unchanged items still return one row each, with matches, undecided actions and alerts blanked and details\_omitted set to true, so the batch stays one row per input.

## `includeSourceDetails` (type: `boolean`):

Adds the source Remarks text to each returned action. Remarks carry CBP's own modification history, for example which producers were removed from an order and when.

## Actor input object example

```json
{
  "watchItems": [
    {
      "entityName": "Hoshine Silicon Industry Co. Ltd.",
      "reference": "supplier-001"
    },
    {
      "entityName": "Kingtom Aluminio S.R.L.",
      "country": "DO",
      "reference": "supplier-002"
    },
    {
      "country": "CN",
      "industry": "Apparel, Footwear and Textiles",
      "reference": "category-cn-apparel"
    },
    {
      "merchandiseKeywords": [
        "cotton",
        "seafood"
      ],
      "reference": "commodity-watch"
    }
  ],
  "mode": "snapshot",
  "onlyChanges": false,
  "includeSourceDetails": true
}
```

# Actor output Schema

## `results` (type: `string`):

Exactly one row per watch item, including invalid, undecided and failed checks.

## `changes` (type: `string`):

Flattened deterministic monitor changes, ready for webhook consumers.

## `matches` (type: `string`):

One row per matched WRO or Finding, with its CBP source link.

## `alerts` (type: `string`):

Flattened DIRECT\_MATCH\_FOUND, NO\_DIRECT\_MATCH\_SCOPE, NO\_CONTEXT\_MATCH\_SCOPE and UNDECIDED\_ACTIONS alerts.

## `diagnostics` (type: `string`):

Failure-focused projection; filter status client-side.

## `runSummary` (type: `string`):

No description

# 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 = {
    "watchItems": [
        {
            "entityName": "Hoshine Silicon Industry Co. Ltd.",
            "reference": "supplier-001"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("automa-flow/cbp-wro-findings-supplier-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 = { "watchItems": [{
            "entityName": "Hoshine Silicon Industry Co. Ltd.",
            "reference": "supplier-001",
        }] }

# Run the Actor and wait for it to finish
run = client.actor("automa-flow/cbp-wro-findings-supplier-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 '{
  "watchItems": [
    {
      "entityName": "Hoshine Silicon Industry Co. Ltd.",
      "reference": "supplier-001"
    }
  ]
}' |
apify call automa-flow/cbp-wro-findings-supplier-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automa-flow/cbp-wro-findings-supplier-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/7vH4zmFsea4Dm3BFx/builds/dw6FlwBAhYkuFZ0fE/openapi.json
