Skip to content

Tickets (Cases)

The Tickets module, known internally as Cases, is a lightweight support desk. Use it to track customer-reported issues, internal escalations, or any work item that needs an SLA and an audit trail.

Anatomy

  • Subject and description
  • Status: New, Assigned, Pending, Closed, Rejected, Duplicate
  • Priority: Low, Normal, High, Urgent. There is no separate severity field.
  • Type: Question, Incident or Problem
  • Account and contacts, the people who reported it
  • Assignees and teams
  • SLA targets: first response, next response and resolution, each defaulted from the priority
  • Custom fields: environment, version, escalation level

How tickets arrive

  • Email, through an inbound mailbox on Amazon SES. Replies thread onto the ticket they belong to. SES is the only inbound provider that works today; IMAP, Gmail and Microsoft 365 mailboxes are not built. See Inbound email.
  • A web form on your own site. A form can create a ticket instead of a lead: the sender is matched to a contact by email (and a contact is created if there is none), the form's priority and type are applied, and routing and SLA targets run as they do for any new ticket. See Web forms.
  • The customer portal, where a signed-in customer can raise a request.
  • CSV import of up to 5,000 tickets at a time, previewed before anything is written. See CSV import and export.
  • By hand in the web app or the phone app, or through the API.

Replies by email

A public reply from your team is emailed to each contact on the ticket, apart from whoever wrote it, and carries the reply text itself. Each email is threaded: it gets its own Message-ID, an In-Reply-To and References built from the ticket's earlier messages, and the subject Re: <ticket name> [Case #<8 characters>]. When the customer answers from their mail client, the reply lands back on the same ticket, by header or, if their client dropped the headers, by the subject tag (the tag alone is only trusted from a contact already on that ticket). Internal notes are never emailed.

For the customer's answer to come back in by email, the org needs a working SES inbound mailbox; without one the email asks the customer to reply through the portal instead.

Macros

A macro is a saved reply: text you write once and insert into a ticket's reply box, with placeholders such as %customer_name%, %case_subject% and %agent_name% filled in from the ticket. They are managed under Settings → Macros on the web and Settings → Saved replies on the phone. An org macro is shared with everyone in the org and only an admin can create or change it; a personal one is visible to its owner alone.

From django-crm 1.13.0 a macro can also change the ticket. Each action is optional:

  • Set status: any status except Duplicate, which only a merge sets.
  • Set priority.
  • Assign to: replaces whoever the ticket is assigned to.
  • Add tags: added to the ticket's own tags, never replacing them.

A macro needs a body, an action, or both.

In the reply box, on the web and the phone, pick a macro with text and it is inserted as before. Its actions appear as chips under Also on send; take off any you do not want, and the rest apply right after the reply posts. If an action is refused (the close needs an approval, say), the reply has still gone out and you are told what was not applied. A macro with no text has an Apply button instead, which changes the ticket at once.

Actions go through the same checks as editing the ticket by hand: you need to be allowed to change the ticket (an admin, its creator or an assignee), a merged ticket's status stays locked, closing still needs its date and any approval your rules ask for, and whoever is newly assigned is emailed. A deactivated assignee or an archived tag on the macro is skipped; if every assignee on it is deactivated, the ticket keeps the assignees it had rather than being left with nobody.

POST /api/macros/<id>/apply/
{ "case_id": "<ticket id>", "only": ["status", "tags"] }

only is optional and limits the actions to the ones listed (status, priority, assignees, tags). The response lists what was applied and what was skipped, with a reason for each. See API: Macros for every field and refusal.

Workflow

There is no enforced status state machine: a ticket can move between statuses directly. The two real controls are an approval rule, which can gate the close, and the reopen policy, under which a customer reply inside a window your org sets reopens a closed ticket to Pending.

Board

/tickets/board in the web app, and the board view of Tickets in the phone app, lay the queue out as columns, either by status or by the stages of a ticket pipeline. Drag a card on the web, or use the card's menu on the phone, to move it. Tickets merged into another one are left off the board.

Parent and child tickets

Related incidents can sit under a parent problem, up to three levels deep. The server enforces the depth and refuses a ticket as its own parent or a link that would make a cycle. From django-crm 1.11.0 the ticket page in the web app carries the same parent and child panel the phone app has: the parent the ticket sits under, the whole tree it belongs to with this ticket marked, Link parent to put it under another ticket, and Detach to take it out. Linking needs write access to the ticket, and the parent picker only offers tickets you can open. A ticket in the tree that you cannot open is shown with its status but without a name or a link.

Closing a parent can close its open children too. Your org sets the default, and each close can override it.

Merge and unmerge

Duplicates can be merged into one primary ticket from the ticket page, on the web and the phone. The email threads come along, and the merge records what it moved so that Unmerge can put it back. Merging needs an admin, or the person who created both tickets.

From django-crm 1.11.0 a merged ticket's status cannot be changed, from the ticket page, the board, a bulk edit or a close with children. The API answers 400 with "Unmerge it first to change its status", and unmerging restores the status the ticket had before the merge.

Approvals

Some orgs gate resolution behind a second pair of eyes. /tickets/approvals lists tickets awaiting approval. The decision is recorded against the approval request, not the ticket: POST /api/cases/approvals/<id>/approve/, with reject/ and cancel/ alongside it. The requester cannot approve their own request, and there is no admin override of that rule.

Solutions library

/solutions is a write-once knowledge base. When you close a ticket with a useful diagnosis, click Save as solution to publish the resolution to the org's library. New tickets surface matching solutions inline as you type the subject, so common issues get answered faster.

Articles move through draft, reviewed and approved, and approving is an admin's call rather than the author's. An article that is both approved and published is readable by your signed-in customers in the portal, and by anyone at all if you turn on the public help center, so releasing one is a decision about people outside your company, not just a filing step.

Articles can carry the same tags as your leads, deals and tickets. Tags are for your team's own filing and are never shown to a customer, but articles sharing one are offered to each other as related reading in the portal.

Customer portal

Customers can sign in to check their own requests instead of asking you. They enter their email address at /portal/login/<org-id>, receive a six digit code, and hold a session for 24 hours. There is no account to create and no password: the portal authenticates a contact, not a user of your CRM.

Signed in, a customer can list the requests they are named on, read the thread, reply, and raise a new one. They cannot attach files.

They can also search your published help articles and read them, and the new-request form suggests matching articles as they type a summary, so a question that already has an answer does not have to become a ticket. Only articles that are both approved and published are visible, and the article page links related ones.

What they see is a deliberately narrower view, not the ticket page with fields hidden:

  • Internal notes are never sent. The visibility filter is applied in the query, so a private note is not loaded at all.
  • Your team is shown as "Support". Which colleague replied is not disclosed.
  • Only their own requests. A colleague at the same account cannot read a ticket they were not named on, and an unknown ticket id returns the same "not found" as somebody else's.

They are emailed when your team posts a public reply or the status changes. That email carries no credential, so forwarding it does not forward access.

See API: Customer portal for the endpoints.

Public help center

From django-crm 1.10.0 an org can also publish its knowledge base as a public help center that needs no sign-in. It is off by default. An admin turns it on under Settings → Help center, on the web or the phone, and picks the slug it is served under. The help center is then at /help-center/<slug> on your CRM's own domain, with one page per article at /help-center/<slug>/articles/<id>.

  • Only articles that are both approved and published appear, the same rule the portal uses. Drafts, reviewed articles and unpublished ones are never shown.
  • It is meant to be found. Article pages carry a canonical link, and each help center has its own sitemap at /help-center/<slug>/sitemap.xml listing every published article. Search result pages are marked noindex.
  • Readers can search it, and each article links related ones by tag.

Analytics

/tickets/analytics, on the web and in the phone app, shows:

  • First-response, next-response and resolution times, with next response measured against a per-priority target you set in the escalation policy and its breach rate reported alongside the others
  • SLA breach rates, overall and per priority
  • Backlog, day by day
  • Per-agent handled count, response time, breaches and satisfaction
  • Customer satisfaction: the average rating, how many ratings there are, and the spread across 1 to 5. The rating comes from a survey emailed half an hour after a ticket closes, skipped if the ticket has been reopened by then.

The web page exports its figures to CSV. The ticket list exports too, with its filters applied.

Bridging to other tools

Outbound webhooks can send ticket.created, ticket.updated and ticket.comment_added (public replies only) to Slack, Zapier, n8n or your own endpoint, signed with HMAC-SHA256. See Webhooks. Escalation rules themselves still act inside the CRM: they re-assign and notify in the app.

See also

  • Tasks: for general to-dos that don't need an SLA.
  • Custom fields: severity tiers, environment, customer plan.
  • Saved views: keep a filtered ticket queue and open it again with one tap.