Skip to main content
The 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.
Each Thread object returned by listThreads and getThread includes an unread_count_customer field. Sum these client-side to compute a customer’s total unread count — no separate endpoint is needed.

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 by listThreads includes unread_count_customer. Sum these client-side to display a “new messages” badge:
To fetch only threads with unread messages, pass ?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


Performance Tip: When building a chat UI, use getThreadMessages with order: 'desc' to load the most recent activity first, then use the next_cursor to load history as the user scrolls up.

Realtime messaging

Instead of polling getThreadMessages 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 the subscribeToThread 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):
The server validates the credential + participant membership before accepting the socket, then keeps it open. All frames are JSON strings, except the heartbeat (a bare 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.

Response Codes