> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Outbound URL reputation and the smishing scan

> How the two-lane URL reputation scorer works — an always-on heuristic engine plus optional external safe-browsing verdicts — what the clean/suspicious/malicious bands mean, and why the score is advisory-by-design.

# Outbound URL reputation and the smishing scan

Smishing — phishing delivered over SMS, WhatsApp, RCS, or email — lives in the
destination link, not in the message text. Orbit's URL reputation scorer judges
the destination URL itself and hands the caller a transparent verdict. This
page explains why routing exists, how the two scoring lanes combine, and where
the score surfaces.

<Note>
  The scorer is an **advisory, tenant-owned control**. It scores and explains;
  what you do with the verdict — warn in your compose UI, stash it on the
  message's audit trail, or gate sends in your own integration — is your call.
  Orbit does not gate outbound globally on it. This page documents the control
  you own; it is not legal advice.
</Note>

***

## 1. Why scan links, not just content

Orbit already has two link-adjacent defenses, and neither one checks where a
link actually points:

* **Short links and click tracking** rewrite destinations to your short-link
  domain so clicks are measurable. That is a routing feature — it says nothing
  about whether the destination is safe.
* **Content moderation** (keyword rules, spam scoring, and AI brand-safety on
  the [policy-scan pipeline](/concepts/policy-scan-pipeline-model)) reads the
  message *text* for impersonation and regulated claims.

A smishing link passes all of that because the text looks clean — "Your parcel
needs confirmation" — while the destination `royalmail-parcel.top` is the
attack. Until this scorer exists, nothing in the path reputation-checked the
destination host itself, whether the message is outbound or inbound. The URL
reputation scan closes exactly that gap, and it runs **before** short links are
rewritten so a cloaked destination still gets scored.

***

## 2. The two-lane model

Scoring runs as two additive lanes:

**Lane 1 — heuristic engine (always on).** A pure, deterministic scorer that
inspects observable properties of the URL: the host shape, the TLD, the
structure, and lure vocabulary. No external credential, no third-party feed,
no network call — it works offline and is fully unit-testable. This lane is
always the baseline.

**Lane 2 — external verdicts (operator-supplied, optional).** If your
organization wires a safe-browsing feed — Google Safe Browsing, VirusTotal,
URLhaus, or a similar reputation source — its verdict for a given URL is
injected into the scan options and folded in as the **dominant signal**. The
heuristic lane still runs underneath, so a feed outage degrades to Lane 1,
never to zero coverage.

Either lane alone can condemn or clear a URL; when both fire, the external
verdict dominates because it carries the highest weight. You never need an
external feed to get value — Lane 1 alone catches the classic smishing tells.

***

## 3. The signal vocabulary

The score is **additive and transparent** — every finding carries the exact
points it contributed, and the per-URL score is the capped sum of those
findings. It is never a fabricated reputation number.

**Per-URL finding severities:**

* `info` — weak structural tells (e.g. plain `http://` with no TLS, excessive URL length, smishing lure keywords).
* `warn` — obfuscation or structural tells (e.g. punycode/IDN labels, percent-encoded host characters, an abused TLD, excessive subdomain depth, non-standard ports, embedded `http(s)://` redirects in the path).
* `block` — strong tells (e.g. raw IP-literal hosts, embedded `user:pass@` credentials, brand impersonation).
* An injected external verdict of `malicious` lands as `block` severity; `suspicious` lands as `warn`.

**Per-URL bands**, derived from the total score (0 is clean, 100 worst):

| Band         | Score  | What it means                              |
| ------------ | ------ | ------------------------------------------ |
| `clean`      | 0–24   | Nothing meaningful found                   |
| `suspicious` | 25–59  | Structural tells — review before sending   |
| `malicious`  | 60–100 | Strong phishing or feed-confirmed evidence |

**Aggregate recommendation** for a whole message body — `allow` (all clean),
`review` (any URL suspicious), `block` (any URL malicious). The body-level
score is the worst single URL's score, because one bad link condemns the
message.

Because the scorer exposes every weight and every threshold in its per-URL
`findings` array, a UI or an audit log can show *why* a URL scored what it did
— not just the verdict.

***

## 4. Advisory by design — the scorer scores and explains, callers decide

The scorer never selects a route and never touches a provider. It is a pure
function: you pass a body (or a URL list) and get back the verdict. This
matters for three reasons:

* **False-positive containment.** Heuristic rules can flag a legitimate link
  (e.g. an internal tracking URL with a deep path). If the scorer blocked
  sends outright, an ambiguous tell would black-hole real traffic. Advisory
  scoring keeps the route open.
* **Where to enforce is your choice.** Warn in the compose UI before send,
  stash the verdict on the message metadata for your audit trail, or gate
  sends in your own integration code. Orbit never forces either position.
* **Routing stays untouched.** Because the scorer never selects an outbound
  route, it shares no routing dependency with your send path — the same reason
  a scan outage can never strand traffic.

Feeding an external feed's verdict back is also deliberate: the caller passes
it in as scan options, so the network call and the credential live in *your*
boundary, not in the scorer.

***

## 5. Where the score is read

Three surfaces expose the verdict, and they are deliberately different shapes:

**Send-time stamp (automatic).** Every outbound message send body re-scans the
original composed text — before short links rewrite it — and stamps a compact
verdict onto the message's metadata as `url_reputation`:
`{ band, score, recommendation, flagged_hosts }`. The stamp rides the message
record so your audit trail and any downstream dashboards can surface it. A
warn-level log line accompanies any non-clean band. The send itself proceeds
regardless — the stamp is audit, not a gate.

**Compose-time probe (two endpoints).** Before a message is sent, you can
score a draft:

* `POST /api/v1/messages/url-scan` — the dedicated scanner: takes the draft
  body, returns the full per-URL breakdown (band, score, findings). Rate
  limit: 120 requests per minute per tenant. Body cap: 10,000 characters.
  URL cap: 25 distinct URLs analyzed per body (a DoS guard; anything beyond
  the 25th distinct URL is ignored).
* `POST /api/v1/messages/lint` — the composite lint endpoint returns the
  URL reputation verdict alongside policy violations, spam score hits, and
  AI brand-safety in one response, so a compose dialog can make one call.

Both probe endpoints are read-only and side-effect-free. They exist so your
compose experience can warn the sender *before* the message leaves.

**Explicit URL lists.** Structured destinations that never appear in body
text — rich RCS deep-link chip URLs, dial/fallback URLs — can be scanned
through the same engine by passing the list rather than a body. The
verdict shape and worst-URL-wins aggregation are identical.

***

## 6. Working with the verdict

A `clean` band needs no action; a `suspicious` band means a human (or your
automated policy) should review the destination before the send proceeds; a
`malicious` band means treat the destination as hostile — either swap the link
for a canonical one, drop the send, or escalate to your fraud tooling.

Fold the verdict into the same fraud posture you use for the rest of your
messaging risk surface: the URL reputation band sits beside the SMS-pumping
(AIT) risk score, the complaint feedback loop, and your brand-identity trust
score. A URL scan that trips `suspicious` on a destination you also score as
high pumping risk is a much stronger signal than either alone. The verdict
never gates routing — it informs your tenant policy.

<Warning>
  A degraded scan fails open deliberately: on a rare availability blip the
  compose-time probe returns a clean verdict rather than blocking drafts, and
  the send-time stamp is skipped rather than black-holing the send. The
  send-time service re-scan still runs on the original composed body upstream
  of short links. If your integration's gate depends on the probe, treat a
  missing verdict as "unknown", not "approved", if you want strictness.
</Warning>

***

## Related

* [The policy-scan pipeline model](/concepts/policy-scan-pipeline-model) — where the content scanners sit in the request lifecycle
* [SMS-pumping protection](/guides/sms-pumping-protection) — reading destination risk before send, alongside URL reputation
* [SMS complaint feedback loop](/concepts/sms-complaint-feedback-loop) — the other inbound reputation signal
* [Short links](/concepts/short-links) — the click-tracking layer the scan runs upstream of
* [Inbound media threat scan model](/concepts/inbound-media-threat-scan-model) — the sibling inbound content scanner
