{
  "id": "CON-024",
  "slug": "con-024",
  "title": "The opt-in URL must serve a certificate that verifies",
  "statement": "The opt-in URL must present a valid, verifiable TLS certificate — not expired, self-signed, or issued for another hostname.",
  "rationale": "A crawler that hits a certificate error stops there and reports the opt-in as unverifiable, so the rejection reads as \"we could not see your consent surface\" rather than as a certificate problem. Brands lose days to that translation, because the page loads perfectly in a browser that has been clicking through the warning for months.",
  "layer": "CONSENT_FLOW",
  "layerSlug": "consent-flow",
  "object": "opt-in URL TLS chain",
  "severity": "BLOCKING",
  "detectability": [
    "CRAWL"
  ],
  "failureClass": "TERMINAL_WEBSITE",
  "authorities": [
    "AWS",
    "Bandwidth"
  ],
  "codes": [
    {
      "provider": "Bandwidth/DCA",
      "code": "2103",
      "remediable": true
    }
  ],
  "applicabilityText": "Applies to every 10DLC registration.",
  "universal": true,
  "remediation": "Renew or reissue the certificate for the exact hostname in the submitted URL, including the www or bare-domain form you actually registered. Test it from outside your network — an internal proxy or a pinned certificate hides the failure from everyone inside. Done when an external SSL checker reports a complete, valid chain.",
  "example": "opt_in_url: https://acmecoffee.com/checkout — certificate valid for acmecoffee.com, chain complete.",
  "pitfalls": [
    "A certificate valid for acmecoffee.com and not for www.acmecoffee.com fails whenever the submitted URL uses the other form, which is the most common version of this.",
    "An expired intermediate still lets most browsers through and fails a strict crawler — the browser test is not the test that matters."
  ],
  "notes": "CRAWLER GAP, now half closed. RuleInput carries per-URL fetch outcomes (`urlFetches`), which WEB-073 and POL-015 read deterministically for the brand and policy hosts. This rule covers the opt-in URL, which is often a third address the crawler is not yet asked to fetch, so it stays judged and returns UNCLEAR until a `urlFetches` entry exists for that URL. Recorded rather than omitted: the obligation is real and the remaining gap is the fetch layer's.",
  "phase": "approval",
  "automated": true,
  "url": "https://ekas.io/rules/10dlc/consent-flow/con-024/",
  "markdown": "https://ekas.io/rules/10dlc/consent-flow/con-024.md",
  "registry": "https://ekas.io/rules/10dlc/",
  "updated": "2026-07-25",
  "licence": "CC BY 4.0 — https://creativecommons.org/licenses/by/4.0/"
}
