Upload Certificate
POST
/v1/certificates/self
Upload a PEM-encoded certificate plus its private key for use on custom-domain endpoints. Once uploaded, attach the cert to one or more endpoints via PUT /v1/endpoints/{id}/certificate.
The private key is encrypted at rest with the platform's certificate encryption key (set via SECURITY_CERT_ENCRYPTION_KEY on the api-server). It can never be retrieved through the API — to rotate, delete and re-upload.
Both PEM blocks must parse cleanly. The cert's public key must match the supplied private key (verified server-side). Intermediate CA chains can be appended to the cert_pem as additional -----BEGIN CERTIFICATE----- blocks; the platform serves the full chain in the TLS handshake.
Requires the custom_ssl entitlement on your plan.
Request body
Friendly label for the cert. Shown in the dashboard. Max 128 chars.
PEM-encoded leaf certificate, optionally followed by intermediate CA certs (concatenated, in order). Headers -----BEGIN CERTIFICATE----- required.
PEM-encoded private key matching the leaf cert. Supports RSA (PKCS#1, PKCS#8) and ECDSA (SEC1, PKCS#8). Encrypted keys (DEK-Info) not supported — decrypt locally before uploading.
Response (200 OK)
An acknowledgement object. To retrieve the stored certificate object, call GET /v1/certificates/self.
ID of the domain the certificate was attached to (and set as default for).
Human-readable confirmation.
Errors
Missing fields, malformed PEM, cert/key mismatch, expired cert (not_after in the past), or unsupported key algorithm.
Plan doesn't include the custom_ssl entitlement.
Cert with the same fingerprint already uploaded.
curl -X POST "https://api.ngris.com/v1/certificates/self" \
-H "X-API-KEY: <your_api_key>" \
-H "Content-Type: application/json" \
-d "$(jq -n \
--arg name "wildcard.example.com" \
--arg cert "$(cat fullchain.pem)" \
--arg key "$(cat privkey.pem)" \
'{name: $name, cert_pem: $cert, key_pem: $key}')"
from ngris import Ngris
client = Ngris(api_key="<your_api_key>")
with open("fullchain.pem") as f:
cert_pem = f.read()
with open("privkey.pem") as f:
key_pem = f.read()
resp = client.certificates.upload({
"name": "wildcard.example.com",
"cert_pem": cert_pem,
"key_pem": key_pem,
})
print(resp["message"])
{
"ok": true,
"domain_id": 42,
"message": "Certificate uploaded and set as default for this domain."
}