You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
Upgrading
Run the migrations (0032_verified_guest_access) and configure outbound
email. Use a shared cache across workers for the code budgets.
Existing permanent guest links (guest/<guest_token>/, the widget's old guest_token) stop working. Guests renew them with the lookup form (email
code plus ticket reference). ESCALATED["GUEST_ACCESS"]["LEGACY_TOKENS"] = True keeps the old links working during a transition.
Clients that do not send verification_id and verification_code can no
longer create guest tickets or start widget chats. The widget's create
responses now carry the grant (with expires_at) where they carried the
permanent token.
The new URL does not make files already in MEDIA private. Stop your web
server or CDN from serving MEDIA_ROOT/escalated/attachments/ publicly; see docs/private-attachments.md. Links expire, so clients that cached
attachment URLs must refetch the ticket.
Webhooks saved under the old names keep receiving those events, now under
the new names in the delivery's event field and X-Escalated-Event
header. Receivers that switch on the event name should accept the new
names. The edit form shows (and saving stores) the canonical names.
Stored names are read as aliases, not rewritten: ticket.replied, internal_note.added, internal_note.created, tag.added, ticket.tagged, tag.removed, ticket.untagged, chat.session_started
and chat.session_ended. A workflow saved with the ticket.replied
trigger is stored and shown as reply.created and keeps firing on replies.
Added
Live chat events (ADR 2026-10-21): chat.started, chat.assigned, chat.transferred, chat.message and chat.ended are now sent to
outbound webhooks and run workflows, and the webhook form and the workflow
builder offer them. Before this, chat events reached neither. The webhook
payload is {"chat": {"session_id", "ticket_id", "status", "agent_id", "department_id"}}, plus from_agent_id/to_agent_id/to_department_id
(always null; transfers are agent to agent) for a transfer, reply_id/author_type (agent or visitor) for a message and ended_by (agent or customer) for an end. It never carries the
visitor's chat token (customer_session_id), the visitor's email or the
message body. Workflows on chat events run against the chat's ticket. A new chat_assigned signal is sent when an agent takes a chat (accepted from the
queue or routed at start). Webhooks and workflows saved under chat.session_started / chat.session_ended receive chat.started / chat.ended. Upgrade: run python manage.py migrate escalated
(0033_workflow_chat_triggers only updates the trigger choices; no schema
change).
Security
Verified guest access, as in escalated-laravel (docs/guest-access.md).
Guest tickets (browser and widget), widget live chats and ticket lookup now
need an eight-digit code emailed to the guest (10-minute expiry, five
guesses, single use; at most 3 codes per mailbox per client IP and 10 per
mailbox per hour). POST verification and POST lookup are served under guest/, widget/ and <API_PREFIX>/mobile/guest/. New tickets get an
expiring grant (default 24 hours, GUEST_ACCESS["TTL_MINUTES"]) whose nonce
is stored only as a hash, instead of the permanent guest_token. /widget/config sends guest_verification_required: true and status_lookup_enabled: true, and GET widget/tickets/<reference> returns
the ticket's status for a matching Authorization: Bearer <grant>. Guest/Create receives verification_url and lookup_url, and the guest
ticket page no longer carries ticket or reply metadata, the requester
record or agent emails.
Attachments are delivered privately, as in escalated-laravel. Serialized
attachments (AttachmentSerializer, ApiAttachmentSerializer) no longer
carry the file's MEDIA URL. Their url is a signed link to the new escalated:attachment_download view (<prefix>/attachments/<id>/), valid
for ATTACHMENT_DOWNLOAD_TTL_MINUTES (default 10). The view checks current
ticket access: agents and admins who can view the ticket, the requester for
attachments on the ticket and its public replies, and guests holding a link
issued by their guest page (bound to the ticket's active guest grant, so
renewing or revoking the grant revokes them). Internal-note attachments are only served to agents
and admins. Responses are Content-Disposition: attachment with a safe
filename, application/octet-stream except for a few inert types, X-Content-Type-Options: nosniff and Cache-Control: private, no-store.
API clients send their Escalated bearer token with the download.
Changed
Webhooks use the shared event vocabulary (escalated-developer-context ADR
2026-10-21): internal notes go out as note.created (was internal_note.added) and tag changes as ticket.tag_added / ticket.tag_removed (were tag.added / tag.removed). The admin Webhooks
form offers only the canonical names. The names live in escalated/event_names.py.