and backticks survive intact.\nAn unquoted heredoc has produced an empty message that went out blank.\n\n```bash\ncat \u003c\u003c'EOF' | curviate message send \"\u003cchat_id>\" - --preview\nHi Thomas,\n\nthanks for connecting. I saw the work you shared last week.\nEOF\n```\n\n## Retrieval mode: `--mode` and `--max-age`\n\nExactly four reads decide between a stored copy and a live LinkedIn call: `profile me`,\n`profile \u003cid>`, `inbox get` and `inbox messages`. Two of them are here.\n\n| `--mode` | Behaviour |\n|---|---|\n| `auto` (default) | A stored copy while it is fresh, otherwise fetch. |\n| `live` | Always fetch from LinkedIn. |\n| `refill` | A stored copy at any age; fetch only when nothing is stored. |\n| `cache_only` | Never fetch. A store miss is refused, not fetched. |\n\n`--max-age \u003cseconds>` (0 to 31536000) overrides those presets in both directions; `--max-age 0` is\nthe same as `--mode live`. Every response carries `source: store | live` plus `observed_at` under\n`--json`, and a `provenance:` line on stderr in human mode. Read `source` rather than assuming.\n\n- **`cache_only` with `--max-age` is a usage error, exit `2`**, raised before any network call.\n `cache_only` never reaches LinkedIn at any age, so a freshness threshold cannot change its answer.\n Drop `--max-age`, or use `--mode refill`.\n- **`cache_only` on a store miss is exit `14` (`NOT_STORED`)**: the chat may exist perfectly well on\n LinkedIn, this API just holds no copy. It is not \"not found\" (`4`), so re-checking the chat id is\n the wrong move, and it is not retryable as sent. Re-read with `refill`, `auto` or `live`.\n- **`inbox messages`: one bare `--mode live` page restarts the chat walk and leaves it unservable by\n `cache_only` afterwards.** A listing is served from the store only over a *closed* walk.\n `inbox messages --all` walks to `cursor: null` and closes it. When you need a chat to be\n `cache_only`-servable later, reach for `--all` rather than a single live page.\n- **Every other command refuses the flags outright rather than ignoring them**: `unknown flag\n --mode`, exit `2`.\n\n## Reading: `inbox`\n\n| Command | What it does | Confidence |\n|---|---|---|\n| `curviate inbox list` | Conversations, newest activity first, 20 by default. `--unread` / `--no-unread` filter by read state; `--inbox \u003cfolder>` selects `primary` (default), `inmail`, `archived`, `spam`, `jobs` or `starred`. | proven |\n| `curviate inbox get \u003cchat_id>` | One chat's detail including `last_message` (full text and sender), the cheap triage read. Accepts `--mode`/`--max-age`. | proven |\n| `curviate inbox messages \u003cchat_id>` | The messages in one chat. Full `text` per message; `is_sender` (0 or 1) says who sent each. `--before`/`--after` take ISO-8601 UTC with a `Z` suffix. Accepts `--mode`/`--max-age`. | proven |\n| `curviate inbox search \"\u003cquery>\"` | Free-text search of the account's own inbox: participant names and message content. | proven |\n| `curviate inbox mark-read \u003cchat_id>` | Mark a chat read. | proven |\n\n**Listing chats does not mark anything read**, so triage freely.\n\nA chat item already carries the counterpart's identity: `user_id` (the `ACoAA…` member id), an\nembedded `user{}` (`id`, `type`, `display_name`, `profile_url`, `public_picture_url`), and `name`,\nthe chat's own display name, which is populated on direct messages. Escalate to `curviate profile\n\u003cuser_id>` only for what `user{}` does not carry: headline, network distance, the full profile.\n\n### Traps\n\n- **`--limit` on `inbox list` and `inbox messages` is validated client-side to 1-25** (default 20).\n Outside that range the command exits `2`.\n- **Neither `inbox list` nor `inbox search` reliably answers \"does a chat with X exist\".** A single\n `inbox list --limit 25` call has repeatedly omitted a chat created seconds earlier, across\n candidates, not a one-off race. `inbox search` has a different defect: its result set is ranked\n and capped rather than exhaustive, so a common first name can omit an exact match that a rarer name\n finds immediately. When the answer matters, walk `inbox list` by `--cursor` to exhaustion (or a\n generous `--max-pages`) rather than trusting one page or the search.\n- **Inbox threads carry no vanity slug.** `user.profile_url` is built from the member id and\n `public_identifier` is absent even under `--verbose`. Join inbox-driven flows on the member id.\n- **`inbox list` has no date-range flags.** `--before`/`--after` exist on `inbox messages` only, and\n filter messages within one chat. Filter a chat list client-side on `last_message_timestamp`.\n- **A very recent send or delete can take minutes to appear in `inbox messages`** (LinkedIn-side\n indexing). `message get \u003cchat_id> \u003cmessage_id>` reflects it immediately, use that to confirm a\n send, not a re-list.\n\n## Discovering inboxes: `inboxes`\n\n| Command | What it does | Confidence |\n|---|---|---|\n| `curviate inboxes list` | Every inbox the account can act in: its own, plus the company pages it administers. `--kind personal\\|company` and `--company-id \u003cid>` narrow it. Beta. | proven |\n| `curviate inboxes chats \u003cinbox_id>` | One inbox's conversations. Each chat id is send-ready. Beta. | proven |\n\n**A `COMPANY_…` chat id sends as that page, with no extra flag.** Pass it to `message send` and the\nmessage goes out from the page rather than from you; the output confirms with\n`Sent as \u003cname> (company page)`. Company inboxes are reply-only: they cannot start a conversation.\nThe page's own admin inbox has a second, richer surface under `company` (see `curviate-profile`).\n\n## Sending: `message`\n\n| Command | What it does | Confidence |\n|---|---|---|\n| `curviate message new --to \u003crecipient> \"\u003ctext>\"` | Start a new chat. `--to` takes a profile URL, a bare slug or a member id, and resolves it for you. `--attach \u003cfile>` is repeatable. | proven |\n| `curviate message send \u003cchat_id> \"\u003ctext>\"` | Reply in an existing chat. A `COMPANY_` chat id sends as that page. | proven |\n| `curviate message get \u003cchat_id> \u003cmessage_id>` | One message. Reflects a very recent send immediately, unlike the thread listing. | proven |\n| `curviate message edit \u003cchat_id> \u003cmessage_id> \"\u003ctext>\"` | Edit a message within LinkedIn's allowed window. | proven |\n| `curviate message delete \u003cchat_id> \u003cmessage_id>` | Delete a message. | proven |\n| `curviate message react \u003cchat_id> \u003cmessage_id> \u003cemoji>` | Add an emoji reaction to a message. | proven |\n| `curviate message attachment \u003cchat_id> \u003cmessage_id> \u003cattachment_id>` | Download an attachment. | proven |\n| `curviate message inmail --to \u003crecipient> --subject \"\u003csubject>\" \"\u003ctext>\"` | Send an InMail. Both `--to` and `--subject` are required. Consumes an InMail credit. | proven |\n| `curviate message inmail-balance` | Remaining InMail credits. Check before a run that depends on them. | proven |\n\n**Chat ids** look like `CLASSIC_2-MzJmZTg1…` for a personal chat and `COMPANY_\u003cid>_2-…` for a page.\n\n**There is no idempotency key and no server-side de-duplication.** A send that times out may already\nhave landed. Re-read the thread (`inbox messages`, or `message get` for the id you just wrote)\nbefore re-issuing anything.\n\n## Event delivery: `webhook`\n\nMessage and account events arrive by webhook rather than by polling.\n\n| Command | What it does | Confidence |\n|---|---|---|\n| `curviate webhook create --source \u003cs> --request-url \u003chttps url> --account-ids \u003cids>` | Register an endpoint. `--source` is `messaging`, `user` or `account_status`. Also `--name`, `--events`, `--data`, `--no-enabled`. | proven |\n| `curviate webhook list` | Registered webhooks. | proven |\n| `curviate webhook events` | The canonical event catalogue, read it before subscribing to a name. | proven |\n| `curviate webhook get \u003cid>` | One webhook. | proven |\n| `curviate webhook update \u003cid>` | Update in place. `--request-url`, `--name`, `--enabled`, `--events`, `--data`, `--account-ids`. The source is immutable. | proven |\n| `curviate webhook delete \u003cid>` | Remove a subscription permanently. | proven |\n| `curviate webhook verify --secret \u003cs> --header \u003csig> --body \u003cjson\\|file\\|->` | Verify a delivery signature offline, no network call. `--max-age-secs` rejects a replay older than the given age (default 300). | proven |\n\nVerify the signature on every delivery before acting on its body.\n\n## Full command surface\n\n\u003c!-- generated: command surface, CLI 0.33.0 -->\n\nRead from the CLI's own `--help` at version 0.33.0. Descriptions, traps and confidence\ntags elsewhere in this skill are hand-written and carry the version they were established against.\n\nEvery command below that takes flags at all also accepts `--json`.\n\n| Command | Arguments | Flags |\n|---|---|---|\n| `curviate inbox list` | *(none)* | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--fields`, `--limit`, `--cursor`, `--all`, `--max-pages`, `--page-delay`, `--preview`, `--verbose`, `--beta`, `--unread`, `--inbox` |\n| `curviate inbox get` | `CHATID` | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--fields`, `--preview`, `--verbose`, `--beta`, `--mode`, `--max-age` |\n| `curviate inbox mark-read` | `CHATID` | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--fields`, `--preview`, `--verbose`, `--beta` |\n| `curviate inbox messages` | `CHATID` | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--fields`, `--limit`, `--cursor`, `--all`, `--max-pages`, `--page-delay`, `--preview`, `--verbose`, `--beta`, `--mode`, `--max-age`, `--before`, `--after` |\n| `curviate inbox search` | `QUERY` | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--fields`, `--limit`, `--cursor`, `--all`, `--max-pages`, `--page-delay`, `--preview`, `--verbose`, `--beta` |\n| `curviate inboxes list` | *(none)* | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--fields`, `--preview`, `--verbose`, `--beta`, `--kind`, `--company-id` |\n| `curviate inboxes chats` | `INBOXID` | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--fields`, `--limit`, `--cursor`, `--all`, `--max-pages`, `--page-delay`, `--preview`, `--verbose`, `--beta` |\n| `curviate message` | `CHATID` `TEXT` | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--preview`, `--verbose`, `--beta`, `--attach` |\n| `curviate message new` | `TEXT` | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--preview`, `--verbose`, `--beta`, `--to` *(required)*, `--attach` |\n| `curviate message send` | `CHATID` `TEXT` | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--preview`, `--verbose`, `--beta`, `--attach` |\n| `curviate message get` | `CHATID` `MESSAGEID` | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--fields`, `--preview`, `--verbose`, `--beta` |\n| `curviate message edit` | `CHATID` `MESSAGEID` `TEXT` | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--preview`, `--verbose`, `--beta` |\n| `curviate message delete` | `CHATID` `MESSAGEID` | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--preview`, `--verbose`, `--beta` |\n| `curviate message react` | `CHATID` `MESSAGEID` `EMOJI` | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--preview`, `--verbose`, `--beta`, `-emoji, --emojiAlias` |\n| `curviate message attachment` | `CHATID` `MESSAGEID` `ATTACHMENTID` | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--fields`, `--preview`, `--verbose`, `--beta`, `-o, --output` |\n| `curviate message inmail` | `TEXT` | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--preview`, `--verbose`, `--beta`, `--to` *(required)*, `--subject` *(required)* |\n| `curviate message inmail-balance` | *(none)* | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--fields`, `--preview`, `--verbose`, `--beta` |\n| `curviate webhook create` | *(none)* | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--fields`, `--limit`, `--cursor`, `--preview`, `--verbose`, `--beta`, `--source` *(required)*, `--request-url` *(required)*, `--account-ids` *(required)*, `--name`, `--no-enabled`, `--events`, `--data` |\n| `curviate webhook list` | *(none)* | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--fields`, `--limit`, `--cursor`, `--all`, `--max-pages`, `--page-delay`, `--preview`, `--verbose`, `--beta` |\n| `curviate webhook events` | *(none)* | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--fields`, `--limit`, `--cursor`, `--preview`, `--verbose`, `--beta` |\n| `curviate webhook get` | `ID` | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--fields`, `--limit`, `--cursor`, `--preview`, `--verbose`, `--beta` |\n| `curviate webhook update` | `ID` | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--fields`, `--limit`, `--cursor`, `--preview`, `--verbose`, `--beta`, `--request-url`, `--name`, `--enabled`, `--events`, `--data`, `--account-ids` |\n| `curviate webhook delete` | `ID` | `--api-key`, `--profile`, `--account`, `--base-url`, `--timeout`, `--fields`, `--limit`, `--cursor`, `--preview`, `--verbose`, `--beta` |\n| `curviate webhook verify` | *(none)* | `--secret` *(required)*, `--header` *(required)*, `--body` *(required)*, `--max-age-secs` |\n\n\u003c!-- /generated -->\n\n## Exit codes to branch on here\n\n| Code | Meaning | What to do |\n|---|---|---|\n| `1` | `INTERNAL` from the server itself: a genuine bug on the platform side. | Worth one retry; if it repeats it is a bug to report, not a state to work around. |\n| `2` | Usage or invalid input, often raised before any network call: a `--limit` outside 1-25, `cache_only` with `--max-age`, a missing `--subject` on an InMail. | Fix the invocation. Never retry unchanged. |\n| `4` | Not found: a wrong chat, message or member identifier. | Re-resolve the id; do not retry as sent. |\n| `5` | Three causes, one code: read `error.code`. `NO_ACTIVE_SEAT`: the account is on no active seat. `LINKEDIN_FEATURE_NOT_SUBSCRIBED`: LinkedIn itself lacks the feature. `BETA_NOT_ENABLED`: the operation is beta-gated and this workspace has not opted in (pass `--beta` for one call, or a human enables it in Settings). | Branch on `error.code`: the three fixes have nothing in common, and none is fixed by retrying unchanged. |\n| `6` | `PLATFORM_RATE_LIMIT` and its siblings. Carries `retry_after` in whole seconds. A response naming `budgetRow` means only that row is paused; every other row on the account keeps working. | **Back off and retry** after that many seconds. On a named `budgetRow`, switch to other work on the account rather than backing off across the board. |\n| `7` | Transient platform fault: a request that got no response at all (network error, DNS failure, timeout) or one that came back as something other than a valid API answer. Carries `retryLikelyToSucceed: true`. | Retry with backoff. |\n| `8` | Account or connection state. Read `error.code`: `ACCOUNT_RESTRICTED`, `LINKEDIN_AUTH_FAILED`, `LINKEDIN_COOKIE_INVALID` need a reconnect. | Depends on `error.code`. |\n| `10` | The edit or delete window expired, or the recipient is unreachable. | Not retryable as sent. Do not resend. |\n| `13` | `BUDGET_EXHAUSTED`: a safety rule of your own refused the send, not LinkedIn. Read `error.safetyReason`: `ceiling` means the row named in `error.budgetRow` hit its configured limit; `activity_window` means the account is outside the hours it works in (no `budgetRow` on that one). **Nothing reached LinkedIn and nothing was spent; the message was not delivered.** `reset_at` can be weeks out, and may be `null` where no clock frees it. | **Do not back off and retry.** `error.safetyHint.parameter` names the exact setting to change. Read `quotas[]` via `curviate account get \u003cacc_id> --json`, then wait for the named reset or change that setting. |\n| `14` | `NOT_STORED`: a `cache_only` read the store cannot answer. | Re-read with `refill`, `auto` or `live`. |\n","licenseDocuments":[]}}
인터페이스는 현지화되어 있으며, Skill 소개와 설명 문서는 선택한 언어로 AI가 자동 번역합니다. 코드·명령어·경로·전문 용어는 원문을 유지하며 원작자 문서가 우선합니다.
Skills 둘러보기
개발 및 엔지니어링 · SKILL.md

curviate-inbox

curviate-inbox

Read and send LinkedIn messages with the Curviate CLI. Covers `inbox` (list, get, messages, search, mark-read), `inboxes` (personal and company-page discovery), `message` (new, send, get, edit, delete, react, attachment, InMail), replying as a company page, retrieval mode (`--mode`/`--max-age`) on the two inbox reads, and `webhook` for delivery of message e…

Independent GitHub snapshot · Automated classification · Original documentation governs

Skill 문서

GitHub

name: curviate-inbox description: "Read and send LinkedIn messages with the Curviate CLI. Covers inbox (list, get, messages, search, mark-read), inboxes (personal and company-page discovery), message (new, send, get, edit, delete, react, attachment, InMail), replying as a company page, retrieval mode (--mode/--max-age) on the two inbox reads, and webhook for delivery of message events. Use when triaging conversations, reading a thread, sending or replying to a DM or InMail, or wiring event delivery." version: 0.1.0 author: Curviate license: MIT tags: [LinkedIn, CLI, Agents, Sales, Recruiting, Outreach] repository: https://github.com/Curviate/curviate-plugin

Curviate: inbox and messaging

Messaging is the highest-consequence surface here: every send lands in a real person's inbox and cannot be unsent. Preview first, always.

Command surface established against CLI 0.33.0.

Before any command

npm install -g @curviate/cli && curviate --version    # needs Node 18 or newer
curviate login --api-key <key>                        # or export CURVIATE_API_KEY
curviate account list --json                          # the acc_id for --account
  • Credentials resolve flag > environment > stored profile (CURVIATE_API_KEY, CURVIATE_BASE_URL, CURVIATE_ACCOUNT).
  • --profile <name> picks the stored credential set; --account <acc_id> picks which connected LinkedIn account sends this message. On a multi-account tenant, getting this wrong sends from the wrong person. Name the account explicitly on every write.
  • --preview before every send. It renders the resolved request (recipient, text, acting account) without sending. On a read command it is refused with exit 2.
  • --json on anything you parse; --fields a,b,c to project (a message carries 22 fields); --verbose when a slim response looks suspiciously empty.
  • Put global flags at the end of the command.
  • Branch on the exit code, never on prose. See the table at the end.

Text input

Message text is a positional argument, and - reads stdin. Use a quoted heredoc: the quoted delimiter disables every shell expansion, so apostrophes, accents, $ and backticks survive intact. An unquoted heredoc has produced an empty message that went out blank.

cat <<'EOF' | curviate message send "<chat_id>" - --preview
Hi Thomas,

thanks for connecting. I saw the work you shared last week.
EOF

Retrieval mode: --mode and --max-age

Exactly four reads decide between a stored copy and a live LinkedIn call: profile me, profile <id>, inbox get and inbox messages. Two of them are here.

--modeBehaviour
auto (default)A stored copy while it is fresh, otherwise fetch.
liveAlways fetch from LinkedIn.
refillA stored copy at any age; fetch only when nothing is stored.
cache_onlyNever fetch. A store miss is refused, not fetched.

--max-age <seconds> (0 to 31536000) overrides those presets in both directions; --max-age 0 is the same as --mode live. Every response carries source: store | live plus observed_at under --json, and a provenance: line on stderr in human mode. Read source rather than assuming.

  • cache_only with --max-age is a usage error, exit 2, raised before any network call. cache_only never reaches LinkedIn at any age, so a freshness threshold cannot change its answer. Drop --max-age, or use --mode refill.
  • cache_only on a store miss is exit 14 (NOT_STORED): the chat may exist perfectly well on LinkedIn, this API just holds no copy. It is not "not found" (4), so re-checking the chat id is the wrong move, and it is not retryable as sent. Re-read with refill, auto or live.
  • inbox messages: one bare --mode live page restarts the chat walk and leaves it unservable by cache_only afterwards. A listing is served from the store only over a closed walk. inbox messages --all walks to cursor: null and closes it. When you need a chat to be cache_only-servable later, reach for --all rather than a single live page.
  • Every other command refuses the flags outright rather than ignoring them: unknown flag --mode, exit 2.

Reading: inbox

CommandWhat it doesConfidence
curviate inbox listConversations, newest activity first, 20 by default. --unread / --no-unread filter by read state; --inbox <folder> selects primary (default), inmail, archived, spam, jobs or starred.proven
curviate inbox get <chat_id>One chat's detail including last_message (full text and sender), the cheap triage read. Accepts --mode/--max-age.proven
curviate inbox messages <chat_id>The messages in one chat. Full text per message; is_sender (0 or 1) says who sent each. --before/--after take ISO-8601 UTC with a Z suffix. Accepts --mode/--max-age.proven
curviate inbox search "<query>"Free-text search of the account's own inbox: participant names and message content.proven
curviate inbox mark-read <chat_id>Mark a chat read.proven

Listing chats does not mark anything read, so triage freely.

A chat item already carries the counterpart's identity: user_id (the ACoAA… member id), an embedded user{} (id, type, display_name, profile_url, public_picture_url), and name, the chat's own display name, which is populated on direct messages. Escalate to curviate profile <user_id> only for what user{} does not carry: headline, network distance, the full profile.

Traps

  • --limit on inbox list and inbox messages is validated client-side to 1-25 (default 20). Outside that range the command exits 2.
  • Neither inbox list nor inbox search reliably answers "does a chat with X exist". A single inbox list --limit 25 call has repeatedly omitted a chat created seconds earlier, across candidates, not a one-off race. inbox search has a different defect: its result set is ranked and capped rather than exhaustive, so a common first name can omit an exact match that a rarer name finds immediately. When the answer matters, walk inbox list by --cursor to exhaustion (or a generous --max-pages) rather than trusting one page or the search.
  • Inbox threads carry no vanity slug. user.profile_url is built from the member id and public_identifier is absent even under --verbose. Join inbox-driven flows on the member id.
  • inbox list has no date-range flags. --before/--after exist on inbox messages only, and filter messages within one chat. Filter a chat list client-side on last_message_timestamp.
  • A very recent send or delete can take minutes to appear in inbox messages (LinkedIn-side indexing). message get <chat_id> <message_id> reflects it immediately, use that to confirm a send, not a re-list.

Discovering inboxes: inboxes

CommandWhat it doesConfidence
curviate inboxes listEvery inbox the account can act in: its own, plus the company pages it administers. --kind personal|company and --company-id <id> narrow it. Beta.proven
curviate inboxes chats <inbox_id>One inbox's conversations. Each chat id is send-ready. Beta.proven

A COMPANY_… chat id sends as that page, with no extra flag. Pass it to message send and the message goes out from the page rather than from you; the output confirms with Sent as <name> (company page). Company inboxes are reply-only: they cannot start a conversation. The page's own admin inbox has a second, richer surface under company (see curviate-profile).

Sending: message

CommandWhat it doesConfidence
curviate message new --to <recipient> "<text>"Start a new chat. --to takes a profile URL, a bare slug or a member id, and resolves it for you. --attach <file> is repeatable.proven
curviate message send <chat_id> "<text>"Reply in an existing chat. A COMPANY_ chat id sends as that page.proven
curviate message get <chat_id> <message_id>One message. Reflects a very recent send immediately, unlike the thread listing.proven
curviate message edit <chat_id> <message_id> "<text>"Edit a message within LinkedIn's allowed window.proven
curviate message delete <chat_id> <message_id>Delete a message.proven
curviate message react <chat_id> <message_id> <emoji>Add an emoji reaction to a message.proven
curviate message attachment <chat_id> <message_id> <attachment_id>Download an attachment.proven
curviate message inmail --to <recipient> --subject "<subject>" "<text>"Send an InMail. Both --to and --subject are required. Consumes an InMail credit.proven
curviate message inmail-balanceRemaining InMail credits. Check before a run that depends on them.proven

Chat ids look like CLASSIC_2-MzJmZTg1… for a personal chat and COMPANY_<id>_2-… for a page.

There is no idempotency key and no server-side de-duplication. A send that times out may already have landed. Re-read the thread (inbox messages, or message get for the id you just wrote) before re-issuing anything.

Event delivery: webhook

Message and account events arrive by webhook rather than by polling.

CommandWhat it doesConfidence
curviate webhook create --source <s> --request-url <https url> --account-ids <ids>Register an endpoint. --source is messaging, user or account_status. Also --name, --events, --data, --no-enabled.proven
curviate webhook listRegistered webhooks.proven
curviate webhook eventsThe canonical event catalogue, read it before subscribing to a name.proven
curviate webhook get <id>One webhook.proven
curviate webhook update <id>Update in place. --request-url, --name, --enabled, --events, --data, --account-ids. The source is immutable.proven
curviate webhook delete <id>Remove a subscription permanently.proven
curviate webhook verify --secret <s> --header <sig> --body <json|file|->Verify a delivery signature offline, no network call. --max-age-secs rejects a replay older than the given age (default 300).proven

Verify the signature on every delivery before acting on its body.

Full command surface

Read from the CLI's own --help at version 0.33.0. Descriptions, traps and confidence tags elsewhere in this skill are hand-written and carry the version they were established against.

Every command below that takes flags at all also accepts --json.

CommandArgumentsFlags
curviate inbox list(none)--api-key, --profile, --account, --base-url, --timeout, --fields, --limit, --cursor, --all, --max-pages, --page-delay, --preview, --verbose, --beta, --unread, --inbox
curviate inbox getCHATID--api-key, --profile, --account, --base-url, --timeout, --fields, --preview, --verbose, --beta, --mode, --max-age
curviate inbox mark-readCHATID--api-key, --profile, --account, --base-url, --timeout, --fields, --preview, --verbose, --beta
curviate inbox messagesCHATID--api-key, --profile, --account, --base-url, --timeout, --fields, --limit, --cursor, --all, --max-pages, --page-delay, --preview, --verbose, --beta, --mode, --max-age, --before, --after
curviate inbox searchQUERY--api-key, --profile, --account, --base-url, --timeout, --fields, --limit, --cursor, --all, --max-pages, --page-delay, --preview, --verbose, --beta
curviate inboxes list(none)--api-key, --profile, --account, --base-url, --timeout, --fields, --preview, --verbose, --beta, --kind, --company-id
curviate inboxes chatsINBOXID--api-key, --profile, --account, --base-url, --timeout, --fields, --limit, --cursor, --all, --max-pages, --page-delay, --preview, --verbose, --beta
curviate messageCHATID TEXT--api-key, --profile, --account, --base-url, --timeout, --preview, --verbose, --beta, --attach
curviate message newTEXT--api-key, --profile, --account, --base-url, --timeout, --preview, --verbose, --beta, --to (required), --attach
curviate message sendCHATID TEXT--api-key, --profile, --account, --base-url, --timeout, --preview, --verbose, --beta, --attach
curviate message getCHATID MESSAGEID--api-key, --profile, --account, --base-url, --timeout, --fields, --preview, --verbose, --beta
curviate message editCHATID MESSAGEID TEXT--api-key, --profile, --account, --base-url, --timeout, --preview, --verbose, --beta
curviate message deleteCHATID MESSAGEID--api-key, --profile, --account, --base-url, --timeout, --preview, --verbose, --beta
curviate message reactCHATID MESSAGEID EMOJI--api-key, --profile, --account, --base-url, --timeout, --preview, --verbose, --beta, -emoji, --emojiAlias
curviate message attachmentCHATID MESSAGEID ATTACHMENTID--api-key, --profile, --account, --base-url, --timeout, --fields, --preview, --verbose, --beta, -o, --output
curviate message inmailTEXT--api-key, --profile, --account, --base-url, --timeout, --preview, --verbose, --beta, --to (required), --subject (required)
curviate message inmail-balance(none)--api-key, --profile, --account, --base-url, --timeout, --fields, --preview, --verbose, --beta
curviate webhook create(none)--api-key, --profile, --account, --base-url, --timeout, --fields, --limit, --cursor, --preview, --verbose, --beta, --source (required), --request-url (required), --account-ids (required), --name, --no-enabled, --events, --data
curviate webhook list(none)--api-key, --profile, --account, --base-url, --timeout, --fields, --limit, --cursor, --all, --max-pages, --page-delay, --preview, --verbose, --beta
curviate webhook events(none)--api-key, --profile, --account, --base-url, --timeout, --fields, --limit, --cursor, --preview, --verbose, --beta
curviate webhook getID--api-key, --profile, --account, --base-url, --timeout, --fields, --limit, --cursor, --preview, --verbose, --beta
curviate webhook updateID--api-key, --profile, --account, --base-url, --timeout, --fields, --limit, --cursor, --preview, --verbose, --beta, --request-url, --name, --enabled, --events, --data, --account-ids
curviate webhook deleteID--api-key, --profile, --account, --base-url, --timeout, --fields, --limit, --cursor, --preview, --verbose, --beta
curviate webhook verify(none)--secret (required), --header (required), --body (required), --max-age-secs

Exit codes to branch on here

CodeMeaningWhat to do
1INTERNAL from the server itself: a genuine bug on the platform side.Worth one retry; if it repeats it is a bug to report, not a state to work around.
2Usage or invalid input, often raised before any network call: a --limit outside 1-25, cache_only with --max-age, a missing --subject on an InMail.Fix the invocation. Never retry unchanged.
4Not found: a wrong chat, message or member identifier.Re-resolve the id; do not retry as sent.
5Three causes, one code: read error.code. NO_ACTIVE_SEAT: the account is on no active seat. LINKEDIN_FEATURE_NOT_SUBSCRIBED: LinkedIn itself lacks the feature. BETA_NOT_ENABLED: the operation is beta-gated and this workspace has not opted in (pass --beta for one call, or a human enables it in Settings).Branch on error.code: the three fixes have nothing in common, and none is fixed by retrying unchanged.
6PLATFORM_RATE_LIMIT and its siblings. Carries retry_after in whole seconds. A response naming budgetRow means only that row is paused; every other row on the account keeps working.Back off and retry after that many seconds. On a named budgetRow, switch to other work on the account rather than backing off across the board.
7Transient platform fault: a request that got no response at all (network error, DNS failure, timeout) or one that came back as something other than a valid API answer. Carries retryLikelyToSucceed: true.Retry with backoff.
8Account or connection state. Read error.code: ACCOUNT_RESTRICTED, LINKEDIN_AUTH_FAILED, LINKEDIN_COOKIE_INVALID need a reconnect.Depends on error.code.
10The edit or delete window expired, or the recipient is unreachable.Not retryable as sent. Do not resend.
13BUDGET_EXHAUSTED: a safety rule of your own refused the send, not LinkedIn. Read error.safetyReason: ceiling means the row named in error.budgetRow hit its configured limit; activity_window means the account is outside the hours it works in (no budgetRow on that one). Nothing reached LinkedIn and nothing was spent; the message was not delivered. reset_at can be weeks out, and may be null where no clock frees it.Do not back off and retry. error.safetyHint.parameter names the exact setting to change. Read quotas[] via curviate account get <acc_id> --json, then wait for the named reset or change that setting.
14NOT_STORED: a cache_only read the store cannot answer.Re-read with refill, auto or live.
저장한 Skills