API
Tickets a partir de seus próprios sistemas
Um ticket aberto pela API é um ticket de verdade: cria um canal no Discord, seu time responde normalmente e a resposta volta direto para o visitante. Ele nunca precisa de uma conta Discord.
curl https://api.ticketanizer.com/v1/tickets \
-H "X-API-Key: tk_dein_schluessel" \
-H "Content-Type: application/json" \
-d '{
"panel_id": 3,
"visitor_name": "Anna Berger",
"first_message": "Meine Bestellung ist nicht angekommen.",
"metadata": { "Bestellnummer": "2026-8891", "Tarif": "Pro" }
}'
{
"ticket_request_id": 4102,
"session_token": "3Qk1r…",
"status": "pending"
}
O canal aparece em poucos segundos. Guarde o session_token — é a única coisa que deixa aquele visitante ler e responder seu ticket.
Duas chaves, dois trabalhos
Qual você envia decide o que a chamada pode fazer.
X-API-Key
Sua chave do painel. Cobre todo o servidor: abrir tickets, lê-los, responder como time.
Fica no seu servidor e em lugar nenhum mais. Num navegador, deixa qualquer visitante ler cada ticket.
X-Session-Token
Volta quando você abre um ticket e cobre exatamente aquele ticket — não o servidor.
Esse é seguro para passar para o visitante cujo ticket é.
Permissões por chave
Você marca o que a chave pode fazer ao criá-la. Nunca pode fazer mais.
ticket:createAbrir ticketsticket:readLer tickets, mensagens e painéisticket:replyEscrever num ticket como timestats:readLer números agregados
Uma permissão ausente retorna 403, não 401. A chave está bem — só falta essa marca.
Envie apenas os campos documentados aqui. Um nome de campo desconhecido não é silenciosamente ignorado, mas rejeitado com 422 — um erro de digitação deve aparecer imediatamente, não como um ticket vazio.
Os endpoints
Seis chamadas cobrem todo o fluxo. A referência completa com cada campo fica ligada embaixo.
| POST | /v1/tickets | Abrir um ticket e receber um token de sessão |
| GET | /v1/tickets | Listar tickets, paginados por cursor |
| GET | /v1/tickets/{id} | Um ticket com sua conversa |
| POST | /v1/tickets/{id}/messages | Responder como time — a rota para e-mail, CRM ou helpdesk |
| GET | /v1/panels | Listar painéis para achar o panel_id |
| GET | /v1/stats | Números para seu próprio relatório |
Limites
Contados por chave, não por IP — então todos seus clientes podem compartilhar um backend. Cada resposta diz quanto você tem sobrando.
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118
X-RateLimit-Reset: 47
Retry-After: 47
Tentativas
Quando uma requisição expira, você não sabe se o ticket foi criado. Mande uma chave que você escolhe e a tentativa retorna a mesma resposta em vez de um segundo ticket.
Idempotency-Key: 7f3a1c9e-4b21-4f0a-9d55-1e8c2b7a6d40
Guardamos a chave por 24 horas. Reusar uma chave para uma requisição genuinamente diferente retorna 422 — melhor um erro que o número errado do ticket.
Webhooks: quando algo deve chegar do seu lado
A direção oposta: em vez de você perguntar, o Ticketanizer te chama. Cada evento sai como um POST assinado para uma URL à sua escolha — para um bridge de e-mail, um CRM, ou simplesmente uma notificação no seu próprio sistema.
{
"event": "message.created",
"sent_at": "2026-08-01T12:34:56Z",
"guild_id": "1470847667327471738",
"ticket": {
"id": 42, "number": 17,
"panel_id": 3, "panel_name": "Support",
"status": "open", "priority": 2,
"opener_id": "234...", "metadata": null
},
"message": {
"id": 8891,
"discord_message_id": "13...",
"origin": "discord",
"is_staff": true,
"is_ai": false,
"author_id": "234...",
"author_bot": false,
"author_name": "Lisa",
"content": "Ich schaue mir das an.",
"created_at": "2026-08-01T12:34:56Z"
}
}
X-Ticketanizer-Event: message.created
X-Ticketanizer-Delivery: 90183
X-Ticketanizer-Timestamp: 1785600896
X-Ticketanizer-Signature: t=1785600896,v1=9f2c…
X-Ticketanizer-Delivery é único por entrega — guarde e você consegue identificar uma repetição e evitar processar duas vezes.
sent_at e created_at em webhooks estão em UTC. A API de leitura ainda retorna horários no horário do servidor sem deslocamento — tenha isso em mente ao mesclar pushes de webhook com polling.
Verificando a assinatura
Calcule HMAC-SHA256 sobre <timestamp>.<rawBody> com seu segredo e compare o resultado contra um dos valores v1. Duas coisas fáceis de perder: o header pode carregar vários valores v1 (durante uma rotação de segredo), e a comparação deve rodar em tempo constante.
const crypto = require("crypto");
function verify(rawBody, header, secret) {
const parts = header.split(",").map(p => p.split("="));
const ts = parts.find(([k]) => k === "t")?.[1];
// v1 kann MEHRFACH vorkommen — waehrend einer Secret-Rotation
const sigs = parts.filter(([k]) => k === "v1").map(([, v]) => v);
if (!ts || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const expected = crypto.createHmac("sha256", secret)
.update(ts + "." + rawBody)
.digest("hex");
return sigs.some(s => s.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(s), Buffer.from(expected)));
}
Assinamos o corpo raw — exatamente os bytes que chegam. Verificar depois de parsear e re-serializar o JSON produz uma assinatura diferente e nada além de falhas.
Os eventos
Você marca o que quer quando cria o endpoint. Sem seleção você recebe todos os eventos — com uma exceção, veja abaixo.
created | Ticket aberto |
claimed · unclaimed | Ticket atribuído ou liberado novamente |
closed · reopened | Ticket fechado ou reabertor |
escalated | Ticket repassado para outro painel |
priority · member_added · member_removed | Prioridade mudada, alguém adicionado ou removido do ticket |
feedback | Avaliação dada após fechamento |
message.created | Toda mensagem no ticket, texto completo. Apenas quando explicitamente marcado — conteúdo de conversa não deve fluir sem permissão para um endpoint que alguém configurou para mudanças de status. |
deleted · transcript · snoozed | Ticket deletado, transcrição armazenada, ticket suspenso. Se o seu sistema espelha tickets, marque deleted também — caso contrário, um ticket deletado ficará aberto do seu lado para sempre. |
O que saber sobre entrega
TentativasResponda com um status 2xx. Qualquer outra coisa conta como falha e tentamos novamente — após 1, 5, 15 e 60 minutos, depois não mais.Proteção contra loopCada mensagem traz umaorigin(discord,web,inbox,relay). Se você responder em reação a um evento, filtre os seus próprios — senão sua resposta dispara o próximo evento.DuplicatasResponda muito devagar e a mesma entrega chega novamente. Isto é deliberado: duas vezes é melhor que nenhuma. O ID de entrega é o que te protege de processar duas vezes.Endpoints mortosApós 20 entregas seguidas que falham de verdade, desligamos o endpoint e escrevemos o motivo no dashboard. Uma breve interrupção não faz isso — a contagem começa apenas quando todas as tentativas se esgotam.Rotacionando o segredoQuando você rotaciona, o segredo antigo permanece válido por 7 dias e assinamos com ambos. Isso deixa você trocar com calma sem perder uma entrega.
Entregas são visíveis no dashboard — incluindo o payload enviado e a resposta do seu servidor. De lá você também pode repetir uma entrega manualmente, contanto que ainda esteja no log.
A referência completa
Cada endpoint, campo e código de erro — gerado do serviço rodando e testável direto no navegador.