PKI Certificates
Run an internal CA, issue certificates, manage CSRs and trust anchors. For ACME and public cert issuance, see ACME. For the conceptual model, see PKI.
Base path: /v1/pki/
Create an internal CA
Start with a root:
| curl -X POST https://scaivault.scailabs.ai/v1/pki/ca \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "acme-root",
"common_name": "Acme Root CA",
"ca_type": "root",
"key_type": "ec",
"key_size": 256,
"validity_days": 3650
}'
|
1
2
3
4
5
6
7
8
9
10
11
12 | resp = httpx.post(
"https://scaivault.scailabs.ai/v1/pki/ca",
headers={"Authorization": f"Bearer {os.environ['SCAIVAULT_TOKEN']}"},
json={
"name": "acme-root",
"common_name": "Acme Root CA",
"ca_type": "root",
"key_type": "ec",
"key_size": 256,
"validity_days": 3650,
},
)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | const resp = await fetch("https://scaivault.scailabs.ai/v1/pki/ca", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SCAIVAULT_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "acme-root",
common_name: "Acme Root CA",
ca_type: "root",
key_type: "ec",
key_size: 256,
validity_days: 3650,
}),
});
|
Response:
1
2
3
4
5
6
7
8
9
10
11
12
13 | {
"id": "ca_root_abc",
"name": "acme-root",
"common_name": "Acme Root CA",
"ca_type": "root",
"key_type": "ec",
"key_size": 256,
"valid_from": "2026-04-23T14:00:00Z",
"valid_until": "2036-04-20T14:00:00Z",
"is_active": true,
"certificates_issued": 0,
"created_at": "2026-04-23T14:00:00Z"
}
|
Then an intermediate:
1
2
3
4
5
6
7
8
9
10
11
12 | curl -X POST https://scaivault.scailabs.ai/v1/pki/ca \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "acme-mtls-intermediate",
"common_name": "Acme mTLS Intermediate",
"ca_type": "intermediate",
"parent_ca_id": "ca_root_abc",
"key_type": "ec",
"key_size": 256,
"validity_days": 1825
}'
|
Fields
| Field |
Required |
Description |
name |
Yes |
Tenant-unique |
common_name |
Yes |
Subject CN |
ca_type |
Yes |
root or intermediate |
parent_ca_id |
For intermediates |
Parent CA ID |
key_type |
No |
rsa (default) or ec |
key_size |
No |
2048/4096 for RSA; 256/384 for EC |
validity_days |
No |
Default 3650 for roots, 1825 for intermediates |
Get CA certificate in PEM
| curl -H "Authorization: Bearer $TOKEN" \
https://scaivault.scailabs.ai/v1/pki/ca/ca_root_abc/certificate
|
Response:
| {
"certificate_pem": "-----BEGIN CERTIFICATE-----\n..."
}
|
Distribute this to your clients as a trust root.
Create a PKI role
A role constrains issuance: what domains, what key types, what TTL.
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | curl -X POST https://scaivault.scailabs.ai/v1/pki/roles \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "svc-mtls",
"ca_id": "ca_intermediate_mtls",
"allowed_domains": ["*.svc.cluster.local", "*.internal"],
"allow_subdomains": true,
"allow_ip_sans": true,
"max_ttl": "720h",
"key_type": "ec",
"key_bits": 256,
"require_cn": true
}'
|
Role fields
| Field |
Description |
ca_id |
Which CA signs certs for this role |
allowed_domains |
Allow-list of domain patterns |
allow_subdomains |
Whether *.allowed-domain.com is OK |
allow_ip_sans |
Allow IP SANs |
max_ttl |
Cap on requested TTL |
key_type, key_bits |
Required key type for issued certs |
require_cn |
Reject CSRs without a CN |
Issue a certificate
| curl -X POST https://scaivault.scailabs.ai/v1/pki/issue/svc-mtls \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"common_name": "billing.svc.cluster.local",
"alt_names": ["billing-api.svc.cluster.local"],
"ip_sans": ["10.0.5.100"],
"ttl": "168h"
}'
|
Response (201 Created):
| {
"certificate": "-----BEGIN CERTIFICATE-----\n...",
"private_key": "-----BEGIN EC PRIVATE KEY-----\n...",
"ca_chain": ["-----BEGIN CERTIFICATE-----\n..."],
"serial_number": "1A:2B:3C:4D:5E:6F:...",
"not_after": "2026-04-30T14:00:00Z"
}
|
The private key is returned exactly once. Save it securely — ScaiVault doesn't keep a plaintext copy by default. If the role is configured with store_private_keys: true, you can retrieve it later via POST /v1/pki/certificates/{id}/private-key.
Sign a CSR
When a peer generates their own key, they send you a CSR. Sign it directly:
| curl -X POST https://scaivault.scailabs.ai/v1/pki/sign/svc-mtls \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"csr_pem": "-----BEGIN CERTIFICATE REQUEST-----\n...",
"ttl": "168h"
}'
|
Response contains certificate, ca_chain, serial_number, not_after — no private key.
CSR workflow (with approval)
For CSRs that need human review before signing:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | # 1. Import the CSR
curl -X POST https://scaivault.scailabs.ai/v1/pki/csr/import \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"csr_pem": "-----BEGIN CERTIFICATE REQUEST-----\n..."}'
# → {"id": "csr_abc", "status": "pending", ...}
# 2. Review what was requested
curl -H "Authorization: Bearer $TOKEN" \
https://scaivault.scailabs.ai/v1/pki/csr/csr_abc
# 3. Approve or reject
curl -X POST https://scaivault.scailabs.ai/v1/pki/csr/csr_abc/approve \
-H "Authorization: Bearer $TOKEN"
# 4. Sign against a CA
curl -X POST https://scaivault.scailabs.ai/v1/pki/csr/csr_abc/sign \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"ca_id": "ca_intermediate_mtls", "validity_days": 90}'
|
Alternatively, generate the CSR in ScaiVault itself (private key stays in the vault):
| curl -X POST https://scaivault.scailabs.ai/v1/pki/csr \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"subject": {"common_name": "billing.svc.cluster.local"},
"san_dns": ["billing-api.svc.cluster.local"],
"key_type": "ec",
"key_size": 256
}'
|
Reject a CSR with reason:
| curl -X POST https://scaivault.scailabs.ai/v1/pki/csr/csr_abc/reject \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"reason": "CN not in allowed scope"}'
|
List issued certificates
| curl -H "Authorization: Bearer $TOKEN" \
"https://scaivault.scailabs.ai/v1/pki/certificates?ca_id=ca_intermediate_mtls&expiring_within=30d"
|
Response:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | {
"data": [
{
"id": "cert_abc",
"common_name": "billing.svc.cluster.local",
"issuer_ca_id": "ca_intermediate_mtls",
"serial_number": "1A:2B:...",
"valid_from": "2026-04-23T14:00:00Z",
"valid_until": "2026-04-30T14:00:00Z",
"is_revoked": false
}
],
"cursor": null,
"has_more": false
}
|
Validate a certificate chain
| curl -X POST https://scaivault.scailabs.ai/v1/pki/certificates/validate \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"certificate_pem": "-----BEGIN CERTIFICATE-----\n...",
"chain_pem": "-----BEGIN CERTIFICATE-----\n...",
"check_revocation": true
}'
|
Response:
| {
"valid": true,
"chain_valid": true,
"not_expired": true,
"not_revoked": true,
"trusted_anchor": "trust_anchor_xyz"
}
|
Revoke a certificate
| curl -X POST https://scaivault.scailabs.ai/v1/pki/revoke \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"serial_number": "1A:2B:3C:...", "reason": "key_compromise"}'
|
Reasons (RFC 5280): unspecified, key_compromise, ca_compromise, affiliation_changed, superseded, cessation_of_operation, certificate_hold, privilege_withdrawn, aa_compromise.
CRL
Get the current CRL for a CA:
| curl https://scaivault.scailabs.ai/v1/pki/ca/ca_intermediate_mtls/crl
|
Returns application/pkix-crl (DER) or PEM depending on Accept header.
Force immediate regeneration (default is hourly):
| curl -X POST https://scaivault.scailabs.ai/v1/pki/ca/ca_intermediate_mtls/crl \
-H "Authorization: Bearer $TOKEN"
|
Trust anchors
Register an external CA certificate you want to trust:
| curl -X POST https://scaivault.scailabs.ai/v1/pki/trust-anchors \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "vendor-xyz-root",
"certificate_pem": "-----BEGIN CERTIFICATE-----\n..."
}'
|
List and delete:
| curl -H "Authorization: Bearer $TOKEN" https://scaivault.scailabs.ai/v1/pki/trust-anchors
curl -X DELETE -H "Authorization: Bearer $TOKEN" https://scaivault.scailabs.ai/v1/pki/trust-anchors/trust_anchor_xyz
|
Trust anchors are consulted by POST /v1/pki/certificates/validate.
Renew a certificate
| curl -X POST https://scaivault.scailabs.ai/v1/pki/certificates/cert_abc/renew \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"ttl": "168h"}'
|
Issues a new cert with the same subject and SANs, returns the new PEM + key. The old cert stays valid until its not_after.
Import an existing certificate
For certificates issued elsewhere that you want to track in ScaiVault:
| curl -X POST https://scaivault.scailabs.ai/v1/pki/certificates/import \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"certificate_pem": "-----BEGIN CERTIFICATE-----\n...",
"private_key_pem": "-----BEGIN PRIVATE KEY-----\n...",
"chain_pem": "-----BEGIN CERTIFICATE-----\n..."
}'
|
What's next
- ACME — public certificates from Let's Encrypt et al.
- PKI — conceptual model.
- PKI Reference — all endpoints.