API v1 documentation
Integrate voyage risk scoring
Score maritime routes synchronously or in batches, retain results for audit, and add live corridor monitoring when your entitlement includes it.
Quickstart
- Store the issued key as
ARCNAUTICAL_API_KEY. - Confirm the key environment and quota with
GET /api/v1/usage. - Create an assessment with a unique
Idempotency-Key.
curl --fail-with-body https://arcnautical.com/api/v1/usage \
-H "Authorization: Bearer $ARCNAUTICAL_API_KEY" \
-H "X-Request-Id: integration-check-001"
curl --fail-with-body https://arcnautical.com/api/v1/voyage-assessments \
-X POST \
-H "Authorization: Bearer $ARCNAUTICAL_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: voyage-$(uuidgen)" \
-d '{
"customer_reference": "lane-NLRTM-CNSHA-001",
"route": {
"origin": { "locode": "NLRTM" },
"destination": { "locode": "CNSHA" },
"vessel_type": "container",
"load_condition": "laden",
"speed_knots": 16,
"dwt_tonnes": 100000
}
}'
const response = await fetch(
'https://arcnautical.com/api/v1/voyage-assessments',
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.ARCNAUTICAL_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
'X-Request-Id': crypto.randomUUID()
},
body: JSON.stringify({
customer_reference: 'lane-NLRTM-CNSHA-001',
route: {
origin: { locode: 'NLRTM' },
destination: { locode: 'CNSHA' },
vessel_type: 'container',
load_condition: 'laden',
speed_knots: 16,
dwt_tonnes: 100000
}
})
}
);
const body = await response.json();
if (!response.ok) throw new Error(`${body.code}: ${body.message}`);
console.log(body);
import os
import uuid
import requests
response = requests.post(
"https://arcnautical.com/api/v1/voyage-assessments",
headers={
"Authorization": f"Bearer {os.environ['ARCNAUTICAL_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
"X-Request-Id": str(uuid.uuid4()),
},
json={
"customer_reference": "lane-NLRTM-CNSHA-001",
"route": {
"origin": {"locode": "NLRTM"},
"destination": {"locode": "CNSHA"},
"vessel_type": "container",
"load_condition": "laden",
"speed_knots": 16,
"dwt_tonnes": 100000,
},
},
timeout=60,
)
response.raise_for_status()
assessment = response.json()
Authentication
Send the API key as a Bearer token on every authenticated request. Keys are shown once and should be stored in a server-side secret manager.
Authorization: Bearer arc_test_... # evaluation
Authorization: Bearer arc_live_... # production
| Scope | Allows |
|---|---|
voyage:score | Synchronous and batch assessments, plus retained-result retrieval. |
voyage:monitor | Live corridor monitors and monitor history. |
webhooks:manage | Webhook endpoint lifecycle and signed test delivery. |
usage:read | Entitlement limits and current consumption. |
Test and live environments
| Key | Data | Quota | Monitoring |
|---|---|---|---|
arc_test_... | Real route-risk computation | Separate test bucket | Cannot create active corridor monitors |
arc_live_... | Real route-risk computation | Separate live bucket | Available with scope and entitlement |
The environment is selected by the issued key, not by changing the base URL. Resources, usage, batches, monitors, and webhook endpoints are isolated by environment.
Assessment contract
Routes use uppercase five-character UN/LOCODEs. Vessel assumptions are optional and belong inside the route object.
Required
route.originroute.destination
Optional
customer_referenceroute.via, up to 20 coordinates- vessel type, load, speed, DWT, fuel, bunker price
Observed response shape
{
"id": "76e0b22d-d46d-4f76-82fa-0b54341ce1fb",
"customer_reference": "lane-NLRTM-CNSHA-001",
"route": {
"origin_locode": "NLRTM",
"dest_locode": "CNSHA",
"via_waypoints": [],
"distance_nm": 10082.3,
"geometry": { "type": "Feature", "geometry": { "type": "LineString" } }
},
"score": 55,
"risk_level": "elevated",
"drivers": [
{
"source": "cii",
"label": "Country instability",
"summary": "...",
"event_count": 1,
"contribution_band": "high",
"relative_rank": 1
}
],
"confidence": 1,
"missing_sources": [],
"source_status": { "piracy": "ok", "weather": "ok" },
"methodology_version": "voyage_risk_external_v1",
"assessed_at": "2026-07-16T17:40:00.000Z",
"expires_at": "2026-08-15T17:40:00.000Z",
"disclaimer": "..."
}
Retrieve the same customer-owned result with GET /api/v1/voyage-assessments/{id} until its expires_at timestamp.
Batch workflow
POST /api/v1/voyage-assessment-batcheswith a unique idempotency key.- Poll
GET /api/v1/voyage-assessment-batches/{id}untilcompleted,partial, orfailed. - Read results from
GET /api/v1/voyage-assessment-batches/{id}/items.
Test keys accept up to the test_max_routes shown by /usage; live keys use live_max_routes. Only accepted route items reserve quota.
Endpoint guide
| Method | Path | Scope | Success |
|---|---|---|---|
| POST | /api/v1/voyage-assessments | voyage:score | 201 |
| GET | /api/v1/voyage-assessments/{id} | voyage:score | 200 |
| POST | /api/v1/voyage-assessment-batches | voyage:score | 202 |
| GET | /api/v1/voyage-assessment-batches/{id} | voyage:score | 200 |
| GET | /api/v1/voyage-assessment-batches/{id}/items | voyage:score | 200 |
| POST | /api/v1/corridor-monitors | voyage:monitor | 201 |
| GET | /api/v1/corridor-monitors | voyage:monitor | 200 |
| POST | /api/v1/webhook-endpoints | webhooks:manage | 201 |
| GET | /api/v1/usage | usage:read | 200 |
Open the complete OpenAPI 3.1 contract for update, delete, history, pagination, schemas, and all response codes.
Reliability contract
Idempotency
Every resource-creating POST requires Idempotency-Key. Keep the key stable only when retrying the identical method, path, and JSON body. Results are replayable for 24 hours. Reusing a key with a different body returns 409 idempotency_conflict.
Retries
Retry GET/HEAD requests and idempotency-keyed POST requests after transport failures or HTTP 408, 425, 429, 500, 502, 503, and 504. Honor Retry-After; otherwise use exponential backoff with jitter. Do not automatically retry non-idempotent mutations.
Limits and tracing
- Per-key rate limit: 120 requests per minute.
- Read
X-RateLimit-Limit,X-RateLimit-Remaining, andX-RateLimit-Reset. - Assessment responses include
X-ArcNautical-Quota-Limit,X-ArcNautical-Quota-Remaining, andX-ArcNautical-Quota-Reset. - Send a unique
X-Request-Idand retain the returned value for support.
Error handling
All API errors use the same envelope. Validation errors add field_violations.
{
"code": "validation_failed",
"message": "Request validation failed.",
"request_id": "req_6f3d...",
"field_violations": [
{ "field": "route.destination", "message": "UN/LOCODE is required." }
]
}
| Status | Meaning | Action |
|---|---|---|
| 400 / 422 | Invalid JSON, headers, or route fields | Correct the request; do not retry unchanged. |
| 401 / 403 | Invalid key, environment, entitlement, or scope | Check the issued key and entitlement. |
| 402 | Monthly assessment quota exhausted | Wait for reset or request a limit change. |
| 409 | Idempotency or resource-state conflict | Inspect code; retry in-progress keys with backoff. |
| 429 | Rate or concurrency limit | Honor Retry-After. |
| 500 / 503 / 504 | Temporary server, edge, or upstream failure | Retry only when the request is replay-safe. |
Live monitors and webhooks
Live keys with the required scopes can create retained corridor monitors. ArcNautical recomputes each corridor on the entitlement cadence and emits material-change, data-degradation, recovery, batch-completion, and quota events.
Webhook endpoints must be public HTTPS URLs. The creation response returns the HMAC secret once.
X-ArcNautical-Signature: t=1721149200,v1=<hex digest>
signed_payload = timestamp + "." + raw_request_body
expected = HMAC_SHA256(webhook_secret, signed_payload)
Compare signatures in constant time, reject stale timestamps, and deduplicate using the event id. Return any 2xx response promptly; delivery retries use increasing delays before becoming dead.
Data-use boundaries
- Responses contain derived scores, drivers, route context, confidence, and source-status summaries.
- Raw third-party feed payloads and internal scoring weights are not redistributed.
- The API is decision-support intelligence, not navigation, safety, legal, insurance, sanctions-compliance, or operational advice.
- Test access is for agreed evaluation only; it does not authorize production or customer-facing use.
X-Request-Id. Never send the full API key.Request test access
Test keys are issued after a manual review of the integration use case. You can inspect the complete contract and run a public, non-retained preview before requesting a key.