Skip to main content

How to launch a campaign

This guide takes you from a signed-in session to a live outbound campaign. It covers both channels — email and LinkedIn — which share the same shape: import contacts to get a tag, create a draft against that tag, write the copy, then launch. The main flow uses personalized mode — one message written per contact, which is how most campaigns run and what lands best. If you’d rather send one shared message per step, the shorter template-mode path is at the end.
Campaigns are platform-mode commands. You’ll need, in order:
  1. landbase-cli auth login — a signed-in session.
  2. landbase-cli account set — an active account.
  3. A tag of contacts (step 2 below).
  4. A connected sending channel — a sending inbox (email) or a connected LinkedIn account (LinkedIn), both configured in the web app.
Throughout, <channel> is either email or linkedin — the subcommands are identical unless noted.

1. Sign in and pick the account

account set shows a picker and saves your choice, so it’s a one-time step per machine. Full walkthrough: How to select and onboard an account.

2. Import contacts to get the tag

A campaign always targets a tag, and the tag comes from importing contacts — so this is a required pre-campaign step, not an optional one. Start an import and note the tagName/tagId it produces:
contacts-import status <importId> surfaces both tagName and tagId once the import is terminal — that name (or id) is what the campaign’s audience refers to. Full walkthrough: How to import contacts and the contacts-import reference.
Prefer tagName in the campaign body — it’s the human-readable name the import shows you. Use the numeric tagId only if a name is ambiguous (matches more than one tag).

3. Connect the sending channel

Sending inboxes and LinkedIn accounts are connected in the web app, not through the CLI:
  • Email — connect at least one sending inbox on the account.
  • LinkedIn — connect at least one LinkedIn account. Launch sends from every connected LinkedIn account.
You can create a draft without this, but launch needs a connected channel.

4. Create a draft (personalized mode)

Write a campaign definition that targets your tag with "messaging": { "mode": "personalized" }. In personalized mode the steps are a skeleton — order and timing only, no inline message — because you’ll write one message per contact in step 6. The body differs slightly per channel:
Note the campaignId — you’ll use it for every step below. Key differences between the two bodies: email steps start at order: 1 with channel: "email" and daysDelay; LinkedIn steps start at order: 0 (the connection request), use isMessageInclude, and take delayDays or minutesDelay. Full field reference: email-campaigns / linkedin-campaigns.

5. Export the audience

Export the campaign’s resolved contacts as one wide CSV — contact-context columns plus empty message cells for each step:
You’ll fill the message cells in the next step. There is exactly one file: fill it in place and resubmit the same file — don’t create a second CSV.

6. Write the messages

First pull seller-side context so the copy has a value prop to lean on, then fill one personalized message per contact, per step, directly in audience.csv:
The join key (email for email, linkedin_url for LinkedIn) is already filled and must stay exactly as exported. Write genuinely per-contact copy that references each contact’s role, company, and industry — don’t reuse a templated sentence across rows.
Richer account onboarding produces sharper copy. If onboarding get shows sparse aboutCompany / aboutAudience / aboutCompetition, fill it in with onboarding before writing.

7. Upload the messages

Upload the filled CSV. The back-end matches rows by the join key and ingests them asynchronously; --watch polls until the ingest is terminal:
Re-uploading a fixed file is a full replace, so you can iterate until coverage is complete.

8. Check coverage, then launch

Spot-check that contacts have their messages, then launch:
launch --watch polls the status (up to 10 times, 10s apart) until it reaches scheduled.
Launch ≠ sends. Reaching scheduled means the audience is enrolled and handed to the send scheduler — not that messages have gone out. Sends then happen on a throttled cadence: email is gated by inbox caps and the account send window (default 06:00–15:00 PT); LinkedIn is gated by rate limits and per-account caps. Launch is also blocked until every matched contact has a message — if coverage shows gaps, fix those rows and re-upload (step 7) before launching.
messages --watch and launch --watch can each block for a minute or more. When driving from a coding agent, run the watched call as a sub-agent so it doesn’t tie up your main session.

9. Track it

The delivery funnel, exclusions, and timing estimates appear here as the scheduler works through the audience.

Simpler: template mode

If you don’t need per-contact copy, template mode (the default) carries one shared message per step inline in the create body — no CSV, no upload. Skip steps 5–7: create with an inline message on each step, then launch.
In template mode, email message bodies may use only these bracketed variables: [Recipient's Name], [Recipient's Company Name], [Your Company], [Opener].

If something fails

  • campaign_coverage_incomplete on launch — a personalized campaign still has contacts without a complete message set. Run coverage, fix those rows in the CSV, re-upload with messages, then re-launch.
  • tag_name_ambiguous — the tagName matches more than one tag. Re-issue create with the explicit tagId.
  • AUTH_FAILED — the session expired (7-day TTL). Re-run auth login; see troubleshoot authentication.
  • No sends after scheduled — check the sending channel is connected and, for email, that you’re inside the send window.