---
title: "Get the most recent currently-valid certificate for a domain"
method: GET
path: "/mcp-api/get_latest_cert/{domain}"
tags: ["mcp"]
---

# Get the most recent currently-valid certificate for a domain

`GET /mcp-api/get_latest_cert/{domain}`

## Request

- Base URL: `https://api.ctindex.io`
- URL: `https://api.ctindex.io/mcp-api/get_latest_cert/{domain}`
- Auth: one of:
  - API key in header `X-API-Key`
  - HTTP bearer

Returns the latest certificate associated with the given domain name, wrapped in `{cert: <detail>}`.

**Validity semantics (task #772):** currently-valid certificates (`not_after` in the future) are always preferred. **Precert semantics (task #776):** by default (`include_precerts=false`) a final leaf always beats ANY precertificate — a precert is only returned when the index holds no final cert for the domain at all. Pass `include_precerts=true` to let precerts compete on equal terms (newest `not_before` wins, with the final leaf still preferred over its own precert twin on ties). Only when the index holds NO currently-valid certificate for the domain is an expired one returned — and then the response carries an explicit top-level `warning` field noting that the returned cert's `not_after` is in the past (the live cert may simply not have been ingested yet). Never render this endpoint's output in alerting UX without honoring that `warning`.

**`raw_pem` is always null here (by design):** raw certificate bytes live in cold storage and are deliberately excluded from domain-shaped tools to keep LLM context small (task #66). To get the PEM body, take `cert.sha256` from this response and call `get_certificate` with `include_raw=true`.

**Self-healing for cold domains (task #113 / GH #266):** unlike the v1 REST route, this tool does NOT raise HTTP 404 when no cert is found — it returns `{cert: null, backfill_status: ...}` so the LLM can decide whether to retry. When `backfill_status == 'in_progress'`, wait `retry_after_seconds` seconds and call this tool again. Use `eta_basis` for the stopping rule: `'live'` = measured ETA; `'jobs_coarse'` = coarse estimate from job-completion rate; `'unknown'` = no projection — `terminal_at_latest` (ISO-8601 UTC) and `seconds_until_terminal` give the concrete stop point. `in_progress_over_budget` (no `retry_after_seconds`) means the projection exceeds the interactive budget — do NOT poll in a loop. Any other status means the result will not change on retry.

## Path parameters

- `domain` string, required

## Query parameters

- `include_enrichment` boolean: When true, attach RDAP + DNS + ASN/hosting context for the domain under ``cert.enrichment`` (task #501).
- `include_signals` boolean: When true, attach derived attribution signals under ``cert.enrichment.signals``: issuer_diversity_score, wildcard_breadth, third_party_vendors, and weak_crypto_reasons. Implies enrichment computation. Requires a paid plan (Pro or higher); the free tier receives HTTP 403 ``tier_not_entitled``.
- `include_precerts` boolean: When false (default), a final (leaf) certificate is always preferred over ANY precertificate; a precert is returned only when the index holds no final cert for the domain. When true, precerts compete on equal terms (newest issuance wins even if only its precert has been observed).
- `wait_for_backfill` boolean: When false, cold domains return a 'not_indexed' sentinel immediately without triggering a background backfill. Default true preserves the existing self-healing behaviour.

## Cookies

- `ctix_session` string, nullable

## Response `200`

Successful Response

- MCPLatestCertResponse: Response for ``GET /mcp-api/get_latest_cert/{domain}``. Unlike the v1 ``/v1/domains/{domain}/latest`` route — which raises HTTP 404 when no cert exists — the MCP mirror returns a structured body with ``cert: null`` plus the backfill sentinel so the LLM has enough information to retry (or stop) instead of seeing an opaque transport-level error. The custom ``model_dump`` below is the contract-preserving fix for a subtle FastAPI gotcha: ``response_model_exclude_none=True`` (used by every other MCP response model to keep happy-path bodies sentinel-free) would also strip ``cert`` when it's ``None`` on the cold-domain path, leaving the response as just ``{backfill_status, ...}`` with no ``cert`` key at all — which silently diverges from the FastMCP ``mcp_server.py`` mirror that always returns ``{"cert": <detail or null>, ...}``. We can't use the route-level ``response_model_exclude_none`` here, so we replicate its happy-path behavior manually by always keeping ``cert`` and dropping only the three sentinel keys when they're ``None``.
  - `backfill_status` string, nullable
  - `retry_after_seconds` integer, nullable
  - `hint` string, nullable
  - `progress` object, nullable
  - `typical_eta_seconds` integer, nullable
  - `eta_seconds` integer, nullable
  - `eta_basis` string, nullable
  - `index_freshness` object, nullable
  - `cert` CertificateDetail
    - `sha256` string, required
    - `subject_cn` string, nullable
    - `subject_o` string, nullable
    - `sans` string[]
    - `issuer_cn` string, nullable
    - `issuer_o` string, nullable
    - `not_before` string, date-time, nullable
    - `not_after` string, date-time, nullable
    - `is_wildcard` boolean
    - `is_precert` boolean: True when this row is a CT precertificate (poison-extension log entry), False for a final leaf certificate. Precert and final-leaf twins have different SHA-256 hashes and are returned as separate rows; present on every list and detail row. Exception (GH #262): get_expiring_certs collapses twin pairs to the final leaf by default.
    - `precert_sha256` string, nullable: SHA-256 fingerprint of the CT precertificate twin that was collapsed into this final-leaf row (get_expiring_certs default twin collapsing, GH #262). Omitted when no twin was collapsed.
    - `key_type` string, nullable
    - `key_size` integer, nullable
    - `signature_algorithm` string, nullable
    - `is_weak_crypto` boolean, nullable
    - `key_fields_source` string, nullable
    - `first_seen` string, date-time, nullable: When CertIndex wrote this certificate to its database (batch-write timestamp). Multiple unrelated certificates often share an identical first_seen within the same ingestion batch. It is not the CT-log append time and is never substituted for a missing logged_at. Compare with logged_at to measure detection latency.
    - `logged_at` string, date-time, nullable: Timestamp from the CT log entry itself (MerkleTreeLeaf TimestampedEntry.timestamp, RFC 6962 §3.4) — when the log committed this certificate, NOT when CertIndex ingested it. Null for rows indexed before this field shipped, for crt.sh-sourced rows (reconciled_from_crtsh=true), and for any path that could not supply it. A null value means the CT-log timestamp is unavailable for that row; it does not mean the certificate was observed at first_seen, and first_seen is never synthesized into logged_at. Compare with first_seen (CertIndex write time) to measure detection latency. See logged_at_coverage disclosure block.
    - `reconciled_from_crtsh` boolean, nullable
    - `last_seen` string, date-time, nullable
    - `match_class` string, nullable: Attribution class when match_class_filter was supplied on search: first_party, lookalike_candidate, third_party_integration, substring_collision, or shared_hosting. Null on unfiltered search.
    - `match_reason` string, nullable: Semicolon-separated rule=value audit string for match_class. Null on unfiltered search.
    - `from_cold_tier` boolean, nullable: True when this certificate was served exclusively from the Tier 1 cold ClickHouse index (historical data not present in the hot Neon index). Absent/null for certificates served from the primary index. Cold-sourced rows carry key_fields_source='unavailable' and no raw_pem.
    - `raw_pem` string, nullable
    - `log_id` string, nullable
    - `leaf_index` integer, nullable
    - `enrichment` DomainEnrichment: RDAP + DNS + ASN context for a single domain (task #501).
      - `domain` string, required
      - `registration` EnrichmentRegistration
        - `registrar` string, nullable
        - `created` string, nullable
        - `updated` string, nullable
        - `expires` string, nullable
        - `status` string[]
        - `nameservers` string[]
      - `dns` EnrichmentDNS
        - `a` string[]
        - `aaaa` string[]
        - `mx` EnrichmentMX[]
          - `preference` integer, required
          - `exchange` string, required
        - `ns` string[]
        - `txt` string[]
      - `asn` EnrichmentASN[]
        - `ip` string, required
        - `asn` integer, nullable
        - `prefix` string, nullable
        - `country` string, nullable
        - `network_owner` string, nullable
      - `signals` EnrichmentSignals: Derived attribution signals for a domain. Attached under ``enrichment.signals`` only when the caller passes ``include_signals=true`` AND holds a paid plan (Pro or higher — the free tier gets a 403 ``tier_not_entitled``). Signals are derived from the domain's currently-valid certificates and TXT inventory: issuer diversity, wildcard breadth, third-party vendor fingerprints, and explicit weak-crypto reasons.
        - `issuer_diversity_score` number
        - `distinct_issuers` integer
        - `issuers` string[]
        - `wildcard_breadth` integer
        - `wildcard_names` string[]
        - `third_party_vendors` ThirdPartyVendor[]
          - `vendor` string, required
          - `source` string, required
          - `evidence` string, nullable
        - `weak_crypto_reasons` string[]
        - `certs_considered` integer
        - `certs_capped` boolean
        - `partial` boolean
        - `errors` string, nullable
      - `fetched_at` string, nullable
      - `partial` boolean
      - `errors` object, nullable
      - `cached` boolean
    - `parsed_extensions` ParsedExtensions: Customer-facing extension view parsed from the raw cert bytes (GH #205) — EKU, SAN breakdown by type, basic constraints and the embedded SCT list, so consumers don't have to parse PEM themselves.
      - `extended_key_usage` string[], nullable
      - `san_types` object
      - `basic_constraints` object, nullable
      - `sct_list` object[]
      - `is_precert_poisoned` boolean
  - `warning` string, nullable

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/ctindex/apis/certindex-api.md) · [All operations](https://skmtc.dev/ctindex/apis/certindex-api/llms.txt) · [OpenAPI document](https://skmtc.dev/ctindex/apis/certindex-api/revisions/d5bc0ec85bc9?raw)
