Facebook Pixel
CLI

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-120

Twenty-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-120

Step 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.json

Rehearse 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-count

Worth putting at the top of any outreach script. Knowing you have eleven invitations left this week changes what you queue up.

On this page