Skip to main content

Documentation Index

Fetch the complete documentation index at: https://orbit-docs.devotel.io/llms.txt

Use this file to discover all available pages before exploring further.

Per-API-key IP allowlist

Bind an Orbit API key to one or more source IPs (or CIDR ranges) so the key only authenticates when the request originates from your infrastructure. A leaked key is then useless to anyone outside your network — the request fails with 401 INVALID_API_KEY before it touches your billing or tenant data. This is a per-key control. The org-wide IP allowlist (under Settings → Security) gates the dashboard and SAML login surfaces; the per-key list gates programmatic API authentication.

Constraints

  • Up to 50 entries per key.
  • Each entry must be a valid IP (v4 or v6) or CIDR range. The auth package validates each entry before storage.
  • An empty list ([]) or null clears the allowlist — the key becomes unrestricted again.
  • Changes take effect on the next request — there is no propagation lag.

Read the current allowlist

Owner / admin / developer scope.
curl https://orbit-api.devotel.io/api/v1/settings/api-keys/{keyId}/allowed-ips \
  -H "X-API-Key: dv_live_sk_..."
Response:
{
  "data": {
    "id": "key_abc123",
    "allowed_ips": ["203.0.113.42", "198.51.100.0/24"]
  },
  "meta": {
    "request_id": "req_xyz789",
    "timestamp": "2026-05-09T10:14:22Z"
  }
}
allowed_ips is null when the key is unrestricted.

Replace the allowlist

PATCH is replace-only — it never merges. To add an entry, fetch the list, append, and PATCH the full list back.
curl -X PATCH https://orbit-api.devotel.io/api/v1/settings/api-keys/{keyId}/allowed-ips \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "allowed_ips": [
      "203.0.113.42",
      "198.51.100.0/24",
      "2001:db8::/32"
    ]
  }'

Clear the allowlist (make unrestricted)

curl -X PATCH https://orbit-api.devotel.io/api/v1/settings/api-keys/{keyId}/allowed-ips \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "allowed_ips": null }'

What a blocked request looks like

When a key has allowed_ips set and a request arrives from a non-allowed source IP:
HTTP/1.1 401 Unauthorized
Content-Type: application/json

{
  "error": {
    "code": "INVALID_API_KEY",
    "message": "API key is not valid for this source IP."
  }
}
The error returns generic INVALID_API_KEY rather than IP_NOT_ALLOWED so a leaked key can’t enumerate the allowlist by trial-and-error. Every blocked request is recorded in the audit log under api_key.allowed_ips_violation with the source IP for forensic review.

Audit log

Every replacement of the allowlist writes an audit-log entry under api_key.allowed_ips_updated with:
  • actor_user_id
  • resource_id — the API key id
  • details.count — the number of entries in the new list
  • ip_address — the operator’s source IP
Combine with the dashboard at Settings → Audit Log to track who changed which key when.

Validation rules

InputBehaviour
nullClears the list. Key becomes unrestricted.
[] (empty array)Clears the list. Key becomes unrestricted.
Valid IP (203.0.113.42)Stored as a /32 (v4) / /128 (v6) match.
Valid CIDR (198.51.100.0/24)Stored as-is; matched as a range.
IPv6 / IPv6 CIDRSupported — 2001:db8::/32 is valid.
Malformed entryWhole request rejected with 422 VALIDATION_ERROR; nothing persisted.
> 50 entriesWhole request rejected with 422 VALIDATION_ERROR.

See also