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 are message. message has no search subcommand. An unrecognised word after message is parsed as a chat id, and the next argument as the message text, so curviate message search "sophie" does not search anything: it attempts to send the text sophie to a chat called search. To search, use curviate inbox search "sophie". Add --preview to 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 sync and inbox 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 --surface is 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
EOF

Empty 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-balance

Examples

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_ID

Read 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_ID

Mark a chat as read

curviate inbox mark-read chat_YOUR_CHAT_ID --account acc_YOUR_ACCOUNT_ID

Start 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_ID

Reply to an existing chat

curviate message chat_YOUR_CHAT_ID "Thanks for the context, that makes sense." --account acc_YOUR_ACCOUNT_ID

Send a message with a file attachment

curviate message chat_YOUR_CHAT_ID "See the attached brief." --attach ./brief.pdf --account acc_YOUR_ACCOUNT_ID

Reply 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_ID

On 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_ID

React to a message

curviate message react chat_YOUR_CHAT_ID msg_YOUR_MESSAGE_ID --emoji 👍 --account acc_YOUR_ACCOUNT_ID

Download 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_ID

Send 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_ID

Check InMail balance

curviate message inmail-balance --account acc_YOUR_ACCOUNT_ID

Search your inbox

curviate inbox search "sophie" --account acc_YOUR_ACCOUNT_ID

The command is inbox search, not message search. See the warning at the top of the page.

Errors you may hit

CodeExitCauseFix
ACCOUNT_NOT_FOUND4--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_FOUND4The chat, message, or attachment id does not exist on this account.Re-read the id from inbox list or inbox messages.
UNAUTHORIZED3Missing or invalid API key.Set CURVIATE_API_KEY, or run curviate login.
INVALID_REQUEST2A malformed id, an empty message body, or a missing required flag.Read the message on stderr, it names the offending field.
MESSAGE_WINDOW_EXPIRED10message edit or message delete ran outside the window LinkedIn allows.Nothing to retry, the message is final.
RECIPIENT_UNREACHABLE10The member cannot receive a message from this account (not connected, or messaging restricted).Send a connection request first, or use message inmail.
RATE_LIMIT_ACCOUNT6Too many actions on this LinkedIn account in the window.Back off, then retry. See rate limits.

Full envelope shapes are in the error reference.

Next steps

  • Inboxes: company-page and other secondary mailboxes.
  • Connect: send an invitation before you can message a non-connection.
  • Profile: resolve a LinkedIn URL or slug to the ids these commands expect.
  • Errors: the full code list and retry semantics.
COMPANY · LEGAL

Privacy Policy

Redmer Holding GmbHLast updated August 4, 2026

Who we are

Curviate is operated by Redmer Holding GmbH ("Curviate", "we", "us"), a German GmbH registered at Amtsgericht Bonn, HRB 29957, registered address Hostertstraße 16, 53332 Bornheim, Germany. Full company details are on our Imprint. We haven't appointed a statutory Data Protection Officer, since our processing doesn't reach the scale or sensitivity that requires one. Privacy questions go to privacy@curviate.com.

The two roles we play

When you create an account and use Curviate, we process your own data (identity, billing, API keys, connector authorizations). For that data, we are the controller.

When you use Curviate to act on your own connected LinkedIn account, viewing profiles, sending messages, managing engagement, that content and those contacts belong to that account and its people. You are the controller of that data; we are the processor, acting only on your instructions, under a Data Processing Agreement available on request (see below). If one of your contacts has a question about being reached through Curviate, you're who they should contact first; email privacy@curviate.com if you need help routing it.

What we collect, and why

DataWhy
Account identity (name, email, sign-in method)Create and secure your account
Your LinkedIn credentialsOperate the actions you request
LinkedIn content returned by an API callFulfil that specific request, nothing more
API keys and connector (OAuth) authorizationsAuthenticate your API, CLI, MCP, or SDK requests
Billing detailsCharge you correctly and meet our tax obligations
Usage and security logsKeep the service reliable and abuse-free
Support messagesRespond to you
Website analytics, only if you opt inUnderstand how the site is used

We rely on our contract with you, our legitimate interest in running and securing the service, our legal obligations (tax law, for example), and, for analytics, your consent. We never sell your data or use it to train models.

Where it's processed, and who else touches it

Our infrastructure runs in the EU. Hosting: Railway. Database and auth: Supabase, Ireland. Email: Resend. Payments: Stripe. Network security: a DDoS-protection provider sits in front of our app and never sees or stores request content. LinkedIn connectivity: a third-party infrastructure provider that lets us execute LinkedIn actions on your behalf. Error tracking: Sentry, Frankfurt. Product analytics: PostHog, Frankfurt. Uptime monitoring: Better Stack.

We give the current, named list of every provider above to any customer who asks: security@curviate.com.

Data processing agreement

A data processing agreement under Article 28 of the GDPR is available to business customers on request. Email security@curviate.com and we will send you the current version.

Outside the EU

All customer LinkedIn data, account data, and telemetry are processed and stored exclusively in EU regions of our sub-processors. A few providers we rely on (Stripe and Sentry, for example) are headquartered outside the EU/EEA; where that applies, it's covered by their own GDPR safeguards, typically the EU Standard Contractual Clauses.

How long we keep it

DataRetention
Account and workspace dataWhile your account is active
Closed accountDeleted immediately and irreversibly; see Deleting your account below
LinkedIn credentialsUntil you disconnect that account
LinkedIn contentNot stored; any transient cache clears within 1 hour, never indexed, never used for training
API keysUntil you revoke or rotate them
Connector (OAuth) authorizationsAccess token ~1 hour; refresh token up to ~12 months, or until you revoke it, whichever comes first
Billing recordsAs required by German tax law, currently up to 10 years
LogsA short operational window; metadata only, never message content

The 12-month figure above is a server-side credential for a connected AI agent or app. It is not a cookie and doesn't touch your browser session; see Cookies below for that. You can see and revoke every connector from Authorized applications in your dashboard at any time.

Cookies

We keep cookies to a minimum, and ask before anything beyond the essentials runs.

Strictly necessary, no consent needed:

NamePurposeExpiry
cc_cookieRemembers your cookie choice12 months
curviate-themeRemembers light/dark mode (local storage, not a cookie)Persistent
sb-*-auth-tokenKeeps you signed inWhile active; cleared on sign-out

Analytics, only if you accept:

NamePurposeExpiry
_gaGoogle Analytics: distinguishes visitors2 years
_gidGoogle Analytics: distinguishes visitors24 hours
_ga_<container id>Google Analytics: persists session state2 years

No advertising cookies, ever. Accept and reject are equally easy, and you can change your mind any time via Cookie Preferences in the footer; we won't ask again for 12 months unless something material changes. Our LinkedIn connect flow and OAuth authorization screen never set anything beyond the essentials, so no banner appears there.

Connecting an AI agent or app

Curviate is built for AI agents and automated clients as much as for people. If you connect an app like Claude, or your own code, via an API key or an OAuth connector, it can act on your workspace within the access you gave it. What it does with anything it receives back, including what it sends to its own AI model, is between you and that provider; review its practices before connecting it. Review and revoke any connection any time from your dashboard.

Deleting your account

You can delete your account yourself, from Settings in your dashboard. It takes effect immediately and it cannot be undone. There is no grace period and nothing to restore afterwards, so export anything you want to keep before you start.

Deleting removes your sign-in identity, which frees your email address for reuse straight away, along with your profile, your workspace membership and settings, your API keys, and your seats. For any connected LinkedIn account, we instruct our infrastructure provider to delete it, and your access ends immediately. Records of the connection itself can remain in our systems; email privacy@curviate.com if you need those removed as well. LinkedIn content was never stored in the first place, so there is none of it to delete.

A few things are kept on purpose. We would rather name them than claim a clean sweep:

  • Billing records, for as long as German tax law requires. They hold plan, seat count, amount, and payment references; no name, no email, no LinkedIn data.
  • A record that the deletion happened, so we can show you or a regulator that we did it.
  • A security log of which requests were made, kept for 90 days and then removed automatically. It records that a request happened, never what was in it.
  • A one-way fingerprint, if you used a free trial, that lets us recognise a repeat trial. It holds no readable identifier and cannot be read back into your name, your email, or your LinkedIn profile.

Internal workspace identifiers can also remain in operational records such as queue entries and rate-limit counters. Those carry no name, no email, and no content. If you want to know exactly what is left for your own account, ask us at privacy@curviate.com.

Your rights

You can access, correct, delete, restrict, or object to your data, port it elsewhere, and withdraw consent at any time: email privacy@curviate.com. A copy of your data in a machine-readable format is available on request. We don't make automated decisions about you that have a legal or similarly significant effect. You can also complain to a supervisory authority; ours is the Landesbeauftragte für Datenschutz und Informationsfreiheit Nordrhein-Westfalen (LDI NRW), www.ldi.nrw.de, though you're free to complain to the one in your own country instead.

Keeping it secure

Credentials are encrypted and never logged, returned, or shared. LinkedIn actions run through native, humanized flows; full detail is on our Security & Compliance page. If a breach puts your rights at risk, we'll notify the authorities and you, as GDPR requires. Curviate isn't directed at, or offered to, anyone under 16.

Changes

We'll update this page when our practices change, and reset the cookie prompt if the change is material.

Contact