Developer guide

U.S. CSL API in Java: screen shipment parties

Screen shipment parties through the U.S. CSL API in Java. Keep each candidate’s originating list and restriction with the review evidence.

250 free live screenings. One lifetime allowance. No credit card required.
On this page

Screen shipment parties from Java

The Consolidated Screening List concerns multiple source lists, so begin with the buyer, consignee, end user and other parties selected by the trade-compliance process. Create a separate subject and operation for each relevant legal identity and link them to the internal shipment reference.

In an invented export, the buyer and end user are different organizations. The Java service should retain both screening references rather than attach the buyer’s result to the whole shipment. Goods classification, destination and licensing assessments continue in their own controls.

Prepare the synthetic exercise

Create an invented shipment with a buyer and a different end user. Run the organization sandbox request once for each saved party operation. The Java exercise should preserve two screening references; a completed buyer check must not become a completed shipment-wide review.

Java setup and request files
RequirementValue or action
RuntimeUse Java 21 and Maven. Save the client as src/main/java/Screen.java and the pom.xml shown below at the project root. The example uses the JDK HTTP client and Jackson 2.x for JSON tree validation.
Client fileSave the client as src/main/java/Screen.java
Screening endpointPOST /api/v1/screenings
SANCTIONSKIT_API_KEYSandbox key; screenings:write and results:read scopes
SCREENING_OPERATION_IDPersist one operation ID; reuse only with the identical request body
request.jsonExample Harbor Trading: invented subject; sandbox@1: synthetic records
request.json: synthetic sandbox request
{
  "subject": {
    "name": "Example Harbor Trading",
    "entityType": "organization"
  },
  "package": "sandbox@1",
  "retention": "standard"
}

Add the project configuration

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion><groupId>example</groupId><artifactId>sanctions-screen</artifactId><version>1.0.0</version>
  <properties><maven.compiler.release>21</maven.compiler.release><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties>
  <dependencies><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.18.10</version></dependency></dependencies>
  <build><plugins>
    <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.13.0</version></plugin>
    <plugin><groupId>org.codehaus.mojo</groupId><artifactId>exec-maven-plugin</artifactId><version>3.5.0</version><configuration><mainClass>Screen</mainClass></configuration></plugin>
  </plugins></build>
</project>

Implement the Java request boundary

Keep the Java transport adapter independent of the shipment coordinator. The coordinator supplies one legal subject at a time and associates the result with a party role. Jackson parsing should preserve source-list fields instead of flattening every candidate into one boolean.

Java: authenticated screening client
// Java 21+, Jackson 2.x. Backend or trusted CLI. See the adjacent pom.xml.
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public final class Screen {
    public static void main(String[] args) {
        try { run(args); }
        catch (Exception error) {
            // Parser exceptions may contain excerpts, so do not print their messages.
            System.err.println("Screening incomplete. Check the operation before retrying.");
            System.exit(1);
        }
    }
    static void run(String[] args) throws Exception {
        String key = System.getenv("SANCTIONSKIT_API_KEY");
        String operation = System.getenv("SCREENING_OPERATION_ID");
        if (key == null || operation == null || operation.isBlank()) throw new IllegalArgumentException("Missing configuration");
        byte[] raw = Files.readAllBytes(Path.of(args.length > 0 ? args[0] : "request.json"));
        if (raw.length > 65536) throw new IllegalArgumentException("Oversized request");
        ObjectMapper mapper = new ObjectMapper();
        JsonNode body = mapper.readTree(raw);
        if (body == null || !body.isObject() || !body.has("subject") || body.has("sources") == body.has("package")) {
            throw new IllegalArgumentException("Invalid request object");
        }
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://www.sanctionskit.com/api/v1/screenings"))
            .timeout(Duration.ofSeconds(25))
            .header("Authorization", "Bearer " + key).header("Content-Type", "application/json")
            .header("Idempotency-Key", operation).POST(HttpRequest.BodyPublishers.ofByteArray(raw)).build();
        try (HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10))
                .followRedirects(HttpClient.Redirect.NEVER).build()) {
            // A file subscriber avoids buffering the complete body in memory.
            Path temp = Files.createTempFile("sanctions-response-", ".json");
            try {
                var future = client.sendAsync(request, HttpResponse.BodyHandlers.ofFile(temp));
                HttpResponse<Path> response;
                try { response = future.get(30, TimeUnit.SECONDS); }
                catch (Exception error) { future.cancel(true); throw error; }
                if (response.statusCode() < 200 || response.statusCode() >= 300 || Files.size(temp) > 8 * 1024 * 1024) {
                    throw new IllegalStateException("Incomplete HTTP response");
                }
                JsonNode envelope = mapper.readTree(temp.toFile());
                JsonNode data = envelope == null ? null : envelope.get("data");
                if (data == null || !data.isObject() || !data.path("id").isTextual() || data.path("id").asText().isEmpty()
                        || !data.path("status").isTextual() || !data.path("matches").isArray()) {
                    throw new IllegalStateException("Unexpected screening response");
                }
                String status = data.get("status").asText(); int count = data.get("matches").size();
                if (!(status.equals("potential_match") || status.equals("no_match"))
                        || (status.equals("no_match") && count != 0) || (status.equals("potential_match") && count == 0)) {
                    throw new IllegalStateException("Inconsistent screening response");
                }
                System.out.println(mapper.writeValueAsString(Map.of("id", data.get("id").asText(), "status", status, "candidateCount", count)));
            } finally { Files.deleteIfExists(temp); }
        }
    }
}

Run the client

Run the sandbox request
mvn compile exec:java -Dexec.args="request.json"

Confirm U.S. CSL scope before production

us-csl aggregates component lists from different authorities. A candidate may raise an export-control, procurement or sanctions question, depending on its originating list. This API request screens the supplied party; it does not classify goods, determine the destination rules or grant an export license.

Production source-discovery contract
CheckRequired evidence
GET /api/v1/sourcesProduction key with sources:read; response data array
idus-csl
availability / freshavailable / true
capabilitiesContains the requested subject.entityType
currentVersionActive version; retrieved_at and last_checked_at are not publication dates
lastImportMay describe an import that is not the active version
Missing or ineligible sourceInspect disabledReason; required coverage stays incomplete
Displayed clientAdd these discovery checks before its production call
Read source discovery with your production key
curl --fail-with-body --max-time 25 --header "Authorization: Bearer $SANCTIONSKIT_API_KEY" "https://www.sanctionskit.com/api/v1/sources"

Select us-csl in the production request

Explicit change from the sandbox exercise
SettingProduction request
KeySeparate production credential with screenings:write
Coveragesources: ["us-csl"]; omit package
Operation IDNew ID for the changed body and environment
UsageProduction access and screening allowance apply
SubjectOnly lawfully held, known identifying facts; no invented date precision
request.json: explicit us-csl selection
{
  "subject": {
    "name": "Example Harbor Trading",
    "entityType": "organization"
  },
  "sources": [
    "us-csl"
  ],
  "retention": "standard"
}

Interpret U.S. CSL evidence in this workflow

Keep record.list and record.authority beside data.id and data.coverage. The shipment reviewer needs the originating restriction, not just the fact that a company appeared in the CSL. A buyer and consignee can have separate evidence and separate unresolved decisions.

U.S. CSL candidate fields to retain
Fields in data.matches[]Review use
record.sourceId, record.list and record.authorityDistinguish the CSL transport source from the component list and issuing authority.
record.designations[].legalReferences and wordingPresent the actual restriction context instead of a universal sanctioned flag.
record.id, names and addressesKeep each party and candidate separately traceable, including address evidence relevant to an organization.
  • data.id; data.coverage; data.versions; supplied disclaimer
  • data.matches[]: record, assessment, evidence, conflicts
  • GET /api/v1/results/{id}: results:read scope
  • Retained evidence and case review

Verify the application behavior

Recovery rules for the same saved operation
ConditionNext action
401 / 403Correct credentials or scopes before retrying
coverage_unavailable / stale_coverageKeep required coverage incomplete; no silent source substitution
Timeout after submissionRecover with the original Idempotency-Key and identical body
409Investigate an operation-key conflict
429 / 503Follow the specific error and Retry-After; an allowance cap may require a plan or usage change
Contract and safe diagnostic references
Operational reference
ReferenceUse
error.code / error.requestIdSafe recovery diagnostics; never log the submitted subject, key or full candidate response
potential_match / no_matchCompleted comparison outcomes; neither is an automatic business approval
  • Keep separate buyer and end-user results under one synthetic shipment reference.
  • Preserve different component-list authorities during JSON mapping.
  • Prevent a completed buyer check from satisfying an unchecked consignee step.

Sources and references

Frequently asked questions

What should Java preserve when mapping a CSL candidate?

Keep record.list and record.authority with the record identity and restriction evidence. A DTO that retains only a name and sanctioned flag loses the context needed to interpret different component lists.

Does one Java CSL request check an entire shipment?

No. It checks the subject in that request. Your trade workflow chooses the buyer, consignee, end user and other relevant parties, and keeps a separate operation for each legal identity.

How should Java display candidates from different CSL component lists?

Show the component list and issuing authority beside the candidate, together with available restriction wording and legal references. Do not collapse every record into one sanctioned flag. A reviewer needs the underlying list to understand the result.

What remains outside the Java CSL party-screening example?

Goods classification, destination restrictions, licensing and other transaction controls remain separate tasks. A completed party comparison does not decide whether the planned export is permitted or replace the responsible review process.