REST API and Webhooks Reference
You’re building a “What’s new” screen inside your plugin, a relay for your own mail server or a ticket form on a headless site. You need to know which routes exist, who may call them and what they return, and guessing from browser network logs is slow and fragile.
Core Support registers 29 routes under the core-support/v1 namespace, at https://desk.example.com/wp-json/core-support/v1/. Some are public, some need a key or a signature and some exist only for wp-admin screens. Each one is listed below with its access rule. Routes marked internal serve Core Support’s own screens and may change between releases, so I wouldn’t build on them.
How Access Works
- Signed in: a WordPress user who belongs to this site, sent with WordPress’s cookie and a
wp_restnonce, or an application password. - Agent: a signed-in user with the
core_support_work_ticketscapability. Administrators, Support Agents and Support Managers have it. - Manager: a signed-in user with the
core_support_managecapability. Administrators and Support Managers have it. - Mailbox token, relay secret, site key, portal session: the credentials described with each route.
- Public: anyone, sometimes limited to a connected site’s listed addresses.
Ticket and mail routes answer 503 with storage_unavailable while the “Core Support setup failed” notice is showing. Most errors use WordPress’s usual JSON shape with code, message and data.status.
Tickets
These power the Customer Portal blocks and the agent workspace. Agents see every ticket; customers see only their own.
GET /tickets: signed in. Lists tickets, newest activity first. Parameters:status(open,waitingorresolved),searchandpage. Agents can also filter byassignment(mineorunassigned) andpriority(normal,highorurgent).POST /tickets: signed in. Opens a ticket withsubject,bodyand an optionalrequest_key. A repeat with the same key is stored once. Limited to 20 an hour per user.GET /tickets/<id>: signed in. One ticket with its conversation. Private notes are left out for customers.PATCH /tickets/<id>: signed in. Changesstatus,priorityorassignee. Send the ticket’s currentversion; a stale version is refused, so 2 agents can’t overwrite each other. Customers can only changestatus, and only when the portal settings let them resolve or reopen.POST /tickets/<id>/messages: signed in. Adds a reply withbody,version, an optionalrequest_keyandvisibility:public, orinternalfor agent notes. Limited to 120 an hour per user.PATCH /tickets/<id>/deliveries/<delivery-id>: agents only. Sendsaction(retryorcancel) and the delivery’s currentattempts. Retry works on a failed email, up to 5 attempts in all.GET /tickets/<id>/attachments/<file-id>: signed in, with the same access as the conversation. Files always download. Addinline=1to show PNG, JPEG, GIF, WebP or AVIF images in the browser.
An agent can send the header X-CS-View: customer to get exactly what the customer would see. Responses carry Cache-Control: private, no-store.
Incoming Mail Webhooks
The route key in these paths is site-<blog-id>-<mailbox-key>, such as site-1-support. Incoming mail → Connection details lists the exact URLs.
POST /webhooks/relay/<route-key>: relay secret. Receives raw email from the Cloudflare relay Worker. Body: the raw message, up to 3 MiB.POST /webhooks/inbound/<route-key>: mailbox token. Receives email from FlowMattic, Zapier, Make, n8n or similar tools, as raw source or parsed JSON fields.GET /webhooks/inbound/<route-key>: mailbox token. Tests the connection without sending anything. Returnsok, the mailbox address andmax_bytes.
Send the token as Authorization: Bearer <token>, or as X-Core-Support-Token: <token> when your server strips the Authorization header. Send Email in from FlowMattic, Zapier, Make or n8n lists every JSON field.
Both POST routes answer:
202: accepted. The body includesreceipt_id.401: wrong token or signature.403: automation tools aren’t turned on for this mailbox.404: no mailbox uses this address. Stop retrying and check the URL.413: larger than the mailbox accepts.422: missing or invalid fields (inbound only). Retrying won’t help.503: temporarily unavailable. Retry later.
Relay Signature
The bundled Worker signs every request, and you only need this to write a relay of your own. Each request carries 3 headers:
X-Core-Support-Timestamp: Unix time in seconds. Requests more than 300 seconds off the server’s clock are refused.X-Core-Support-Delivery: a unique ID for this delivery, 16 to 64 letters, digits or hyphens. A retry with the same ID is stored once.X-Core-Support-Signature:v1=followed by a hex HMAC-SHA256.
The signed text is 5 lines joined with \n: v1, the route key, the timestamp, the delivery ID and the SHA-256 hex of the body. The HMAC key is the mailbox’s relay secret.
$payload = implode("\n", ['v1', 'site-1-support', $timestamp, $delivery_id, hash('sha256', $raw_email)]);
$signature = 'v1=' . hash_hmac('sha256', $payload, $relay_secret);
Connected Sites
The site ID is the 12-character ID shown under Core Support → Connected sites.
POST /sites/<site-id>/tickets: a browser request from one of the site’s listed addresses, or a server request with the site key. Opens a ticket fromemail,name,subject,messageand optionalpage_url. Browser requests are limited to 10 an hour per visitor address and 64 KiB each. Server requests sendAuthorization: Bearer <site key>(orX-Core-Support-Key), may addmetadataandattachments, can be up to 8 MiB and always open a ticket directly.GET /sites/<site-id>/portal/config: public, listed addresses only. The site name, whether new tickets are open, the message limit and hint and whether customers may resolve or reopen.POST /sites/<site-id>/portal/code: public, listed addresses only. Emails a 6-digit sign-in code toemail.POST /sites/<site-id>/portal/session: public, listed addresses only. Exchangesemailandcodefor a session token valid 30 days.DELETE /sites/<site-id>/portal/session: portal session. Signs out.GET /sites/<site-id>/portal/tickets: portal session. The customer’s tickets opened on that site.POST /sites/<site-id>/portal/tickets: portal session. Opens a ticket withsubjectandmessage.GET /sites/<site-id>/portal/tickets/<id>: portal session. One ticket and its public conversation.PATCH /sites/<site-id>/portal/tickets/<id>: portal session. Resolves or reopens it withstatusandversion, when your settings allow it.POST /sites/<site-id>/portal/tickets/<id>/messages: portal session. Adds a reply withmessageandversion.GET /sites/<site-id>/portal/tickets/<id>/attachments/<file-id>: portal session. Downloads a file from that ticket.
Portal routes need Customer portal turned on for the site and answer 404 with portal_disabled otherwise. Send the session as Authorization: Bearer <session token> or X-CS-Portal. Host the Customer Portal on a Connected Site has the sign-in and rate limits.
Docs
GET /docs/search: public. Searches published docs withq(required), optionalcollectionslug andsitefor an embedded site. Returns up to 8 results.POST /docs/<id>/feedback: public. Records “Was this helpful?” withhelpful(true or false) and an optionalcomment. One vote per doc, visitor and day, and 20 an hour per visitor.
Changelogs
GET /changelogs: public. Every product, withslug,name,type,url,latest,releasesandfeed.GET /changelogs/<product>: public. A product’s releases, newest first, each withversion,date,stability,url,summary,changesandhtml. Parameters:page,per_page(1 to 50, default 10),stable(true hides pre-releases) andsince(only releases newer than that version).POST /changelogs/admin/parse: internal, users who can edit docs. Reads changelog text into releases for the editor.POST /changelogs/admin/probe: internal, users who can edit docs. Checks a sync source before saving it.POST /changelogs/admin/<id>/sync: internal, users who can edit that changelog. Sync now on the edit screen.
curl 'https://desk.example.com/wp-json/core-support/v1/changelogs/my-plugin?since=1.1.0&stable=1'
Chat
The public Chat routes answer only for this site and the listed addresses of connected sites. New messages work only while Chat is turned on and the answer model’s provider has a key, and config reports that as enabled. Handoffs need Offer “Talk to a person” turned on. The message and handoff limits below are defaults that the core_support_chat_rate_limit filter can change.
GET /chat/config: public. The widget’s settings, wording and branding, includingenabledandhandoff.POST /chat/messages: public. Sendsmessagewith the conversation’sthreadandtoken, plus optionalpage_urlandpage_title. Withstreamset to true, the reply arrives as server-sent events. Limited to 20 a minute per conversation, 60 per 10 minutes per address and 240 a minute for the whole site.GET /chat/threads/<uuid>: the conversation’s token in anX-Chat-Tokenheader. Returns the conversation.POST /chat/handoff: public. Turns a conversation into a ticket withthread,token,emailand optionalnameandnote. Limited to 5 an hour per address.POST /chat/admin/test: internal, managers. Test connection on the Chat screen.POST /chat/admin/learn: internal, managers. Learn all now, one batch at a time.GET /chat/admin/learning: internal, agents. Learning progress for the Knowledge tab.
Team Alert Webhook
Webhooks also go the other way. The Webhook channel under Settings → Team alerts sends a JSON POST to your https:// URL for each new ticket and, if you turn it on, each customer reply.
{
"event": "ticket_created",
"title": "New ticket #42: License question",
"url": "https://desk.example.com/wp-admin/admin.php?page=core-support&ticket=42",
"ticket": {
"id": 42,
"subject": "License question",
"status": "open",
"priority": "normal",
"source": "email",
"customer_name": "Ada",
"customer_email": "ada@example.net",
"created_at": "2026-10-18 09:30:00",
"message": ""
},
"sent_at": "2026-10-18T09:30:02+00:00"
}
eventisticket_created,customer_repliedortest.- The
X-Core-Support-Eventheader repeats the event. ticket.messageis filled only when Include a message preview is on.- With a signing secret,
X-Core-Support-Signatureissha256=followed by the HMAC-SHA256 of the raw body.
Rate limits, server errors and timeouts are retried after 1, 5 and 30 minutes. Team Alerts on Telegram, Pushover, Discord, Slack or a Webhook covers the channel settings.