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 theX-Web2wave-Project-Idheader. The member's role decides what is allowed.
| Role | Allowed |
|---|---|
| Owner, Administrator, project key | Everything: conversations, templates, analytics, settings, billing. |
| Support Manager | Conversations, templates, analytics and reading settings and billing. Cannot change settings or billing. |
| Analyst, Editor, Project Editor | 403 — 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 path | What it does |
|---|---|
GET /support/conversations | List 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}/messages | Only what is new since after_message_id / after_note_id. |
GET /support/conversations/{id}/customer | The user card: users, visits, UTM, subscriptions. |
POST /support/conversations/{id}/reply | Email the customer (JSON, or multipart with attachments[]). |
POST /support/conversations/{id}/ai-draft | Draft a reply with AI; nothing is sent. |
PUT /support/conversations/{id}/status | unread, open or closed. |
PUT /support/conversations/{id}/assignee | Assign a team member or clear with null. |
PUT /support/conversations/{id}/request-type | Set the request type by hand. |
POST /support/conversations/{id}/notes | Add an internal note. |
POST /support/conversations/{id}/mute | Mute the sender. |
GET /support/members | Team members who can be assigned. |
GET/POST /support/templates, PUT/DELETE /support/templates/{id}, POST /support/templates/starters | Reply templates. |
GET /support/analytics | The analytics report. |
GET/PATCH /support/settings, POST /support/settings/check-dns | Settings, DNS check of your own domain. |
GET /support/billing, POST /support/billing/checkout, POST /support/billing/portal | Trial 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"| Parameter | Values |
|---|---|
status | active (default: unread + open), all, unread, open, closed |
assignee | me (client key only), none, any (default), or a member id from /support/members |
request_type | refund_charge, cancel_subscription, pricing_payment, access_login, bug, how_to, data_compliance, services_partnership, feature_feedback, spam_auto, other, or none (not classified) |
q | Search words; all must appear in the email, subject, previews or any message |
from, to | Last message between these dates (project time zone) |
order | desc (default) or asc by last message |
page, per_page | Pagination, up to 100 per page |
include_demo | 1 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 a422that says so. - The conversation becomes open; if it had no assignee, a client key takes it.
- Add files by sending
multipart/form-datawithattachments[](up to 10 files, 20 MB each). - Make retries safe with
client_key(or anIdempotency-Keyheader): 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),typesandtype_series,managers, andstatus_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 }
}
}'GETreturns 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.PATCHneeds the Owner or Administrator role.auto_repliesis merged with what is saved, so{"auto_replies":{"follow_up":{"days":5}}}changes just that one value;nullclears a value; the working days list is replaced as a whole.muted_emailsreplaces the whole list (up to 200 entries;*is a wildcard). Template ids must belong to the project — create templates first (POST /support/templates, orPOST /support/templates/startersfor the four starter ones).- Own domain: set
custom_email(for example[email protected]), add the returnedcustom_domain.dns_recordsat your DNS provider, and callPOST /support/settings/check-dnsuntilverifiedistrue. - Enabling needs the trial or a subscription. If the trial is over,
GET /support/billingshows the state andPOST /support/billing/checkoutreturns a Stripe Checkouturlto open in a browser;POST /support/billing/portalreturns 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 …/replysends 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-draftand…/notesand let a person press send.