Skip to content

API reference ​

IP selector lab ​

Lab endpoints require administrator access and return 404 while experimental features are disabled. Writes require a real administrator session, not a static API key. Domain authorization belongs to the administrator who generated the challenge.

MethodPathBehavior
GET/api/lab/ip-selectorProbe and DNS settings; returns has_secret_key, never the secret
PUT/api/lab/ip-selectorSaves settings; Host/SNI changes preserve independent domain verification, and unverified schedules are disabled
GET/api/lab/ip-selector/statusstatus includes progress, authorization, budget, limits, next_allowed_at, and errors; runs holds up to 20 runs
POST/api/lab/ip-selector/ownership/challengeBody {"domain":"example.com"}; returns TXT record_name and token, revoking previous authorization and scheduling
POST/api/lab/ip-selector/ownership/verifyChecks TXT; returns authorization state on success or 403 on failure
GET/api/lab/ip-selector/ownership/dnsRequires a real administrator session; returns the user's current DNS connection availability as available, optional account_name, and message, without credentials or a guarantee of zone write permission
POST/api/lab/ip-selector/ownership/dnsBody {"domain":"example.com"}; adds verification TXT through the current connection and checks DNS, returning verification, record_created, verified, and message
POST/api/lab/ip-selector/runUses persisted settings only; returns 202 on start, with no request-body target overrides
POST/api/lab/ip-selector/stopCancels work and disables scheduling; stopped: true acknowledges the stop request

Invalid configuration/challenge input returns 400, authorization/budget/cooldown failures return 403, and concurrent runs return 409. Neither 202 nor stopped: true means the background task has finished; poll status. The full candidate count is reserved before starting, cancellation does not refund it, and the UTC daily budget survives restarts. See the lab guide.

New run records include probe (the run's host, sni, path, and statuses), rejected (the rejected count), and up to 100 diagnostics with ip, status, latency_ms, and error. Failed runs also retain diagnostics; results still contains only top-ranked matches. Older records may lack the new fields, and zero rejections may omit the count and diagnostics.

TXT challenge and one-click endpoints use only the requested verification domain, independent of probe Host/SNI. Clients need not save probe settings first. Missing accounts, missing zone permissions, or failed writes return 400; manual entry remains available. A successful write with pending DNS propagation returns 200 with verified: false; wait and check again. record_created: false can mean the same record already exists. Retries reuse a valid challenge without replacing other records or enabling scheduling or scanning. The audit action is lab_ownership_dns.

Administrators read and update limits through GET/PUT /api/admin/settings: lab_daily_request_limit (1–10000000, default 100000), lab_requests_per_second (1–1000, default 10), and lab_max_workers (1–256, default 32). Values must be integers; omitted fields retain stored values. Invalid input returns 400 without saving. Changes apply to subsequent runs, and lowering the budget preserves spent reservations. These are instance limits, not cloud quotas. Status reports the current configured limits; active runs retain their startup snapshot. Reading probe settings caps the displayed worker count to the current ceiling; execution also enforces that ceiling.

AI assistant ​

Assistant endpoints require a real user session through X-Auth-Token, not a static API key. Conversation ownership is enforced independently of shared provider settings.

MethodPathPurpose
GET/api/assistant/settingsEffective mode and configuration status; keys are never returned
PUT/api/assistant/settingsSave scope, endpoint, model, api_key, api_format: chat_completions/responses and reasoning_summary; shared scope and its shared_enabled flag are administrator-only
GET / POST/api/assistant/conversationsList owned conversations / create a conversation
DELETE/api/assistant/conversations/{conversationID}Delete history, not business resources; unavailable while a task runs
POST/api/assistant/conversations/{conversationID}/messagesSubmit message and include_resources; stream: true returns SSE. Generate replies and pending tasks without executing writes
POST/api/assistant/conversations/{conversationID}/tasks/{taskID}action: edit, execute, pause, resume, cancel or retry; editing takes arguments and expected_revision

Editing validates and saves pending arguments, returns a new revision and performs no business writes; delete tasks cannot be edited. Execution requires confirmed: true; edited tasks also require the reviewed expected_revision. Retry additionally requires checked_resources: true and an unknown task status. Execution arguments come from the stored task and cannot be replaced in the execution request. Stale, completed or in-flight tasks cannot execute again. HTTP 200 means the task state was saved; check for status: succeeded to determine execution success.

Streaming messages use Content-Type: text/event-stream with status, text, reasoning, ping, done and error events. The final done includes the saved conversation. Reverse proxies must support streaming without buffering; errors or interrupted streams do not establish that tasks were saved.

See AI assistant for privacy and operational limits.

General authentication ​

Every endpoint is prefixed with /api. Two ways to authenticate:

  • User session: sign in to receive an X-Auth-Token, accepted by every protected endpoint
  • API key: the X-API-Key header (or ?api_key=), a machine-level equivalent of administrator rights

Ordinary endpoints are checked against group permissions (tunnels / domain binding / DNS records / service monitoring / Cloudflare authorization); administrators are unrestricted, and everything under /api/admin/* is administrator-only. Sessions live in the database, so restarting the service does not invalidate them.

Health ​

MethodPathDescriptionAuth
GET/api/healthHealth check, returns the current versionnone

Install wizard ​

Until the panel has an administrator account the process mounts only these endpoints and the frontend bundle; after installation they all answer 404.

MethodPathDescriptionAuth
GET/api/setup/statusSetup state: whether the environment owns the storage, the current database configuration with credentials redacted, the data directory and the setup file pathnone
POST/api/setup/testTry a connection with the submitted database form; nothing is storednone
POST/api/setup/completeSave the storage choice, apply the schema and create the administrator account, then restart the processnone

Registration & identity ​

MethodPathDescriptionAuth
GET/api/auth/configRegistration policy: whether sign-up is open, invite-code mode, whether an email code is required, plus the Turnstile switch and site keynone
POST/api/auth/registerRegister and sign in; invite codes and email codes are validated server-sidenone
POST/api/auth/send-codeSend a registration email code (needs SMTP, 60-second cooldown)none
POST/api/auth/forgot-passwordSend a password-reset code (needs SMTP)none
POST/api/auth/reset-passwordSet a new password with a reset code, dropping all sessions on successnone
GET/api/auth/meThe signed-in identity: ID, username, display name, avatar, email, role and permissionsuser session

OIDC sign-in & identity linking ​

MethodPathDescriptionAuth
GET/api/auth/oidc/providersIDs and names of enabled providersnone
POST/api/auth/oidc/startprovider_id and cf_turnstile_response when required; returns authorization_urlnone
GET/api/auth/oidc/callbackProvider callback; validates authorization and returns to the Web sign-in pageOIDC state and browser cookie
POST/api/auth/oidc/completeticket and email code when required; completes sign-in/linking or returns an email, 2FA or first-password resultticket and browser cookie; linking/password setup also require the original user session
POST/api/auth/oidc/send-codeSend the local email code for a pending ticketticket and browser cookie
GET/api/account/oidcWhether the account has a local password, plus linked identities; no provider secretsuser session
POST/api/account/oidc/startprovider_id, action: bind/password; binding also requires the current local passworduser session
POST/api/account/oidc/passwordSet the first local password using the reauthentication ticket; cannot replace an existing passworduser session and single-use ticket
DELETE/api/account/oidc/{id}Unlink by provider ID; preserves at least one available sign-in methoduser session
GET/api/admin/oidc/providersProvider configuration, with only has_client_secret for secretsadministrator
POST / PUT/api/admin/oidc/providers, /api/admin/oidc/providers/{id}Create/edit name, issuer, client ID, callback, scopes, enabled state and order; an empty secret preserves it, clear_secret removes itadministrator
DELETE/api/admin/oidc/providers/{id}Delete a provider; refused while identities remain linkedadministrator

Account endpoints require a real user session; static API keys cannot act as the account owner. See Multi-user & admin console for first-sign-in email checks and account creation policy.

Sign-in & sessions ​

Every authentication entry point (sign-in, the second step, passkey sign-in, registration, email codes, password recovery and reset) sits behind the sign-in rate limiter: attempts are counted per account and per source IP, and an exhausted budget answers 429 with a Retry-After header (seconds) plus a retry_after field and a message in the body. Thresholds and the off switch live under "system settings → sign-in protection"; see Security and administrator authentication for the mechanics.

MethodPathDescriptionAuth
POST/api/admin/loginSign in (account accepts an email or a username); returns a challenge when 2FA is onnone
POST/api/admin/login/2faComplete sign-in with a TOTP or recovery codechallenge
POST/api/admin/logoutEnd the current sessionuser session
GET/api/admin/statusCheck session validity, returns username and roleuser session
PUT/api/admin/passwordChange the current user's password (revokes all of that user's sessions)user session
PUT/api/admin/usernameChange the current user's username (password required)user session
PUT/api/admin/emailBind or change the current user's email (password required)user session
PUT/api/admin/profileUpdate the current user's display name and avatar URLuser session
POST/api/account/avatarUpload an avatar image (multipart file), saved to the accountuser session
GET/api/notify/settingsThe current user's notification settings (channels, events, email, Telegram status)user session
PUT/api/notify/settingsSave notification settings; an empty tg_bot_token keeps the stored one, and responses never return ituser session
POST/api/notify/testSend a test notification with the current configurationuser session
GET/api/admin/2fa/statusThe current user's 2FA stateuser session
POST/api/admin/2fa/setupBegin authenticator enrollmentuser session
POST/api/admin/2fa/confirmConfirm enrollment and generate recovery codesuser session
POST/api/admin/2fa/disableTurn 2FA off (password plus a TOTP or recovery code)user session

Admin console (administrators only) ​

Users ​

MethodPathDescription
GET/api/admin/usersList all users, with group and last sign-in
POST/api/admin/usersCreate a user (role and group optional)
PUT/api/admin/users/{id}/statusEnable / disable (disabling also signs them out)
PUT/api/admin/users/{id}/groupChange the group
PUT/api/admin/users/{id}/passwordReset the password (signs that user out)
DELETE/api/admin/users/{id}Delete a user (never yourself or the last administrator)

Groups ​

MethodPathDescription
GET/api/admin/groupsList groups and their permission sets
POST/api/admin/groupsCreate a group
PUT/api/admin/groups/{id}Update name and permissions (built-in groups allow permissions only)
DELETE/api/admin/groups/{id}Delete a group (not built-in ones, not ones with members)

Invite codes ​

MethodPathDescription
GET/api/admin/invitesList invite codes
POST/api/admin/invitesGenerate one (group, use count, expiry)
PUT/api/admin/invites/{code}Enable / disable
DELETE/api/admin/invites/{code}Delete

System settings ​

MethodPathDescription
GET / PUT/api/admin/settingsRegistration switch, invite-code mode, default group, email verification, Turnstile site key / secret, audit retention (audit_retention_days), the passkey relying party (passkey_rp_id / passkey_origins), the panel-wide password switch (password_login_disabled) and sign-in rate limiting (rate_limit_enabled, rate_limit_per_account, rate_limit_per_ip, rate_limit_window_minutes, rate_limit_familiar_multiplier, rate_limit_notify); omitted fields keep their stored value
GET / PUT/api/admin/oauthCloudflare OAuth client (client ID / secret / callback / scopes), taking precedence over the environment
GET / PUT/api/admin/encryption-keyApplication encryption key (the environment wins; a change needs a restart)
GET / PUT/api/admin/smtpSMTP settings (encrypted or plain)
POST/api/admin/smtp/testSend a test email

Audit trail ​

Records administrator and configuration changes: sign-ins and sign-outs, users and groups, invite codes, system settings, tunnels and ingress rules, domain bindings, DNS records, monitors and their targets, and IP-selector lab runs. Read-only browsing is not recorded. Retention is configured under system settings, and expired rows are pruned hourly.

MethodPathDescription
GET/api/admin/audit-logsPage through the trail; filters are actor (fuzzy username match, also accepts an account ID), category, action, from / to (unix seconds) and page / page_size (default 50, max 200)
GET/api/admin/audit-logs/statsTotals for the days window (default 7, max 90): operations total, failures failed, active accounts actors and today's operations today

Every row carries the time, the actor (account ID and username), category and action, the target object, the source IP and whether it succeeded.

Passkeys (WebAuthn) ​

A passkey replaces the password with the fingerprint, face unlock or hardware key on the device. The relying party id is derived from the request host (falling back to the panel host) and can be overridden under "system settings → passkeys" for reverse proxies or multi-domain setups; HTTPS is required (localhost excepted). When the relying party cannot be resolved the account page says so and hides the binding entry point.

MethodPathDescriptionAuth
POST/api/auth/passkey/login/beginStart a passwordless sign-in; account is optional and only narrows what the browser offers — leave it out for a discoverable ceremonynone
POST/api/auth/passkey/login/finishVerify the assertion and finish; the passkey is a strong factor on its own, so no TOTP is layered on topnone
POST/api/admin/login/2fa/passkey/beginSwitch the second step to a passkey after a password sign-inchallenge
POST/api/admin/login/2fa/passkey/finishVerify the assertion and complete the two-step sign-inchallenge
GET/api/account/passkeysList this account's passkeys, the relying party and the password switchuser session
POST/api/account/passkeys/beginBegin binding; requires the current passworduser session
POST/api/account/passkeys/finishFinish binding and store the credential (name is a label, auto-numbered when blank)user session
PUT/api/account/passkeys/{id}Renameuser session
DELETE/api/account/passkeys/{id}Delete, requires the current password; refuses to remove the last one while password sign-in is offuser session
PUT/api/account/password-loginToggle this account's password sign-in, requires the current password; turning it off needs a bound passkey firstuser session
PUT/api/admin/users/{id}/password-loginForce another account's password switch, used to recover an account that lost its passkeyadministrator

Every registration flow returns {ceremony_token, public_key}, where public_key is the browser's WebAuthn dictionary and ceremony_token must be echoed back verbatim by the matching finish call (single-use, valid for five minutes). The -allow-password-login command-line flag clears the panel-wide switch and every account switch, for when the install has locked itself out.

The native app additionally needs Digital Asset Links, which the backend serves at the site root (no authentication required):

MethodPathDescription
GET/.well-known/assetlinks.jsonAndroid Digital Asset Links: the package name and SHA-256 certificate fingerprints come from system settings (passkey_android_package / passkey_android_fingerprints, falling back to the shared debug keystore's hash); with no valid fingerprint it answers 404 rather than a broken document

Cloudflare OAuth ​

Each user may authorize several Cloudflare accounts and switch between connections at will.

MethodPathDescriptionAuth
GET/api/cloudflare/oauth/statusConnection list, the active one and available accountsuser session
POST/api/cloudflare/oauth/startBegin an authorization request (adding an account never overwrites older connections)oauth_connect
GET/api/cloudflare/oauth/callbackCloudflare's callback; creates a connection and activates itOAuth state
PUT/api/cloudflare/oauth/connectionSwitch the active connectionoauth_connect
PUT/api/cloudflare/oauth/accountSwitch the Cloudflare account within the active connectionoauth_connect
DELETE/api/cloudflare/oauthRevoke and delete a connection (?connection_id=, defaults to the active one)oauth_connect

Configuration & site branding ​

Tunnel selection and the service address are per user; site branding and the optimized CNAME are global, administrator-only settings.

MethodPathDescriptionAuth
GET/api/configRead configuration (global branding plus this user's selections)user session
GET/api/sitePublic site brandingnone
POST/api/config/tunnelSet this user's tunneluser session
POST/api/config/serviceSet this user's service addressuser session
POST/api/config/preferred-cnameSet the global optimized CNAMEadministrator
PUT/api/config/siteUpdate site brandingadministrator
PUT/api/config/cname-presetsUpdate the CNAME presetsadministrator

Tunnels ​

Requires the tunnels permission; calls run against the Cloudflare account of the user's active connection.

MethodPathDescription
GET/api/tunnelsList tunnels
POST/api/tunnelsCreate a tunnel and return its connector token
GET/api/tunnels/{tunnelID}Tunnel detail and routes
DELETE/api/tunnels/{tunnelID}Delete a tunnel
POST/api/tunnels/{tunnelID}/ingressAdd an application route
PUT/api/tunnels/{tunnelID}/ingressUpdate an application route
DELETE/api/tunnels/{tunnelID}/ingressDelete a route, optionally with its DNS record

DNS records ​

Requires the dns permission.

MethodPathDescription
GET/api/zonesList zones
GET/api/zones/{zoneID}/dns-recordsList a zone's DNS records
POST/api/zones/{zoneID}/dns-recordsCreate a record
PUT/api/zones/{zoneID}/dns-records/{recordID}Edit a record
DELETE/api/zones/{zoneID}/dns-records/{recordID}Delete a record

Creation and editing accept only A / AAAA / CNAME / TXT / MX / NS / SRV / CAA / PTR. Unknown types are read-only; update/delete first read the existing record through the current user's CF client to check its type. Credentials, the dns permission and per-record auditing remain scoped to the current user.

FieldRules
type, nameRequired; type is uppercased, name is trimmed and loses its trailing dot. Supports @, wildcards and ASCII/Punycode
ttlOmitted defaults to 1 (automatic); otherwise an integer 60–86400. Explicit 0 / null is rejected
proxiedOmitted defaults to false; null is rejected. Only A / AAAA / CNAME can be proxied; other types are forced to false
contentRequired for the seven non-structured types, up to 4096 bytes; TXT whitespace is preserved. A/AAAA validate the address family; CNAME/MX/NS/PTR require hostname targets
priorityRequired for MX, integer 0–65535; null MX (content:".") requires priority:0
dataRequired for SRV/CAA, rejected for other types. SRV/CAA do not use a content string

SRV uses a full _service._protocol.name and data.priority/weight/port/target. The numeric fields are integers 0–65535, and target is a hostname or .. Existing legacy data.service/proto/name fields are retained and synchronized with the top-level name on rename. CAA uses data.flags (integer 0–255), data.tag (1–15 ASCII letters/digits), and data.value (single-line string, up to 4096 bytes, empty allowed). Missing/incorrectly typed fields, unknown fields, trailing JSON or a request body over 64 KiB return 400.

json
{"type":"SRV","name":"_sip._tcp.example.com","ttl":300,"data":{"priority":0,"weight":5,"port":5060,"target":"sip.example.com"}}
json
{"type":"CAA","name":"example.com","data":{"flags":128,"tag":"iodef","value":"mailto:security@example.com"}}

GET retains raw data, including fields on unknown record types; do not discard it before editing. Successful POST returns 201 and the record; PUT returns 200 and the record. Web/Android bulk creation adds no endpoint: only A / AAAA / TXT / MX / NS / PTR, up to 100 nonempty lines per client batch, validated/deduplicated locally and sent as serial POSTs. Every record is audited separately; the operation is not atomic. Definite failures remain for retry. Uncertain network/provider responses stop the queue and require checking the actual records before explicit retry; a transport error must not be interpreted as proof that no record was created.

Domain binding ​

Requires the domain_bind permission.

MethodPathDescription
POST/api/domain/bindBind one group (mode selects direct or optimized)
POST/api/domain/bind-batchBind several groups, each with its own mode and service address
POST/api/domain/fallbackSet the fallback origin

Monitoring & status pages ​

Requires the monitors permission. Monitors are isolated by creator: ordinary users see only their own, administrators see all. The create and update endpoints also carry the public-page, alert and recipient settings.

MethodPathDescription
GET/api/monitorsList monitors and target states
POST/api/monitorsCreate a monitor
GET/api/monitors/overviewGlobal summary statistics; each bucket carries an issues array (omitempty, listing only the targets that saw a warn or a down sample that day) with the target identity, warn / down counts, incident_count and the incidents outage windows (start and end time, state, run length, status code and error) that the chart drills into
PUT/api/monitors/{monitorID}Update configuration (public page, alert switch, recipients …)
DELETE/api/monitors/{monitorID}Delete a monitor
POST/api/monitors/{monitorID}/checkRun one probe immediately
GET/api/monitors/{monitorID}/alertsThe last 100 alert records (delivered or why not)
POST/api/monitors/{monitorID}/targetsAdd a probe target
PUT/api/monitors/{monitorID}/targets/{targetID}Edit a probe target
DELETE/api/monitors/{monitorID}/targets/{targetID}Delete a probe target
GET/api/public/status/{token}Public status data; the token may be the system token or the short path

Custom-domain fields on PUT /api/monitors/{monitorID}:

FieldDescription
public_domainThe status page hostname; empty keeps only the link under the panel domain
public_domain_modesimple for direct, preferred for optimized
public_aux_domainRequired in optimized mode: the proxied hostname that reaches the tunnel
public_preferred_cnameThe optimized CNAME for this monitor; empty reads the global default
domain_warningA response field meaning the settings saved but Cloudflare configuration failed

In optimized mode the status hostname and the auxiliary hostname must differ, and the optimized CNAME may not equal the status hostname. Changing any of these fields re-runs the Cloudflare configuration. A failure still returns HTTP 200 and explains itself through domain_warning.

A custom domain only ever exposes that monitor's public page and the assets it needs. Other status pages, the admin console and protected APIs return 404.

Uploads & Telegram ​

Telegram remote control is per user: each account configures its own bot token and authorized Telegram IDs, and the bot only touches that account's resources. An administrator's previous global bot configuration is migrated to their personal settings at startup.

MethodPathDescriptionAuth
POST/api/uploadsUpload a status-page imagemonitors
GET/api/telegram/settingsThis user's bot settingsuser session
PUT/api/telegram/settingsSave this user's bot settings and restart their botuser session
GET/api/telegram/statusThis user's bot statususer session
POST/api/telegram/testSend a test message to this user's authorized Telegram IDsuser session
PUT/api/telegram/endpointSet the panel-wide Telegram API endpoint (a custom proxy) for every user's botadministrator
POST/api/telegram/webhookWebhook entry point for the legacy global bot (backwards compatibility)secret token
POST/api/telegram/webhook/{userID}Per-user webhook entry point, dispatched only to the bot for that userIDsecret token

A web control panel for Cloudflare Tunnel