Skip to main content

10DLC preflight: score, verdict, and finding codes

A 10DLC brand + campaign submission goes through TCR vetting, and most first-time rejections come from a small set of deterministic content and completeness patterns — missing opt-out wording, public link shorteners, placeholder sample messages, an opt-in description that is too thin. Every rejected resubmission costs a new vetting fee and restarts the review clock. The preflight engine scores your submission against that known rejection-pattern catalog before you pay the fee, and returns a score, a verdict, and per-field findings with the exact offending fragment and a concrete rewrite suggestion. This page is the reference for that engine: the three endpoint surfaces that run it, the request/response contract, every rule code and remediation, rate limits, and a worked CI gate that blocks a submission until the verdict is pass. For the end-to-end registration walkthrough (registering the brand, creating the campaign, tracking vetting), see Register for 10DLC.
This page describes a tenant-owned compliance control. It is not legal advice. Whether a submission satisfies TCR and carrier requirements is your brand’s responsibility — confirm with qualified counsel.

Three surfaces, one engine family

Three endpoints run preflight engines. The two 10DLC surfaces both run the same engine (tcr-preflight/v1); the short-code surface runs the parallel engine (shortcode-preflight/v1) against a program-brief payload shape. All routes below are rooted at https://api.orbit.devotel.io/api/v1: Pick the surface by what you want linted, not by the rule set — the two 10DLC surfaces emit identical finding structures. The ad-hoc endpoint needs no persisted state; the wizard endpoint reads the draft saved through the wizard flow. The short-code engine is a sibling surface: same score/verdict/findings[] contract, different rule catalog and payload shape. All three endpoints are idempotent reads — nothing is sent to TCR or a carrier, and nothing is created or stored.

Request and response contract

Ad-hoc 10DLC payload

POST /compliance/10dlc/preflight accepts brand and campaign objects shaped like the submission you intend to send:
  • brand_vetting_score — your current TCR vetting score (0–100) when you already hold one. It drives the throughput-tier rule (R-CAMP-THROUGHPUT-TIER); omit it or pass null for an un-vetted brand and the linter assumes the Low Volume Standard baseline tier.
  • campaign.expected_msg_per_day_per_number — your declared forecast volume per number per day, used by the same tier rule.

Wizard payload

POST /compliance/10dlc/wizard/preflight takes no brand or campaign body — it loads the draft you saved through PATCH /compliance/10dlc/wizard/draft and lints that. Both body fields are optional:
  • expected_msg_per_day_per_number — forecast volume from your volume-planning widget, applied on top of the persisted draft.
  • brand_vetting_score — current score, same semantics as the ad-hoc endpoint.
Linting the persisted draft instead of a client-side copy removes the risk that your findings describe an older revision than the one you are about to submit.

Response shape

Both 10DLC endpoints return the same envelope:
  • score — 0–100; 100 means no known pattern matched. Each error finding deducts 25 points, each warn 8, each info 2.
  • verdict — block when any error-severity finding is present, warn when any warn finding is present or the score lands below 75, otherwise pass.
  • findings[] — one entry per rule hit. field is the dot-path of the offending field (e.g. campaign.sample_message[2]); match quotes the exact fragment when one exists; suggestion is the rewrite that resolves the finding.
  • engine — the rule-engine version, stamped on every response so you can correlate score changes when the catalog evolves.
Lint pass is not carrier approval. A pass verdict means “no known rejection pattern matched.” Your submission still goes through the normal TCR and per-carrier review, which includes human vetting. Treat the preflight as the deterministic first pass that removes the rejectable-by-pattern defects; the carrier review that follows is the authoritative pass.

Failure catalogue

Each finding carries a stable ruleId. The table covers every code the tcr-preflight/v1 engine emits, with the remediation for each. The scoring weights and verdict thresholds above (error −25, warn −8, info −2; warn verdict below 75) are fixed for tcr-preflight/v1. If the catalog or weights change, the engine label rolls to a new version stamp, which lets you correlate a score change to an engine release rather than to your own payload edit.

Rate limits and correlation

  • Both 10DLC preflight endpoints are rate-limited to 30 requests per minute per organization. The wizard surface additionally enforces an admin role.
  • A 429 means you exceeded the per-minute budget; back off and retry after the window closes. There is no monthly quota.
  • Every response carries meta.request_id. Log that ID in the job that calls the linter — the linter is deterministic for a given payload and catalog version, so pairing the request ID with the result lets you reconstruct any deviation between two runs of the same payload.

Worked example: pre-submit CI gate

Run the preflight as a gate in the pipeline that prepares a 10DLC submission, and fail the job on any verdict other than pass. The linter is a pure function of the payload and engine version, so a green run means there is no known rejection pattern left to match at this engine version.
Treat warn as a gate failure. A warn verdict means a finding carriers frequently reject on is present (missing STOP wording, too few samples, throughput-tier mismatch often not fatal but worth fixing). Failing only on block lets those through. Log verdict + score on every run; when a submission is rejected despite a pass, diff the stored payload against what TCR saw — the engine stamp tells you whether the rule catalog moved between the two runs.
See also: