Ceryx logo
Menu
View as Markdown

Certificates

Store your own TLS certificates, including wildcards, and see which certificate Ceryx serves for a domain.

Certificates stored through the API take priority over Let's Encrypt. A wildcard certificate stored for *.example.com serves subdomains such as app.example.com that have no certificate of their own. Responses never include private keys. See How certificates work for the full order in which Ceryx picks a certificate.

List stored certificates

GET/api/certificates

Responses

Status Description Body
200 Certificates stored through the API. array of CertificateListItem

Example

curl http://ceryx:8080/api/certificates

Responds with 200:

[
  {
    "domain": "*.example.com",
    "meta": {
      "vendor": "Example CA",
      "san": [
        "*.example.com"
      ],
      "expires_at": "2027-02-01T00:00:00Z",
      "issued_at": "2026-11-03T00:00:00Z",
      "stored_at": 1793750400
    }
  }
]

Create or replace a certificate

POST/api/certificates

Stores the certificate for domain, replacing any existing one.

Request body

A CertificateCreate object.

Field Type Required Description
domain string Yes Optional; must equal the domain in the URL.
certificate string Yes PEM-encoded certificate.
private_key string Yes PEM-encoded private key.
chain string No PEM-encoded intermediate chain.
meta CertificateMeta No Free-form metadata. Ceryx adds issued_at (when not given) and stored_at on every write.

Responses

Status Description Body
201 The stored certificate (without the private key). Certificate
400 Missing or invalid input. Error
500 The storage backend failed, or a Ceryx environment variable the request needs has an unusable value (detail names it). Error

Example

jq -n \
  --rawfile certificate cert.pem \
  --rawfile private_key key.pem \
  --rawfile chain chain.pem \
  '{domain: "*.example.com", certificate: $certificate, private_key: $private_key, chain: $chain,
    meta: {vendor: "Example CA", san: ["*.example.com"], expires_at: "2027-02-01T00:00:00Z"}}' |
curl -X POST http://ceryx:8080/api/certificates \
  -H 'Content-Type: application/json' \
  -d @-

Responds with 201:

{
  "domain": "*.example.com",
  "has_certificate": true,
  "has_chain": true,
  "meta": {
    "vendor": "Example CA",
    "san": [
      "*.example.com"
    ],
    "expires_at": "2027-02-01T00:00:00Z",
    "issued_at": "2026-11-03T00:00:00Z",
    "stored_at": 1793750400
  }
}

Get the certificate served for a domain

GET/api/certificates/{domain}

Returns the certificate Ceryx would serve for domain: an exact stored match, then a stored wildcard (*.parent), then certificate files from the route settings.

Path parameters

Name Type Description
domain string Certificate domain; may be a wildcard such as *.example.com.

Responses

Status Description Body
200 The certificate (without the private key). Certificate
404 No such resource. Error

Example

curl http://ceryx:8080/api/certificates/app.example.com

Responds with 200:

{
  "domain": "app.example.com",
  "has_certificate": true,
  "has_chain": true,
  "meta": {
    "vendor": "Example CA",
    "san": [
      "*.example.com"
    ],
    "expires_at": "2027-02-01T00:00:00Z",
    "issued_at": "2026-11-03T00:00:00Z",
    "stored_at": 1793750400
  }
}

Update a stored certificate

PUT/api/certificates/{domain}

Updates the certificate stored for exactly domain (no wildcard or file fallback). certificate and private_key are replaced together; a replaced certificate takes the request's chain, or none. meta replaces the stored metadata when given.

Path parameters

Name Type Description
domain string Certificate domain; may be a wildcard such as *.example.com.

Request body

A CertificateUpdate object.

Field Type Required Description
domain string No Optional; must equal the domain in the URL.
certificate string No PEM-encoded certificate; requires private_key.
private_key string No PEM-encoded private key; requires certificate.
chain string No Used only with a new certificate; omitting it clears the chain.
meta CertificateMeta No Free-form metadata. Ceryx adds issued_at (when not given) and stored_at on every write.

certificate requires private_key.

private_key requires certificate.

Responses

Status Description Body
200 The updated certificate (without the private key). Certificate
400 Missing or invalid input. Error
404 No such resource. Error
500 The storage backend failed, or a Ceryx environment variable the request needs has an unusable value (detail names it). Error

Example

curl -X PUT 'http://ceryx:8080/api/certificates/*.example.com' \
  -H 'Content-Type: application/json' \
  -d '{
    "meta": {
      "vendor": "Example CA",
      "san": [
        "*.example.com"
      ],
      "expires_at": "2027-02-01T00:00:00Z",
      "note": "Renewed by the platform team"
    }
  }'

Responds with 200:

{
  "domain": "*.example.com",
  "has_certificate": true,
  "has_chain": true,
  "meta": {
    "vendor": "Example CA",
    "san": [
      "*.example.com"
    ],
    "expires_at": "2027-02-01T00:00:00Z",
    "note": "Renewed by the platform team",
    "issued_at": "2026-11-10T09:30:00Z",
    "stored_at": 1794303000
  }
}

Delete a stored certificate

DELETE/api/certificates/{domain}

Idempotent; deleting a missing certificate also returns 204.

Path parameters

Name Type Description
domain string Certificate domain; may be a wildcard such as *.example.com.

Responses

Status Description Body
204 Deleted. None
500 The storage backend failed, or a Ceryx environment variable the request needs has an unusable value (detail names it). Error

Example

curl -X DELETE 'http://ceryx:8080/api/certificates/*.example.com'

Responds with 204 and no body.

Esc

Type to search the docs.