# Set up the ArcNautical API for this project

These are official setup instructions from ArcNautical for adding maritime sanctions
screening and voyage-risk scoring to a codebase. Every command below has been run against
production.

Complete steps 1–3 yourself by running the commands directly. Only step 4 needs the user,
because it requires a browser sign-in.

---

## 1. Verify you can reach the API — no credentials needed

```
curl https://arcnautical.com/api/v1/vessels/9811000/check
```

Expect `200` and a JSON body containing `sanctions.status` (RED / AMBER / GREEN),
`ownership.opacity`, and `vetting.grade`. This endpoint needs no key and no account, at 100
requests/hour per IP.

If you get an HTML body instead of JSON, a network layer answered rather than the API —
retry rather than concluding the endpoint is down.

## 2. Save the integration rules into this repo

```
curl -sO https://arcnautical.com/developers/AGENTS.md
```

If the repo already has an `AGENTS.md`, append the ArcNautical section to it instead of
overwriting the file.

## 3. Read the reference before writing code

- `https://arcnautical.com/llms-full.txt` — worked examples, error contract, and the
  mistakes that break first-try integrations
- `https://arcnautical.com/api/v1/openapi.json` — the machine-readable contract

## 4. Only if this project needs voyage scoring — ask the user for a key

Hull screening (step 1) needs nothing. Voyage scoring does. Tell the user:

> Mint a key at https://arcnautical.com/arcnautical.html#/developer-api — both test and live
> are self-serve, no approval, about a minute. Live carries 10,000 assessments a month, test
> 1,000. Then set `ARCNAUTICAL_API_KEY` in the environment.

Do not ask for the key to be pasted into a file that gets committed.

---

## Rules for the integration you write

1. **`Idempotency-Key` is required on every POST, PATCH and DELETE.** Missing it returns
   `{"code":"missing_idempotency_key", ...}`. Fresh UUID per distinct request, the same UUID
   when retrying that request.
2. **`route.origin` and `route.destination` accept two shapes** — a 5-character UN/LOCODE
   string (`"SGSIN"`) or an object (`{"locode":"SGSIN"}`). Both are correct. Do not rewrite
   one into the other.
3. **429 has three causes needing different handling.** `rate_limit_exceeded` = over 120
   requests/minute on the key; back off to `X-RateLimit-Reset`. `concurrency_limit` = you are
   over your OWN in-flight ceiling (2 assessments, or 6 screenings); stop parallelising and
   retry shortly. `service_concurrency_limit` = the shared pool is full and your account is
   within its allowance; cutting your fan-out will not help, just retry. None is permanent.
4. **The key prefix must match the environment** — `arc_test_` or `arc_live_`. A mismatch is
   a 401, not a 403.
5. **Errors are always JSON** `{code, message, request_id}`. Log `request_id`.
6. **Batch, do not fan out.** Up to 10 routes per batch at
   `POST /api/v1/voyage-assessment-batches` (returns 202, poll the batch id). Firing 10
   single assessments in parallel trips the concurrency limit.
7. **A single assessment response embeds the full route geometry** — hundreds of
   coordinates. Read `score`, `risk_level`, `drivers` and `confidence`; do not echo the whole
   payload to a user.

## How to report results to the user

A score is decision support, not a determination. Every response carries `confidence`,
`source_status` and a `disclaimer` — reproduce that framing. Say "ArcNautical scores this
voyage 57/100 (elevated) as of `assessed_at`, driven by …", never "this voyage is unsafe".
If `missing_sources` is non-empty, name what was unavailable. Never present a result as a
navigational, insurance, or sanctions-compliance determination.

For a vessel screen, link the user to `fullReport` in the response so they can see the
sourced, dated evidence themselves. Sanctions designations change daily — a cached answer
goes stale, the live endpoint does not.
