Concepts & data model
The vocabulary you'll see throughout the docs and the API. Glossary first, then the relationships between resources.
Glossary
Broker
The freight broker (or 3PL, or dispatch company) onboarding carriers. In the data model that's the organization row — one org per broker account. Inside an org you can have multiple users: one super-admin (org founder, full access) plus invited sub-users (teammates, links + carrier views only). See Team & roles for the role boundary.
Carrier
The trucking company you're onboarding. Identified by MC number and/or US DOT number, both of which we look up against FMCSA in real time during the form.
Carriers don't have logins on CarrierPacket.Link — they fill out a packet, sign, and verify their email. Their identity in the system is captured as a submission.
Packet
A reusable template that defines: branding (font, colors, dark/light), which fields the carrier fills out, which documents they upload, and the broker–carrier agreement text. Owned by the org — every teammate sees the same packets.
An org can have multiple packets — common pattern is one for each commodity type (van, reefer, flatbed, power-only). Exactly one is starred as the primary at any time; that's the packet served at /p/<your-slug> and embedded by the Embedded snippet. If you don't type a URL slug when creating a packet or link, we auto-generate a short one (2–3 chars).
Packet design and starring live in the Designer and on the Packets page. Both are super-admin only — your org's super-admin builds the templates the whole team shares.
Link
A per-attribution share URL at /a/<link-slug>. Both super-admins and sub-users can create them (links are the sub-user's main creation surface). Hand each agent (or trade-show booth, or marketing channel) their own link — each routes one extra notification destination (email and/or SMS) on top of org-level recipients, and a signup that comes in through the link is attributed to whoever owns it.
Manage them on the Links page. Slugs are tiny (2–3 chars, auto-generated if you don't type one), immutable, and resolve to the org's starred primary packet — so re-starring a different packet automatically updates what every link serves.
Submission
One carrier completing one packet. The row that lands in your Carriers list. Carries all the data the carrier provided (legal name, MC, USDOT, addresses, dispatch contact, services, signature, IP, timestamps) plus a status:
| status | meaning |
|---|---|
| 0 | Awaiting email (form submitted, verification email sent — the signed PDF doesn't exist yet; the cryptographic signature is minted only when the carrier clicks the link) |
| 1 | Legacy — Pending verify. Pre-PAdES rollout rows that already had a signed PDF before the deferred-signing flow shipped. New submissions never land here. |
| 2 | Verified (email confirmed; PAdES signature minted) |
| 3 | Rejected (you marked it bad from the Carriers detail modal) |
| 4 | Archived (you set it aside) |
Document
A file the carrier uploaded with their submission — W-9, COI (proof of insurance), MC authority letter, references, ACH info, etc. Each row stores the original filename, MIME type, size, and (importantly) expires_on.
The expires_on field powers the renewal-tracking workflow — GET /api/v1/documents?expiring_within=30 returns every doc due to expire in the next 30 days.
API key
A bearer token your code sends in the Authorization header to authenticate against the public REST API. Format: cpl_<6-char-prefix>_<32-char-body>. Generate at Embedded → API keys.
We hash and store keys (never the plaintext) and show you the full value once on creation. Each key has an optional IP allowlist and tracks last_used_at.
Webhook
An HTTPS endpoint we POST event payloads to. Configured at Embedded → Webhooks with a URL, a list of events to subscribe to (carrier.intent, carrier.signed, carrier.verified, carrier.rejected), and a signing secret. Up to 5 webhooks per org.
Every payload is signed with HMAC-SHA256 in the X-CPL-Signature header so you can verify it really came from us.
How they relate
organization (broker account)
├── slug short slug for /p/<slug> (immutable, derived from org id)
├── primary_packet_id → packets.id; the starred packet served by /p/<slug>
├── users one org, many users (1 super-admin + invited teammates)
├── packets one org, many packets
│ └── carrier_profile_versions one packet, many submissions
│ ├── link_id → links.id; set when carrier hit /a/<link-slug>
│ └── carrier_documents one submission, many docs (≤6)
├── links per-attribution share URLs (/a/<slug>), 1 notify-email + 1 notify-sms each
├── api_keys ≤ unbounded; revoke = soft-delete
└── webhooks ≤ 5 per org
Tenant isolation is enforced everywhere: every query filters by org = <current org>, both in the dashboard and the API. There's no cross-org data access path, even by ID guessing — GET /api/v1/submissions/42 returns 404 if submission 42 isn't yours.
URL conventions
| Pattern | What it is |
|---|---|
| /p/<org_slug> | Carrier-facing form for the org's primary (starred) packet. The default link you share or embed. |
| /a/<link_slug> | Same packet, but the submission is attributed to the link (and to the teammate who owns it) — routes notifications to the link's email/SMS in addition to org-level ones. |
| /c/<embed_token> | Legacy per-packet share link. Still resolved for backwards compatibility, but no longer generated by the UI. |
| /api/v1/<resource> | REST API endpoints. Bearer-token auth required. |
| /app/* | Authenticated broker dashboard. |
| /app/docs/* | These docs. Public, no auth. |
IDs vs tokens
You'll see two kinds of identifiers on submissions:
id— integer primary key, sequential. Use this in the dashboard URL (/app/carriers.php?id=1138) and as the{id}in REST endpoints.hashid— 64-char unguessable hex string. Used internally; reserved for future "share this signed packet" links. Don't depend on the format.
Each org has an organization.slug (deterministic 2–3-char base62 derived from the org id). It's what carriers see in /p/<slug>. Immutable — the URL you put on a business card stays valid forever, and starring a different packet is what swaps what it serves.
Links have their own links.slug, same flavor (deterministic, immutable). One per agent / channel / campaign. Listed and managed on the Links page.