New You can now talk to your Gelee agent from Claude, ChatGPT or Cursor through our MCP. Read the docs →

Reference

Conversations and sending

Read whole threads, reply, start new conversations and InMails, edit or unsend your own messages, and work the AI SDR's waiting drafts. Sends go out at once: there is no approval step on the API.

Last updated 22 October 2026

Sending needs the send permission

Anything the other person sees needs a key made with Read + write + send messages. Every message is checked the same way the app checks one: your do-not-contact list, people who opted out, your workspace's link rules and the safety checks on the text. One that fails answers 422 with the reason, and nothing is sent.

Recent conversations

GET/inbox

Read live from LinkedIn across every connected seat, with the whole story of each person. Slower than other reads, so it is capped at 30 calls a minute.

Query
limitinteger
Default 50, max 100.
scopecampaign | all
campaign (default) is people from your campaigns. all is everything in the inboxes, including personal messages and recruiters.

seats names any seat whose inbox could not be read (failed) or was not reached in time (not_read), so a gap is never silent. A seat shared with another workspace only ever shows your own campaign conversations.

curl "https://www.gelee.ai/api/v1/inbox?limit=50" \
  -H "Authorization: Bearer gk_your_key"
Response
{ "data": [
    { "id": "chat_88", "seat_id": "SExF…", "attendee_name": "Jane Doe",
      "attendee_provider_id": "ACoAA…", "unread_count": 1,
      "last_message": { "text": "Sounds great!",
                        "at": "2026-10-14T13:04:11Z", "from_me": false },
      "linkedin_url": "https://www.linkedin.com/in/janedoe",
      "campaign": { "id": "9a2c…", "name": "Q4 Founders" },
      "sender": { "seat_id": "SExF…", "name": "Sofia Salazar", … },
      "booking_likelihood": 78, "booking_likelihood_tier": "A",
      "flags": { "interested": true, … }, … }
  ],
  "scope": "campaign",
  "seats": { "total": 8, "failed": [], "not_read": [] } }

Read a whole thread

GET/inbox/{chat_id}/messages

From our stored copy, so it is fast and costs the seat nothing on LinkedIn. The newest page comes first and each page reads oldest to newest. Pass cursor to page back.

Query
limitinteger
1 to 100, default 30.
cursorstring
The cursor a previous page returned.
scopecampaign | all
all to read a conversation that is not from a campaign.
curl "https://www.gelee.ai/api/v1/inbox/chat_88/messages?limit=30" \
  -H "Authorization: Bearer gk_your_key"
Response
{ "data": [
    { "id": "msg_90…", "chat_id": "chat_88",
      "text": "Hi Jane, saw your post about hiring SDRs…",
      "from_me": true, "sender_name": "Sofia Salazar",
      "sent_at": "2026-10-02T10:05:00Z", "message_type": "MESSAGE",
      "subject": null, "attachments": [], "reactions": [] },
    { "id": "msg_91…", "text": "Sounds great!", "from_me": false, … }
  ],
  "chat": { "chat_id": "chat_88", "seat_id": "SExF…",
            "attendee_name": "Jane Doe", "attendee_provider_id": "ACoAA…" },
  "cursor": "eyJ0IjoiMjAyNi0xMC0wMiJ9" }

Reply in a conversation

POST/inbox/{chat_id}/messagessend permission
Body
textstringrequired
Up to 1,900 characters. Links are tracked like the app's.
stop_aiboolean
Default true: the AI SDR stops replying in this conversation, so your system owns it. Send false to keep it on.
scopeall
To reach a conversation that is not from a campaign.

Any AI draft waiting in that conversation is dismissed, so the person never gets two answers. counted_as is conversation when they wrote to you in the last 14 days and outreach otherwise, in which case it counts against the seat's own message limits like a campaign message.

Refused withWhy
409 duplicate_messageThe same text already went to this person in the last day.
422 text_blocked / link_blockedIt failed a safety check, or has a link that is not one of your workspace's own.
429 seat_pacedThis seat sent a message a few seconds ago. Wait and retry.
429 seat_limit_reachedThe seat is out of messages for now. The message says when to try again.
curl -X POST "https://www.gelee.ai/api/v1/inbox/chat_88/messages" \
  -H "Authorization: Bearer gk_your_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: crm-msg-5d70-1" \
  -d '{
    "text": "Thanks Jane, here is the report: https://acme.example/r/5d70"
  }'
Response
{ "data": { "message_id": "msg_92…", "chat_id": "chat_88",
            "seat_id": "SExF…", "sent_at": "2026-10-22T15:04:11Z",
            "drafts_dismissed": 1, "ai_stopped": true,
            "counted_as": "conversation" } }

Start a conversation or InMail

POST/conversationssend permission
Body
seat_idstringrequired
From GET /me or GET /seats.
linkedin_url or linkedin_idstringrequired
A profile link works for anyone already in one of your campaigns. For anyone else, pass their member id (ACoA…). We never look a stranger up live, because that spends the seat's profile views.
textstringrequired
Up to 1,900 characters.
inmailobject
{ "subject": "…" } sends an InMail instead. Needed for anyone who is not a 1st-degree connection.

Counts against the seat's message or InMail limits exactly like a campaign send. Connection requests are not sent here: they go through a campaign, so pacing and your campaign rules always apply.

curl -X POST "https://www.gelee.ai/api/v1/conversations" \
  -H "Authorization: Bearer gk_your_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: crm-open-5d70" \
  -d '{
    "seat_id": "SExF…",
    "linkedin_id": "ACoAAB…",
    "text": "Hi Jane, congrats on the Series A.",
    "inmail": {
      "subject": "Quick idea for Acme"
    }
  }'
Response
{ "data": { "message_id": "msg_93…", "chat_id": "chat_97",
            "seat_id": "SExF…", "sent_at": "2026-10-22T15:06:00Z",
            "drafts_dismissed": 0, "ai_stopped": false,
            "counted_as": "outreach" } }

Edit, unsend, react, mark read

PATCH/inbox/{chat_id}/messages/{message_id}send permission

Only your own seat's messages can be changed. The other person's messages never can.

CallWhat it does
PATCH …/messages/{message_id}Edit a message your seat sent: { "text": "…" }. The change shows on LinkedIn.
DELETE …/messages/{message_id}Unsend it. It disappears on LinkedIn too.
POST …/messages/{message_id}/reactionsReact with an emoji: { "reaction": "👍" }. The other person sees it.
POST /inbox/{chat_id}/readMark it read in Gelee and on LinkedIn. Needs Read + write only.
curl -X PATCH "https://www.gelee.ai/api/v1/inbox/chat_88/messages/msg_92…" \
  -H "Authorization: Bearer gk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Thanks Jane, here is the report (updated link): https://acme.example/r/5d70"
  }'

AI drafts waiting for a yes

GET/drafts

The person's message, the AI's draft and the whole story of the person, newest first.

Query
statusstring
pending, needs_human, follow_up or autopilot_pending. All four by default.
campaign_idstring
Only this campaign.
limit, offsetinteger
Max 200.
CallWhat it does
GET /drafts/{id}One draft.
PATCH /drafts/{id}{ "text": "…" } changes the words without sending. A draft about to auto-send is taken back for a person.
POST /drafts/{id}/approveSend it, like Approve in the app. Optional { "text" } sends your edit instead. Send permission. A draft that already went out answers 409 draft_not_pending.
POST /drafts/{id}/skipNothing is sent and the AI stops drafting in that conversation. Optional { "reason" }.
curl "https://www.gelee.ai/api/v1/drafts?status=pending" \
  -H "Authorization: Bearer gk_your_key"
Response
{ "data": [
    { "id": "77c1…", "status": "pending", "name": "Jane Doe",
      "their_message": "What does pricing look like for 3 seats?",
      "ai_draft": "Happy to walk you through it. Does Thursday at 2pm work?",
      "intent": "pricing_question",
      "campaign": { "id": "9a2c…", "name": "Q4 Founders" },
      "sender": { "seat_id": "SExF…", "name": "Sofia Salazar", … },
      "chat": { "chat_id": "chat_88", "app_url": "…" },
      "booking_likelihood": 78, … ,
      "created_at": "2026-10-22T14:58:00Z" }
  ],
  "pagination": { "limit": 50, "offset": 0, "total": 3 } }

Replies handed to a person

GET/needs-human

Replies the AI SDR would not answer on its own, the same list as Inbox, Needs Human. reason_label is the plain-English version to show a person.

reasonreason_label
needs_humanThe SDR could not answer this one
wants_emailThey asked to be emailed instead
ai_calloutThey asked whether they are talking to a bot
auto_bookedA booking attempt needs a person to confirm it
empty_or_too_shortThe draft came back empty
apology / leaked_reasoning / ai_admissionThe draft failed a quality check
bump_recheck_failedWe could not re-read the thread before following up
blacklist_check_failedWe could not verify this person against the blocklist
curl "https://www.gelee.ai/api/v1/needs-human" \
  -H "Authorization: Bearer gk_your_key"
Response
{ "data": [
    { "id": "77c1…", "chat_id": "chat_88", "name": "Jane Doe",
      "message": "Can you send pricing to jane@acme.com instead?",
      "ai_draft": "Happy to send that over. What is the best address?",
      "reason": "wants_email",
      "reason_label": "They asked to be emailed instead",
      "waiting_since": "2026-10-22T03:12:00Z", "hours_waiting": 6, … }
  ],
  "pagination": { "limit": 50, "offset": 0, "total": 3 } }

Went quiet after a yes

GET/stalled-leads

People who replied positively and then went quiet, stalest first. Each row has the whole story plus days_since_activity, last_activity_at and sentiment.

Query
daysinteger
Quiet for at least this many days. Default 5.
campaign_id, limit, offset
Narrow and page.
curl "https://www.gelee.ai/api/v1/stalled-leads?days=5" \
  -H "Authorization: Bearer gk_your_key"

Follow-up reminders

POST/followupsread_write

The inbox Follow-ups tab. The seat comes from the conversation, never from the request. When one comes due, a follow-up draft appears for approval, as in the app.

CallWhat it does
GET /followupsSoonest first. ?status=pending (default), snoozed or done.
PATCH /followups/{id}{ "status": "done" | "pending" | "snoozed", "due_at", "note" }
DELETE /followups/{id}Remove the reminder.
curl -X POST "https://www.gelee.ai/api/v1/followups" \
  -H "Authorization: Bearer gk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "chat_id": "chat_88",
    "due_at": "2026-11-03",
    "note": "Asked to reconnect after their board meeting"
  }'
Response
{ "data": { "id": "f_3c…", "chat": { "chat_id": "chat_88", "app_url": "…" },
            "name": "Jane Doe", "sender": { "seat_id": "SExF…", … },
            "due_at": "2026-11-03", "status": "pending",
            "note": "Asked to reconnect after their board meeting" } }

Common patterns

  • Your own inbox: subscribe to the reply_received webhook, read the thread, and answer with POST /inbox/{chat_id}/messages. The AI steps back from that conversation by default.
  • Approve from your own tool: list GET /drafts, show the draft, then approve or skip.
  • Never double-send: always pass an Idempotency-Key that is unique to the message.