# 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. For a production integration — ask the user for a free key

Step 1 returns the verdict only, with no key: enough to prototype. A production integration
screens with a key: `POST /api/v1/screenings` returns the full screening record with
evidence (every match with its list, programme and confidence), and the same key covers
batches of 50 hulls, vessel monitors, signed webhooks and voyage scoring. Tell the user:

> Get a free key at
> https://arcnautical.com/arcnautical.html?intent=api&utm_source=prompt_md&utm_campaign=get_a_key#/register
> — no card, no approval, about a minute. A live key on a new account carries 2,000
> screenings once (a one-time allowance) and 10,000 voyage assessments a month, a test key
> 500 screenings and 1,000 voyage assessments a month. Then set
> `ARCNAUTICAL_API_KEY` in the environment.

Do not ask for the key to be pasted into a file that gets committed. Once it is set:

```
curl -X POST https://arcnautical.com/api/v1/screenings \
  -H "Authorization: Bearer $ARCNAUTICAL_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2df206f1-49ed-4d83-9c2d-38a861592534" \
  -d '{"imo":"9811000"}'
```

Expect `201`. A `sanctions.status` of `INCOMPLETE` is not a clean result, and is not charged.

---

## 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.
