Skip to content
nsin docs

Upload a custom certificate

POST
/domains/{domain}/ssl/
curl --request POST \
--url https://api.nsin.ir/domains/example.com/ssl/ \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "certificate": "example", "private_key": "example", "hostnames": [ "example" ] }'

Installs your own certificate and private key for the domain. Include the full chain (leaf and intermediates) in certificate — a leaf-only upload makes clients fail chain verification.

hostnames selects which of the certificate’s SANs this upload should cover; use the eligible list from POST /domains/{domain}/ssl/parse to pick them. Omitted, it is the apex and its wildcard (example.com, *.example.com), so the certificate must carry both.

The upload is pinned: those hostnames serve it from now on, ahead of the certificate NSIN manages, and edge nodes pick it up within seconds. A certificate browsers would not trust is refused — a private CA (for example a Cloudflare Origin CA certificate), a self-signed one, or a chain missing its intermediate (code says which). Requires ssl.manage and a plan that includes custom certificates.

domain
required
string

The domain name (for example example.com) — not a numeric id.

Example
example.com
Media type application/json
object
certificate
required

PEM-encoded certificate chain. Include intermediates — a leaf-only bundle makes clients fail chain verification.

string
private_key
required

PEM-encoded private key matching the certificate.

string
hostnames

Which of the certificate’s SANs this upload should cover. Use the eligible list from POST /domains/{domain}/ssl/parse.

Array<string>
Example generated
{
"certificate": "example",
"private_key": "example",
"hostnames": [
"example"
]
}

Certificate installed.

Media type application/json
object
message
string
domain
string
hostnames
Array<string>
expires_at
string format: date-time
issued_at
string format: date-time
subject
string
sans
Array<string>
Example generated
{
"message": "example",
"domain": "example",
"hostnames": [
"example"
],
"expires_at": "2026-04-15T12:00:00Z",
"issued_at": "2026-04-15T12:00:00Z",
"subject": "example",
"sans": [
"example"
]
}

Missing fields, unparseable PEM, an encrypted private key, key/certificate mismatch, a hostname the certificate does not cover or that is not part of this domain — or a certificate browsers would not accept: code is cert_untrusted or cert_incomplete_chain (with issuer and the raw trust_error), or cert_expired / cert_not_yet_valid when the certificate is outside its validity dates (with expires_at / valid_from).

Media type application/json
object
error
required

Human-readable description of what went wrong.

string
code

cert_untrusted — the issuer is not a public CA, or the certificate is self-signed. cert_incomplete_chain — the intermediate certificate is missing; paste the full chain. cert_expired — the certificate’s notAfter has passed; renew it and upload the new one. cert_not_yet_valid — its notBefore is still in the future.

string
Allowed values: cert_untrusted cert_incomplete_chain cert_expired cert_not_yet_valid
issuer

The certificate’s issuer.

string
trust_error

The underlying verification error.

string
expires_at

With cert_expired: when the certificate expired.

string format: date-time
valid_from

With cert_not_yet_valid: when the certificate becomes valid.

string format: date-time
Examples
Example incompleteChain
{
"error": "This certificate is missing its intermediate certificate. …",
"code": "cert_incomplete_chain",
"issuer": "CN=R11,O=Let's Encrypt,C=US",
"trust_error": "x509: certificate signed by unknown authority"
}

Missing, malformed, revoked or expired API key. (A valid key whose account has been deactivated gets 403 account is inactive instead.)

Media type application/json

The single error shape used by every endpoint.

object
error
required

Human-readable description of what went wrong.

string
Examples
Example invalidKey
{
"error": "invalid API key"
}

The domain has no active plan.

Media type application/json

The single error shape used by every endpoint.

object
error
required

Human-readable description of what went wrong.

string
Example
{
"error": "read-only API key"
}

Read-only key, insufficient role, or the plan does not include custom certificates.

Media type application/json

The single error shape used by every endpoint.

object
error
required

Human-readable description of what went wrong.

string
Example
{
"error": "read-only API key"
}

No such domain, or it is not visible to this account. Domains you cannot access are reported as not found rather than forbidden.

Media type application/json

The single error shape used by every endpoint.

object
error
required

Human-readable description of what went wrong.

string
Example
{
"error": "read-only API key"
}

The key exceeded its request budget (300 requests per minute by default).

Media type application/json

The single error shape used by every endpoint.

object
error
required

Human-readable description of what went wrong.

string
Examples
Example limited
{
"error": "rate limit exceeded"
}