ProjectR Docs
Backend (Convex)

API — support

Generated catalog of Convex functions in convex/support/. Regenerated by `bun run docs:api`.

Auto-generated from source. Do not edit by hand — run bun run docs:api.

query (3)

FunctionDescriptionArgs
getContactMessageGet a single contact message by ID with user profile info{ messageId: v.id("contactMessages"), }
getSupportStatsAggregated stats for the support dashboard within a time window. Single-pass scan via by_created_at index — appropriate for low-volume contactMessages table.{ sinceDays: v.optional(v.number()), }
listContactMessagesList contact messages for dashboard with optional status, category, and search filters{ status: v.optional( v.union( v.literal("new"), v.literal("read"), v.literal("replied"), v.literal("resolved") ) ), category: v.optional( v.union( v.literal…

mutation (4)

FunctionDescriptionArgs
bulkUpdateMessageStatusBulk update status across multiple messages (dashboard row-select bulk action){ messageIds: v.array(v.id("contactMessages")), status: v.union( v.literal("new"), v.literal("read"), v.literal("replied"), v.literal("resolved") ), }
replyToMessageReply to a contact message (dashboard) — saves sanitized reply and sends email to user. The replyText is sanitized with sanitizeHtml() before storage and before it is forwarded to the email action, preventing admin-authored HTML/JS from reaching the user inbox (defect #9).{ messageId: v.id("contactMessages"), replyText: v.string(), }
submitContactMessageSubmit a contact message from the mobile app{ category: v.union( v.literal("order_issue"), v.literal("product_question"), v.literal("returns_refunds"), v.literal("account_help"), v.literal("general_inq…
updateMessageStatusUpdate contact message status (dashboard){ messageId: v.id("contactMessages"), status: v.union( v.literal("new"), v.literal("read"), v.literal("replied"), v.literal("resolved") ), }

internalAction (2)

FunctionDescriptionArgs
sendContactNotificationSend notification email to the support inbox when a user submits a contact message. Recipients come from SUPPORT_ALERT_EMAILS (comma separated) and fall back to cx@theraw.com.my. The reply-to header carries the sender's real address when they have one, so an agent can answer from their mail client. Throws on failure so the retrier schedules a retry (#10).{ messageId: v.optional(v.id("contactMessages")), replyTo: v.optional(v.string()), senderName: v.string(), senderEmail: v.string(), senderPhone: v.optional(v…
sendReplyNotificationSend reply notification email to the user when admin replies. Receives an already-sanitized replyText from the mutation (#9). Throws on failure so the retrier schedules a retry (#10).{ userEmail: v.string(), userName: v.string(), originalSubject: v.string(), replyText: v.string(), }

On this page