email-campaigns —
create → audience → messages → coverage → launch — with LinkedIn-specific
deltas: steps run from order 0 (the connection request), messages are
body-only, and there’s an extra sendability report. For a guided walkthrough
see How to launch a campaign.
linkedin-campaigns is a platform-mode command. Before running it:landbase-cli auth login— authenticate the session.landbase-cli account set— pick and persist the target account.landbase-cli contacts-import start— import contacts; this produces the tag the campaign targets.- A connected LinkedIn account, configured in the web app — required to launch. Launch sends from the campaign owner’s connected LinkedIn account(s) — yours, or the assigned user’s if the campaign is assigned.
linkedin-campaigns requires an account id...; pass --account=<id> per call as an escape hatch.linkedin-campaigns create
Create a campaign draft. Reads a JSON body describing the audience and the message sequence, and returns{ campaignId, status }. A template campaign
carries one shared message per message-bearing step; a personalized campaign
carries a skeleton sequence whose per-contact copy you upload later with
messages.
Usage
string
required
Path to a JSON file with the campaign definition, or
- to read it from
stdin. See create --json config below for the body shape.{ campaignId, status }; counts come from
status after launch.
create --json config
audience names its tag by exactly one of tagName (preferred — the
human-readable name contacts-import shows you) or tagId (the numeric id). If
a name matches more than one tag the call fails with tag_name_ambiguous;
re-issue with the explicit tagId.
Assigning to a teammate requires the account Owner role
(
assignment_not_allowed otherwise); anyone may self-assign.
account users already lists only who
you can assign to. The assigned user must be an active member with the
LinkedIn channel permission and a connected LinkedIn account
(hasConnectedLinkedin: true) — validated at create/assign and re-checked at
launch. See assign to re-assign a draft later.sequence.steps[] entry:
Example (template mode)
linkedin-campaigns audience
Export the campaign’s resolved contacts as one wide CSV so you (or an AI agent) can write one personalized message per contact, per message-bearing step. The file has the contact-context columns plus an emptymessage_<order> cell for
each message-bearing order. Fill those cells in place and resubmit the same
file with messages. Used only for personalized campaigns.
Usage
integer
required
The campaign (draft) id returned by
create.string
Write the CSV to this file and print a JSON summary to stdout. Without it, the
raw CSV is written to stdout.
integer
Cap the number of exported contacts (positive integer).
linkedin_url, first_name, last_name, title, company_name, company_website, industry, then message_<order> per message-bearing order.
linkedin_url is the join key and must stay exactly as exported; each
message_<order> is ≤ 5000 chars. message_0 is the connection-request note
(sent with the invite); message_1.. are follow-ups.
With --out, the CSV is written to the file and stdout is a JSON summary;
without --out, the raw CSV is written to stdout (not JSON):
orders are the message-bearing step orders that drive the message_<order>
columns.
linkedin-campaigns messages
Upload the filled wide CSV to the campaign. The back-end validates the file, matches rows bylinkedin_url, and ingests them asynchronously. Without
--watch the command returns the ingest-start response immediately; with
--watch it polls to a terminal phase and prints the final ingest status.
Usage
integer
required
The campaign id to upload messages for.
string
required
Path to the filled messages CSV (the same file exported by
audience).
Re-uploading a fixed file is a full replace. Terminal ingest phases:
completed, partial, failed.
The start response (without --watch):
--watch, the final status once terminal — the coverage.samples carry
LinkedIn URLs and ordered bodies:
coverage is null while the ingest is still in flight.
linkedin-campaigns coverage
Inspect message coverage before launching — which matched contacts have a complete message set. Bare, it returns the coverage report; with--show <linkedinUrl> it returns that one contact’s stored per-step messages.
Usage
integer
required
The campaign id to report coverage for.
string
A contact’s LinkedIn URL. Returns that contact’s stored messages instead of the
report.
coverage and sendability are draft-only reports. After launch they
return 409 coverage_unavailable / sendability_unavailable.matched is the resolved audience size, covered the
contacts with a complete message set, uncovered a sample still missing them:
--show <linkedinUrl> — one contact’s ordered messages (null if none yet):
linkedin-campaigns sendability
Explain why the sendable audience is smaller than the tag: totals plus a per-reason breakdown.sendable is what the campaign will message; excluded
is the count of tagged contacts not currently sendable. The contacts* reason
counts are independent diagnostics that may overlap and need not sum to
excluded.
Usage
integer
required
The campaign (draft) id.
coverage). sendable is what the campaign will
message (== total); excluded = tagged − sendable. The contacts* reason
counts are optional diagnostics that may overlap and need not sum to excluded:
linkedin-campaigns assign
Re-assign a draft campaign to another account user. The assigned user becomes the campaign owner and their connected LinkedIn account(s) send. Works on drafts only — once launched, assignment is final (campaign_not_reassignable).
Usage
integer
required
The draft campaign id.
string
required
The
identityId of the user to assign, from
account users.assignment_not_allowed otherwise); anyone may self-assign. The assigned user
must have the LinkedIn channel permission and a connected LinkedIn account
(hasConnectedLinkedin: true in account users).
Returns the campaign with its new owner:
linkedin-campaigns launch
Launch a draft. Takes no body — the campaign enrolls its audience and hands off to the send pipeline, sending from the campaign owner’s connected LinkedIn account(s). Launch is blocked (campaign_coverage_incomplete) until every
matched contact has a complete message set. With --watch the command polls
the status (up to 10 times, 10s apart) until it reaches a terminal state
(target: scheduled).
Usage
integer
required
The draft campaign id to launch.
Assigned-launch confirmation. Launching a campaign assigned to someone
other than its creator requires
--yes — without it the CLI exits 1
with CONFIRMATION_REQUIRED, because after launch the assignment is final
and the assignee’s LinkedIn account(s) send. Unassigned (or self-assigned)
campaigns launch unchanged, no --yes needed. The check is best-effort: if
the pre-launch status fetch fails, the CLI prints a warning to stderr and
proceeds rather than blocking the launch.{ campaignId, status }:
--watch, stdout is the final
status object instead.
linkedin-campaigns status
Status for one campaign — type, lifecycle, and allocated/sent stats. Usageinteger
required
The campaign id.
{ campaignId, name?, status, campaignType?, stats? }. See
output schemas.
linkedin-campaigns list
List the account’s campaigns, newest first. Usage{ data: [...], totalCount }. See
output schemas.

