Skip to content

Feature Guide REST API

fuomag9 edited this page Sep 26, 2026 · 6 revisions

Feature Guide: REST API

Programmatic access to all Caddy Proxy Manager resources via a REST API.

Table of Contents

  1. Overview
  2. Authentication
  3. API Tokens
  4. Endpoints
  5. Endpoint Notes
  6. OpenAPI Documentation
  7. Examples

Overview

A full REST API is available under /api/v1/. It supports the same operations as the web UI: managing proxy hosts, certificates, access lists, settings, users, and more.

The interactive OpenAPI 3.1.0 specification is available at /api-docs in the web UI, or as raw JSON at /api/v1/openapi.json.


Authentication

The API supports two authentication methods:

Bearer token (recommended for automation)

curl -H "Authorization: Bearer <your-api-token>" \
  https://your-instance:3000/api/v1/proxy-hosts

Session cookie (browser)

If you are already logged in via the web UI, API requests from the same browser session are authenticated automatically.


API Tokens

Manage tokens from the API Tokens page (/api-tokens) or the Profile page.

Create a token

  1. Go to API Tokens or Profile.
  2. Enter a name and optional expiration date.
  3. Click Create.
  4. Copy the token immediately -- it is shown only once.

Token properties

Property Description
Name Human-readable label
Expiration Optional future date after which the token stops working
Last used Updated automatically (debounced to 60 seconds)

Tokens are stored as SHA-256 hashes in the database. The raw token cannot be recovered after creation.

Admin users can view and delete any token. Non-admin users can only manage their own tokens.

Since v1.13.1, changing a password signs out the user's other sessions but does not revoke their API tokens; delete a token here if it may be compromised. Deleting a user deletes the tokens they created.


Endpoints

Resource Methods Path
Health GET /api/health
Tokens GET, POST, DELETE /api/v1/tokens
Proxy Hosts GET, POST, PUT, DELETE /api/v1/proxy-hosts
L4 Proxy Hosts GET, POST, PUT, DELETE /api/v1/l4-proxy-hosts
Certificates GET, POST, PUT, DELETE /api/v1/certificates
CA Certificates GET, POST, PUT, DELETE /api/v1/ca-certificates
Client Certificates GET, POST, DELETE /api/v1/client-certificates
Client Cert Roles GET /api/v1/client-certificates/:id/roles
Access Lists GET, POST, PUT, DELETE /api/v1/access-lists
Access List Entries POST, DELETE /api/v1/access-lists/:id/entries
Settings GET, PUT /api/v1/settings/:group
Instances GET, POST /api/v1/instances
Instance PUT, DELETE /api/v1/instances/:id
Instance Sync Key Pin PUT, DELETE /api/v1/instances/:id/sync-key-pin
Sync Key Pins GET, PUT, DELETE /api/v1/instances/sync-key-pins
Own Sync Key GET /api/v1/instances/sync-key
Instance Sync POST /api/v1/instances/sync
Users GET, POST, PUT, DELETE /api/v1/users
Groups GET, POST, PATCH, DELETE /api/v1/groups
Group Members POST, DELETE /api/v1/groups/:id/members
mTLS Roles GET, POST, PUT, DELETE /api/v1/mtls-roles
mTLS Role Certs POST, DELETE /api/v1/mtls-roles/:id/certificates
mTLS Access Rules GET, POST, PUT, DELETE /api/v1/proxy-hosts/:id/mtls-access-rules
Forward Auth Access GET, PUT /api/v1/proxy-hosts/:id/forward-auth-access
Forward Auth Sessions GET, DELETE /api/v1/forward-auth-sessions
Sessions GET, DELETE /api/v1/sessions
Audit Log GET /api/v1/audit-log
DNS Providers GET /api/v1/dns-providers
OAuth Providers GET, POST, PUT, DELETE /api/v1/oauth-providers
Caddy Apply POST /api/v1/caddy/apply

PUT /api/v1/instances/:id and the sync key routes (/api/v1/instances/:id/sync-key-pin, /api/v1/instances/sync-key-pins, /api/v1/instances/sync-key) are available since v1.13.1.

All endpoints return JSON. Error responses use standard HTTP status codes (400, 401, 403, 404, 500) with a JSON body containing an error field; a 500 response also carries an errorId to find the matching entry in the web container log.


Endpoint Notes

Instances and sync key pins

(since v1.13.1)

These endpoints are admin only. See Feature Guide Instance Sync for how sync key pinning works.

  • PUT /api/v1/instances/:id changes an instance's name, baseUrl, apiToken or enabled flag; fields left out are kept. A new token keeps the slave's sync key pin, so update an instance rather than deleting and re-adding it to change its token. A baseUrl that points to a different sync endpoint, like DELETE, removes the old URL's pin unless another instance or INSTANCE_SLAVES entry still uses that URL. Base URLs must be http or https with no credentials, query string or fragment ("Base URL must not contain a query string or fragment"). Tokens must be 32–512 characters with no leading or trailing whitespace.
  • Instance responses include syncKeyPin: the pinned key (keyId, publicKey, pinnedAt, source), or null until a sync pins one.
  • PUT /api/v1/instances/:id/sync-key-pin with {"publicKey":"<base64>"} pins the slave's key by hand (source manual), replacing any pin. DELETE on the same path resets the pin, so the next sync pins whatever key the slave presents; it returns 404 when nothing is pinned.
  • GET /api/v1/instances/sync-key-pins lists every pin with its normalized url and the instances and INSTANCE_SLAVES entries that use it (slaves). PUT and DELETE with ?url=<slave base URL> pin or reset by URL, for INSTANCE_SLAVES entries, slaves not added yet, and pins no slave uses. Without url they return 400 "The url query parameter is required".
  • GET /api/v1/instances/sync-key returns this instance's own sync key (keyId, publicKey). Call it on a slave to compare with, or pin, the key its master holds.
# On the slave: read its sync key
curl -s -H "Authorization: Bearer $SLAVE_TOKEN" \
  https://slave.example.com:3000/api/v1/instances/sync-key | jq

# On the master: pin that key for instance 3
curl -s -X PUT -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"publicKey":"<publicKey from the slave>"}' \
  https://your-instance:3000/api/v1/instances/3/sync-key-pin | jq

Read the slave's key over a channel you trust, not through the sync connection.

Users

  • GET /api/v1/users, GET /api/v1/users/:id and PUT /api/v1/users/:id responses include username, the user's sign-in username (since v1.13.1), or null when the account has none. They never include the password hash.
  • POST /api/v1/users applies the password policy (since v1.13.1): 12+ characters, upper- and lowercase letters, a digit and a special character. A weak password gets 400 with the missing requirements, e.g. {"error":"Password must be at least 12 characters long, must include at least one number"}.
  • DELETE /api/v1/users/:id also deletes the user's sessions, API tokens, sign-in methods, forward-auth sessions and grants, and group memberships (since v1.13.1).

Sign-in usernames (since v1.13.1):

  • "username" in PUT /api/v1/users/:id (admin only, also for your own account) sets the user's sign-in username. POST /api/v1/users accepts it too; without it, the new user gets their email address as username only when that address is a valid username no other account uses, and no username otherwise. CPM no longer makes a username up from the email address.
  • A username must be 3–255 characters of lowercase letters (a-z), digits and _ . @ - (surrounding whitespace is removed), and must not be another account's username, email address or forward-auth portal name (the <name> of an email <name>@localhost), ignoring case. Otherwise the request gets 400, e.g. {"error":"Username must be 3-255 characters long and use only lowercase letters (a-z), digits and the characters _ . @ -"} or {"error":"Another account already signs in with this name or has it as its email address"}.
  • "username": null, or no username, leaves the username unchanged, so a GET response can be sent back as it is. Sending the username the user already has is no change either.
  • Changing email leaves username unchanged. An email address that is another account's email or username (for a @localhost address, also when the part before @localhost is another account's username) gets 400 with the reason.
  • A request refused with 400 changes nothing: PUT does not apply role or status either, and POST creates no user.
  • A changed username is recorded in the audit log as Changed user <id> sign-in username to <username>.
# Set the sign-in username of user 5
curl -s -X PUT -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"username": "alice"}' \
  https://your-instance:3000/api/v1/users/5 | jq

See Feature Guide User Management#sign-in-usernames.

Settings

  • DNS provider credentials saved through PUT /api/v1/settings/dns-provider are stored encrypted, as the dashboard form stores them, and so is the token of the legacy cloudflare group (since v1.13.1). Credentials that older releases stored in plaintext are encrypted on startup, logged as Encrypted N DNS provider credential(s) that were stored in plaintext. GET responses only list which credential fields are set.
  • WAF custom directives. PUT /api/v1/settings/waf, and proxy host saves with waf settings, reject only the directive lines that the save newly drops (since v1.13.1). A stored line that a newer release no longer sends to Caddy does not block unrelated changes; it is left out of the generated config and reported in the web container log. See Feature Guide WAF.

OpenAPI Documentation

The interactive API docs are available at /api-docs in the web UI. This page renders the full OpenAPI 3.1.0 specification with:

  • Try-it-out functionality for all endpoints
  • Request/response schema documentation
  • Authentication configuration

The raw spec is also available at /api/v1/openapi.json for code generation tools.


Examples

List all proxy hosts

curl -s -H "Authorization: Bearer $TOKEN" \
  https://your-instance:3000/api/v1/proxy-hosts | jq

Create a proxy host

curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My App",
    "domains": ["app.example.com"],
    "upstreams": ["192.0.2.5:8080"],
    "ssl_forced": true,
    "enabled": true
  }' \
  https://your-instance:3000/api/v1/proxy-hosts | jq

Apply Caddy configuration

curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  https://your-instance:3000/api/v1/caddy/apply | jq

Get settings for a group

curl -s -H "Authorization: Bearer $TOKEN" \
  https://your-instance:3000/api/v1/settings/general | jq

Related Documentation


Need help? Open an issue with the request/response details (redact tokens and sensitive data).

Clone this wiki locally