Dashboard

IP Allowlists

Restrict API keys to trusted IP addresses and networks, with rules for individual keys or an entire organization.

Every allowlist action—including listing—requires a fresh email OTP. Key owners can list, set, edit, and delete IP allowlists for their own keys. Organization admins can manage allowlists for keys in their organization and set one organization-wide list.

These gateway endpoints identify an organization by its team_id. Admin access comes from the gateway team's current membership roster, with the admin or owner role. The separate enterprise portal organization ID is not a team_id.

How rules apply

Each list accepts up to 100 IPv4 addresses, IPv6 addresses, or CIDR ranges. A request can match any entry within a list. For example, 203.0.113.8 allows one address and 203.0.113.0/24 allows a network.

When both key and organization lists are set, the request IP must match both. A key owner cannot override the organization list by adding a wider range or deleting the key's list.

  • A key assigned to a team inherits that team's list, including keys created after the list is saved.
  • A personal key without a team_id inherits the lists of all teams its owner currently belongs to. If several teams have lists, the IP must satisfy every list.
  • An empty list ([]) adds no restriction at that level. Deleting a list has the same effect. Neither operation removes inherited rules.
  • Rules apply to inference and key-authenticated management requests, including requests to change an allowlist. Admins' keys are subject to the same IP checks.

The gateway uses the client IP resolved by its trusted proxy configuration. Add your application's public egress or NAT address; a private device address usually is not the address the gateway receives. Setting forwarding headers yourself does not establish a trusted client IP.

Include your current public egress IP before restricting the key you use for management. If you lock yourself out, reconnect from an allowed network or use another authorized key whose own rules permit your connection. An organization admin can update your key's rules from an allowed network.

Authentication and endpoints

Send your API key as Authorization: Bearer YOUR_API_KEY. Use the base URL of your gateway; the examples below use https://enterprise.blackbox.ai.

Action Individual key Organization
List current entries GET /key/ip-allowlist GET /team/ip-allowlist
Set or edit the entire list PUT /key/ip-allowlist PUT /team/ip-allowlist
Delete the entire list DELETE /key/ip-allowlist DELETE /team/ip-allowlist
Read the audit history GET /key/ip-allowlist/audit GET /team/ip-allowlist/audit

/organization/ip-allowlist is an alias for /team/ip-allowlist and uses the same optional team_id parameter.

For key endpoints, omit key to manage the key used for authentication. To target another key, pass key in the GET query string or the PUT/DELETE JSON body. It accepts the target's raw key or SHA-256 token hash. Prefer a hash in query strings to avoid exposing a raw secret in URL logs. A token hash identifies a target; it cannot authenticate a request.

A key owner can manage their own keys. A team admin can manage keys assigned to that team and personal keys owned by its current members. Admin membership in one team does not grant access to a key assigned to another team. Organization endpoints require admin or owner membership in the target team.

Choosing the team

team_id is optional on organization endpoints. Pass it in the GET query string or the PUT/DELETE JSON body to target a specific team. When you omit it, the gateway resolves the team from the API key you authenticate with:

  1. Key assigned to a team: that team is used. You must be an admin or owner of it, otherwise the request returns 403. The gateway never falls back to another team you administer.
  2. Personal key (no team_id): if you are an admin or owner of exactly one team, that team is used. If you administer none, the request returns 403.

Master keys have no team, so they must always pass team_id. Every organization response returns the team_id that was used, including the 202 from a passcode request, and the passcode email names the team. Check it before you enter the code.

Required OTP for every action

Before each GET, PUT, or DELETE, request a six-digit code using the matching endpoint:

Target Request a code Required JSON
Key POST /key/ip-allowlist/request-passcode action, optional key, and allowed_ips for set
Organization POST /team/ip-allowlist/request-passcode action, optional team_id, and allowed_ips for set

/organization/ip-allowlist/request-passcode is an alias for the team endpoint. Use action: "list" for GET, "set" for PUT (both initial setup and editing), "delete" for DELETE, or "audit" to read the audit history.

For example, request a code to view your own key's list:

curl -X POST https://enterprise.blackbox.ai/key/ip-allowlist/request-passcode \
  -H "Authorization: Bearer $BLACKBOX_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"action":"list"}'

A successful request returns 202 with expires_in: 300; for organization targets it also includes the resolved team_id. The code goes to the requesting user's registered email address. When an admin manages a member's key or a team list, the code goes to that admin. You cannot choose a different recipient in the request. Every caller needs a registered mailbox; master keys do not bypass OTP.

Read the code from your email and set ALLOWLIST_OTP locally. Send it in the X-OTP-Code header for the action. PUT and DELETE also accept an otp string in their JSON body. GET requires the header; do not place passcodes in URLs.

A code is bound to the requesting user, key or team, and action. For set, it also binds the complete normalized allowed_ips array. The action request must match the code request. For example, before the key PUT example below, request:

curl -X POST https://enterprise.blackbox.ai/key/ip-allowlist/request-passcode \
  -H "Authorization: Bearer $BLACKBOX_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"action":"set","allowed_ips":["203.0.113.8","198.51.100.0/24","2001:db8:1234::/48"]}'

Replace these documentation addresses with your actual addresses in both requests. To target a different key, include the same key selector in both requests. For organization actions, include the same team_id in both requests, or omit it from both. The code is bound to the resolved team, so if your team memberships change between the two requests, request a new code.

Codes expire after five minutes and allow at most five incorrect attempts. Requests for another code for the same email, action, and target have a 60-second cooldown. Each code can be used only once, even for listing. A code is consumed before the action runs: if the action fails afterward, obtain a new code before retrying. Ownership/admin permissions and the caller's IP restrictions are checked again for the action.

The following examples assume you have just requested a code for that specific action and stored it in ALLOWLIST_OTP. A code from one example cannot be reused for another.

Manage your own key

List the current entries:

curl https://enterprise.blackbox.ai/key/ip-allowlist \
  -H "Authorization: Bearer $BLACKBOX_API_KEY" \
  -H "X-OTP-Code: $ALLOWLIST_OTP"

Set the list, or edit it by sending the complete replacement:

curl -X PUT https://enterprise.blackbox.ai/key/ip-allowlist \
  -H "Authorization: Bearer $BLACKBOX_API_KEY" \
  -H "X-OTP-Code: $ALLOWLIST_OTP" \
  -H 'Content-Type: application/json' \
  -d '{"allowed_ips":["203.0.113.8","198.51.100.0/24","2001:db8:1234::/48"]}'

The example IPs are documentation addresses. Replace them with your actual public egress addresses before sending the request. To add, edit, or remove one entry, read the list, change that entry locally, and PUT the complete updated array. Concurrent replacements use the last successfully saved list.

Example response:

{
  "allowed_ips": ["203.0.113.8", "198.51.100.0/24", "2001:db8:1234::/48"],
  "updated_at": "2026-10-29T12:00:00.000Z",
  "organization_allowlists": [],
  "propagation_seconds": 30
}

Addresses are normalized, duplicate entries are removed, and CIDRs are normalized to their network address. organization_allowlists shows inherited lists as objects containing team_id and allowed_ips.

Delete your key's list:

curl -X DELETE https://enterprise.blackbox.ai/key/ip-allowlist \
  -H "Authorization: Bearer $BLACKBOX_API_KEY" \
  -H "X-OTP-Code: $ALLOWLIST_OTP"

Manage a member's key

Use an organization admin's API key to authenticate and specify the target key:

curl -X PUT https://enterprise.blackbox.ai/key/ip-allowlist \
  -H "Authorization: Bearer $ADMIN_API_KEY" \
  -H "X-OTP-Code: $ALLOWLIST_OTP" \
  -H 'Content-Type: application/json' \
  -d '{"key":"MEMBER_KEY_SHA256","allowed_ips":["203.0.113.0/24"]}'

Use the same key selector with GET to list its entries or DELETE to remove its list. A key owner can also use this selector to manage another key they own.

Set rules for the whole organization

Save one list for the team:

curl -X PUT https://enterprise.blackbox.ai/team/ip-allowlist \
  -H "Authorization: Bearer $ADMIN_API_KEY" \
  -H "X-OTP-Code: $ALLOWLIST_OTP" \
  -H 'Content-Type: application/json' \
  -d '{"team_id":"YOUR_TEAM_ID","allowed_ips":["203.0.113.0/24","2001:db8:1234::/48"]}'

If your admin key is assigned to the team, or you administer only one team, you can omit team_id (Choosing the team). Omit it from the passcode request too:

curl -X PUT https://enterprise.blackbox.ai/team/ip-allowlist \
  -H "Authorization: Bearer $ADMIN_API_KEY" \
  -H "X-OTP-Code: $ALLOWLIST_OTP" \
  -H 'Content-Type: application/json' \
  -d '{"allowed_ips":["203.0.113.0/24","2001:db8:1234::/48"]}'

Read the organization list. The team_id query parameter is optional, as above:

curl "https://enterprise.blackbox.ai/team/ip-allowlist?team_id=YOUR_TEAM_ID" \
  -H "Authorization: Bearer $ADMIN_API_KEY" \
  -H "X-OTP-Code: $ALLOWLIST_OTP"

Remove the organization list:

curl -X DELETE https://enterprise.blackbox.ai/team/ip-allowlist \
  -H "Authorization: Bearer $ADMIN_API_KEY" \
  -H "X-OTP-Code: $ALLOWLIST_OTP" \
  -H 'Content-Type: application/json' \
  -d '{"team_id":"YOUR_TEAM_ID"}'

Organization responses include team_id, allowed_ips, updated_at, and propagation_seconds. Removing the organization list leaves each key's individual list in place.

Audit history

The gateway records every successful list, set, and delete on a key or organization allowlist. Each entry records:

  • the action
  • the acting user and their registered email
  • the client IP
  • the time
  • the list as it stood after the action (empty for a delete)

Reading the audit history is not itself recorded, and denied requests are not recorded.

Target Endpoint Who can read it
Key GET /key/ip-allowlist/audit The key owner, or an admin who can manage that key
Organization GET /team/ip-allowlist/audit Admins and owners of the team

/organization/ip-allowlist/audit is an alias for the team endpoint. Select the target the same way as for listing: omit key to read your own key's history, or pass key in the query string (prefer the SHA-256 token hash). For organizations, team_id is optional (Choosing the team).

Reading the history requires a code requested with action: "audit":

curl -X POST https://enterprise.blackbox.ai/team/ip-allowlist/request-passcode \
  -H "Authorization: Bearer $ADMIN_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"team_id":"YOUR_TEAM_ID","action":"audit"}'

Then send the code in the X-OTP-Code header:

curl "https://enterprise.blackbox.ai/team/ip-allowlist/audit?team_id=YOUR_TEAM_ID&limit=50&offset=0" \
  -H "Authorization: Bearer $ADMIN_API_KEY" \
  -H "X-OTP-Code: $ALLOWLIST_OTP"

Entries are returned newest first. limit accepts 1 to 200 and defaults to 50. offset defaults to 0. Page through older entries by increasing offset by limit; each page needs a new code.

Example response:

{
  "scope": "team",
  "team_id": "YOUR_TEAM_ID",
  "limit": 50,
  "offset": 0,
  "entries": [
    {
      "action": "set",
      "allowed_ips": ["203.0.113.0/24", "2001:db8:1234::/48"],
      "actor_user_id": "admin@example.com",
      "actor_email": "admin@example.com",
      "client_ip": "203.0.113.8",
      "created_at": "2026-10-29T12:00:00.000Z"
    }
  ]
}

action is view, set, or delete. Key responses use "scope": "key" and omit team_id. A key's history covers only that key's own list; changes to an inherited organization list appear in the organization's history. Recording is best-effort: if the audit store is unavailable, the allowlist action still completes and its entry may be missing.

Key history

To read the history of the key you authenticate with, request a code without a key:

curl -X POST https://enterprise.blackbox.ai/key/ip-allowlist/request-passcode \
  -H "Authorization: Bearer $BLACKBOX_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"action":"audit"}'

Then read it:

curl "https://enterprise.blackbox.ai/key/ip-allowlist/audit?limit=50&offset=0" \
  -H "Authorization: Bearer $BLACKBOX_API_KEY" \
  -H "X-OTP-Code: $ALLOWLIST_OTP"

An admin reading a member's key history passes that key in both requests. Put it in the passcode request's JSON body and in the audit request's query string:

curl -X POST https://enterprise.blackbox.ai/key/ip-allowlist/request-passcode \
  -H "Authorization: Bearer $ADMIN_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"key":"MEMBER_KEY_SHA256","action":"audit"}'

curl "https://enterprise.blackbox.ai/key/ip-allowlist/audit?key=MEMBER_KEY_SHA256&limit=50&offset=0" \
  -H "Authorization: Bearer $ADMIN_API_KEY" \
  -H "X-OTP-Code: $ALLOWLIST_OTP"

Use the key's SHA-256 token hash rather than the raw key, since the audit request carries it in the URL. Example response:

{
  "scope": "key",
  "limit": 50,
  "offset": 0,
  "entries": [
    {
      "action": "set",
      "allowed_ips": ["203.0.113.8"],
      "actor_user_id": "admin@example.com",
      "actor_email": "admin@example.com",
      "client_ip": "203.0.113.8",
      "created_at": "2026-10-29T12:05:00.000Z"
    },
    {
      "action": "view",
      "allowed_ips": [],
      "actor_user_id": "member@example.com",
      "actor_email": "member@example.com",
      "client_ip": "198.51.100.7",
      "created_at": "2026-10-29T12:00:00.000Z"
    }
  ]
}

The second entry shows a member viewing their key's list while it was still empty.

Propagation and errors

Saved rules normally take effect on the next request. Allow up to 30 seconds for cached policy and membership snapshots to expire, including when cache invalidation is unavailable. Requests already in progress are not cancelled.

Status Meaning
400 Missing or invalid OTP, expired/used code, mismatched action or payload, missing fields, invalid IP/CIDR, or more than 100 entries. Also returned when team_id is omitted and can't be resolved: you administer several teams, or you're using a master key.
401 Missing API key.
403 Invalid credentials, insufficient ownership/admin permissions (including omitting team_id when you administer no team), or an IP denied by a key or organization list. IP denial returns error.code: "IP_NOT_ALLOWED".
409 The passcode is already being processed.
429 Passcode resend cooldown or too many incorrect attempts. Resend responses include Retry-After and error.retry_after.
503 OTP delivery/verification is unavailable, or the gateway cannot verify the IP policy or save/read its configuration. Enforcement returns error.code: "IP_ALLOWLIST_UNAVAILABLE"; retry later.

An unknown client IP is denied whenever a non-empty list applies. Existing key expiration, blocked-key, model, and gateway access checks continue to apply.