---
title: "Create webhook endpoint"
method: POST
path: "/api/v1/webhook-endpoints"
tags: ["Webhooks"]
---

# Create webhook endpoint

`POST /api/v1/webhook-endpoints`

## Request

- Base URL: `https://arcnautical.com`
- URL: `https://arcnautical.com/api/v1/webhook-endpoints`
- Auth: one of:
  - HTTP bearer
  - API key in header `X-ArcNautical-Key`

Creates an environment-specific HTTPS webhook endpoint after SSRF-safe URL validation.

Required scope: webhooks:manage.

## Headers

- `Idempotency-Key` string, required

## Request body

- CreateWebhookEndpointRequest
  - `url` string, uri, required
  - `event_types` WebhookEventType[], required

## Response `201`

Webhook endpoint created; the signing secret is shown once.

- CreateWebhookEndpointResponse
  - `id` string, required
  - `url` string, uri, required
  - `event_types` WebhookEventType[], required
  - `is_active` boolean, required
  - `secret` string, required: HMAC signing secret. Returned only at creation.
  - `created_at` string, date-time, required

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