Module 16 · business · intermediate
Lesson 16 · Client Onboarding Pipeline
Goal
Every agency and freelancer repeats the same 10–15 steps for each new client. Automate them as one reliable pipeline: one request in, a fully set-up project out, and a clear report of what happened. This is orchestration: many integrations, one outcome.
Prerequisites
- Modules 04 (error handling), 05 (Gmail), 15 (Settings-as-data, injected inputs)
- For the live part: Google Drive, Slack and ClickUp accounts (any can be swapped; see Exercise 3)
Concepts
- Plan, then act. One Code node turns the request + package template into a complete plan (names, dates, email). Every later node just executes the plan.
?dry_run=truereturns the plan, which makes it easy to review, test and demo. - Templates as data. Packages (tasks, offsets in business days, access needed, kickoff window) live in Settings. Adding a package is editing JSON, not rewiring nodes.
- Business days. "Kickoff within 2 days" means working days. A helper skips weekends; a weekend start date moves to Monday.
- Idempotency. Payment and CRM webhooks get retried. A registry in workflow static data records each client's last onboarding; a complete one blocks repeats with
409unlessforce: true. - Continue on error + report. Each integration node has On Error → Continue (regular output). A failure becomes data, the remaining steps still run, and the final report lists what worked. You fix the one broken thing and re-run, instead of untangling a half-finished onboarding.
- Trust ids, not silence. Some nodes return an empty item instead of an error when misconfigured (ClickUp did, without credentials). A step only counts as done if it returned an
id. - Fan-out, then fan-in. Tasks become one item each (so ClickUp creates them all), then Aggregate collapses back to one item so the email is sent once.
Build it
Step 1: Webhook and Settings
- Webhook
New Client: POSTonboarding/new-client, respond via node. - Edit Fields
Settings (edit me):agency_name,sender_name,kickoff_link(Cal.com/Calendly),slack_channel_prefix,drive_parent_folder_id,clickup(team,space,listids), andpackages. For example,growth: label,kickoff_within_days,access_needed, andtaskswithname,offset_days(business days from start) andowner_role.
Step 2: Validate and build the plan
Code Validate & Build Plan (workflow/code/plan.js):
- collect all validation errors (client name, email format, known package, parseable date) so the caller can fix everything at once
- start date: given or tomorrow, moved to a weekday
slug(lowercase, dashes),folder_name(with start month),channel_name(prefix + slug, ≤ 80 chars as Slack requires)- tasks with due dates via
addBusinessDays, and the welcome email body (kickoff link, kickoff-by date, access list)
IF Valid? → false: Respond 400 with errors.
Step 3: Registry and mode
Code Check Registry (registry-check.js): read $getWorkflowStaticData('global').onboarded[slug]; already_onboarded = previous status complete and not force (dry runs never blocked).
Switch Mode: dry run → Respond with the plan; already onboarded → Respond 409; fallback run → the side effects.
Step 4: Side effects (each with Settings → On Error → Continue (regular output))
- Google Drive
Create Drive Folder: Resource Folder, Create, name{{ $json.folder_name }}, parent folder By ID =drive_parent_folder_id. - Slack
Create Slack Channel: Resource Channel, Create, name =channel_name, visibility private. (Slack app scopes needed:groups:writefor private channels,channels:managefor public ones; check your Slack app's OAuth settings.) - Code
Task Items(task-items.js): return one item per task. - ClickUp
Create ClickUp Tasks: team/space/list from Settings (folderless list on), name{{ $json.name }}, Additional Fields → Due Date{{ $json.due }}, Content with owner role and client contact. - Aggregate
Tasks Done: all item data → one item. - Gmail
Send Welcome Email: To/Subject/Message from the plan'semail, type Text, attribution off.
Step 5: Report
Code Onboarding Report (report.js): for each step, ok = no error and an id; build steps and failed_steps, status complete or partial, and save it to the registry. → Respond with the report, plus IF Any Step Failed? → Alert Team (Exercise 1).
Publish (static data only persists for production runs).
Test it
./test/test.sh # 19 checks, no credentials
WITH_CREDS=1 TEST_EMAIL=you@example.com ./test/test.sh # real onboarding + duplicate guard
Hook it up: point your Stripe checkout.session.completed handler, contract tool (PandaDoc/DocuSign "completed" webhook) or CRM "deal won" automation at the endpoint (Exercise 2).
Common problems
- Everything
partialwith "no id returned": credentials are missing, or ids in Settings are stillYOUR_…placeholders. - Slack "name_taken": the channel already exists (an earlier partial run). Rename, delete, or treat
name_takenas success (Exercise 4). - Duplicate tasks after a re-run: a partial re-run repeats every step. Make steps skip what already exists (Exercise 4).
- Emails sent several times: the Aggregate before Gmail is missing, so Gmail ran once per task.
- Registry forgets clients: you ran it manually from the editor; static data only persists for production executions. For real use, keep the registry in a Sheet or database.