Skip to content

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=true returns 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 409 unless force: 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: POST onboarding/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, list ids), and packages. For example, growth: label, kickoff_within_days, access_needed, and tasks with name, offset_days (business days from start) and owner_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))

  1. Google Drive Create Drive Folder: Resource Folder, Create, name {{ $json.folder_name }}, parent folder By ID = drive_parent_folder_id.
  2. Slack Create Slack Channel: Resource Channel, Create, name = channel_name, visibility private. (Slack app scopes needed: groups:write for private channels, channels:manage for public ones; check your Slack app's OAuth settings.)
  3. Code Task Items (task-items.js): return one item per task.
  4. 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.
  5. Aggregate Tasks Done: all item data → one item.
  6. Gmail Send Welcome Email: To/Subject/Message from the plan's email, 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 partial with "no id returned": credentials are missing, or ids in Settings are still YOUR_… placeholders.
  • Slack "name_taken": the channel already exists (an earlier partial run). Rename, delete, or treat name_taken as 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.