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.
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.Data Schemas
Thread Structure
Message Structure
Realtime messaging
Instead of pollinggetThreadMessages on a timer, you can receive a thread’s new messages the moment they are sent — over a WebSocket. Use the subscribeToThread helper, exported from the SDK package root. It works out of the box on browsers, Deno, Bun, and Node 22+ — no extra packages to install.
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.

