{"ns":"helpdesk","title":"Relay Desk","version":"3.3","summary":"Relay Desk is a customer-service platform. Agents work tickets in a console with views, first-reply SLA timers, a ticket workspace (public reply vs internal note, macros, properties), bulk actions, a live chat queue, a knowledge base and reports. Customers use a public help center to file requests, look up their status and search articles.","problem":"Support teams need to answer customers quickly without ever exposing internal discussion, losing a teammate’s reply, or silently breaking an SLA; customers need a simple way to ask for help and follow up.","personas":[{"name":"Support agent","need":"Work a queue of tickets, reply, add internal notes, use macros and chat, and see which tickets are about to breach."},{"name":"Customer","need":"Submit a request, receive a request number and check its status without seeing internal notes."},{"name":"Team lead","need":"Trust that reports, SLA counts and merges reflect what actually happened."}],"goals":["Internal notes are never visible to customers.","SLA state always matches the ticket’s priority, age and status.","A reply is never sent over a teammate’s newer reply without the agent seeing it first, and destructive actions need explicit confirmation."],"features":[{"id":"AUT","title":"Agent sign-in","summary":"Email and password sign-in to the console.","reqs":[{"id":"FR-AUT-01","weight":"Must","text":"A valid agent signs in and lands on \"Your unsolved tickets\" with the Views sidebar showing counts.","accept":["URL becomes /helpdesk/agent/views/mine; the header shows the agent name and role","Seven views: Your unsolved tickets, Unassigned tickets, All unsolved tickets, SLA breached, Pending tickets, Recently solved, All tickets (60)"]},{"id":"FR-AUT-02","weight":"Should","text":"Empty and wrong credentials are rejected on the sign-in page.","accept":["Empty form: \"Enter your email and password\"","Wrong password: \"Invalid email or password\"; the page stays on sign-in"]}]},{"id":"LST","title":"Ticket list","summary":"Views with search, filters, sorting and paging.","reqs":[{"id":"FR-LST-01","weight":"Should","text":"The list pages 25 tickets at a time and supports search, status/priority filters and Clear.","accept":["\"All tickets\" shows \"60 tickets\" and the pager \"1–25 of 60\"; after Next twice \"51–60 of 60\" with Next disabled","Search \"refund\" lists only tickets whose subject or tags mention refund, including #1001","A priority filter with no matches shows \"No tickets match this view and \"refund\".\"; Clear restores 60 tickets"]},{"id":"FR-LST-02","weight":"Could","text":"Opening a ticket link while signed out signs the agent in and then opens that ticket; reload keeps the agent on it."}]},{"id":"SLA","title":"SLA timers","summary":"First-reply SLA per ticket.","reqs":[{"id":"FR-SLA-01","weight":"Must","text":"The first-reply deadline is the ticket’s creation time plus its priority target: Urgent 1 h, High 4 h, Normal 8 h, Low 24 h; a ticket past the deadline with no reply is breached and listed in \"SLA breached\".","accept":["#1001 (High, 6 h old, no reply) is in \"SLA breached\" with a red negative badge such as \"−2h 0m\"","The \"SLA breached\" view count matches the number of breached tickets"]},{"id":"FR-SLA-02","weight":"Should","text":"Badges reflect state: running, warning, paused, met.","accept":["#1004 (High, New, 90 min old) shows a neutral badge of about \"2h 30m\"","Pending and On-hold tickets show \"⏸ Paused\"","Solved tickets or tickets with a first reply show \"✓ Met\", never a running timer"]}]},{"id":"WRK","title":"Ticket workspace","summary":"Reply, internal notes, solving rules and properties.","reqs":[{"id":"FR-WRK-01","weight":"Must","text":"A public reply is added to the thread under the agent’s name, keeps the status the agent chose and records the first response.","accept":["The composer defaults to \"Public reply\" with hint \"Reply to Elena Park — the customer will see this\"","Choosing Pending changes the button to \"Submit as Pending\"; after sending: toast \"Ticket #1001 updated · Pending\", reply last in the thread, status pill Pending","\"First reply due\" in Properties shows \"✓ Met\""]},{"id":"FR-WRK-02","weight":"Must","text":"An internal note is visible to agents only and is never shown to the customer.","accept":["The Internal note tab turns the composer yellow with \"Internal note — only agents can see this\"","The note appears in the thread with a yellow \"Internal note\" tag","The customer’s status lookup shows only public messages; no note, including seeded ones, appears"]},{"id":"FR-WRK-03","weight":"Must","text":"A ticket cannot be solved without an assignee.","accept":["Submitting as Solved on an unassigned ticket shows \"Assignee is required to solve a ticket\" and the status stays New","\"Take it\" assigns the agent; solving then succeeds with toast \"Ticket #1002 updated · Solved\" and the ticket leaves \"Unassigned tickets\""]},{"id":"FR-WRK-04","weight":"Should","text":"The Root cause field is shown only for tickets of type Incident, is required to solve one and is cleared when the type changes.","accept":["Type Question: no Root cause field; Type Incident: \"Root cause — required to solve\" appears","Solving an Incident without it shows \"Root cause is required to solve an Incident\" and the status stays New","Switching the type back to Question, saving, then to Incident again shows an empty Root cause"]},{"id":"FR-WRK-05","weight":"Should","text":"Assignee is a searchable combobox listing \"— Unassigned —\" and every agent.","accept":["Typing \"pri\" leaves only Priya Nair; \"zzz\" shows \"No agent matches \"zzz\"\"","Enter selects the highlighted option and closes the list","\"Save properties\" persists the assignee across reload"]},{"id":"FR-WRK-06","weight":"Should","text":"Agents can create a ticket on behalf of a customer; it links to an existing customer record.","accept":["\"Requester email is required\" then \"Subject is required\" when missing","Success: toast \"Ticket #NNNN created\" and the workspace opens with status New","\"Other tickets\" lists the customer’s earlier tickets"]},{"id":"FR-WRK-07","weight":"Could","text":"Unknown ticket ids show a friendly page: \"Ticket not found\" and \"There is no ticket #9999.\""}]},{"id":"SND","title":"Sending and concurrency","summary":"Undo window and protection against overwriting teammates.","reqs":[{"id":"FR-SND-01","weight":"Must","text":"If a teammate has replied since the agent opened the ticket, the agent’s reply is not sent; the conflict is announced and the draft is kept.","accept":["Alert \"Sam Okoye updated this ticket while you were writing. Review the new reply before you submit.\"; Submit is disabled; the draft stays in the composer","\"Review changes\" shows the teammate’s reply and the new status without altering the draft","The second submit succeeds; the thread contains exactly one copy of each reply, the teammate’s first","Presence banners \"👀 Sam Okoye is also viewing this ticket\" and \"✍ Sam Okoye is typing a reply…\" appear on the ticket"]},{"id":"FR-SND-02","weight":"Should","text":"Replies are held for 5 seconds with an Undo; undoing sends nothing and keeps the draft.","accept":["Status bar \"Sending in 5s…\" with \"Undo\"; Submit is disabled","Undo shows \"Sending cancelled — your draft is kept\"; the thread, status and SLA are unchanged after reload"]}]},{"id":"MAC","title":"Macros","summary":"Canned replies that also set properties.","reqs":[{"id":"FR-MAC-01","weight":"Must","text":"Applying \"Refund issued\" fills the reply, sets status Solved and adds the tag refund; submitting applies all three.","accept":["Reply starts \"Hi Elena, we have issued a full refund.\"; button reads \"Submit as Solved\"; hint lists tags: refund","After submit: toast \"Ticket #1001 updated · Solved\", status pill Solved, tag refund present and the ticket is in \"Recently solved\""]},{"id":"FR-MAC-02","weight":"Could","text":"A private macro such as \"Escalate to Tier 2\" switches the composer to an internal note and sets group Technical and the priority it defines; the button reads \"Submit as Open\"."}]},{"id":"BLK","title":"Bulk actions","summary":"Act on several selected tickets.","reqs":[{"id":"FR-BLK-01","weight":"Should","text":"Bulk assign and bulk status update the selected tickets; ineligible tickets are reported and left unchanged.","accept":["Two selected tickets: \"2 tickets assigned to you\"; both leave \"Unassigned tickets\" and the count drops by 2","Setting Solved on an unassigned ticket: \"0 updated · 1 skipped (no assignee): #NNNN\" and the row stays"]}]},{"id":"MRG","title":"Merge tickets","summary":"Destructive merge of a duplicate into another ticket.","reqs":[{"id":"FR-MRG-01","weight":"Must","text":"Merge requires choosing the target, previewing it and typing MERGE; the source is closed and cannot be merged again.","accept":["The preview shows the target ticket and requester; \"Merge tickets\" stays disabled until exactly \"MERGE\" is typed (lowercase does not count)","Result: toast \"#1005 merged into #1001\"; #1001 gains a system note about the merge","The merged ticket is Closed with banner \"This ticket was merged into #1001 and is closed.\"; its \"Merge into another ticket…\" is disabled","Merging a ticket into itself shows \"A ticket cannot be merged into itself\""]},{"id":"FR-MRG-02","weight":"Should","text":"Requesters with similar names are separate customers and are told apart by email.","accept":["#1001 Elena Park and #1005 Elena Parks have different emails and different orders (BZ-48213 vs BZ-48231)"]}]},{"id":"CHT","title":"Live chat","summary":"Queue of waiting visitors and chat-to-ticket conversion.","reqs":[{"id":"FR-CHT-01","weight":"Must","text":"An agent can accept a waiting chat, converse with the visitor and convert the transcript into a ticket when ending the chat.","accept":["The queue shows \"Waiting (3)\" with Visitor 4821 on /orders/BZ-77120","Accept opens \"Chat with Visitor 4821\" with the visitor’s opening message; sending a message shows a typing indicator and then the visitor’s answer","End chat with \"Convert to ticket\" ticked and a subject: toast \"Chat ended · ticket #NNNN created\"; the ticket has channel Chat, the agent as assignee and a transcript note"]}]},{"id":"POR","title":"Help center","summary":"Public request form, status lookup and knowledge base.","reqs":[{"id":"FR-POR-01","weight":"Must","text":"The request form validates input and, once valid, creates a request and returns its number.","accept":["Invalid email: \"Enter a valid email address\" with the field marked invalid","Description shorter than 10 characters: \"Describe the issue in at least 10 characters\"","Success: \"Request #NNNN received\" and \"We emailed a confirmation to <email>.\"","Agents see the request in \"Unassigned tickets\" with status New, the chosen group and tag portal"]},{"id":"FR-POR-02","weight":"Should","text":"Status lookup needs the matching request number and email, and shows only public messages.","accept":["Wrong email: \"No request matches that number and email\"","Right email for #1003: \"#1003 Package arrived damaged\" with status \"Awaiting your reply\""]},{"id":"FR-POR-03","weight":"Could","text":"The knowledge base lists 12 articles grouped Accounts, Billing, Shipping, Technical, is searchable and expands articles inline.","accept":["Search \"password\" gives \"1 article\" — \"Reset your password\" (\"Links expire after 30 minutes.\")","No match: \"No articles match \"zzz\".\""]}]},{"id":"RPT","title":"Reports","summary":"KPIs and distributions from the live ticket set.","reqs":[{"id":"FR-RPT-01","weight":"Could","text":"Reports show Total tickets (60), Unsolved, SLA compliance %, SLA breached and CSAT, plus bars by status, channel and priority; they update after tickets change.","accept":["Solving one unsolved ticket lowers Unsolved by 1 and raises the Solved bar by 1"]}]}],"nfr":[{"id":"NFR-01","weight":"Should","text":"The console is usable at phone width (390 px): navigation moves to a bottom bar, Views open in a drawer and the page does not scroll horizontally."},{"id":"NFR-02","weight":"Must","text":"Customer-facing pages never expose internal notes or other customers’ requests, and the agent console requires sign-in."}],"glossary":[{"term":"Internal note","meaning":"A message only agents can see."},{"term":"Macro","meaning":"A canned reply that can also set status, tags, priority and group."},{"term":"First reply SLA","meaning":"Time allowed for the first public reply, by priority."}],"outOfScope":["Email delivery","Attachments","Real push between agents"]}