API Access

Automate certificate management with the REST API. The API supports two authentication methods: OAuth 2.1 and API keys, both with granular per-CA permissions.

Authenticating with OAuth

The API supports OAuth 2.1 with PKCE (S256). Once you have an access token, include it in the Authorization header:

Authorization: Bearer cm_oauth_xxxxxxxxxxxx

Creating an OAuth Client

For server-side applications, create a confidential OAuth client in the dashboard under OAuth Clients. You'll receive a client_id and client_secret. Public clients (e.g. SPAs, CLI tools) can use Dynamic Client Registration instead — no secret is issued, but PKCE is required.

OAuth Endpoints

EndpointURL
Authorizationhttps://certman.app/oauth/authorize
Tokenhttps://certman.app/oauth/token
Revocationhttps://certman.app/oauth/revoke
Dynamic Registrationhttps://certman.app/oauth/register

Authorization Flow

  1. Redirect the user to the authorization endpoint with response_type=code, your client_id, a redirect_uri, PKCE code_challenge, and resource=https://api.certman.app
  2. The user selects a workspace and grants per-CA permissions (read, issue, revoke)
  3. Exchange the returned authorization code for an access token at the token endpoint
  4. Confidential clients authenticate with client_secret_post; public clients use PKCE only

Token Refresh

Access tokens expire after 1 hour. Use the refresh_token grant at the token endpoint to obtain a new access token without user interaction. Refresh tokens are valid for 30 days and rotate on each use — always store the new refresh token from the response.

Authenticating with an API Key

Navigate to API / MCP Access in the sidebar to create keys. API keys must be scoped to specific CAs — they don't have workspace-wide access. Each key can be granted specific permissions per CA:

  • Read — View CA and certificate details
  • Issue — Create new certificates
  • Revoke — Revoke existing certificates

On the Free plan, API keys get full access (all three permissions) to selected CAs. The Pro plan allows granular control — for example, read-only access to one CA and read+issue for another.

Include your API key in the Authorization header:

Authorization: Bearer cm_xxxxxxxxxxxx

Base URL

All API endpoints are available at https://api.certman.app

OpenAPI Specification

The full API specification is available in OpenAPI 3.0 format at https://api.certman.app/v1/openapi. You can use this with tools like Swagger Editor, Postman, or any OpenAPI-compatible client to explore and test the API.

Rate Limits

API requests are rate-limited to ensure fair usage. Certificate issuance and renewal operations have a lower limit due to their computational cost. If you exceed the rate limit, you'll receive a 429 response.

Error Format

All error responses follow a consistent format:

{ "error": "Human-readable error message" }

Common HTTP status codes:

  • 400 — Bad request (invalid parameters)
  • 401 — Unauthorized (missing or invalid credentials)
  • 403 — Forbidden (insufficient permissions)
  • 404 — Not found
  • 429 — Too many requests (rate limited)
  • 500 — Internal server error

Endpoints

GET /v1/whoami

Returns information about the authenticated API key, including the associated user, workspace, and permissions.

Example Request

curl https://api.certman.app/v1/whoami \
  -H "Authorization: Bearer cm_xxxxxxxxxxxx"

Response

200 OK
{
  "user": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "email": "user@example.com"
  },
  "workspace": {
    "id": "660e8400-e29b-41d4-a716-446655440001",
    "name": "My Workspace",
    "slug": "my-workspace"
  },
  "apiKey": {
    "id": "770e8400-e29b-41d4-a716-446655440002",
    "name": "Production Key",
    "prefix": "cm_prod"
  },
  "permissions": {
    "880e8400-e29b-41d4-a716-446655440003": {
      "canRead": true,
      "canIssue": true,
      "canRevoke": false
    }
  }
}
GET /v1/cas

Returns a list of all Certificate Authorities that the API key has read access to. Revoked CAs are automatically excluded.

Query Parameters

Parameter Type Description
parentCaId string Filter by parent CA ID. Use to list only children of a specific CA.
type string root | intermediate | all (default: all)

Example Request

curl https://api.certman.app/v1/cas \
  -H "Authorization: Bearer cm_xxxxxxxxxxxx"

Response

200 OK
{
  "cas": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Production Root CA",
      "common_name": "Production Root CA",
      "organization": "Acme Corp",
      "key_algorithm": "ECDSA-P384",
      "valid_from": "2024-01-01T00:00:00.000Z",
      "valid_to": "2034-01-01T00:00:00.000Z",
      "created_at": "2024-01-01T00:00:00.000Z",
      "parent_ca_id": null,
      "depth": 0,
      "path_length": null
    },
    {
      "id": "660e8400-e29b-41d4-a716-446655440001",
      "name": "Production Intermediate CA",
      "common_name": "Production Intermediate CA",
      "organization": "Acme Corp",
      "key_algorithm": "ECDSA-P256",
      "valid_from": "2024-06-01T00:00:00.000Z",
      "valid_to": "2029-06-01T00:00:00.000Z",
      "created_at": "2024-06-01T00:00:00.000Z",
      "parent_ca_id": "550e8400-e29b-41d4-a716-446655440000",
      "depth": 1,
      "path_length": 0
    }
  ]
}
GET /v1/cas/:id

Returns detailed information about a specific Certificate Authority, including its certificate PEM.

Path Parameters

Parameter Type Description
id UUID Certificate Authority ID

Example Request

curl https://api.certman.app/v1/cas/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer cm_xxxxxxxxxxxx"

Response

200 OK
{
  "ca": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Production Root CA",
    "common_name": "Production Root CA",
    "organization": "Acme Corp",
    "country": "US",
    "state": "California",
    "locality": "San Francisco",
    "key_algorithm": "ECDSA-P384",
    "certificate_pem": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
    "serial_number": "01AB23CD45EF",
    "valid_from": "2024-01-01T00:00:00.000Z",
    "valid_to": "2034-01-01T00:00:00.000Z",
    "created_at": "2024-01-01T00:00:00.000Z",
    "has_passphrase": false,
    "parent_ca_id": null,
    "depth": 0,
    "path_length": null,
    "revoked_at": null,
    "revocation_reason": null
  }
}
GET /v1/cas/:id/chain

Returns the full certificate chain for a CA, from the specified CA up to the root. Useful for building trust stores or verifying certificate chains.

Path Parameters

Parameter Type Description
id UUID Certificate Authority ID

Example Request

curl https://api.certman.app/v1/cas/550e8400-e29b-41d4-a716-446655440000/chain \
  -H "Authorization: Bearer cm_xxxxxxxxxxxx"

Response

200 OK
{
  "caId": "660e8400-e29b-41d4-a716-446655440001",
  "chainLength": 2,
  "chain": [
    "-----BEGIN CERTIFICATE-----\n(intermediate CA cert)\n-----END CERTIFICATE-----",
    "-----BEGIN CERTIFICATE-----\n(root CA cert)\n-----END CERTIFICATE-----"
  ]
}
GET /v1/certificates

Returns a paginated list of certificates with optional filtering. Only certificates from CAs that the API key has read access to are returned.

Query Parameters

Parameter Type Description
caId string Filter by Certificate Authority ID
status string active | revoked | all (default: all)
search string Search by common name (case-insensitive partial match)
createdFrom string Filter certificates created on or after this date (ISO 8601)
createdTo string Filter certificates created on or before this date (ISO 8601)
expiringInDays integer Filter certificates expiring within N days
page integer Page number (1-indexed) (default: 1)
pageSize integer Number of items per page (default: 20)

Example Request

curl "https://api.certman.app/v1/certificates?status=active&pageSize=10" \
  -H "Authorization: Bearer cm_xxxxxxxxxxxx"

Response

200 OK
{
  "certificates": [
    {
      "id": "660e8400-e29b-41d4-a716-446655440001",
      "ca_id": "550e8400-e29b-41d4-a716-446655440000",
      "common_name": "api.example.com",
      "san_dns": [
        "api.example.com",
        "www.example.com"
      ],
      "san_ip": [
        "192.168.1.1"
      ],
      "key_algorithm": "ECDSA-P256",
      "valid_from": "2024-06-01T00:00:00.000Z",
      "valid_to": "2025-06-01T00:00:00.000Z",
      "serial_number": "AABBCCDD",
      "revoked_at": null,
      "revocation_reason": null,
      "created_at": "2024-06-01T00:00:00.000Z"
    }
  ],
  "total": 42,
  "page": 1,
  "pageSize": 20,
  "totalPages": 3
}
POST /v1/certificates

Issues a new certificate from a Certificate Authority. Supports two modes: 'managed' (Certman generates the key pair) or 'csr' (you provide a Certificate Signing Request).

Idempotency: For CSR mode only, you can provide an Idempotency-Key header to ensure the request is idempotent. If you retry a request with the same key and body, you'll receive the same response. Keys expire after 24 hours. Managed mode does not support idempotency because each request generates a unique private key.

Request Body

Parameter Type Description
caId string ID of the Certificate Authority to issue from
mode string managed | csr (default: managed)
commonName string Common name for the certificate (required for managed mode)
sanDns string[] DNS Subject Alternative Names (supports wildcards like *.example.com)
sanIp string[] IP address Subject Alternative Names (IPv4 or IPv6)
keyAlgorithm string RSA-2048 | RSA-4096 | ECDSA-P256 | ECDSA-P384
validityDays integer Certificate validity in days (max 397 per CA/Browser Forum)
csrPem string Certificate Signing Request in PEM format (required for csr mode)
caPassphrase string CA passphrase (required if the CA is passphrase-protected)

Example Request

curl -X POST https://api.certman.app/v1/certificates \
  -H "Authorization: Bearer cm_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "caId": "550e8400-e29b-41d4-a716-446655440000",
    "mode": "managed",
    "commonName": "api.example.com",
    "sanDns": [
      "api.example.com",
      "www.example.com"
    ],
    "sanIp": [
      "192.168.1.1"
    ],
    "keyAlgorithm": "ECDSA-P256",
    "validityDays": 365
  }'

Response

200 OK
{
  "certificate": {
    "id": "660e8400-e29b-41d4-a716-446655440001",
    "common_name": "api.example.com",
    "certificate_pem": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
    "san_dns": [
      "api.example.com",
      "www.example.com"
    ],
    "san_ip": [
      "192.168.1.1"
    ],
    "valid_from": "2024-06-01T00:00:00.000Z",
    "valid_to": "2025-06-01T00:00:00.000Z",
    "serial_number": "AABBCCDD",
    "created_at": "2024-06-01T00:00:00.000Z"
  },
  "privateKey": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
}

Note: privateKey is only returned for managed certificates. Store it securely.

POST /v1/certificates/:id/revoke

Revokes a certificate. Revoked certificates are included in the CA's Certificate Revocation List (CRL) and will be reported as revoked via OCSP.

Path Parameters

Parameter Type Description
id UUID Certificate ID

Request Body (Optional)

Parameter Type Description
reason string unspecified | keyCompromise | caCompromise | affiliationChanged | superseded | cessationOfOperation | certificateHold | privilegeWithdrawn (default: unspecified)
caPassphrase string CA passphrase (required if the CA is passphrase-protected)

Example Request

curl -X POST https://api.certman.app/v1/certificates/550e8400-e29b-41d4-a716-446655440000/revoke \
  -H "Authorization: Bearer cm_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "keyCompromise"
  }'

Response

200 OK
{
  "message": "Certificate revoked successfully",
  "certificate": {
    "id": "660e8400-e29b-41d4-a716-446655440001",
    "common_name": "api.example.com",
    "revoked_at": "2024-07-15T12:30:00.000Z",
    "revocation_reason": "keyCompromise"
  }
}
POST /v1/certificates/:id/renew

Creates a new certificate with the same properties as an existing certificate. For managed certificates, a new key pair is generated. For CSR-based certificates, you must provide a new CSR.

Idempotency: When providing a CSR, you can use an Idempotency-Key header to ensure the request is idempotent. If you retry a request with the same key and body, you'll receive the same response. Keys expire after 24 hours. Managed renewals do not support idempotency because each request generates a unique private key.

Path Parameters

Parameter Type Description
id UUID Certificate ID

Request Body (Optional)

Parameter Type Description
validityDays integer Certificate validity in days (max 397 per CA/Browser Forum)
csrPem string New CSR in PEM format (required when renewing a CSR-based certificate)
caPassphrase string CA passphrase (required if the CA is passphrase-protected)

Example Request

curl -X POST https://api.certman.app/v1/certificates/550e8400-e29b-41d4-a716-446655440000/renew \
  -H "Authorization: Bearer cm_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "validityDays": 365
  }'

Response

200 OK
{
  "message": "Certificate renewed successfully",
  "certificate": {
    "id": "770e8400-e29b-41d4-a716-446655440002",
    "common_name": "api.example.com",
    "certificate_pem": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
    "san_dns": [
      "api.example.com",
      "www.example.com"
    ],
    "san_ip": [
      "192.168.1.1"
    ],
    "valid_from": "2025-06-01T00:00:00.000Z",
    "valid_to": "2026-06-01T00:00:00.000Z",
    "serial_number": "EEFF0011",
    "created_at": "2025-06-01T00:00:00.000Z"
  },
  "privateKey": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
}

Note: privateKey is only returned for managed certificates. Store it securely.