Skip to content
Talk to our solutions team

HTTP API

Single listener, default :7999. All routes require mTLS except health and the PKI endpoints.

Conventions:

  • Reads on the legacy secrets/certificates surface use PATCH with a request body (a deliberate, documented convention — the body carries scope, which GET cannot).
  • Errors return {code, message, data}. Policy denials are 400 with code vault-access-denied (not 403).
  • Tenant scope fields in bodies: datacenter, cluster, customer, product, environment, tenant. For tenant-scoped secret operations the server ignores body scope in favor of the propagated caller CPET (see Overview).
MethodPathPurpose
GET/healthLiveness
GET/readyReadiness — 503 while the storage backend is unreachable
GET/versionBuild info
MethodPathPurpose
POST/secret/:nameSave. Fields: secret, scope, startdate, enddate, indefinite
PATCH/secret/:nameRead → {startdate, enddate, secret}
PATCH/checksecret/:nameExists → {secretexists}, 404 if not
DELETE/secret/:nameDelete — requires the record id

A secret key can have at most 3 active records at once (error vault-max-active-certificate-limit-reached); writing a new record supersedes the oldest.

MethodPathPurpose
POST/certificate/generate/internalIssue an internal cert (RSA-4096) → {certificateserial}
POST / PATCH / DELETE/certificate/internalSave / read / revoke internal certs
POST / PATCH / DELETE/certificate/externalSame for externally-issued certs
GET/certificate/authmTLS → JWT exchange → {token}
GET/certificate/internal/issuerCA certificate PEM → {issuer}
PATCH/certs/{internal,external,internaljwt,externaljwt}/listList by type; days filter supports rotation dashboards
POST/jwt/internal/generate[/:identifier]Generate JWT signing material (per-realm via identifiername query param, default realm)
PATCH/jwt/internal[/:identifier]Read JWT signing material
POST / PATCH/jwt/external/:identifierExternally-supplied JWT material

The private key half of JWT material is returned only to the owning service (enforced by field-level access rules).

Used by vault-agent on each node:

MethodPathPurpose
POST/agent/join{secret, attributes, csr?} — consume a single-use node token → {node_spiffe_id, node_cert_chain_pem, node_key_pem, trust_bundle_pem, refresh_token, node_cert_lifetime_ns}. Node certs live 24h. Token errors map to 401 / 409 / 410 / 403
POST/agent/renewRenew the node cert; the refresh token rotates on every renewal
POST/agent/decommissionRemove the node

Called by vault-agent on behalf of attested local workloads. Scope is derived from the workload’s registered identity, never from the request:

MethodPathPurpose
POST/workload/identityResolve the caller’s SPIFFE identity
POST/workload/jwtMint a workload JWT — audience required; token carries rcv_aud (receiver audiences) and scope claims
POST/workload/secret/{get,write,delete}Secret ops; names use the vault: / vaultcluster: / vaultkey: prefixes; plain: is rejected
/workload/cert/*Certificate operations proxied to the cert machinery (10 routes)

Requires an operator certificate. Roles are encoded in the certificate SAN as spiffe://<domain>/operator/<role>: operator-root (super-role), identity, tokens, audit, backend, signer, policy, readonly (GETs only). Role enforcement is opt-in via middleware.operator.role_enforcement.enabled (default off, logs a warning).

MethodPathPurpose
POST / GET / DELETE/operator/node-token[/​:id]Issue / list / revoke single-use node join tokens
POST / GET / DELETE/operator/identity[/*spiffe]Register / list / revoke workload identities (SPIFFE ID + attestation selectors + tenant scope)
POST/operator/audit/{query,verify}Query and verify the audit trail (proxied to the audit service)
POST/operator/root/rotate-plan|rotate-execute/:idRoot CA rotation ceremony — scaffolded, returns 501
POST/operator/backend/migrateBackend migration — 501
POST/operator/signer/testSigner self-test — 501
POST/operator/policy/lintPolicy lint — 501
MethodPathPurpose
GET/pki/crlDER CRL (application/pkix-crl), Cache-Control: max-age=300, 24h validity
POST/pki/ocspRFC 6960 OCSP response, 1h validity, single-serial

Both return 503 with vault-revocation-not-configured when the revocation emitter isn’t wired. The revocation list is persisted in the storage backend and survives restarts.