Quickstart
Send your first message in under 5 minutes.
This guide walks you through sending your first message with the Linq Partner API.
Prerequisites
Section titled “Prerequisites”Before you begin, make sure you have:
- A bearer token from your Linq representative
- At least one phone number provisioned on your account
- A recipient phone number in E.164 format (e.g.,
+15556667777)
1. Set up your coding agent (optional)
Section titled “1. Set up your coding agent (optional)”The Linq plugin teaches Claude Code or Cursor how Linq works, and gives it a search_docs tool and an execute tool that runs against your account. Skip this if you’d rather call the API directly — the rest of the guide works either way.
Authenticate once. Both the CLI and the plugin read the same credential:
npm install -g @linqapp/cli@latestlinq login --token <your-bearer-token>/plugin marketplace add linq-team/linq-ai/plugin install linq@linq-aiReload with /reload-plugins, then confirm with claude mcp list — expect plugin:linq:linq … ✔ Connected.
git clone https://github.com/linq-team/linq-aimkdir -p ~/.cursor/plugins/localrsync -a --exclude .git --exclude node_modules linq-ai/ ~/.cursor/plugins/local/linq/Then run Developer: Reload Window.
Now ask for what you want in plain language — Add Linq messaging to this app, I already have an API key — and the agent writes the code in the sections below for you. See AI coding agents for Codex, troubleshooting, and what each skill covers.
2. Install an SDK (optional)
Section titled “2. Install an SDK (optional)”npm install @linqapp/sdkpip install linq-pythonNo installation needed — use curl from your terminal.
3. Send your first message
Section titled “3. Send your first message”Give POST /v3/messages the recipients and the message — no from. Linq picks the sending line and resolves the chat for you:
curl -X POST https://api.linqapp.com/api/partner/v3/messages \ -H "Authorization: Bearer $LINQ_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "to": [ "+14155559876" ], "message": { "parts": [ { "type": "text", "value": "Hi! Thanks for reaching out — how can we help?" } ] } }'await client.messages.create({ to: ["+14155559876"], message: { parts: [ { type: "text", value: "Hi! Thanks for reaching out — how can we help?", }, ], },});client.messages.create( to=["+14155559876"], message={ "parts": [ { "type": "text", "value": "Hi! Thanks for reaching out — how can we help?", }, ], },)client.Messages.Create(context.TODO(), linq.MessageNewParams{ To: linq.F([]string{"+14155559876"}), Message: linq.F(map[string]any{ Parts: linq.F([]any{ map[string]any{ Type: linq.F("text"), Value: linq.F("Hi! Thanks for reaching out — how can we help?"), }, }), }),})The response tells you what it decided:
{ "chat_id": "94c6bf33-31d9-40e3-a0e9-f94250ecedb9", "created_new_chat": true, "from": "+12052535597", "from_selection": { "reason": "new_best_number", "reused_existing_chat": false }, "is_group": false, "service": "iMessage", "message": { "id": "69a37c7d-af4f-4b5e-af42-e28e98ce873a", "parts": [ { "type": "text", "value": "Hi! Thanks for reaching out — how can we help?" } ] }}Keep chat_id — the rest of this guide uses it.
4. Send a follow-up message
Section titled “4. Send a follow-up message”Pass the chat_id from the send response to add messages to the same conversation:
curl -X POST https://api.linqapp.com/api/partner/v3/chats/{chat_id}/messages \ -H "Authorization: Bearer $LINQ_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "parts": [ { "type": "text", "value": "Following up!" } ] }'const message = await client.chats.messages.send(chatId, { parts: [ { type: 'text', value: 'Following up!' } ],});message = client.chats.messages.send( chat_id, parts=[{"type": "text", "value": "Following up!"}],)5. Set up webhooks
Section titled “5. Set up webhooks”To receive real-time notifications when messages are delivered, read, or received, create a webhook subscription:
curl -X POST https://api.linqapp.com/api/partner/v3/webhook-subscriptions \ -H "Authorization: Bearer $LINQ_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "target_url": "https://your-server.com/webhook?version=2026-02-03", "subscribed_events": [ "message.sent", "message.received", "message.delivered", "message.read", "message.failed" ] }'const subscription = await client.webhookSubscriptions.create({ target_url: 'https://your-server.com/webhook?version=2026-02-03', subscribed_events: [ 'message.sent', 'message.received', 'message.delivered', 'message.read', 'message.failed', ],});subscription = client.webhook_subscriptions.create( target_url="https://your-server.com/webhook?version=2026-02-03", subscribed_events=[ "message.sent", "message.received", "message.delivered", "message.read", "message.failed", ],)Tip: If no version is specified, the subscription uses the latest available version at creation time. Pass
?version=YYYY-MM-DDexplicitly to pin a specific payload format. See Webhooks → Versioning and Signature verification.
6. Review your setup with an agent
Section titled “6. Review your setup with an agent”Copy this prompt into your own AI coding agent to check your integration against Linq’s best practices.
You are auditing a codebase that integrates with the Linq Partner API
(iMessage / RCS / SMS messaging). Verify it follows Linq's best practices for
deliverability, chat health, and line reputation. This is READ-ONLY — do not
change code unless I ask.
Step 1 — Ground yourself in Linq's public docs. Start with the index at
https://docs.linqapp.com/llms.txt, then fetch the pages you need — at minimum
Best Practices, Chat Health, Phone Reputation, Sending Messages, and Webhooks,
plus the /v3 API reference for the endpoints below. (https://docs.linqapp.com/llms-full.txt
has every page in one file, but it is large — prefer the index and targeted
pages.) If you cannot fetch these, stop and tell me rather than auditing from
memory.
Step 2 — Locate the integration. Search the codebase for the Linq base URL
(api.linqapp.com/api/partner), "/v3/" request paths, an official SDK — Node
`@linqapp/sdk`, Python `linq-python` (imported as `linq`), or Go
`github.com/linq-team/linq-go` — and the inbound webhook handler, so you know
where sending, onboarding, and webhook handling live.
Step 3 — Audit against these requirements. Each item is something my code is
supposed to do — confirm whether it actually does, and cite the file and line:
Opt-out (compliance — most important)
- The code should scan every inbound message on the message.received webhook for
opt-out keywords — STOP, UNSUBSCRIBE, OPTOUT, CANCEL, END, QUIT (whole message;
exact and case-sensitive, except OPT OUT which matches in any casing, spaced,
hyphenated or not) — plus any clear "stop messaging me" intent, and a match
should immediately stop all outbound to that recipient. Linq rejects sends to
a keyword-opted-out recipient with 403 (error code 2024), but only the exact
keywords trigger that block — conversational stop requests are the code's job
to catch — and a 2024 rejection should be honored, not retried.
- Every send to an opted-out recipient is rejected, including a final courtesy
message. If the code sends one confirmation telling the recipient they can
reply any time to resume, that single request should set override_optout: true.
The override applies only to the request it is set on and does not lift the
block; each use is recorded, so it should appear once per opt-out, never in a
retry loop.
- The code should treat a chat whose health_status is OPTED_OUT as never-send
until Linq clears the status. Linq clears it as soon as the recipient replies
again in any chat with you (any inbound that is not itself an opt-out
keyword), so the code should gate on the current health_status rather than
tracking opt-ins itself.
Sending & line selection
- The code should send with POST /v3/messages using `to` and NO `from`. Linq
then picks the best line, load-balances across your pool, reuses the
recipient's existing healthy line, and fails over off a flagged line
automatically (see from_selection.reason in the response).
- The code should NOT call GET /v3/available_number (or pin a fixed `from`)
before each send — that defeats the automatic load-balancing and failover.
Onboarding new users
- The code should use GET /v3/available_number when onboarding a NEW user, to
get the best available line (and its vcf_url contact card) to show them — e.g.
a number or deeplink shown at signup — so new users spread evenly across the
pool. That is what available_number is for; it is not a per-message call.
Contact card
- The code should create the contact card once per line with
POST /v3/contact_card (initial setup only — later changes use
PATCH /v3/contact_card), and share it through the dedicated
POST /v3/chats/{chatId}/share_contact_card endpoint.
- New contacts should be inbound-first — let the recipient message first. The
card should be shared only after at least one outbound message exists in the
chat, and re-shared about once a day, since there's no confirmation the user
saved it.
Health & reputation gating
- Before sending, the code should check the chat's health_status and the line's
reputation from GET /v3/phone_numbers, and slow or pause on AT_RISK /
CRITICAL. It should also handle the phone_number.status_updated webhook to
react when a line's reputation changes.
- New users should onboard onto HEALTHY lines. The code should NOT migrate users
off an AT_RISK line to escape the status — improve engagement and let the line
recover instead.
Engagement & cadence
- Outbound should be built to get replies (aim for 3+ replies early and roughly
a 1:2 inbound:outbound ratio). When a recipient stops replying, the code
should slow down and then stop, rather than keep messaging someone who isn't
responding.
Volume & ramp
- The code should keep each line under ~7,000 messages/day (inbound + outbound).
That is a performance guideline, not a reputation threshold — steady high
volume with healthy reply rates is fine.
- The code should not start roughly 50 or more brand-new conversations per line
in a rolling 24 hours. Check bulk import, list upload, and campaign kickoff
paths for anything that opens a whole audience at once, and confirm first
contact is spread across days and across lines.
- The code should ramp a line's daily volume gradually rather than jumping
several-fold above what that line has recently been sending. Look for
scheduled or triggered sends that can take a quiet line to a large day in one
step.
Step 4 — Report:
1. A table: Check | Status (pass / gap / n/a / unknown) | Where (file:line) | Fix.
2. A short action list, highest deliverability and compliance risk first.
Ground every finding in code you actually read. If you cannot determine an item,
mark it unknown rather than guessing. Next steps
Section titled “Next steps”- Best Practices — Patterns for healthy conversations and deliverability
- AI coding agents — Full plugin setup for Claude Code, Cursor, and Codex
- Sending Messages — Text, media, threading, and effects
- Attachments — Send images, videos, and documents
- Webhooks — Signature verification and event handling
- Group Chats — Multi-participant conversations
- Error Codes — Troubleshooting API errors
- API Reference — Complete endpoint specification