Skip to main content

Short links, landing pages, and the publish lifecycle

Orbit’s links surface (/api/v1/links) offers two trackable artifact families and one lifecycle that joins them. The guides — short links with click tracking and the short links cookbook — show you the how-to; this page names the model those guides operate on, so you know what a short link is versus a landing page, why a page has no public address until publish, and who can do what.

Two artifact families

A short link is one redirect record: an id, a 6-character code, and the original URL it points to. Clicks on GET /l/:code resolve the code to the original URL and record attribution — the originating message, the campaign, and the identifying session signals (IP, user-agent, referrer). A landing page is a renderable content block tree published under a short code. The draft holds an ordered list of blocks (hero / product_grid / opt_in_form / coupon), constrained to http(s) URLs and HTML-escaped strings so a page definition can’t inject markup. Publishing renders the tree into the public document served at GET /lp/:code. Both families share the same public address path GET /l/:code, so they draw the same click pipeline — per-link stats, per-campaign rollup, per-contact attribution, and the short_link.click webhook. A short link is created by minting a code with no colliding predecessor. The code is drawn by rejection sampling over a 56-character alphabet (no ambiguous 0/O/1/I/l), so ~41 of every 100 random 6-character strings are safe codes. The service re-tries on collision a handful of times before refusing. The public face is the root-mounted GET /l/:code redirect — anonymous, per-IP rate-limited, mounted directly on the API host (not under /api/v1), so it stays reachable after the X-API-Key middleware rejects unauthenticated /api/v1 traffic. Each click runs the click pipeline: a raw click row, quality scoring against a bot user-agent list, country from the edge CDN geo header, then fan-out to per-link stats, per-contact click history, and GET /campaigns/:campaignId/link-analytics — the SMS link-tracking + attribution rollup for the campaign the link belongs to (see the click-tracking guide §6). Per-campaign attribution lives on the row as campaign_id (set at mint) and message_id (set when the send pipeline mints inline). A link with no campaign_id drifts out of the campaign rollup but still counts tenant-wide at GET /links/insights.

The landing-page model

A landing page’s draft content is validated against a lenient schema (the block list may be empty) so a work-in-progress page can be saved. Publish re-validates against the strict publish schema — at least one block — so a page with no renderable content can never go live. The transition mints a short link whose target is the internal render path (/lp/:code), then stamps status: "published" alongside the short_code and short_link_id. Publish is idempotent: re-publishing an already-published page returns the page as-is with its live short URL, rather than minting a new code. The pair of guards (publish requires renderable content; republish returns as-is) is what keeps the short code stable while you iterate on blocks.

Publication semantics

The lifecycle is draft → published → archived, with archive and draft reversible via PATCH:
  • A page is created as draft by POST /links/landing-pages.
  • POST /links/landing-pages/:id/publish gates on a non-empty block list (422 otherwise), mints the page’s short link, and flips status to published with short_url populated.
  • Republishing flips nothing new: the call returns the existing published page as-is. Re-minting is explicitly avoided so a legitimate not-found never triggers a spurious new code.
  • PATCH can move a page between draft and archived; archived pages stop appearing in pickers but retain their history.
  • Visits and conversions roll up per page at GET /links/landing-pages/:id/analytics, broken down by campaign and by originating message.

Roles and access

Write operations on both families — creating or publishing a landing page, minting a one-off short link, deleting either — are gated to owner, admin, and developer. Reads (list, get, stats, analytics) are open to any authenticated caller. The public redirect and render endpoints carry no role check at all — they serve the recipient, not the operator. This shape means a marketer with read access can study a page’s analytics while only an owner/admin/developer pushes the page live. Contrast with number masking — proxy sessions exist to hide two parties’ identities from each other; a short link’s purpose is to expose click intent. The privacy surface (number masking) forwards traffic through a shared proxy number so neither side sees the other’s real address. The click surface (short links, landing pages) intentionally records who clicked, when, and from where, so campaign attribution and contact engagement history work. Both exist on the same platform; the distinction is which one you want when.

The operator loop

In practice the loop runs: create a draft page, attach it to a campaign, publish (its short_url goes live), embed the short URL in sends or hand it to channels directly, read links/:id/stats or the campaign rollup, patch the blocks, republish (the short URL stays stable), and iterate. The short links cookbook runs the publish step end to end; the click-tracking guide covers the analytics readings; the API reference holds the field shapes.