How to Onboard Clients with a Tracked Welcome Packet and Intake Form
Client onboarding usually ships in three pieces: a welcome PDF nobody confirms reading, a form-tool link that feels like homework, and the follow-up email chasing both. Three tools, three URLs, and you still don't know if the packet was ever opened.
Collapse it into one document: the welcome packet with the intake form as its last page, behind a personal tracked link. The client reads, fills and submits in one visit — and every part of that reports back to you.
Make the packet one document
Pages 1–3: the welcome content — how you work, the first 30 days, who to call. Page 4: a fillable intake form. Any PDF tool that creates form fields works; what matters is the field names, because they become the keys in every submission payload:
company_name (text)
primary_contact (text)
billing_email (text)
current_tools (text)
communication_pref (radio: Email / Slack / Phone)
Then upload it privately:
curl -X POST https://apdf.io/api/docs \
-H "Authorization: Bearer $API_TOKEN" -H "Accept: application/json" \
--data-urlencode "file=https://your-site.example/onboarding-packet.pdf" \
--data-urlencode "name=Welcome Packet — Northlight Onboarding" \
-d "is_public=0"
One client, one link
curl -X POST https://apdf.io/api/docs/91fd5-09179-d072a/links \
-H "Authorization: Bearer $API_TOKEN" -H "Accept: application/json" \
--data-urlencode "name=Nadia Fontaine" \
--data-urlencode "email=nadia@atelierfontaine.example"
{
"data": {
"token": "bsWyzJxs",
"name": "Nadia Fontaine",
"email": "nadia@atelierfontaine.example",
"url": "https://docs.apdf.io/studio/91fd5-09179-d072a?v=bsWyzJxs",
"is_active": true,
"last_viewed_at": null,
"created_at": "2026-07-23T09:31:23.000000Z"
}
}
That URL is the entire kickoff email's call to action: “Everything you need is here — give it a read and fill in the last page.” No attachment, no separate form link, nothing to install on her side: the packet opens in the browser and the form is fillable right in the viewer.
The intake arrives — with a name on it
When Nadia hits Submit on page 4, the whole form lands in your workspace, attributed through her link:
GET /api/docs/91fd5-09179-d072a/form-submissions
{
"doc_id": "91fd5-09179-d072a",
"creator_id": "2CyFB2HjTsRn",
"recipient": {
"name": "Nadia Fontaine",
"email": "nadia@atelierfontaine.example"
},
"fields": [
{ "id": "16R", "name": "company_name", "page": 4, "type": "text", "value": "Atelier Fontaine" },
{ "id": "20R", "name": "primary_contact", "page": 4, "type": "text", "value": "Nadia Fontaine" },
{ "id": "23R", "name": "billing_email", "page": 4, "type": "text", "value": "billing@atelierfontaine.example" },
{ "id": "26R", "name": "current_tools", "page": 4, "type": "text", "value": "Notion, Figma, QuickBooks" },
{ "id": "29R", "name": "communication_pref", "page": 4, "type": "radio", "value": false, "checked": false, "exportValue": "Email" },
{ "id": "34R", "name": "communication_pref", "page": 4, "type": "radio", "value": true, "checked": true, "exportValue": "Slack" },
{ "id": "39R", "name": "communication_pref", "page": 4, "type": "radio", "value": false, "checked": false, "exportValue": "Phone" }
],
"created_at": "2026-07-23T09:34:33.000000Z",
"updated_at": "2026-07-23T09:34:33.000000Z"
}
Everything the kickoff needs, machine-readable: the billing address for the first
invoice, the tool stack for the integration checklist, and
"exportValue": "Slack" telling you where she
actually wants to talk. Prefer a push? An automation on the form-submit event
delivers each intake to your webhook the moment it lands — the
forms tutorial
wires it.
And you know the packet was read
The same visit shows up in the document's sessions — proof the welcome content was read, not skipped on the way to the form:
{
"session_id": "X5q9879PK1GehXdA",
"viewer_id": "2CyFB2HjTsRn",
"recipient": {
"name": "Nadia Fontaine",
"email": "nadia@atelierfontaine.example"
},
"device_type": "desktop",
"browser": "Chrome",
"os": "OS X",
"referer": null,
"utm_source": null,
"created_at": "2026-07-23 09:33:55.864",
"pages_viewed": 4,
"total_duration_ms": 31061,
"completion_pct": 100,
"prints": 0,
"downloads": 0
}
Same viewer_id as the submission — read
and intake are one story. The clients who haven't submitted are visible
too: opened-but-not-filled gets a gentle nudge, never-opened gets a resend. No
more chasing people who already answered.
Where to go from here
The packet is live — these builds close the loop around it.
Related tutorials
Track Who Read the Documents They Were Required to Read
When the auditor asks who read the safety policy, 'we emailed it' is a shrug. Per-person links measure the reading, an in-document acknowledgement checkbox collects the signature, and one script joins them into a defensible roster: read 100% and acknowledged, read but unsigned, never opened.
How to Make a PDF Form Fillable Online and Collect the Submissions
Your PDF already has the fields — don't rebuild it in a form builder. Share it as a link that's fillable in any browser and collect every submission as typed, recipient-attributed data: in the dashboard, over the API, or pushed straight to your webhook and handled by a one-file PHP receiver.
Freelancers: A Proposal Workflow Without Per-Seat Tools
Proposal software is priced for sales teams; freelancers just need the signal. A one-person workflow on the free plan: links per contact reveal the 12-minute open, the internal forward, and the 21:21 return visit that camped on the price page — plus the three-slot pipeline trick and honest edges.