Référence API
API REST OpenAPI 3.1, idempotente, versionnée, prête pour l'automatisation.
L'API REST de PKIFactor est documentée au format OpenAPI 3.1 et exposée sur /api/v1/. Elle est :
- Idempotente sur les opérations critiques (émission, révocation) via en-tête
Idempotency-Key - Versionnée par préfixe d'URL (
/api/v1/,/api/v2/) - Paginée par curseur opaque (
?cursor=...&limit=50) ou par offset (?offset=0&limit=50) - Filtrable par DSL léger (
?filter=status:issued,cn:*.acme.com)
Toute opération d'émission ou de révocation peut être rejouée en toute sécurité avec le même en-tête Idempotency-Key. PKIFactor retournera la même réponse pendant 24h.
3.1. Authentification
PKIFactor supporte trois mécanismes.
3.1.1. API Keys (recommandé pour M2M)
# Création via UI : Settings → API Keys → Generate
# Format : pkf_<32_chars_alphanum>
curl https://pki.exemple.com/api/v1/certificates \
-H "Authorization: Bearer pkf_a1b2c3d4e5f6..."
Les clés API sont scopées par organisation et peuvent être restreintes à un sous-ensemble de permissions (certificates:read, certificates:issue, templates:write, etc.).
3.1.2. mTLS (recommandé pour CI/CD haute sécurité)
# Le client présente un certificat émis par PKIFactor lui-même
curl https://pki.exemple.com/api/v1/certificates \
--cert client.pem \
--key client.key \
--cacert ca-chain.pem
3.1.3. OAuth 2.0 / OIDC (recommandé pour les utilisateurs)
# 1. Obtention d'un token via votre IdP (Keycloak, Okta, Azure AD, Google)
TOKEN=$(curl -X POST https://idp.exemple.com/realms/acme/protocol/openid-connect/token \
-d "grant_type=password" \
-d "client_id=pkifactor" \
-d "username=alice@acme.com" \
-d "password=*****" \
| jq -r .access_token)
# 2. Utilisation du token Bearer
curl https://pki.exemple.com/api/v1/certificates \
-H "Authorization: Bearer $TOKEN"
3.2. Endpoints principaux
3.2.1. Certificats
| Méthode | Endpoint | Description |
|---|---|---|
POST | /api/v1/certificates | Émettre un certificat |
GET | /api/v1/certificates | Lister (filtres + pagination) |
GET | /api/v1/certificates/{id} | Détail |
GET | `/api/v1/certificates/{id}/download?fmt=pem | der |
POST | /api/v1/certificates/{id}/renew | Renouveler |
POST | /api/v1/certificates/{id}/revoke | Révoquer |
# Lister les certificats expirant dans les 30 jours pour une org donnée
curl "https://pki.exemple.com/api/v1/certificates?filter=org:1,status:issued,expires_lt:30d&limit=100" \
-H "Authorization: Bearer $TOKEN"
{
"data": [
{
"id": 1024,
"serial": "3F:A1:B2:C3:...",
"subject": { "common_name": "api.acme.com" },
"sans": ["api.acme.com", "api-v2.acme.com"],
"status": "issued",
"not_before": "2026-01-15T08:00:00Z",
"not_after": "2026-04-15T08:00:00Z",
"pki": { "id": 1, "name": "Vault PKI", "type": "vault" },
"template": { "id": 5, "code": "web-tls" }
}
],
"pagination": {
"next_cursor": "eyJpZCI6MTAyNH0=",
"total": 147
}
}
3.2.2. Templates CLM
# Création d'un template avec ZetaCA post-quantum
curl -X POST https://pki.exemple.com/api/v1/clm/templates \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "PQ Server (ML-DSA-87)",
"code": "pq-server",
"organization_id": 1,
"pki_id": 3,
"pki_role": "pq-srv",
"key_algorithm": "ML_DSA_87",
"validity_days": 365,
"key_usage": ["digital_signature"],
"extended_key_usage": ["server_auth"],
"requires_approval": true,
"approver_role": "pki-approver"
}'
3.2.3. Audit logs
curl "https://pki.exemple.com/api/v1/audit?event=certificate.issued&since=2026-04-01" \
-H "Authorization: Bearer $TOKEN"
{
"data": [
{
"id": "01J...",
"ts": "2026-04-18T10:02:34.123Z",
"actor": { "type": "user", "id": 12, "email": "alice@acme.com" },
"event": "certificate.issued",
"resource": { "type": "certificate", "id": 1024, "serial": "3F:A1:B2:..." },
"metadata": {
"template_code": "web-tls",
"pki_id": 1,
"ip": "203.0.113.42"
},
"signature": "ed25519:..."
}
]
}
/api/v1/certificatesIssue a new certificate
Submit a template code + subject; returns a signed X.509 certificate.
template_codestringrequiredsubjectobjectrequiredsansarraykey_formatenumdelivery_formatenum3.3. Pagination, filtres, erreurs
Pagination
GET /api/v1/certificates?cursor=eyJpZCI6MTAwMH0=&limit=50
Le curseur est opaque et stable. Préférer le curseur à l'offset pour les grands volumes.
Filtres (DSL)
?filter=status:issued
?filter=status:issued,cn:*.acme.com
?filter=expires_lt:30d
?filter=tag:prod,tag:critical
Opérateurs supportés : : (égalité), _lt (inférieur), _gt (supérieur), _in (liste), * (wildcard).
Erreurs
Toutes les erreurs suivent le format RFC 7807 (Problem Details) :
{
"type": "https://docs.zetacert.com/errors/template-not-found",
"title": "Template not found",
"status": 404,
"detail": "Template with code 'web-tls' does not exist in organization 'ACME'",
"instance": "/api/v1/certificates",
"trace_id": "01J5..."
}
| Code | Signification |
|---|---|
400 | Requête malformée |
401 | Authentification manquante ou invalide |
402 | Fonctionnalité non couverte par la licence |
403 | Accès refusé (permissions ou organisation) |
404 | Ressource introuvable |
409 | Conflit (idempotency, contrainte unique) |
422 | Validation échouée |
429 | Rate limit dépassé |
5xx | Erreur serveur |
3.4. Exemples SDK
Python
# pip install zetacert-sdk
from zetacert import PKIFactorClient
client = PKIFactorClient(
base_url="https://pki.exemple.com",
api_key="pkf_a1b2c3d4e5f6...",
)
cert = client.certificates.issue(
template_code="web-tls",
common_name="www.acme.com",
sans=["www.acme.com", "acme.com"],
)
print(f"Certificat émis : {cert.serial}")
print(f"Expire le : {cert.not_after}")
# Téléchargement au format PEM
with open("www.acme.com.pem", "wb") as f:
f.write(client.certificates.download(cert.id, fmt="pem"))
Go
// go get github.com/zetacert/pkifactor-go
package main
import (
"context"
"fmt"
"log"
"github.com/zetacert/pkifactor-go"
)
func main() {
client := pkifactor.NewClient(
pkifactor.WithBaseURL("https://pki.exemple.com"),
pkifactor.WithAPIKey("pkf_a1b2c3d4e5f6..."),
)
cert, err := client.Certificates.Issue(context.Background(), &pkifactor.IssueRequest{
TemplateCode: "web-tls",
CommonName: "www.acme.com",
SANs: []string{"www.acme.com", "acme.com"},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Certificat émis : %s\n", cert.Serial)
}
TypeScript
// npm install @zetacert/pkifactor-sdk
import { PKIFactorClient } from "@zetacert/pkifactor-sdk";
const client = new PKIFactorClient({
baseUrl: "https://pki.exemple.com",
apiKey: process.env.PKIFACTOR_API_KEY!,
});
const cert = await client.certificates.issue({
templateCode: "web-tls",
commonName: "www.acme.com",
sans: ["www.acme.com", "acme.com"],
});
console.log(`Certificat émis : ${cert.serial}`);
console.log(`Expire le : ${cert.notAfter}`);