Recipes
Complete, runnable workflows — sourcing to outreach, inbox triage, content reporting and scheduled jobs — built from CLI commands you can copy today.
Every recipe here runs as written once CONNECTSAFELY_API_KEY and
CONNECTSAFELY_ACCOUNT_ID are set. They are ordinary shell, so each step is
inspectable, resumable and diffable — which is most of the reason to reach for a CLI
in the first place.
1. Sourcing to outreach, in four commands
The core motion: find people, look at the list, turn it into calls, send it paced.
# Find the geo id once — locations are ids, not strings
connectsafely search-geo-locations --keywords "Ohio" --fields 'locations.name,locations.geoId'
# Source, keeping only the three fields the next step needs
connectsafely search-people \
--json '{
"keywords": "VP Marketing",
"count": 25,
"filters": { "locationId": "106981407" }
}' \
--fields 'people.profileId,people.firstName,people.headline' \
--output ndjson > prospects.ndjson
# Look at what you found before you act on it
head -5 prospects.ndjson
# Turn rows into calls
jq -c '{
command: "send-connection-request",
args: { profileId: .profileId },
ref: .firstName
}' prospects.ndjson > invites.ndjson
# Rehearse, then send at a human rhythm
connectsafely batch invites.ndjson --dry-run
connectsafely batch invites.ndjson --pace 45-120Twenty-five prospects sourced and contacted, with a review step in the middle and a file you can re-run tomorrow. No profile data passed through an AI model's context.
2. Personalised outreach, generated then reviewed
Bulk-identical messages are the fastest route to complaints. Generate the message per person, write it to the batch file, read it, and then send:
# 1. Source with enough context to write from
connectsafely search-people --keywords "Head of RevOps" --count 10 \
--fields 'people.profileId,people.firstName,people.headline' \
--output ndjson > leads.ndjson
# 2. Read each lead's recent activity, one file per lead
while read -r lead; do
id=$(echo "$lead" | jq -r '.profileId')
connectsafely get-latest-posts --profile-id "$id" --count 3 \
--fields 'posts.content' > "context-$id.json"
done < leads.ndjson
# 3. Have your model write invites.ndjson from leads.ndjson + context-*.json
# (each row: {"command":"send-connection-request",
# "args":{"profileId":…,"customMessage":…},"ref":…})
# 4. Read what it wrote — this is the step that matters
jq -r '.args.customMessage' invites.ndjson
# 5. Send
connectsafely batch invites.ndjson --pace 45-120Step 4 is not optional. The file exists precisely so a human can read twenty-five messages in thirty seconds before they go out.
3. Morning inbox triage
# What arrived
connectsafely list-conversations --count 20 \
--fields 'conversations.conversationUrn,conversations.unreadCount,conversations.participants.name' \
--output markdown
# Read one thread in full
connectsafely get-conversation-messages \
--conversation-urn 'urn:li:msg_conversation:(…)' --count 30 \
--fields 'messages.text,messages.senderName,messages.sentAt'
# Reply
connectsafely conversations-send-message --json '{
"conversationUrn": "urn:li:msg_conversation:(…)",
"message": "Thanks for coming back to me — Thursday at 10 works."
}'
# Park it for later instead
connectsafely conversations-star --conversation-urn 'urn:li:msg_conversation:(…)'--output markdown is the right choice for the first command: it is a table, it is
compact, and if a model is doing the triage it reads Markdown structure natively.
4. Weekly engagement report
#!/usr/bin/env bash
set -uo pipefail
week=$(date +%Y-W%V)
ME=your-vanity-name # your own profile slug
{
echo "# LinkedIn — week $week"
echo
echo "## Network"
connectsafely get-network-summary --output markdown
echo
echo "## Social Selling Index"
connectsafely get-ssi --output markdown
echo
echo "## Recent posts"
connectsafely get-latest-posts --profile-id "$ME" --count 10 \
--fields 'posts.content,posts.numLikes,posts.numComments' \
--output markdown
echo
echo "## Who looked you up"
connectsafely get-profile-visitors --count 10 --max-visitors 10 \
--fields 'visitors.name,visitors.headline,visitors.viewedAt' \
--output markdown
} > "report-$week.md"Four reads, one Markdown file, ready to paste into Slack or hand to a model for a summary. Every command is read-only, so this is safe to run as often as you like.
5. A nightly cron job
This is the case that is genuinely awkward with a tool server and trivial with a binary:
# crontab -e
0 9 * * 1-5 CONNECTSAFELY_API_KEY=... CONNECTSAFELY_ACCOUNT_ID=... \
/usr/local/bin/connectsafely batch /opt/outreach/today.ndjson \
--pace 60-180 --quiet >> /var/log/connectsafely.log 2>&1--quiet keeps the log to results and errors. --pace 60-180 stretches the run
across the morning. The exit code lands in your job monitor, so a run that hit a
daily cap (5) is distinguishable from one that lost the network (6).
6. A GitHub Action
name: LinkedIn digest
on:
schedule: [{ cron: '0 8 * * 1' }]
jobs:
digest:
runs-on: ubuntu-latest
steps:
- run: npm install -g @connectsafely/cli
- name: Collect metrics
env:
CONNECTSAFELY_API_KEY: ${{ secrets.CONNECTSAFELY_API_KEY }}
CONNECTSAFELY_ACCOUNT_ID: ${{ secrets.CONNECTSAFELY_ACCOUNT_ID }}
run: |
connectsafely get-network-summary > network.json
connectsafely get-ssi > ssi.json
- uses: actions/upload-artifact@v4
with:
name: linkedin-metrics
path: '*.json'The CLI detects CI and emits JSON without being asked. A non-zero exit fails the step with a code you can read in the log.
7. Publishing a post
# Text
connectsafely create-post --json '{
"text": "Three things we learned shipping a CLI for AI agents…",
"visibility": "ANYONE"
}' --dry-run
# With media — initialise the upload, then post
connectsafely upload-init --json '{
"mediaType": "image",
"fileSize": 184320,
"filename": "chart.png"
}'
connectsafely upload-and-post --json @post-with-image.jsonRehearse a public post with --dry-run every time. It is the one action where a
mistake is visible to your entire network within seconds.
8. Checking your own limits before a run
connectsafely get-account-quota --account-id "$CONNECTSAFELY_ACCOUNT_ID"
connectsafely get-inmail-credits
connectsafely get-connection-countWorth putting at the top of any outreach script. Knowing you have eleven invitations left this week changes what you queue up.
Related
- Sending at a human rhythm — the batch format in full
- Cutting the cost of every answer — why these recipes use
--fieldseverywhere - Command reference — every flag used above
