{
  "id": "CMP-132",
  "slug": "cmp-132",
  "title": "Where the platform has no upload field, proof must be a URL in the flow text",
  "statement": "On a platform offering no evidence upload, every artifact must be reachable from a public URL written into the message-flow text.",
  "rationale": "Some platforms surface no file-upload field anywhere in campaign registration, so evidence attached in your own console never reaches the reviewer — they read the message-flow text and nothing else. The submission then looks complete from your side and arrives empty at theirs, which is why the rejection reason is \"no opt-in evidence\" on a campaign whose evidence you can see.",
  "layer": "CAMPAIGN",
  "layerSlug": "campaign",
  "object": "campaign.message_flow + hosted evidence URLs",
  "severity": "BLOCKING",
  "detectability": [
    "DETERMINISTIC"
  ],
  "failureClass": "RETRY_FIELD",
  "authorities": [
    "Twilio"
  ],
  "codes": [
    {
      "provider": "Twilio",
      "code": "30909",
      "remediable": true
    },
    {
      "provider": "Twilio",
      "code": "30921",
      "remediable": true
    }
  ],
  "applicabilityText": "Applies to every 10DLC registration.",
  "universal": true,
  "remediation": "Host each artifact at a URL that opens without a login and paste those URLs into the message-flow text itself, labelled with what each one shows. Done when the flow text alone would let a reviewer reach every piece of evidence.",
  "example": "Screenshot of the checkout consent box: https://cdn.acmecoffee.com/compliance/optin.png · Verbal script: https://cdn.acmecoffee.com/compliance/script.pdf",
  "notes": "The research marks the \"no upload path exists\" finding as an inference drawn from uniform Twilio wording across eight rejection codes rather than from a published statement, so the provider condition is applied in the check and the rule passes on any other target. Cite the underlying obligation — evidence must reach the reviewer — rather than this phrasing if a user pushes back.",
  "phase": "approval",
  "automated": true,
  "url": "https://ekas.io/rules/10dlc/campaign/cmp-132/",
  "markdown": "https://ekas.io/rules/10dlc/campaign/cmp-132.md",
  "registry": "https://ekas.io/rules/10dlc/",
  "updated": "2026-07-25",
  "licence": "CC BY 4.0 — https://creativecommons.org/licenses/by/4.0/"
}
