MessagingService class (accessed via client.messaging) handles real-time customer ↔ store communication. It manages threaded conversations, message delivery, read state, and full thread lifecycle management.
Security Model
Messaging threads carry sensitive customer information — order inquiries, complaints, contact details, and uploaded attachments. To prevent one customer from reading or changing another customer’s conversation, every customer-facing messaging endpoint requires a signed customer session token (xAuthToken) issued by client.authentication. Ownership is enforced on every call:
Obtain a customer session via
client.authentication.login(...) or client.authentication.verifyOtp(...) and pass customerSession.access_token as xAuthToken on every call below.
If you authenticate shoppers with your own identity provider rather than through Galactic Core, send
xExternalAuth (a signed identity assertion) or xIdpToken (your provider’s own token, which
Galactic Core forwards to the store’s configured verifier) in place of xAuthToken. The examples
below use xAuthToken, but every one of these endpoints accepts any of the three — send exactly one.
Thread Discovery & Filtering
listThreads
Retrieve message threads with advanced server-side filtering. Pinned threads are automatically returned at the top of the result set.
Response
The
customerId query parameter must match the customer_id on xAuthToken, otherwise the request is rejected.Computing total unread
Each thread returned bylistThreads includes unread_count_customer. Sum these client-side to display a “new messages” badge:
?unread=true as a filter.
getThread
Retrieve high-level metadata for a single thread.
Response
The thread must belong to the customer on your session token, otherwise the request is rejected.
updateThread 🔒
Update thread state including status, priority, or lifecycle flags.
Requires a Secret Key (for merchant lifecycle changes) or a customer
xAuthToken belonging to the thread owner (for customer-driven actions like muting or archiving their own conversation). A customer request must own the thread, otherwise it is rejected.markThreadRead 🔒
Marks all customer messages in the thread as read and resets the store unread count to 0.
The caller must own the thread. The
id must be a valid UUID — malformed values return 400.Message Operations
getThreadMessages
Fetch paginated history for a thread. Deleted messages are automatically excluded.
Response
Only the thread’s owning customer (or a Secret Key holder) can read its messages.
sendMessage 🔒
Reply to an existing conversation.
The caller must own the thread. Returns
201 with the newly persisted message body on success.editMessage 🔒
Update the content of one of the customer’s own messages. Sets is_edited: true and records
edited_at. Works with a publishable key plus the customer’s xAuthToken (a customer-self write,
same browser-origin pattern as cart/wishlist) — the session token proves ownership of the message.
Response
You can only edit or delete messages the customer personally authored. Store and system messages cannot be modified.
deleteMessage 🔒
Soft-deletes one of the customer’s own messages. The message is hidden from list responses but
retained for audit trails. Works with a publishable key plus the customer’s xAuthToken (the
session token proves ownership). Returns success, the message_id, and the deleted_at timestamp.
Same ownership check as
editMessage — you can only delete a message you authored. Soft delete only; the row is retained for audit.createConversation 🔒
Initiate a new support ticket with an initial message.
Response
Whenever
body.customer_id is supplied, it must match the customer_id on xAuthToken. Returns 201 with both the new thread and the initial message.thread_type is inferred when it is not supplied: order_inquiry when order_id is present,
product_inquiry when product_id is present, and general otherwise. The response echoes the
resolved thread_type along with order_id and product_id, so a storefront can confirm the
classification its own request produced without a follow-up read.
Data Schemas
Thread Structure
Message Structure
Realtime messaging
A thread’s new messages arrive as they are sent over a WebSocket, rather than on the interval agetThreadMessages poll would impose. The subscribeToThread helper is exported from the SDK package root and uses the runtime’s built-in WebSocket, so it runs on browsers, Deno, Bun, and Node 22+ with no additional dependency.
subscribeToThread
Opens a WebSocket to the thread and calls onMessage for every new message — including the store’s replies — as it arrives. Pass your publishable key plus one customer credential (the signed-in customer’s session token, or a bring-your-own-auth assertion). It returns an unsubscribe function.
The value
subscribeToThread returns is a callable unsubscribe function (call it to disconnect)
that also carries two senders for publishing your own state:
markRead() both sends the instant “seen” cue over the socket and persists your read position via
POST /v1/messaging/threads/{id}/read, so the receipt survives a reconnect.
The connection is authorized server-side: the customer credential is validated and confirmed to be the thread’s participant before the socket is accepted, so a customer only ever receives their own thread’s messages. The same call works for both Galactic-Core-authenticated customers (pass authToken) and bring-your-own-auth customers (pass externalAuth).
This is a WebSocket, not a request that counts toward your monthly request quota.
Raw WebSocket protocol (without the SDK)
If you’re not using the TypeScript SDK — a mobile app, another language, or your own client — connect to the WebSocket directly and speak the frame protocol below. Everything thesubscribeToThread
helper does (messages, presence, typing, read receipts) is available on the raw socket.
Connect (browsers/WS handshakes can’t set headers, so credentials go in the query string):
ping/pong).
Server → client frames (things you receive):
Client → server frames (things you send):
pid is the participant id (the shopper’s customer id, or store for the merchant); role is
customer or store. Presence is per-conversation — it reflects who’s connected to this
thread, not global online state. Build a “seller is online” dot from presence.join/leave, a typing
bubble from presence.typing, and read ticks from presence.read.

