SSL Certificate Chain API — /api/v1/security/ssl-chain
Full certificate chain with each link tagged `EndEntity | Intermediate | Root`. Same live TLS handshake as `/ssl-info`, minimal payload — chain plus any chain issues detected (missing intermediate, broken link, expired non-root, weak signature). Use this when you only need chain-of-trust data.
01 · OVERVIEW
What this endpoint returns
Chain-only slice of the full SSL/TLS report — same live handshake, just narrower. Each cert is typed and issue-detected.
- Method GET
- Path /api/v1/security/ssl-chain
- Stability STABLE
- Version v2026-07-18
- Summary Full certificate chain (leaf → intermediates → root) with type tags + chain-issue detection.
- Related Use `/security/ssl-info` for the full posture report (grade, HSTS, CAA, AI diagnosis). This endpoint is the chain-only slice.
02 · PARAMETERS
Request parameters
Pass as query-string parameters. Only `domain` is required.
- domain (required) — string Hostname or URL (scheme/path stripped).
- raw — boolean, default false Include each cert as PEM (`chain[].pem`). Adds ~4 KB per link.
03 · RESPONSE
Response schema
Wrapped in `{ success: true, data: {...} }`. The data payload has three top-level fields.
- data.found — boolean True when the handshake succeeded.
- data.domain — string Normalised hostname.
- data.chain — array<object> Ordered from position 0 (End-Entity leaf) up to Root CA. See per-cert breakdown below.
- data.chain_issues — array<object> Detected chain problems with `severity: critical | warning | info` and human-readable `message`. Examples: 'Server sent no intermediate certificate', 'Chain link broken at position 1', 'Intermediate certificate expires in 4 days'.
- chain[].position — integer 0 = leaf, increments up the chain to the root.
- chain[].type — string `EndEntity` (position 0), `Intermediate` (CA cert not self-signed), `Root` (self-signed CA cert).
- chain[].status — string `Valid` or `Invalid` (past valid_to).
- chain[].subject — object `{common_name, organization, organizational_unit, country, state, locality, email}` — decoded X.500 DN.
- chain[].issuer — object Same shape as `subject`.
- chain[].validity — object `{from, to, days, days_remaining, is_expired, age_days}`.
- chain[].serial_number — string Uppercase hex.
- chain[].fingerprint / fingerprint256 — string SHA-1 + SHA-256 colon-separated.
- chain[].public_key — object `{type, bits, curve, strength, label}`.
- chain[].signature_algorithm — object `{oid, name, hash, weak}` — weak flag = SHA-1/MD5-based sig.
- chain[].key_usage — array<string> Extended Key Usage OIDs.
- chain[].subject_alt_names — array<string> SANs (only meaningful on the leaf; usually empty on CA certs).
- chain[].is_ca / is_self_signed / is_wildcard — booleans Structural flags.
- chain[].pem — string (when raw=true) PEM-encoded cert body.
04 · EXAMPLE
Example response
Abridged shape for `?domain=github.com`.
{
"success": true,
"data": {
"found": true,
"domain": "github.com",
"chain": [
{
"position": 0,
"type": "EndEntity",
"status": "Valid",
"subject": { "common_name": "github.com" },
"issuer": { "common_name": "Sectigo Public Server Authentication CA DV E36", "organization": "Sectigo Limited" },
"validity": { "from": "2026-07-03T…", "to": "2026-10-01T…", "days_remaining": 75, "is_expired": false },
"serial_number": "72010E03F4A067FE4E796266430718F6",
"fingerprint256": "17:F8:FD:2E:…",
"public_key": { "type": "EC", "bits": 256, "curve": "prime256v1", "label": "ECDSA P-256", "strength": "strong" },
"signature_algorithm": { "name": "SHA256-RSA", "weak": false }
},
{
"position": 1,
"type": "Intermediate",
"status": "Valid",
"subject": { "common_name": "Sectigo Public Server Authentication CA DV E36" },
"issuer": { "common_name": "Sectigo Public Server Authentication Root E46" }
},
{
"position": 2,
"type": "Intermediate",
"status": "Valid",
"subject": { "common_name": "Sectigo Public Server Authentication Root E46" },
"issuer": { "common_name": "USERTrust ECC Certification Authority" }
},
{
"position": 3,
"type": "Root",
"status": "Valid",
"subject": { "common_name": "USERTrust ECC Certification Authority" },
"issuer": { "common_name": "USERTrust ECC Certification Authority" }
}
],
"chain_issues": []
}
}05 · CHAIN ISSUES
Detected chain problems
`data.chain_issues[]` is populated when any of the following are detected. Empty array = healthy chain.
- Server sent no intermediate certificate (warning) chain.length === 1. Browsers work around via AIA, but Java + curl + older Android will fail the handshake.
- Chain link broken at position N (warning) Cert N's `issuer.common_name` doesn't match cert N+1's `subject.common_name`. Usually means the chain is out of order or contains a stray cert.
- Certificate expires in K days (warning / critical) Any cert in the chain within 30 days of expiry. `< 7 days` = critical, `< 30 days` = warning. Applies to intermediates too — a fresh leaf can't save you if the intermediate expired yesterday.
- Certificate has expired (critical) `validity.is_expired: true`. Chain is broken regardless of leaf validity.
- Weak signature: NAME (critical / info) SHA-1 or MD5-based signature. Critical for leaf + intermediate certs; info-level for roots (self-signed, not verified by anyone else).
06 · CURL EXAMPLES
One-line invocations
curl -s 'https://api.domainscan.in/v1/security/ssl-chain?domain=github.com' \
-H 'Origin: https://domainscan.in' | jq '.data.chain | map(.type)'
# → ["EndEntity","Intermediate","Intermediate","Root"]issues=$(curl -s 'https://api.domainscan.in/v1/security/ssl-chain?domain=github.com' \
-H 'Origin: https://domainscan.in' | jq '.data.chain_issues | length')
if [ "$issues" -gt 0 ]; then
echo "⚠️ Chain has $issues issue(s) — inspect the endpoint output"
fi07 · ERRORS
Error codes
- 200 + data.found=false TLS handshake failed (host down, port 443 closed, TCP timeout). `data.chain` is `[]`.
- 400 VALIDATION_FAILED `domain` missing or malformed.
- 401 / 403 Auth or origin check failed — see /security/ssl-info error docs.
08 · BEST PRACTICES
Integration tips
- Use this when `/ssl-info` reports a valid leaf but browsers still complain. Most common cause is a missing intermediate — chain.length === 1 + `chain_issues` will name the exact problem.
- Watch intermediate expiry, not just the leaf. Intermediates rotate less often but do rotate — an intermediate expiring next week breaks every downstream cert even if the leaf is fresh.
- Prefer `/ssl-info` for anything but chain diagnosis. Same underlying handshake, but with grade + HSTS + CAA + AI. This endpoint exists for narrow chain-only integrations.