API Reference¶
All endpoints are prefixed with /api/v1 (configurable via API_V1_STR).
Base URL for examples: http://localhost:8000/api/v1
Authentication¶
POST /auth/token¶
Obtain a JWT access token.
- Auth required: No
- Content-Type:
application/x-www-form-urlencoded
| Field | Type | Required | Description |
|---|---|---|---|
username |
string |
Yes | Username |
password |
string |
Yes | Password |
Response 200:
{
"access_token": "eyJ...",
"token_type": "bearer",
"refresh_token": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4..."
}
Errors: 401 — incorrect credentials.
POST /auth/refresh¶
Exchange a refresh token for a new access token and refresh token. The submitted refresh token is immediately invalidated (token rotation).
- Auth required: No (refresh token passed in request body)
- Content-Type:
application/json
| Field | Type | Required | Description |
|---|---|---|---|
refresh_token |
string |
Yes | A valid, non-expired refresh token |
Response 200:
{
"access_token": "eyJ...",
"token_type": "bearer",
"refresh_token": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4..."
}
Errors: 401 — token missing, expired, or already revoked.
POST /auth/logout¶
Invalidate the refresh token and access token associated with the current session.
- Auth required: Yes (Bearer token)
Response 204: No content.
Errors: 401 — not authenticated.
POST /auth/invalidate¶
Invalidate all refresh tokens for the authenticated user, signing out every active session.
- Auth required: Yes (Bearer token)
Response 204: No content.
Errors: 401 — not authenticated.
Users¶
POST /users/¶
Create a new user.
- Auth required: Optional (required for admin/superuser roles after first user)
- Content-Type:
application/json
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
username |
string |
Yes | — | Unique username |
email |
string |
Yes | — | Unique email |
password |
string |
Yes | — | Min 8 characters |
role |
string |
No | user |
superuser, admin, user |
organization_id |
int |
No | null |
Organization to assign |
can_create_ca |
bool |
No | false |
Capability flag |
can_create_cert |
bool |
No | false |
Capability flag |
can_revoke_cert |
bool |
No | false |
Capability flag |
can_export_private_key |
bool |
No | false |
Capability flag |
can_delete_ca |
bool |
No | false |
Capability flag |
Response 201: User object.
Errors: 400 — username/email already exists. 403 — insufficient permissions for role.
GET /users/¶
List all users.
- Auth required: Superuser
| Parameter | Type | Default | Description |
|---|---|---|---|
skip |
int |
0 |
Offset |
limit |
int |
100 |
Max results |
Response 200: Array of user objects.
GET /users/me¶
Get the current authenticated user.
- Auth required: Any active user
Response 200: User object.
GET /users/{user_id}¶
Get a user by ID.
- Auth required: Self, Admin, or Superuser
Response 200: User object. Errors: 403, 404.
PATCH /users/{user_id}¶
Update a user.
- Auth required: Self (limited fields), Admin (same org), or Superuser
- Content-Type:
application/json
| Field | Type | Who can set |
|---|---|---|
email |
string |
Self, Admin (same org), Superuser |
password |
string |
Self, Admin (same org), Superuser |
role |
string |
Superuser only |
is_active |
bool |
Superuser only |
organization_id |
int |
Superuser only |
can_create_ca |
bool |
Admin (same org), Superuser |
can_create_cert |
bool |
Admin (same org), Superuser |
can_revoke_cert |
bool |
Admin (same org), Superuser |
can_export_private_key |
bool |
Admin (same org), Superuser |
can_delete_ca |
bool |
Admin (same org), Superuser |
Response 200: Updated user object. Errors: 403, 404.
DELETE /users/{user_id}¶
Delete a user.
- Auth required: Superuser (cannot delete self)
Response 204. Errors: 400 (self-delete), 404.
Organizations¶
POST /organizations/¶
Create an organization.
- Auth required: Admin or Superuser
- Content-Type:
application/json
| Field | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Unique name (non-empty) |
description |
string |
No | Description |
Response 201: Organization object. Errors: 400 — name already exists.
GET /organizations/¶
List organizations.
- Auth required: Any active user
- Superusers see all. Others see only their own organization.
Response 200: Array of organization objects.
GET /organizations/{organization_id}¶
Get an organization by ID.
- Auth required: Member of the organization, or Superuser
Response 200: Organization object. Errors: 403, 404.
PUT /organizations/{organization_id}¶
Update an organization.
- Auth required: Admin in the organization, or Superuser
- Content-Type:
application/json
| Field | Type | Description |
|---|---|---|
name |
string |
New name (non-empty) |
description |
string |
New description |
Response 200: Updated organization object. Errors: 400, 403, 404.
DELETE /organizations/{organization_id}¶
Delete an organization.
- Auth required: Superuser only
- Organizations with users cannot be deleted.
Response 204. Errors: 400 (has users), 404.
POST /organizations/{organization_id}/users/{user_id}¶
Add a user to an organization.
- Auth required: Admin in the organization, or Superuser
Response 200: Updated user object. Errors: 403, 404.
DELETE /organizations/{organization_id}/users/{user_id}¶
Remove a user from an organization.
- Auth required: Admin in the organization, or Superuser
Response 200: Updated user object. Errors: 403, 404.
GET /organizations/{organization_id}/users¶
List users in an organization.
- Auth required: Member of the organization, or Superuser
Response 200: Array of user objects. Errors: 403, 404.
Certificate Authorities¶
POST /cas/¶
Create a new CA.
- Auth required:
create_cacapability, Admin (same org), or Superuser - Content-Type:
application/json
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string |
Yes | — | CA name |
subject_dn |
string |
Yes | — | X.509 distinguished name |
description |
string |
No | null |
Description |
key_size |
int |
No | CA_KEY_SIZE |
RSA key size |
valid_days |
int |
No | CA_CERT_DAYS |
Validity in days |
parent_ca_id |
int |
No | null |
Parent CA ID (creates an intermediate) |
path_length |
int |
No | null |
BasicConstraints path length |
allow_leaf_certs |
bool |
No | null |
Override leaf cert policy (auto-managed if null) |
Response 201: CA detail (includes private key, is_root, allow_leaf_certs). Errors: 400, 403.
GET /cas/¶
List all CAs visible to the current user.
- Auth required: Any active user
- Superusers see all. Others see only their organization's CAs.
Response 200: Array of CA objects (without private keys).
GET /cas/{ca_id}¶
Get a CA by ID.
- Auth required: Read access to the CA
Response 200: CA object (without private key). Errors: 403, 404.
GET /cas/{ca_id}/private-key¶
Get a CA including its private key.
- Auth required:
export_private_keycapability, Admin (same org), or Superuser - Audit-logged
Response 200: CA detail (includes private key). Errors: 403, 404.
GET /cas/{ca_id}/chain¶
Get the certificate chain from a CA up to the root.
- Auth required: Read access to the CA
Response 200: Array of CA objects ordered from the specified CA to the root. Errors: 403, 404.
GET /cas/{ca_id}/children¶
Get direct child CAs of the specified CA.
- Auth required: Read access to the CA
Response 200: Array of child CA objects. Errors: 403, 404.
DELETE /cas/{ca_id}¶
Delete a CA and all its certificates. CAs with child CAs cannot be deleted.
- Auth required:
delete_cacapability, Admin (same org), or Superuser - Audit-logged
Response 204. Errors: 403, 404, 409 (has child CAs).
Certificates¶
POST /certificates/?ca_id={ca_id}¶
Issue a new certificate under the specified CA.
- Auth required:
create_certcapability on the CA, Admin (same org), or Superuser - Content-Type:
application/json
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
common_name |
string |
Yes | — | Common Name |
subject_dn |
string |
Yes | — | Distinguished name |
certificate_type |
string |
Yes | — | server, client, or ca |
key_size |
int |
No | CERT_KEY_SIZE |
RSA key size |
valid_days |
int |
No | CERT_DAYS |
Validity in days |
include_private_key |
bool |
No | true |
Generate a private key |
san_dns_names |
string[] |
No | — | DNS SAN entries (server/dual-purpose only) |
san_ip_addresses |
string[] |
No | — | IP SAN entries (server/dual-purpose only) |
san_email_addresses |
string[] |
No | — | Email SAN entries (client/dual-purpose only) |
For server and dual_purpose certificates, if no DNS SANs are supplied the Common Name is auto-added as a DNS SAN. For client certificates, if no email SANs are supplied and the Common Name parses as an email address, it is auto-added. SAN type restrictions are enforced: servers reject email SANs, clients reject DNS and IP SANs.
Response 201: Certificate detail (includes private key if generated). Errors: 400 (includes when CA has allow_leaf_certs=false, or when SAN types violate the certificate-type restrictions), 403, 404.
POST /certificates/sign-csr¶
Sign an externally-generated Certificate Signing Request. The submitted CSR's subject, SANs, and public key are used as defaults; any explicit fields in the request body override them. The CSR's signature is verified before signing.
- Auth required:
create_certcapability on the resolved CA, Admin (same org), or Superuser - Content-Type:
application/json - Audit-logged
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
csr |
string |
Yes | — | PEM-encoded CSR |
ca_id |
int |
Conditional | — | Issuing CA by ID (required if ca_name omitted) |
ca_name |
string |
Conditional | — | Issuing CA by name, scoped to the caller's org (or globally for superusers) |
certificate_type |
string |
Yes | — | server, client, or dual_purpose |
valid_days |
int |
No | CERT_DAYS |
Validity in days |
common_name |
string |
No | From CSR | Override the Common Name |
subject_dn |
string |
No | From CSR | Override the full DN |
san_dns_names |
string[] |
No | From CSR | Override DNS SANs |
san_ip_addresses |
string[] |
No | From CSR | Override IP SANs |
san_email_addresses |
string[] |
No | From CSR | Override email SANs |
Exactly one of ca_id or ca_name must be provided. The certificate service never returns a private key for this endpoint — the client retains the key it generated alongside the CSR.
Response 201: Certificate object (no private key). Errors: 400 (malformed CSR, invalid signature, SAN-type violation, or CA has allow_leaf_certs=false), 403, 404 (CA not found).
GET /certificates/¶
List certificates visible to the current user.
- Auth required: Any active user
| Parameter | Type | Description |
|---|---|---|
ca_id |
int |
Filter by issuing CA |
Response 200: Array of certificate objects (without private keys).
GET /certificates/{cert_id}¶
Get a certificate by ID.
- Auth required: Read access to the certificate
Response 200: Certificate object (without private key). Errors: 403, 404.
GET /certificates/{cert_id}/private-key¶
Get a certificate including its private key.
- Auth required:
export_private_keycapability, Admin (same org), or Superuser - Audit-logged
Response 200: Certificate detail (includes private key). Errors: 403, 404.
POST /certificates/{cert_id}/revoke¶
Revoke a certificate.
- Auth required:
revoke_certcapability, Admin (same org), or Superuser - Content-Type:
application/json - Audit-logged
| Field | Type | Required | Description |
|---|---|---|---|
reason |
string |
No | Revocation reason |
Response 200: Updated certificate object (status revoked). Errors: 403, 404, 409 (already revoked).
Export¶
All export endpoints return PEM files with Content-Disposition: attachment headers.
GET /export/ca/{ca_id}/certificate¶
Download a CA certificate as PEM.
- Auth required: Read access to the CA
GET /export/ca/{ca_id}/private-key¶
Download a CA private key as PEM.
- Auth required:
export_private_key - Audit-logged
GET /export/certificate/{cert_id}¶
Download a certificate as PEM.
- Auth required: Read access to the certificate
GET /export/certificate/{cert_id}/private-key¶
Download a certificate private key as PEM.
- Auth required:
export_private_key - Audit-logged
- Returns
404if no private key exists.
GET /export/certificate/{cert_id}/chain¶
Download a certificate with its full chain as PEM.
- Auth required: Read access to the certificate
Audit Logs¶
GET /audit-logs/¶
Query audit logs.
- Auth required: Admin (org-scoped) or Superuser (all)
| Parameter | Type | Default | Description |
|---|---|---|---|
action |
AuditAction |
— | Filter by action type |
user_id |
int |
— | Filter by acting user |
resource_type |
string |
— | Filter by resource type |
resource_id |
int |
— | Filter by resource ID |
since |
datetime |
— | Start of time range |
until |
datetime |
— | End of time range |
skip |
int |
0 |
Offset |
limit |
int |
100 |
Max results (1–1000) |
Response 200: Array of audit log objects. Errors: 403.
Public PKI Endpoints¶
These endpoints are mounted at the application root (not under /api/v1/) and require no authentication. The {slug} format is {name-slug}-{id} (e.g. my-root-ca-3). Both the name prefix and ID are validated.
GET /crl/{slug}¶
Download the CRL for a CA in DER format.
- Auth required: None
- Content-Type:
application/pkix-crl
GET /crl/{slug}.pem¶
Download the CRL for a CA in PEM format.
- Auth required: None
- Content-Type:
application/x-pem-file
GET /ca/{slug}.crt¶
Download a CA certificate in DER format.
- Auth required: None
- Content-Type:
application/pkix-cert
GET /ca/{slug}.pem¶
Download a CA certificate in PEM format.
- Auth required: None
- Content-Type:
application/x-pem-file