---
title: "Get a cargo screening"
method: GET
path: "/api/v1/cargo-screenings/{id}"
tags: ["Cargo screening"]
---

# Get a cargo screening

`GET /api/v1/cargo-screenings/{id}`

## Request

- Base URL: `https://arcnautical.com`
- URL: `https://arcnautical.com/api/v1/cargo-screenings/{id}`
- Auth: one of:
  - HTTP bearer
  - API key in header `X-ArcNautical-Key`

Replays a stored cargo screening verbatim, including the methodology_version it ran under.

Required scope: cargo:screen.

## Path parameters

- `id` string, uuid, required

## Response `200`

Successful response.

- CargoScreening
  - `id` string, uuid, required
  - `status` 'OK' | 'WARNING' | 'CRITICAL', required: The SHIPMENT decision — the worse of the cargo verdict and the named hull last known sanctions verdict. Clean cargo onto a sanctioned hull is not an OK. Act on this field.
  - `status_basis` 'cargo' | 'vessel': Which side drove the top-line status.
  - `cargo_status` 'OK' | 'WARNING' | 'CRITICAL': The goods verdict alone, ignoring the vessel.
  - `goods_description` string, required
  - `hs_code` string, nullable
  - `customer_reference` string, nullable
  - `match_basis` 'hs_code' | 'description' | 'null', nullable
  - `matches` CargoMatch[]
    - `list` 'CHPL'
    - `hs6` string
    - `tier` '1' | '2' | '3.A' | '3.B' | '4.A' | '4.B': CHPL tier. 1 is the highest concern.
    - `description` string
    - `basis` 'hs_code' | 'description': hs_code is an exact heading match. description is a KEYWORD HEURISTIC and is not a classification.
  - `destination` CargoDestination
    - `class` 'comprehensive' | 'high_risk' | 'diversion_risk' | 'standard'
    - `label` string
    - `basis` string: Why this classification, naming the programme or guidance.
  - `humanitarian` CargoHumanitarian: Medicine and medical-device carve-outs, screened against OFAC's published lists per programme. The three comprehensive programmes do not work the same way: Iran (31 CFR 560.530(a)(3)(i)) is a self-executing general licence carving out a device list AND seven medicine classes; North Korea (31 CFR 510.521) carves out a DIFFERENT device list, published 11 June 2026, and has no medicine-class exclusion at all; Cuba has no self-executing general licence — the export needs a Department of Commerce licence first.
    - `medical_goods` boolean
    - `medical_goods_basis` 'inn_drug_name' | 'medical_term' | 'inn_stem' | 'ofac_device_list' | 'null', nullable: What made it read as medical. A keyword heuristic over free text, never a classification.
    - `general_licence_status` 'covered' | 'excluded' | 'review_required' | 'no_general_licence' | 'not_applicable': covered = medical goods, a general licence exists, nothing excluded it. excluded = a published exclusion names it. review_required = in scope of an exclusion this screen cannot settle. no_general_licence = medical goods into a programme with no self-executing general licence. not_applicable = not medical goods, or no comprehensive programme.
    - `general_licence` string, nullable: The general licence this item was ASSESSED AGAINST. It stays populated when general_licence_status is `excluded`, because naming which licence fails to reach the item is the useful half of that answer — it is not an authorisation. Null means the programme has no self-executing general licence at all (Cuba), not that one is unknown.
    - `exclusions` CargoExclusion[]
      - `kind` 'medical_device' | 'medicine_class', required
      - `authority` string, required: The clause that produced this, e.g. 31 CFR 560.530(a)(3)(ii) for the device list or (a)(3)(iii) for the medicine classes.
      - `published_text` string, required: The published list line or class term, verbatim, so the finding can be checked against the source.
      - `matched_text` string, required: The phrase in your own goods_description that produced this finding.
      - `certainty` 'named' | 'probable', required: named = the description says what the list says. probable = in scope, but a published qualifier decides it and you did not state it.
      - `effect` 'excluded' | 'review', required
      - `threshold` object, nullable: Present where the published line carries a numeric qualifier, e.g. pumps over 1 litre/minute, autoclaves over 20 litres.
        - `quantity` 'flow_rate' | 'volume' | 'area' | 'temperature' | 'purity'
        - `operator` string
        - `value` number
        - `unit` string
        - `stated_value` number, nullable: The value read out of your description, in the list line unit. Null means you did not state one.
        - `resolves_with` string: What to supply to turn this from review into an answer.
      - `note` string, nullable
    - `conditions` string[]: The conditions on the licence. A general licence for medicine does NOT cover every medicine, nor any end user.
    - `list_edition` CargoListEdition: Which edition of the OFAC list answered. A compliance control whose data version is invisible cannot be audited, and a stale list is otherwise indistinguishable from a current one.
      - `name` string
      - `authority` string
      - `published` string: The edition as OFAC labels it, e.g. "updated February 2, 2017".
      - `source_url` string, uri
      - `source_sha256` string: sha256 of the published artefact this snapshot was taken from.
      - `retrieved_at` string, date-time
      - `item_count` integer
      - `lines_without_matcher` integer: Published lines this screen has no automated matcher for and did NOT test your description against. Read the source list for these.
  - `vessel` object, nullable: Present when imo was supplied. A null sanctions_status with a note means the hull is UNSCREENED, which is not the same as clear.
    - `imo` string
    - `sanctions_status` string, nullable
    - `screening_id` string, uuid, nullable
    - `screened_at` string, date-time, nullable
    - `note` string
  - `lists_screened` string[], required: Exactly what this screen read.
  - `lists_not_screened` string[], required: Exactly what it did NOT read. Read this before relying on an OK.
  - `methodology_version` string
  - `screened_at` string, date-time
  - `expires_at` string, date-time
  - `disclaimer` string

## Other responses

- `400`: Validation error. `code` is one of: - `missing_idempotency_key` — A POST arrived with no Idempotency-Key header. - `invalid_idempotency_key` — The Idempotency-Key was present but unusable (empty or over-long). - `invalid_json` — The body did not parse as JSON.
- `401`: Missing or invalid API key. `code` is one of: - `unauthorized` — No credential was supplied. - `invalid_api_key` — The key is unknown, revoked, expired, or its prefix does not match the environment.
- `402`: The allowance for this resource is used up: the month's, or a one-time total (live screenings on an account opened from 2026-10-01, or a pilot), which does not reset (then there is no reset instant and no Retry-After). The refused request is NOT metered. message states used/limit and the reset instant when there is one; a replay under an Idempotency-Key already answered is still free and is not metered, reads are never metered, and a test-environment key has its own allowance. `code` is one of: - `quota_exhausted` — The allowance for this resource is spent: the month's (GET /api/v1/usage carries the reset date), or a one-time total (live screenings on an account opened from 2026-10-01, or a pilot), which does not reset.
- `403`: The key is not entitled to this environment or scope. `code` is one of: - `insufficient_scope` — The key does not hold the scope this endpoint requires. - `entitlement_inactive` — The account has no active entitlement. - `ip_not_allowed` — The key is IP-restricted and this source address is not on its list. - `monitoring_unavailable_in_test` — Vessel and corridor monitors are not available on a test key. - `simulation_unavailable_in_live` — Simulated monitor events are available only on a test key. A synthetic risk change on a live monitor would enter your compliance record as a real finding about a real vessel.
- `404`: Resource not found. `code` is one of: - `not_found` — No such resource, or it is not yours. A malformed id answers this way too, deliberately. - `unsupported_api_version` — The path names an /api/vN this service does not publish. Answered by the edge, so it is the one code that carries no rate-limit headers. NEVER retry: our own guidance says an HTML body means a network layer answered and should be retried, and this exists so that advice is not applied to a permanent 404.
- `405`: The path exists but not with this method. The Allow header names the methods that do. `code` is one of: - `method_not_allowed` — The path exists but not with this verb. The Allow header names the verbs that do work.
- `409`: Idempotency conflict. `code` is one of: - `idempotency_conflict` — This Idempotency-Key was already used with a different body. Never resolves — use a new key. - `idempotency_in_progress` (retryable) — The original request with this key is still running, and this retry waited for it (up to 30s) without it finishing. Retry with the same key: the next retry waits again and returns the original result the moment it lands, flagged X-ArcNautical-Idempotent-Joined: true. Carries Retry-After. - `export_not_ready` (retryable) — The export job has not finished. Poll it; carries Retry-After. - `delivery_in_flight` (retryable) — That webhook delivery is being attempted right now. - `endpoint_disabled` — The webhook endpoint is disabled. Re-enable it first. - `endpoint_deleted` — The webhook endpoint no longer exists. - `monitor_limit_exceeded` — Corridor monitor ceiling reached. Delete one first. - `vessel_monitor_limit_exceeded` — Vessel monitor ceiling reached. Delete one first. - `vessel_monitor_exists` — A monitor already watches this hull.
- `415`: Content-Type describes a body that is not JSON (form-encoded, multipart, XML). application/json, text/plain and an absent header are all accepted. `code` is one of: - `unsupported_media_type` — Content-Type declared something other than application/json. Omitting the header is accepted; declaring the wrong one is not.
- `422`: Request or route validation failed. Also returned for a body field this endpoint does not read — field_violations names it and lists the accepted keys. `code` is one of: - `validation_failed` — One or more fields are invalid; field_violations names each one. - `route_not_found` — Both ports are known but no sea route connects them. - `export_too_large` — The requested range exceeds the per-export row cap. Narrow the filters and export in parts. - `webhook_url_blocked` — The URL resolves to a private, loopback or link-local address. - `webhook_url_unresolvable` — The URL host does not resolve.
- `429`: Rate limit exceeded. `code` is one of: - `rate_limit_exceeded` (retryable) — Over the per-key request rate. Shed RATE; back off to X-RateLimit-Reset. - `concurrency_limit` (retryable) — Over your own in-flight ceiling. Shed CONCURRENCY, or use the batch endpoint. Retry-After carries a live estimate, not a fixed number. - `service_concurrency_limit` (retryable) — The shared pool is full and your account is within its allowance. A graded screen QUEUES for that pool first and is served round-robin between accounts, so this means the queue itself ran out of time — you were not stuck behind one customer's batch. Reducing your fan-out will NOT help; retry unchanged after Retry-After. include_vetting: false skips this queue entirely, and /api/v1/screening-batches is not subject to it at all. - `export_concurrency_limit` (retryable) — Too many exports building at once. Wait for one to finish.
- `500`: Internal or upstream error. `code` is one of: - `internal_error` (retryable) — An unhandled failure. Retry with backoff; quote request_id if it persists. - `assessment_failed` (retryable) — Voyage scoring failed to produce a result. - `screening_failed` (retryable) — The screening engine failed to produce a verdict. No unit is charged. - `export_failed` (retryable) — The export job failed while building. - `export_row_count_grew` — The matching rows grew past the cap while the file was being written, so it was abandoned rather than shipped short. Narrow the filters — an identical retry fails identically.
- `503`: Service temporarily unavailable. `code` is one of: - `api_paused` (retryable) — The API is paused for a short while (an operator lever against abuse, rarely pulled). Nothing ran and nothing was charged. Retry the same request after Retry-After.
- `504`: The request exceeded its server-side time budget and was abandoned. Raised by us, below the socket timeout at the edge, so it arrives as JSON with a request_id. `code` is one of: - `assessment_timeout` (retryable) — Scoring exceeded its deadline. Retry; a warm route usually succeeds. - `screening_timeout` (retryable) — The screening exceeded its time budget (screeningComputeBudgetMs) and was abandoned before it could be stored. No unit is charged, and the Idempotency-Key is RELEASED — so retry with the SAME key; it is a clean attempt, not a replay. Deliberately raised below the socket timeout at the edge, so this arrives as JSON with a request_id rather than as a gateway 504 we never see.

---

[API](https://skmtc.dev/arcnautical/apis/arcnautical-api.md) · [All operations](https://skmtc.dev/arcnautical/apis/arcnautical-api/llms.txt) · [OpenAPI document](https://skmtc.dev/arcnautical/apis/arcnautical-api/revisions/40cd2659cdd7?raw)
