Operate
Support
Built-in support inbox: per-tenant ticket store, agent assignment, customer portal, internal notes, SLA timer.
Features
- **Tickets CRUD + replies + status transitions** — assignee + priority + tags. Public portal at `/ticket/:token` for customers without an account.
- **Inbound email pipeline** — `internal/support/inbound` accepts parsed mail from the Cloudflare Email Worker. 4 routing modes in priority order: (1) reply-token `t_<token>@*` — threads onto existing ticket; (2) plus-addressed `<base>+<slug>@*` — per-tenant mail on a shared MX; (3) BYOD custom domain match; (4) legacy slug-subdomain.
- **Priority-based SLA policies** — `firstResponseMinutes` + `resolutionMinutes` + `businessHoursOnly` per priority (low / normal / high / urgent). **Honest:** SLAs apply by ticket priority; there's no audience-routed SLA (no `audienceId` column on `sla_policies`).
- **Canned responses (macros)** — saved reply templates; insert into a reply with one click.
- **Saved views** — filtered ticket queues with shareable URL. Per-agent or shared.
- **CSAT survey** — `POST /support/csat` writes a rating + comment; `GET .../csat/stats` aggregates. **Honest:** the response does NOT automatically write a Customers trait or auto-enroll detractors in a Workflow — wire that yourself via the `support.csat_submitted` event + an Automation workflow with `branch` + `update_profile` steps.
- **Contact Widget → ticket pipeline** — public submission via `POST /chatbot/message` creates a `support_tickets` row + emits `ticket.created_via_widget`. AI mode does RAG over your KB before offering to escalate.
- **SDK helpers** — `support.createTicket()` + `submitCsat()` on RN 0.18.1+ + Web 2.15.0+. In-product help flows ship without hand-rolled fetch.
- **Honest non-features:** no Slack Connect channel, no assignment rules + round-robin (manual assignee per ticket), no audience-based SLA routing, no automatic timeline panel built into the ticket detail (the customer's profile timeline lives in the Customers module; cross-link is via shared `userId`, not auto-embed).
Common use cases
- Inbox + portal + SLA for SaaS support where the customer's profile is one click away (same `userId`, same tenant).
- Product-led support entry-point via the Contact Widget with AI deflection from your KB.
- B2B SaaS that needs priority-based SLA policies + canned replies + saved views without paying Zendesk per-agent.
Support tickets
Open a support ticket from the SDK
FREEEnd-user calls `sendora.support.openTicket(...)` from your app. Returns a portal token the customer can use to read replies without signing up.
const ticket = await sendora.support.openTicket({
subject: "Payment failed",
body: "Stripe returned card_declined…",
email: "alice@example.com",
category: "billing",
});
// Returns: { id, portalToken } — link customer to /portal?token=…Read ticket via portal token
FREECustomer-facing: surface ticket history without authentication. Pass the `portalToken` returned from openTicket.
const ticket = await fetch(
`https://api.sendoracloud.com/api/v1/support/portal/by-token/${portalToken}`
).then(r => r.json());Submit CSAT rating
FREEAfter ticket close, surface a 1-5 rating widget. Drives the support module's CSAT dashboard + agent leaderboard.
await sendora.support.submitCsat({
ticketId: "tkt_abc",
rating: 5,
comment: "Fast resolution, thanks!"
});