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. 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.
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.
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


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

A thread’s new messages arrive as they are sent over a WebSocket, rather than on the interval a getThreadMessages 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 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