Skip to content

Integrations API

12 operations. Every call needs a session cookie or an Authorization: Bearer nct_… token unless marked Public. Conventions, errors and scopes

GET/cloudflare/ips

Cloudflare IP ranges in use

The list the origin lock allows: its source (built-in copy, Cloudflare API or the published text files), size, age and the state of the 3-hourly refresh. stale is set when fetching has failed for more than 24 hours; the last good list stays in use.

Response 200 · OK · application/json
IPListView
  • builtinDatestringrequired
    The day the built-in list was fetched (YYYY-MM-DD)
  • changedAtstring (date-time) | nullrequired
    The last time the content changed; null while the built-in list is in use
  • failingSincestring (date-time) | nullrequired
  • fetchedAtstring (date-time) | nullrequired
    The last successful fetch; null while the built-in list is in use
  • hashstringrequired
  • lastErrorstringrequired
    Why the last fetch failed; empty after a successful one
  • sourceenum: builtin | api | textrequired
    Where the list came from: the copy built into the binary, the Cloudflare API or the published text files
  • stalebooleanrequired
    Fetching has failed for more than 24 hours
  • v4Countinteger (int64)required
  • v6Countinteger (int64)required
Errors
  • 401Unauthorized
  • 403Forbidden
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

POST/cloudflare/ips/refresh

Refresh Cloudflare's IP ranges now (team admin)

Fetches the list like the periodic job does (API first, text files second), validates it and stores it. A changed list is pushed to the servers with locked routes (a route update, no redeploy). 502 ip_refresh_failed when neither source works or the answer fails validation; the stored list stays.

Response 200 · OK · application/json
IPListView
  • builtinDatestringrequired
    The day the built-in list was fetched (YYYY-MM-DD)
  • changedAtstring (date-time) | nullrequired
    The last time the content changed; null while the built-in list is in use
  • failingSincestring (date-time) | nullrequired
  • fetchedAtstring (date-time) | nullrequired
    The last successful fetch; null while the built-in list is in use
  • hashstringrequired
  • lastErrorstringrequired
    Why the last fetch failed; empty after a successful one
  • sourceenum: builtin | api | textrequired
    Where the list came from: the copy built into the binary, the Cloudflare API or the published text files
  • stalebooleanrequired
    Fetching has failed for more than 24 hours
  • v4Countinteger (int64)required
  • v6Countinteger (int64)required
Errors
  • 401Unauthorized
  • 403Forbidden
  • 500Internal Server Error
  • 502Bad Gatewayip_refresh_failed

Errors are RFC 9457 application/problem+json with a stable code field.

GET/integrations/cloudflare

Get the Cloudflare connection

The API token is never returned.

Response 200 · OK · application/json
CloudflareConnectionView
  • accountIdstringrequired
    Account used for Cloudflare Tunnel calls
  • accountNamestringrequired
  • AccountRef[] | nullrequired
    Accounts the token's zones belong to
  • autoManagebooleanrequired
    New domains in a connected zone get their DNS records managed automatically
  • configuredbooleanrequired
  • defaultProxiedbooleanrequired
    New managed records start proxied (orange cloud)
  • statusenum: | ok | errorrequired
    error after Cloudflare rejected the token; empty when not configured
  • statusMessagestringrequired
  • tokenExpiresAtstring (date-time) | nullrequired
  • verifiedAtstring (date-time) | nullrequired
  • warningsstring[] | nullrequired
  • zoneCountinteger (int64)required
  • zonesSyncedAtstring (date-time) | nullrequired
Errors
  • 401Unauthorized
  • 403Forbidden
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

PUT/integrations/cloudflare

Connect Cloudflare

Stores a scoped API token (sealed at rest, write-only) after verifying it against Cloudflare and caching its zone list; every domain of the team is matched against the zones. Omit the token to change only the settings. 422 invalid_token when Cloudflare rejects the token.

Request body · application/json · required
PutInputBody1
  • accountIdstring
    Account used for Cloudflare Tunnel calls; defaults to the account of the token's zones
  • autoManageboolean
    New domains in a connected zone get their DNS records managed automatically (default true)
  • defaultProxiedboolean
    New managed records start proxied (orange cloud; default false)
  • dns01CredentialIdstring
    Use the token of an existing Cloudflare DNS-01 credential instead of typing one
  • tokenstring
    Write-only: a Cloudflare API token with Zone → Zone: Read and Zone → DNS: Edit (add Zone → Zone Settings: Read for SSL-mode warnings and Account → Cloudflare Tunnel: Edit for tunnels). Omit to keep the stored token.
Response 200 · OK · application/json
CloudflareConnectionView
  • accountIdstringrequired
    Account used for Cloudflare Tunnel calls
  • accountNamestringrequired
  • AccountRef[] | nullrequired
    Accounts the token's zones belong to
  • autoManagebooleanrequired
    New domains in a connected zone get their DNS records managed automatically
  • configuredbooleanrequired
  • defaultProxiedbooleanrequired
    New managed records start proxied (orange cloud)
  • statusenum: | ok | errorrequired
    error after Cloudflare rejected the token; empty when not configured
  • statusMessagestringrequired
  • tokenExpiresAtstring (date-time) | nullrequired
  • verifiedAtstring (date-time) | nullrequired
  • warningsstring[] | nullrequired
  • zoneCountinteger (int64)required
  • zonesSyncedAtstring (date-time) | nullrequired
Errors
  • 401Unauthorized
  • 403Forbidden
  • 422Unprocessable Entityinvalid_token
  • 500Internal Server Error
  • 502Bad Gateway

Errors are RFC 9457 application/problem+json with a stable code field.

DELETE/integrations/cloudflare

Disconnect Cloudflare

Forgets the token and the zone list. Records NebulaCtrl created stay in Cloudflare (unmanaged) unless removeRecords=true.

ParameterInTypeDescription
removeRecordsquerybooleanDelete the DNS records NebulaCtrl created from Cloudflare first (the removal is refused when that fails)
Response 204 · No Content

No body.

Errors
  • 401Unauthorized
  • 403Forbidden
  • 404Not Found
  • 409Conflict
  • 422Unprocessable Entity
  • 500Internal Server Error
  • 502Bad Gateway

Errors are RFC 9457 application/problem+json with a stable code field.

POST/integrations/cloudflare/test

Re-verify the token and refresh the zone list

Verifies the stored token, refreshes the cached zones and re-matches every domain of the team.

Response 200 · OK · application/json
CloudflareConnectionView
  • accountIdstringrequired
    Account used for Cloudflare Tunnel calls
  • accountNamestringrequired
  • AccountRef[] | nullrequired
    Accounts the token's zones belong to
  • autoManagebooleanrequired
    New domains in a connected zone get their DNS records managed automatically
  • configuredbooleanrequired
  • defaultProxiedbooleanrequired
    New managed records start proxied (orange cloud)
  • statusenum: | ok | errorrequired
    error after Cloudflare rejected the token; empty when not configured
  • statusMessagestringrequired
  • tokenExpiresAtstring (date-time) | nullrequired
  • verifiedAtstring (date-time) | nullrequired
  • warningsstring[] | nullrequired
  • zoneCountinteger (int64)required
  • zonesSyncedAtstring (date-time) | nullrequired
Errors
  • 401Unauthorized
  • 403Forbidden
  • 404Not Found
  • 422Unprocessable Entity
  • 500Internal Server Error
  • 502Bad Gateway

Errors are RFC 9457 application/problem+json with a stable code field.

GET/integrations/cloudflare/zones

List the connected zones (zone picker)

The cached zone list; empty without a connection.

Response 200 · OK · application/json
ZonesOutputBody
  • Zone[] | nullrequired
Errors
  • 401Unauthorized
  • 403Forbidden
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

GET/integrations/tailscale

Get the Tailscale integration

The OAuth client secret is never returned (secretSet only).

Response 200 · OK · application/json
TailscaleIntegration
  • allowedTagsstring[] | nullrequired
  • clientIdstringrequired
  • configuredbooleanrequired
  • defaultTagsstring[] | nullrequired
  • headscaleCredentialenum: apikey | preauthrequired
    apikey: NebulaCtrl mints single-use pre-auth keys and deletes nodes; preauth: one reusable tagged pre-auth key for every sidecar
  • headscaleUserstringrequired
    Headscale user that owns minted pre-auth keys (empty = tags-only keys)
  • httpsEnabledboolean | nullrequired
    Whether the tailnet has HTTPS certificates enabled (known after a successful test)
  • TailscaleLastTestrequired
  • loginServerstringrequired
    Headscale control URL (headscale provider)
  • providerenum: tailscale_oauth | headscalerequired
  • secretSetbooleanrequired
  • tailnetstringrequired
    Tailnet name, or - for the tailnet of the OAuth client
Errors
  • 401Unauthorized
  • 403Forbidden
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

PUT/integrations/tailscale

Create or update the Tailscale integration

Stores an OAuth client (scopes auth_keys and devices:core) or, with provider: headscale, a Headscale server (login URL plus an API key, or a reusable tagged pre-auth key). The secret is sealed at rest; omit clientSecret to keep the stored one. Nothing contacts the control server until the connection test or first use.

Request body · application/json · required
PutInputBody
  • allowedTagsstring[] | null
    Tags NebulaCtrl may use; must include every default tag
  • clientIdstring
    OAuth client id (tailscale_oauth only)
  • clientSecretstring
    Write-only: the OAuth client secret, the Headscale API key or the Headscale pre-auth key. Omit to keep the stored secret.
  • defaultTagsstring[] | null
    Tags minted keys carry (default tag:nebula-svc)
  • headscaleCredentialenum: apikey | preauth
    apikey: NebulaCtrl mints single-use pre-auth keys and deletes nodes; preauth: one reusable tagged pre-auth key (headscale only)
  • headscaleUserstring
    Headscale user that owns minted pre-auth keys; empty creates tags-only keys (headscale only)
  • loginServerstring
    Headscale control URL (headscale only)
  • providerenum: tailscale_oauth | headscale
    tailscale_oauth (default) or headscale
  • tailnetstring
    Tailnet name; empty or - uses the tailnet of the OAuth client
Response 200 · OK · application/json
TailscaleIntegration
  • allowedTagsstring[] | nullrequired
  • clientIdstringrequired
  • configuredbooleanrequired
  • defaultTagsstring[] | nullrequired
  • headscaleCredentialenum: apikey | preauthrequired
    apikey: NebulaCtrl mints single-use pre-auth keys and deletes nodes; preauth: one reusable tagged pre-auth key for every sidecar
  • headscaleUserstringrequired
    Headscale user that owns minted pre-auth keys (empty = tags-only keys)
  • httpsEnabledboolean | nullrequired
    Whether the tailnet has HTTPS certificates enabled (known after a successful test)
  • TailscaleLastTestrequired
  • loginServerstringrequired
    Headscale control URL (headscale provider)
  • providerenum: tailscale_oauth | headscalerequired
  • secretSetbooleanrequired
  • tailnetstringrequired
    Tailnet name, or - for the tailnet of the OAuth client
Errors
  • 401Unauthorized
  • 403Forbidden
  • 422Unprocessable Entity
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

DELETE/integrations/tailscale

Remove the Tailscale integration

Refused (409 integration_in_use) while a server uses a managed tailnet.

Response 204 · No Content

No body.

Errors
  • 401Unauthorized
  • 403Forbidden
  • 404Not Found
  • 409Conflictintegration_in_use
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

GET/integrations/tailscale/devices

List the tailnet's devices (device picker)

Response 200 · OK · application/json
DevicesOutputBody
  • TailscaleDevice[] | nullrequired
Errors
  • 401Unauthorized
  • 403Forbidden
  • 422Unprocessable Entity
  • 500Internal Server Error
  • 502Bad Gateway

Errors are RFC 9457 application/problem+json with a stable code field.

POST/integrations/tailscale/test

Test the Tailscale connection

Exchanges a token, lists devices, mints and revokes a 60 second key with the default tags. Leaves no residue.

Response 200 · OK · application/json
TailscaleTestResult
  • TailscaleCheck[] | nullrequired
  • okbooleanrequired
Errors
  • 401Unauthorized
  • 403Forbidden
  • 404Not Found
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.