Messaging
The inbox and message commands cover everything related to LinkedIn conversations: listing chats, sending messages, editing and deleting, downloading attachments, and sending InMail. All commands are account-scoped and require --account (or a default set via curviate config set-account).
Before you start. Every command on this page needs an API key and a connected LinkedIn account. Run curviate account list to read your acc_... ids; if that returns an empty list, connect an account first (Authentication & Accounts). Without one, every command here exits non-zero with ACCOUNT_NOT_FOUND.
Reads are
inbox, writes aremessage.messagehas nosearchsubcommand. An unrecognised word aftermessageis parsed as a chat id, and the next argument as the message text, socurviate message search "sophie"does not search anything: it attempts to send the textsophieto a chat calledsearch. To search, usecurviate inbox search "sophie". Add--previewto any write command to see the exact request before it is sent.
Message IDs and attachment IDs are verbatim identifiers. Chat IDs additionally accept a full LinkedIn messaging thread URL (https://www.linkedin.com/messaging/thread/<id>/), from which the bare conversation ID is extracted automatically with no extra network call, as well as the LinkedIn conversation ID (2-...) directly, anywhere a <chat_id> is expected.
Changed in 0.15.0:
inbox syncandinbox sync-chat <chat_id>are removed; message history now syncs implicitly, with no separate sync step.inbox mark-read <chat_id>is new.message inmail --surfaceis removed: the v2 send-InMail request carries no surface/type discriminator, so every InMail sends from the account's own credits, whatever surface (classic, Sales Navigator, or Recruiter) it holds.
Multiline / piped text. Every write command that takes a <text> argument (message new, message send, message edit, message inmail) accepts - in place of the text to read the body from stdin. This is the clean way to send multi-paragraph content:
curviate message <chat_id> - --account acc_YOUR_ACCOUNT_ID <<'EOF'
Hi Jane,
Following up on our chat, the brief is attached.
Best, Alex
EOFEmpty stdin is rejected (exit code 2).
Inbox commands
Inbox commands do not accept --preview, except inbox mark-read.
# List chats (paginated). --unread shows unread only; --no-unread shows read only; omit for all.
curviate inbox list [--unread|--no-unread] [--limit <n>] [--cursor <token>] [--all] [--max-pages <n>]# Get a single chat by ID (also accepts a LinkedIn thread URL or 2-... conversation ID)
curviate inbox get <chat_id># Mark a chat as read (write, --preview accepted)
curviate inbox mark-read <chat_id># List messages in a chat (paginated). --before/--after filter by ISO-8601 UTC timestamp (Z suffix required).
curviate inbox messages <chat_id> [--before <iso-8601-utc>] [--after <iso-8601-utc>] [--limit <n>] [--cursor <token>] [--all] [--max-pages <n>]# Free-text search your own inbox (paginated read, rejects --preview).
# Matches participant names AND message content. <query> is positional, so
# quote multi-word terms.
curviate inbox search <query> [--limit <n>] [--cursor <token>] [--all] [--max-pages <n>]inbox search searches the connected account's own inbox. LinkedIn's search is token-prefix: a term that matches no inbox token returns an empty list (a genuine no-match), not an error. Paginate with the returned cursor (--all streams every page).
The default response is slim: id, name, type, unread_count, user_id, and last_message (id, sender_id, text). With --verbose it also carries account_id, is_group, is_1to1, and the additional last_message fields (account_id, attachments[]).
Message commands
Write commands do not show pagination flags (--limit, --cursor, --all, --max-pages); those apply only to the list reads above.
# Start a new chat and send the first message (write, multipart)
# --to accepts a LinkedIn profile URL, public slug, or attendee provider ID.
# URLs and slugs are resolved to a provider ID via a single profile lookup;
# a provider ID (ACoAA...) is used directly. Pass - as the text to read stdin.
curviate message new --to <url|slug|provider_id> "<text>" [--attach <file>...]# Send a message to an existing chat (write, multipart).
# Both the bare form and the explicit `send` subcommand work; <chat_id> accepts a thread URL.
curviate message <chat_id> "<text>" [--attach <file>...]
curviate message send <chat_id> "<text>" [--attach <file>...]# Get a single message by ID
curviate message get <chat_id> <message_id># Edit a message (write, --preview accepted). Pass - as the text to read stdin.
curviate message edit <chat_id> <message_id> "<new text>"# Delete a message (write, --preview accepted)
curviate message delete <chat_id> <message_id># React to a message (write)
# Flag is --emoji (not --reaction)
curviate message react <chat_id> <message_id> --emoji <emoji># Download a message attachment as binary (--preview not accepted)
# -o writes the bytes to a file; omit to stream to stdout
curviate message attachment <chat_id> <message_id> <attachment_id> [-o <file>]# Send an InMail (write)
# --to accepts a LinkedIn profile URL, public slug, member URN (urn:li:member:<digits>),
# or attendee provider ID. URLs and slugs are resolved via a profile lookup; a URN or
# provider ID is used directly. Pass - as the text to read stdin.
# Sends from the account's own InMail credits. There is no --surface flag in v2.
curviate message inmail --to <url|slug|urn|provider_id> --subject "<subject>" "<text>"# Get InMail credit balance (read)
curviate message inmail-balanceExamples
List your chats
curviate inbox list --account acc_YOUR_ACCOUNT_ID
# Only unread chats (does not mark them read)
curviate inbox list --unread --account acc_YOUR_ACCOUNT_IDRead messages in a chat
curviate inbox messages chat_YOUR_CHAT_ID --limit 20 --account acc_YOUR_ACCOUNT_ID
# Only messages after a given time (ISO-8601 UTC, Z suffix required)
curviate inbox messages chat_YOUR_CHAT_ID --after 2026-06-01T00:00:00Z --account acc_YOUR_ACCOUNT_ID
# Use a LinkedIn messaging thread URL directly as the chat ID
curviate inbox messages "https://www.linkedin.com/messaging/thread/2-EXAMPLE/" --account acc_YOUR_ACCOUNT_IDMark a chat as read
curviate inbox mark-read chat_YOUR_CHAT_ID --account acc_YOUR_ACCOUNT_IDStart a new conversation
# --to accepts a LinkedIn profile URL or slug (resolved to a provider ID),
# or a provider ID directly.
curviate message new --to https://www.linkedin.com/in/jane-doe "Hi, wanted to reach out about your work on AI." --account acc_YOUR_ACCOUNT_IDReply to an existing chat
curviate message chat_YOUR_CHAT_ID "Thanks for the context, that makes sense." --account acc_YOUR_ACCOUNT_IDSend a message with a file attachment
curviate message chat_YOUR_CHAT_ID "See the attached brief." --attach ./brief.pdf --account acc_YOUR_ACCOUNT_IDReply as a company page
If you administer a LinkedIn company page, you can answer the page's inbox threads as the page itself rather than as your personal profile. The acting identity travels in the chat id: pass a COMPANY_ chat id (obtained from the Inboxes commands) and the message is sent on behalf of that page.
# Discover a company inbox, list its threads, then reply as the page.
curviate inboxes list --kind company --account acc_YOUR_ACCOUNT_ID
curviate inboxes chats COMPANY_YOUR_MAILBOX_PRIMARY --account acc_YOUR_ACCOUNT_ID
curviate message send COMPANY_YOUR_MAILBOX_YOUR_CHAT "Thanks for reaching out. A teammate will follow up shortly." --account acc_YOUR_ACCOUNT_IDOn a successful company send the CLI reports the identity it acted as, for example Sent as Acme Inc (company page), so you are never guessing which identity spoke. Run the command with --preview first to confirm before sending: a COMPANY_ chat id previews as Will send as a company page.
Company pages are reply-only. They can answer an existing conversation but cannot start a new one, so there is no company equivalent of message new. For the full walkthrough (discovery, the company_id mapping, and the identity echo) see the Reply as a company page guide.
Edit or delete a message
# Preview an edit without sending
curviate message edit chat_YOUR_CHAT_ID msg_YOUR_MESSAGE_ID "Updated message text." --preview --account acc_YOUR_ACCOUNT_ID
# Delete a message
curviate message delete chat_YOUR_CHAT_ID msg_YOUR_MESSAGE_ID --account acc_YOUR_ACCOUNT_IDReact to a message
curviate message react chat_YOUR_CHAT_ID msg_YOUR_MESSAGE_ID --emoji 👍 --account acc_YOUR_ACCOUNT_IDDownload an attachment
# -o writes binary bytes to a local file (an explicit save, not a content cache)
curviate message attachment chat_YOUR_CHAT_ID msg_YOUR_MESSAGE_ID att_YOUR_ATTACHMENT_ID -o attachment.pdf --account acc_YOUR_ACCOUNT_IDSend an InMail
# --to accepts a URL, slug, member URN, or provider ID
curviate message inmail \
--to https://www.linkedin.com/in/jane-doe \
--subject "Opportunity at Acme" \
"Hi Jane, I came across your profile and wanted to share an opportunity." \
--account acc_YOUR_ACCOUNT_IDCheck InMail balance
curviate message inmail-balance --account acc_YOUR_ACCOUNT_IDSearch your inbox
curviate inbox search "sophie" --account acc_YOUR_ACCOUNT_IDThe command is inbox search, not message search. See the warning at the top of the page.
Errors you may hit
| Code | Exit | Cause | Fix |
|---|---|---|---|
ACCOUNT_NOT_FOUND | 4 | --account names an id this API key does not own, or you have no connected account yet. | Run curviate account list and use an id from that output. |
RESOURCE_NOT_FOUND | 4 | The chat, message, or attachment id does not exist on this account. | Re-read the id from inbox list or inbox messages. |
UNAUTHORIZED | 3 | Missing or invalid API key. | Set CURVIATE_API_KEY, or run curviate login. |
INVALID_REQUEST | 2 | A malformed id, an empty message body, or a missing required flag. | Read the message on stderr, it names the offending field. |
MESSAGE_WINDOW_EXPIRED | 10 | message edit or message delete ran outside the window LinkedIn allows. | Nothing to retry, the message is final. |
RECIPIENT_UNREACHABLE | 10 | The member cannot receive a message from this account (not connected, or messaging restricted). | Send a connection request first, or use message inmail. |
RATE_LIMIT_ACCOUNT | 6 | Too many actions on this LinkedIn account in the window. | Back off, then retry. See rate limits. |
Full envelope shapes are in the error reference.