Support Desk API and MCP

Read conversations, reply to customers, manage templates, pull analytics and change settings of Support Desk over the API.

Everything you can do in the Support Desk interface is available over the API under /support/*: list and filter conversations, read a conversation, reply to the customer, assign, close, add notes, manage reply templates, pull analytics, and change settings (including auto replies and the subscription link). That makes it a good fit for AI assistants and for your own automations.

Base URL: https://api.web2wave.com/api. The full reference with every parameter is in the Support Desk section of the API reference; this page explains the concepts and shows common flows.

Authentication and roles

Send your key in the api_key header, like every other endpoint.

  • A project API key acts as the project Owner.
  • A client API key (w2w_c_…) acts as that team member. Also send the project in the X-Web2wave-Project-Id header. The member's role decides what is allowed.
RoleAllowed
Owner, Administrator, project keyEverything: conversations, templates, analytics, settings, billing.
Support ManagerConversations, templates, analytics and reading settings and billing. Cannot change settings or billing.
Analyst, Editor, Project Editor403 — no access to Support Desk.

Support Desk holds your customers' correspondence, so an API key is always required — requests that identify themselves only by the Referer/Origin of your funnel domain are refused.

Conversation endpoints also require that Support Desk is enabled and covered by the trial, a subscription or a grant; otherwise they return 403 with an explanation. GET /support/settings and GET /support/billing work before that.

Replies sent with a client key are signed with that person's name and assigned to them if the conversation had no assignee. Replies sent with a project key have no person behind them: they appear as Unknown sender in analytics, and assignee=me is not available.

Endpoints

Method and pathWhat it does
GET /support/conversationsList conversations with filters (below).
GET /support/conversations/{id}One conversation: messages, notes, merged timeline. ?include=customer,events, ?include_html=1, ?mark_read=1.
GET /support/conversations/{id}/messagesOnly what is new since after_message_id / after_note_id.
GET /support/conversations/{id}/customerThe user card: users, visits, UTM, subscriptions.
POST /support/conversations/{id}/replyEmail the customer (JSON, or multipart with attachments[]).
POST /support/conversations/{id}/ai-draftDraft a reply with AI; nothing is sent.
PUT /support/conversations/{id}/statusunread, open or closed.
PUT /support/conversations/{id}/assigneeAssign a team member or clear with null.
PUT /support/conversations/{id}/request-typeSet the request type by hand.
POST /support/conversations/{id}/notesAdd an internal note.
POST /support/conversations/{id}/muteMute the sender.
GET /support/membersTeam members who can be assigned.
GET/POST /support/templates, PUT/DELETE /support/templates/{id}, POST /support/templates/startersReply templates.
GET /support/analyticsThe analytics report.
GET/PATCH /support/settings, POST /support/settings/check-dnsSettings, DNS check of your own domain.
GET /support/billing, POST /support/billing/checkout, POST /support/billing/portalTrial and subscription state, Stripe links.

Responses are JSON with success: true. Errors are { "success": false, "error_msg": "…" } (plus errors per field on a 422). Dates are ISO 8601.

List and filter conversations

curl "https://api.web2wave.com/api/support/conversations?status=active&assignee=none&request_type=refund_charge&per_page=20" \
  -H "api_key: YOUR_KEY"
ParameterValues
statusactive (default: unread + open), all, unread, open, closed
assigneeme (client key only), none, any (default), or a member id from /support/members
request_typerefund_charge, cancel_subscription, pricing_payment, access_login, bug, how_to, data_compliance, services_partnership, feature_feedback, spam_auto, other, or none (not classified)
qSearch words; all must appear in the email, subject, previews or any message
from, toLast message between these dates (project time zone)
orderdesc (default) or asc by last message
page, per_pagePagination, up to 100 per page
include_demo1 to include the built-in welcome conversation

The response has conversations, pagination, and counts — the whole backlog (unread, open, closed, unassigned), independent of your filters.

{
  "success": true,
  "conversations": [{
    "id": 4812,
    "user_email": "[email protected]",
    "subject": "Charged twice",
    "status": "unread",
    "assignee": null,
    "request_type": "refund_charge",
    "request_type_source": "ai",
    "request_type_confidence": 94,
    "last_message_preview": "Hi, I see two payments on my card…",
    "last_message_at": "2026-10-27T08:41:07+00:00",
    "is_demo": false
  }],
  "pagination": { "page": 1, "per_page": 20, "total": 1, "last_page": 1 },
  "counts": { "unread": 7, "open": 3, "closed": 120, "unassigned": 6 }
}

Read a conversation

curl "https://api.web2wave.com/api/support/conversations/4812?include=customer" -H "api_key: YOUR_KEY"

You get messages (oldest first; direction is inbound from the customer, outbound to the customer; is_automated marks auto replies), notes, a timeline that merges both in display order, and with include=customer the user card with subscriptions.

Reading does not change the status, so a bot that looks at a conversation does not mark it as read by a person. Pass mark_read=1 if you want the cabinet behaviour (unread becomes open).

To follow a conversation, call …/messages?after_message_id=<last_message_id>&after_note_id=<last_note_id> and pass back the ids it returns.

Reply to a customer

curl -X POST "https://api.web2wave.com/api/support/conversations/4812/reply" \
  -H "api_key: YOUR_KEY" -H "Content-Type: application/json" \
  -d '{
    "body": "Hi Anna,\n\nThe second payment was a duplicate — we refunded it today. It can take 5–10 days to appear.",
    "client_key": "reply-4812-2026-10-27"
  }'
  • The email is sent to the customer from your project's Send emails from address; your signature is appended, and {email}, USER_ID, {project_name} … placeholders are filled in. If Send emails from is not set you get a 422 that says so.
  • The conversation becomes open; if it had no assignee, a client key takes it.
  • Add files by sending multipart/form-data with attachments[] (up to 10 files, 20 MB each).
  • Make retries safe with client_key (or an Idempotency-Key header): the same key within 15 minutes returns the message that was already sent instead of emailing the customer again.
  • Limit: 60 replies per minute.

For an AI-assisted flow, call POST …/ai-draft first (optionally with a draft), review the text, then send it with …/reply. Drafts use your templates, so keep them current.

Close, assign, tag, note

# close
curl -X PUT ".../support/conversations/4812/status" -H "api_key: YOUR_KEY" -H "Content-Type: application/json" -d '{"status":"closed"}'

# assign to a member (ids from GET /support/members), or null to unassign
curl -X PUT ".../support/conversations/4812/assignee" -H "api_key: YOUR_KEY" -H "Content-Type: application/json" -d '{"assignee_id": 17}'

# set the request type by hand (never overwritten by the AI afterwards)
curl -X PUT ".../support/conversations/4812/request-type" -H "api_key: YOUR_KEY" -H "Content-Type: application/json" -d '{"request_type":"refund_charge"}'

# internal note, never sent to the customer
curl -X POST ".../support/conversations/4812/notes" -H "api_key: YOUR_KEY" -H "Content-Type: application/json" -d '{"body":"Refunded in Stripe, ch_3Qx…"}'

Acting on a subscription — cancel, pause, refund — uses the existing subscription endpoints; the user card (GET …/customer) tells you which user and subscription to use.

Analytics

curl "https://api.web2wave.com/api/support/analytics?period=30d" -H "api_key: YOUR_KEY"
curl "https://api.web2wave.com/api/support/analytics?period=custom&from=2026-09-01&to=2026-09-30" -H "api_key: YOUR_KEY"

period is today, 7d, 30d (default), 90d or custom (with from and to, up to 366 days). The response mirrors the analytics page:

  • kpis — inbound, outbound, customers, new_customers, closed, answer_rate, median_response, p90_response, waiting; each is { value, previous, delta_pct } against the previous period of the same length. Times are in minutes.
  • series — messages per hour/day (bucket_unit), response_buckets, heatmap (7 weekdays × 24 hours), types and type_series, managers, and status_snapshot (the current backlog).

See Support analytics for the exact definitions — notably what an inquiry is. Reports are cached for an hour; add refresh=1 to rebuild.

Settings

# read
curl "https://api.web2wave.com/api/support/settings" -H "api_key: YOUR_KEY"

# change only what you send
curl -X PATCH "https://api.web2wave.com/api/support/settings" \
  -H "api_key: YOUR_KEY" -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "signature": "Best regards,\n{agent_name}\n{project_name} Support",
    "daily_summary": true,
    "muted_emails": ["[email protected]", "*@newsletter.example.com"],
    "auto_replies": {
      "enabled": true,
      "after_reply": { "off_hours": { "enabled": true, "days": [1,2,3,4,5], "from": "09:00", "to": "18:00" } },
      "by_type": { "refund_charge": 31, "spam_auto": "none" },
      "follow_up": { "enabled": true, "days": 3 },
      "close": { "enabled": true, "days": 2, "send_message": true }
    }
  }'
  • GET returns the same things as the settings tab: the inbox address (inbox.primary — use it as Reply-To or as the forwarding target), the custom domain and its DNS records, signature, daily summary, muted list, auto replies, template placeholders, and billing/trial state.
  • PATCH needs the Owner or Administrator role. auto_replies is merged with what is saved, so {"auto_replies":{"follow_up":{"days":5}}} changes just that one value; null clears a value; the working days list is replaced as a whole. muted_emails replaces the whole list (up to 200 entries; * is a wildcard). Template ids must belong to the project — create templates first (POST /support/templates, or POST /support/templates/starters for the four starter ones).
  • Own domain: set custom_email (for example [email protected]), add the returned custom_domain.dns_records at your DNS provider, and call POST /support/settings/check-dns until verified is true.
  • Enabling needs the trial or a subscription. If the trial is over, GET /support/billing shows the state and POST /support/billing/checkout returns a Stripe Checkout url to open in a browser; POST /support/billing/portal returns the Stripe customer portal link.

MCP

Support Desk is built for AI assistants: an assistant can triage the unread queue, read a conversation together with the customer's subscriptions, draft and send a reply, close it, and report the week's analytics. Dedicated Support Desk tools for the web2wave MCP server are planned on top of this API; until then an MCP client that exposes the generic web2wave API tools can call the endpoints above directly.

Tip: Use a client API key of a Support Manager for an assistant that only answers customers. It can read and reply, but cannot change settings, billing or anything outside Support Desk and Subscriptions.

Caution: POST …/reply sends a real email to a real customer. When an AI writes replies, keep a human in the loop for sensitive types (refunds, data requests), or have it use …/ai-draft and …/notes and let a person press send.