العربية • Deutsch • English • Español • Français • Italiano • 日本語 • 한국어 • Nederlands • Polski • Português (BR) • Русский • Türkçe • 简体中文
Um sistema de tickets de suporte completo e integrável para Rails. Adicione-o a qualquer aplicação — obtenha um helpdesk completo com rastreamento de SLA, regras de escalonamento, fluxos de trabalho de agentes e um portal do cliente. Nenhum serviço externo necessário.
escalated.dev — Saiba mais, veja demos e compare as opções Cloud vs Auto-hospedado.
Três modos de hospedagem. Execute totalmente auto-hospedado, sincronize com uma nuvem central para visibilidade multi-aplicação, ou redirecione tudo para a nuvem. Mude de modo com uma única alteração de configuração.
- Ciclo de vida do ticket — Criar, atribuir, responder, resolver, fechar, reabrir com transições de status configuráveis
- Motor de SLA — Metas de resposta e resolução por prioridade, cálculo de horário comercial, detecção automática de violações
- Regras de escalonamento — Regras baseadas em condições que escalonam, repriorizam, reatribuem ou notificam automaticamente
- Painel do agente — Fila de tickets com filtros, ações em massa, notas internas, respostas predefinidas
- Portal do cliente — Criação de tickets em autoatendimento, respostas e acompanhamento de status
- Painel de administração — Gerenciar departamentos, políticas de SLA, regras de escalonamento, tags e ver relatórios
- Anexos de arquivos — Upload com arrastar e soltar, armazenamento configurável e limites de tamanho
- Linha do tempo de atividades — Log de auditoria completo de cada ação em cada ticket
- Notificações por email — Notificações configuráveis por evento com suporte a webhooks
- Roteamento por departamento — Organizar agentes em departamentos com atribuição automática (round-robin)
- Sistema de tags — Categorizar tickets com tags coloridas
- Tickets de visitante — Envio anônimo de tickets com acesso por link mágico via token de visitante
- Email de entrada — Criar e responder tickets por email (Mailgun, Postmark, AWS SES, IMAP)
- Inertia.js + Vue 3 UI — Frontend compartilhado via
@escalated-dev/escalated - Divisão de tickets — Dividir uma resposta em um novo ticket independente preservando o contexto original
- Ticket snooze — Adiar tickets com predefinições (1h, 4h, amanhã, próxima semana);
rake escalated:wake_snoozed_ticketsos reativa automaticamente - Visualizações salvas / filas personalizadas — Salvar, nomear e compartilhar presets de filtros como visualizações de tickets reutilizáveis
- Widget de suporte integrável — Widget leve
<script>com busca na KB, formulário de tickets e verificação de status - Threading de email — Emails enviados incluem cabeçalhos
In-Reply-ToeReferencespara threading correto em clientes de email - Templates de email com marca — Logo, cor primária e texto do rodapé configuráveis para todos os emails enviados
- Real-time broadcasting — Broadcasting opcional via ActionCable com fallback automático de polling
- Toggle da base de conhecimento — Habilitar ou desabilitar a base de conhecimento pública nas configurações de administração
- Ruby 3.1+
- Rails 7.1+
- Node.js 18+ (para recursos do frontend)
bundle add escalated
npm install @escalated-dev/escalated
rails generate escalated:install
rails db:migrateAdd the Ticketable concern to your User model:
class User < ApplicationRecord
include Escalated::Ticketable
endDefine authorization in your ApplicationController or an initializer:
# config/initializers/escalated.rb
Escalated.configure do |config|
config.admin_check = ->(user) { user.admin? }
config.agent_check = ->(user) { user.agent? || user.admin? }
endVisit /support — you're live.
O Escalated usa Inertia.js com Vue 3. Os componentes do frontend são fornecidos pelo pacote npm @escalated-dev/escalated.
Adicione o pacote Escalated à configuração content do Tailwind para que suas classes não sejam removidas:
// tailwind.config.js
content: [
// ... your existing paths
'./node_modules/@escalated-dev/escalated/src/**/*.vue',
],Adicione as páginas do Escalated ao seu resolver de páginas do Inertia:
// app/javascript/entrypoints/application.js
import { createApp, h } from 'vue'
import { createInertiaApp } from '@inertiajs/vue3'
createInertiaApp({
resolve: name => {
if (name.startsWith('Escalated/')) {
const escalatedPages = import.meta.glob(
'../../../node_modules/@escalated-dev/escalated/src/pages/**/*.vue',
{ eager: true }
)
const pageName = name.replace('Escalated/', '')
return escalatedPages[`../../../node_modules/@escalated-dev/escalated/src/pages/${pageName}.vue`]
}
const pages = import.meta.glob('../pages/**/*.vue', { eager: true })
return pages[`../pages/${name}.vue`]
},
setup({ el, App, props, plugin }) {
createApp({ render: () => h(App, props) })
.use(plugin)
.mount(el)
},
})Registre o EscalatedPlugin para renderizar as páginas do Escalated dentro do layout do seu aplicativo — sem necessidade de duplicação de páginas:
import { EscalatedPlugin } from '@escalated-dev/escalated'
import AppLayout from '@/layouts/AppLayout.vue'
createInertiaApp({
setup({ el, App, props, plugin }) {
createApp({ render: () => h(App, props) })
.use(plugin)
.use(EscalatedPlugin, {
layout: AppLayout,
theme: {
primary: '#3b82f6',
radius: '0.75rem',
}
})
.mount(el)
},
})Your layout component must accept a #header slot and a default slot. Escalated will render its sub-navigation in the header and page content in the default slot. Without the plugin, Escalated uses its own standalone layout.
Consulte o README do @escalated-dev/escalated para documentação completa de temas e propriedades CSS personalizadas.
Tudo permanece no seu banco de dados. Sem chamadas externas. Autonomia total.
Escalated.configure do |config|
config.mode = :self_hosted
endBanco de dados local + sincronização automática com cloud.escalated.dev para caixa de entrada unificada em múltiplas aplicações. Se a nuvem estiver inacessível, seu aplicativo continua funcionando — os eventos entram na fila e são reenviados.
Escalated.configure do |config|
config.mode = :synced
config.hosted_api_url = "https://cloud.escalated.dev/api/v1"
config.hosted_api_key = ENV["ESCALATED_API_KEY"]
endTodos os dados de tickets são proxy para a API na nuvem. Seu aplicativo lida com autenticação e renderiza a interface, mas o armazenamento fica na nuvem.
Escalated.configure do |config|
config.mode = :cloud
config.hosted_api_url = "https://cloud.escalated.dev/api/v1"
config.hosted_api_key = ENV["ESCALATED_API_KEY"]
endOs três modos compartilham os mesmos controllers, interface e lógica de negócios. O padrão de driver cuida do resto.
Create or edit config/initializers/escalated.rb:
Escalated.configure do |config|
config.mode = :self_hosted
config.user_class = "User"
config.table_prefix = "escalated_"
config.route_prefix = "support"
config.default_priority = :medium
# Middleware
config.middleware = [:authenticate_user!]
config.admin_middleware = nil
# Tickets
config.allow_customer_close = true
config.auto_close_resolved_after_days = 7
config.max_attachments_per_reply = 5
config.max_attachment_size_kb = 10240
# SLA
config.sla = {
enabled: true,
business_hours_only: true,
business_hours: {
start: 9, end: 17,
timezone: "UTC",
working_days: [1, 2, 3, 4, 5]
}
}
# Notifications
config.notification_channels = [:email]
config.webhook_url = nil
# Storage (ActiveStorage)
config.storage_service = :local
endAdicione estes ao seu agendador para automação de SLA e escalonamento:
# config/schedule.rb (whenever gem) or use solid_queue/sidekiq-cron
every 1.minute do
runner "Escalated::CheckSlaJob.perform_now"
end
every 5.minutes do
runner "Escalated::EvaluateEscalationsJob.perform_now"
end
every 1.day do
runner "Escalated::CloseResolvedJob.perform_now"
end
every 1.week do
runner "Escalated::PurgeActivitiesJob.perform_now"
endRoutes are automatically mounted when the engine loads. By default they mount at /support.
| Route | Method | Description |
|---|---|---|
/support |
GET | Customer ticket list |
/support/create |
GET | New ticket form |
/support/{ticket} |
GET | Ticket detail |
/support/agent |
GET | Agent dashboard |
/support/agent/tickets |
GET | Agent ticket queue |
/support/agent/tickets/{ticket} |
GET | Agent ticket view |
/support/admin/reports |
GET | Admin reports |
/support/admin/departments |
GET | Department management |
/support/admin/sla-policies |
GET | SLA policy management |
/support/admin/escalation-rules |
GET | Escalation rule management |
/support/admin/tags |
GET | Tag management |
/support/admin/canned-responses |
GET | Canned response management |
/support/agent/tickets/bulk |
POST | Bulk actions on multiple tickets |
/support/agent/tickets/{ticket}/follow |
POST | Follow/unfollow a ticket |
/support/agent/tickets/{ticket}/macro |
POST | Apply a macro to a ticket |
/support/agent/tickets/{ticket}/presence |
POST | Update presence on a ticket |
/support/agent/tickets/{ticket}/pin/{reply} |
POST | Pin/unpin an internal note |
/support/{ticket}/rate |
POST | Submit satisfaction rating |
Connect to ticket lifecycle events via ActiveSupport::Notifications:
ActiveSupport::Notifications.subscribe("escalated.ticket_created") do |event|
ticket = event.payload[:ticket]
# Handle new ticket
endCreate and reply to tickets from incoming emails. Supports Mailgun, Postmark, AWS SES webhooks, and IMAP polling.
# config/initializers/escalated.rb
Escalated.configure do |config|
config.inbound_email_enabled = true
config.inbound_email_adapter = :mailgun
config.inbound_email_address = "support@yourapp.com"
# Mailgun
config.mailgun_signing_key = ENV["ESCALATED_MAILGUN_SIGNING_KEY"]
# Postmark
config.postmark_inbound_token = ENV["ESCALATED_POSTMARK_INBOUND_TOKEN"]
# AWS SES
config.ses_region = "us-east-1"
config.ses_topic_arn = ENV["ESCALATED_SES_TOPIC_ARN"]
# IMAP
config.imap_host = ENV["ESCALATED_IMAP_HOST"]
config.imap_port = 993
config.imap_encryption = :ssl
config.imap_username = ENV["ESCALATED_IMAP_USERNAME"]
config.imap_password = ENV["ESCALATED_IMAP_PASSWORD"]
config.imap_mailbox = "INBOX"
end| Provider | URL |
|---|---|
| Mailgun | POST /support/inbound/mailgun |
| Postmark | POST /support/inbound/postmark |
| AWS SES | POST /support/inbound/ses |
Schedule Escalated::PollImapJob with Solid Queue, Sidekiq, or whenever:
# config/recurring.yml (Solid Queue)
poll_imap:
class: Escalated::PollImapJob
schedule: every minute- Thread detection via subject reference and
In-Reply-To/Referencesheaders - Guest tickets for unknown senders with auto-derived display names
- Auto-reopen resolved/closed tickets on email reply
- Duplicate detection via
Message-IDheaders - Attachment handling with configurable size and count limits
- Audit logging of every inbound email
- All settings configurable from admin panel with env fallback
O Escalated suporta plugins agnósticos de framework construídos com o Plugin SDK. Os plugins são escritos uma vez em TypeScript e funcionam em todos os backends do Escalated.
- Node.js 20+
@escalated-dev/plugin-runtimeinstalled in your project
npm install @escalated-dev/plugin-runtime
npm install @escalated-dev/plugin-slack
npm install @escalated-dev/plugin-jira# config/initializers/escalated.rb
Escalated.configure do |config|
# ... existing config ...
config.sdk_plugins_enabled = true
endSDK plugins run as a long-lived Node.js subprocess managed by @escalated-dev/plugin-runtime, communicating with Rails over JSON-RPC 2.0 via stdio. The subprocess is spawned lazily on first use and automatically restarted with exponential backoff if it crashes. Every ticket lifecycle event is dual-dispatched to both Rails event handlers and the plugin runtime.
import { definePlugin } from '@escalated-dev/plugin-sdk'
export default definePlugin({
name: 'my-plugin',
version: '1.0.0',
actions: {
'ticket.created': async (event, ctx) => {
ctx.log.info('New ticket!', event)
},
},
})- Plugin SDK — SDK TypeScript para criar plugins
- Plugin Runtime — Host de runtime para plugins
- Plugin Development Guide — Documentação completa
- Escalated for Laravel — Pacote Laravel Composer
- Escalated for Rails — Motor Ruby on Rails (você está aqui)
- Escalated for Django — Aplicativo Django reutilizável
- Escalated for AdonisJS — Pacote AdonisJS v6
- Escalated for Filament — Plugin de painel administrativo Filament v3
- Shared Frontend — Componentes de UI Vue 3 + Inertia.js
Mesma arquitetura, mesma interface Vue, mesmos três modos de hospedagem — para cada framework backend importante.
bundle exec rspecMIT