Export Screening API

One screening contract at the edge.

The public API is hosted at https://exportscreening.datasourceapi.com. CCA Workers should use a Cloudflare service binding instead of the public hostname. Both paths execute the same validation, freshness gates, and deterministic scoring code.

Responses identify the active source release, matching name, score version, and supporting fields. The service returns INCOMPLETE with HTTP 503 rather than a false no-match when a required source is missing, stale, or unavailable.

Public HTTPS

HTTP API

POST/api/v1/screen

Submit a name, exact identifier, or both. Screening inputs are accepted only in the request body and responses are private, no-store. Every response includes source details in data.sources, a summary in data.freshness, and the X-Data-Freshness / X-Data-Verified-At headers.

const response = await fetch(
  "https://exportscreening.datasourceapi.com/api/v1/screen",
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      name: "Example Trading Company",
      entityType: "organization",
      country: "AE",
      threshold: 0.82
    })
  }
);

const envelope = await response.json();

Operational endpoints

  • GET /api/v1/status - readiness and source age.
  • GET /api/v1/health - health-check alias.
  • GET /api/v1/sources - public-safe source inventory.
  • GET /openapi.json - OpenAPI 3.1 contract.

CCA Workers

Call over a service binding

Add the API Worker as a binding in the calling Worker's wrangler.jsonc. This keeps traffic on Cloudflare's network and requires no public DNS hop, API key, or cookies.

{
  "services": [
    {
      "binding": "EXPORT_SCREENING",
      "service": "cca-export-control-api"
    }
  ]
}

The fleet-compatible HTTP form uses a synthetic, fully qualified internal URL. The hostname is not routed publicly; the binding dispatches directly to the target Worker.

const response = await env.EXPORT_SCREENING.fetch(
  new Request("https://exportscreening.internal/api/v1/screen", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Request-ID": requestId
    },
    body: JSON.stringify({
      name: "Example Trading Company",
      country: "AE"
    })
  })
);

if (response.status !== 200 && response.status !== 503) {
  throw new Error(`screening failed: ${response.status}`);
}
const envelope = await response.json();

Preferred internal API

Typed Worker RPC

The default Worker entrypoint exposes screen(input) and status(). Current Cloudflare Workers can invoke those methods directly through the same service binding.

const result = await env.EXPORT_SCREENING.screen({
  name: "Example Trading Company",
  identifiers: [
    { kind: "SWIFT/BIC", value: "EXAMPLE123" }
  ],
  threshold: 0.82,
  limit: 20
});

if (result.decision === "INCOMPLETE") {
  throw new Error(result.incompleteReasons.join("; "));
}

Cross-repository consumers should use the HTTP binding above, which wrangler types generates as a Fetcher and which stays synchronized with OpenAPI. Direct RPC is appropriate when the consumer imports the target Worker class or a shared fleet contract so TypeScript can model Service<ScreeningApi>.

Provenance

Sources and freshness

The initial required source is the International Trade Administration's U.S. Consolidated Screening List. It combines Commerce, State, and Treasury lists including SDN, Entity List, Denied Persons, Unverified List, ITAR Debarred, and related programs.

A scheduled Cloudflare Workflow checks every six hours. Exact raw bytes are stored under immutable SHA-256 R2 keys before parsing. A release becomes visible only after record, alias, and identifier counts reconcile and the source's active-release pointer is changed atomically inside its D1 shard.

Safe interpretation

Decision semantics

  • POTENTIAL_MATCH means one or more candidates met the requested threshold and require human review.
  • NO_MATCH means no candidate met the threshold while every required source was current and queryable. It is not an export authorization.
  • INCOMPLETE means the service refused to issue a no-match result because required coverage was stale, missing, or unavailable.