Pular para o conteúdo principal

API Documentation

A PumaHelp API é uma API RESTful completa para gerenciamento de tickets de suporte, permitindo criar, atualizar e gerenciar tickets, comentários, usuários, grupos, macros, webhooks e muito mais.

🔗 Base URL

Todas as requisições devem ser feitas para o subdomínio da sua organização:

https://{seu-app-name}.pumahelp.com/api

Exemplo:

https://acme.pumahelp.com/api
observação

O subdomínio é definido pelo App Name configurado na seção de Configurações do dashboard. Se não souber qual é, verifique em Configurações ou entre em contato com o suporte.


🚀 Como Começar

Passo 1: Obter Acesso

Você tem duas opções para começar a usar a API:

Opção A: Usar credenciais de usuário (JWT)

  • Ideal para: aplicações que já têm usuários cadastrados
  • Requer: email e senha de um usuário existente

Opção B: Criar API Key (Recomendado para integrações)

  • Ideal para: sistemas externos, automações, integrações de longo prazo
  • Requer: acesso ao dashboard PumaHelp com permissões de admin/owner

Passo 2: Fazer Sua Primeira Requisição

Com JWT:

# 1. Fazer login
curl -X POST https://acme.pumahelp.com/api/v1/users/login \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"password": "sua-senha"
}'

# 2. Usar o token recebido
curl -X GET https://acme.pumahelp.com/api/v1/tickets \
-H "Authorization: Bearer SEU_TOKEN_AQUI"

Com API Key:

# 1. Criar API Key no dashboard (Configurações → API Keys)
# 2. Usar a API Key diretamente
curl -X GET https://acme.pumahelp.com/api/v1/tickets \
-H "X-API-Key: SEU_API_KEY_SECRET"

Passo 3: Criar Seu Primeiro Ticket

curl -X POST https://acme.pumahelp.com/api/v1/tickets \
-H "Authorization: Bearer SEU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"subject": "Meu primeiro ticket via API",
"priority": "normal",
"type": "question",
"comment": {
"body": "Testando a integração com sucesso!",
"public": true
}
}'

Passo 4: Próximos Passos


📝 Convenções de Nomenclatura

A API utiliza snake_case para todos os campos de request e response.

Exemplo:

{
"public_id": 12345,
"created_at": "2025-01-01T10:00:00Z",
"assignee_id": "uuid",
"public": true
}
important

Todos os nomes de campos devem usar snake_case_lower (letras minúsculas com underscores).


🔐 Autenticação

A PumaHelp API suporta dois métodos de autenticação:

1. JWT (JSON Web Token)

Para usuários que fazem login via email/senha.

Como obter:

POST https://acme.pumahelp.com/api/v1/users/login
Content-Type: application/json

{
"email": "[email protected]",
"password": "sua-senha"
}

Resposta:

{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"verified": true
}

Como usar:

GET https://acme.pumahelp.com/api/v1/tickets
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

2. API Key

Para integrações automatizadas e sistemas externos.

Como obter:

  1. Faça login na plataforma PumaHelp
  2. Navegue até Configurações → API Keys
  3. Clique em "Criar Nova API Key"
  4. Selecione os scopes necessários
  5. Copie o secret (visível apenas uma vez!)

Como usar:

GET https://acme.pumahelp.com/api/v1/tickets
X-API-Key: seu-api-key-secret-aqui
important

O secret da API Key é exibido apenas uma vez durante a criação. Guarde-o em local seguro!


🔒 Sistema de Scopes

A API utiliza um sistema granular de permissões baseado em scopes (no formato resource:action).

Como Funcionam os Scopes

Tipo de AutenticaçãoComportamento
JWT (Login)Scopes atribuídos automaticamente baseados na role do usuário
API KeyScopes devem ser explicitamente selecionados na criação

Listagem de Scopes Disponíveis

Para obter a lista completa de scopes via API, consulte a seção 🔍 Escopos Disponíveis.


📊 Rate Limiting

A API implementa rate limiting para proteger contra abuso:

Limites por IP (Endpoints Públicos)

  • 100 requisições a cada 10 segundos
  • Fila de até 20 requisições

Limites por Organização (Endpoints Autenticados)

  • 1000 tokens disponíveis
  • 100 tokens repostos a cada 10 segundos

Headers de Resposta:

X-RateLimit-Remaining: 987
Retry-After: 10

Response quando limite excedido: 429 Too Many Requests

{
"errors": ["Rate limit exceeded"]
}

📝 Endpoints

🎫 Tickets

Criar Ticket

POST https://acme.pumahelp.com/api/v1/tickets
Authorization: Bearer {token}
Content-Type: application/json

Scope: ticket:create

Request Body:

{
"subject": "Problema com login",
"priority": "high",
"type": "question",
"group_id": "uuid-do-grupo",
"requester": {
"name": "Cliente Nome",
"email": "[email protected]"
},
"comment": {
"body": "Não consigo fazer login no sistema",
"public": true,
"author_id": "uuid-autor",
"uploads": ["uuid-arquivo-1", "uuid-arquivo-2"]
},
"tags": ["login", "urgent"],
"via": {
"channel": "api"
}
}
CampoTipoObrigatórioDescrição
subjectstringSimAssunto do ticket
prioritystringNãoPrioridade: low, normal, high, urgent (padrão: normal)
typestringNãoTipo: question, incident, problem, task
group_iduuidNãoID do grupo responsável
requesterobjectNãoDados do solicitante (se criar em nome de outro usuário)
requester.namestringNãoNome do solicitante
requester.emailstringNãoEmail do solicitante
requester.external_idstringNãoID externo do solicitante
requester.iduuidNãoID do solicitante
commentobjectSimPrimeiro comentário do ticket
comment.bodystringSimConteúdo do comentário
comment.publicbooleanNãoSe visível para o cliente (padrão: true)
comment.author_iduuidNãoID do autor (se diferente do usuário autenticado)
comment.uploadsarray[uuid]NãoIDs de arquivos anexados
tagsarray[string]NãoTags para categorização
viaobjectNãoCanal de origem
via.channelstringNãoCanal: api, widget, discord

Response: 201 Created

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"public_id": 12345,
"subject": "Problema com login",
"status": "new",
"created_at": "2025-01-01T10:00:00Z",
"conversation_id": "uuid-conversa"
}

Listar Tickets

GET https://acme.pumahelp.com/api/v1/tickets?page=1&page_size=25
Authorization: Bearer {token}

Scope: ticket:read

Query Parameters:

ParâmetroTipoDescriçãoObrigatório
pageintegerNúmero da páginaSim
page_sizeintegerItens por página (max: 100)Sim
querystringFiltros e busca (ver abaixo). Máx. 500 caracteresNão
sort_bystringCampo para ordenação: created_at, updated_at, priority, statusNão
sort_orderstringOrdem: asc ou descNão
includearray[string]Campos extras: assignee, requester, last_comment, tagsNão
Sintaxe do parâmetro query

A busca é uma sequência de termos chave:valor separados por espaço, mais palavras soltas.

Como os termos se combinam:

  • Repetir a mesma chave é "ou": status:open status:pending traz tickets abertos ou pendentes. Vale para todas as chaves — assignee:joao assignee:maria traz os tickets dos dois. A exceção é subject, onde repetir significa "e" (o assunto precisa conter os dois textos), assim como acontece com as palavras da busca livre.
  • Chaves diferentes são "e": status:open priority:high traz apenas os que são abertos e de prioridade alta.
  • Não existem operadores AND/OR escritos. Se você escrever status:open AND priority:high, a palavra AND é tratada como texto de busca. Use apenas espaços.
  • Palavras sem chave: são busca textual no assunto do ticket: boleto atrasado procura tickets cujo assunto contenha "boleto" e "atrasado". Para uma frase exata, use aspas: "nota fiscal".
  • Valores com espaço precisam de aspas (simples ou duplas): subject:'erro no login'.

Chaves disponíveis:

ChaveValoresExemplo
statusnew, open, pending, solved, closedstatus:open
prioritylow, normal, high, urgentpriority:urgent
typequestion, incident, problem, tasktype:incident
tagsnome da tag. Várias na mesma aspa = "e"tags:financeiro, tags:'fiscal urgente'
assigneeUUID, e-mail, external_id, parte do nome, me, ou none/null para não atribuídosassignee:me, assignee:none
requesterUUID, e-mail, external_id, parte do nome, ou merequester:me
subjecttexto contido no assuntosubject:'erro no login'
group / group_idnome do grupo / UUID do grupogroup:suporte
idnúmero do ticketid:1042
archivedtrue ou falsearchived:true
created / updateddata (2026-01-31) ou período: today, yesterday, last_24_hours, last_7_days, last_30_dayscreated:last_7_days

Datas aceitam também os operadores >, >=, < e <=, tanto com data quanto com período: created>=2026-01-01 created<=2026-01-31, created<today (antes de hoje). As datas são interpretadas em UTC.

me significa quem está fazendo a requisição: o usuário do token JWT ou, no caso de uma API Key, o usuário dono da chave.

Exemplos:

# Meus tickets sem resolução
assignee:me status:new status:open status:pending

# Tickets não atribuídos, urgentes
assignee:none priority:urgent

# Resolvidos nas últimas 24 horas
status:solved updated:last_24_hours

# Busca textual combinada com filtro
boleto status:open

# Frase exata no assunto
"nota fiscal" priority:high
Valores inválidos

Um valor que não existe para a chave (status:aberto, archived:sim, created:xpto) devolve 400 Bad Request com a mensagem indicando os valores aceitos. Uma chave desconhecida (foo:bar) não é erro — é tratada como texto de busca.

Para segmentar por cliente, use identificadores — nunca o nome

requester:, assignee: e group: casam por trecho do nome: requester:ana traz também "Mariana" e "Juliana". Isso é proposital (é o que faz a busca por nome funcionar na tela), mas significa que filtrar por nome não isola um cliente.

Se você usa a API para mostrar a cada cliente final apenas os tickets dele, filtre por id, external_id ou group_id, que são comparações exatas:

# Correto — exato
requester:'3f2a9c10-4b8e-4c1a-9f2e-8a7b6c5d4e3f'
requester:'crm-4211'
group_id:'a1b2c3d4-...'

# Perigoso — traz homônimos
requester:'Ana'

Response: 200 OK

{
"count": 150,
"tickets": [
{
"id": "uuid",
"public_id": 12345,
"subject": "Problema com login",
"status": "open",
"priority": "high",
"type": "question",
"created_at": "2025-01-01T10:00:00Z",
"updated_at": "2025-01-01T12:00:00Z",
"solved_at": null,
"archived_at": null,
"group_id": "uuid",
"conversation_id": "uuid",
"tags": ["login", "urgent"],
"assignee": {
"id": "uuid",
"name": "Maria Santos",
"role": "agent",
"email": "[email protected]",
"verified": true
},
"requester": {
"id": "uuid",
"name": "João Silva",
"role": "end-user",
"email": "[email protected]",
"verified": true
},
"last_comment": {
"id": "uuid",
"body": "Último comentário",
"public": true,
"created_at": "2025-01-01T12:00:00Z",
"updated_at": null,
"author": {
"id": "uuid",
"name": "Maria Santos",
"role": "agent",
"email": "[email protected]",
"verified": true
},
"attachments": []
},
"via": {
"channel": "widget"
}
}
]
}

Obter Ticket por ID

GET https://acme.pumahelp.com/api/v1/tickets/{public_id}
Authorization: Bearer {token}

Scope: ticket:read

Response: 200 OK

{
"id": "uuid",
"public_id": 12345,
"subject": "Problema com login",
"status": "open",
"priority": "high",
"type": "question",
"created_at": "2025-01-01T10:00:00Z",
"updated_at": "2025-01-01T12:00:00Z",
"solved_at": null,
"archived_at": null,
"group_id": "uuid",
"conversation_id": "uuid",
"tags": ["login", "urgent"],
"assignee": {
"id": "uuid",
"name": "Maria Santos",
"role": "owner",
"email": "[email protected]",
"verified": true
},
"requester": {
"id": "uuid",
"name": "João Silva",
"role": "end-user",
"email": "[email protected]",
"verified": true
},
"via": {
"channel": "api"
}
}

Atualizar Ticket

PUT https://acme.pumahelp.com/api/v1/tickets/{public_id}
Authorization: Bearer {token}
Content-Type: application/json

Scope: ticket:update

Request Body:

{
"subject": "Problema com login - RESOLVIDO",
"status": "solved",
"priority": "normal",
"type": "incident",
"requester_id": "uuid-novo-requester",
"assignee_id": "uuid-agente",
"group_id": "uuid-grupo",
"archived": false,
"tags": ["login", "resolved"],
"comment": {
"body": "Problema resolvido!",
"public": true,
"author_external_id": "discord-123456789",
"uploads": ["uuid-arquivo"]
}
}
CampoTipoDescrição
subjectstringNovo assunto
statusstringNovo status: new, open, pending, solved, closed
prioritystringNova prioridade
typestringNovo tipo
requester_iduuidTransferir para outro requester
assignee_iduuidAtribuir a agente (use null para desatribuir)
group_iduuidAtribuir a grupo
archivedbooleanArquivar ticket
tagsarray[string]Substituir tags
commentobjectAdicionar comentário com a atualização
comment.author_iduuidAutor do comentário (se diferente do usuário autenticado)
comment.author_external_idstringAutor por external_id — para integrações que autenticam como staff mas postam em nome do cliente (ex.: bot do Discord). author_id tem precedência; ignorado para tokens de end-user; external_id desconhecido cai no usuário autenticado
observação

Todos os campos são opcionais. Para remover assignee, envie null explicitamente.

Response: 204 No Content


Arquivar Ticket

DELETE https://acme.pumahelp.com/api/v1/tickets/{public_id}
Authorization: Bearer {token}

Scope: ticket:delete

Response: 204 No Content

observação

Tickets são arquivados (soft delete), não excluídos permanentemente.


📊 Relatórios

Estatísticas ao Longo do Tempo

GET https://acme.pumahelp.com/api/v1/tickets/stats-over-time?period=30d
Authorization: Bearer {token}

Scope: reports:read

Query Parameters:

ParâmetroValores Aceitos
period24h, 7d, 30d

Response: 200 OK

{
"data": [
{
"date": "2025-01-01",
"created": 45,
"resolved": 38
},
{
"date": "2025-01-02",
"created": 52,
"resolved": 41
}
]
}

Ranking de Agentes

GET https://acme.pumahelp.com/api/v1/tickets/agent-ranking?period=30d
Authorization: Bearer {token}

Scope: reports:read

Query Parameters:

ParâmetroValores Aceitos
period24h, 7d, 30d

Response: 200 OK

{
"data": [
{
"agent": "Maria Santos",
"resolved": 127
},
{
"agent": "João Silva",
"resolved": 98
}
]
}

💬 Comentários

Listar Comentários de um Ticket

GET https://acme.pumahelp.com/api/v1/tickets/{public_id}/comments?page=1&page_size=50
Authorization: Bearer {token}

Scope: comment:read

Query Parameters:

ParâmetroTipoDescriçãoPadrãoObrigatório
pageintegerNúmero da página-Sim
page_sizeintegerItens por página (max: 100)-Sim
sort_bystringCampo para ordenação: created_atcreated_atNão
sort_orderstringOrdem: asc ou descascNão

Response: 200 OK

{
"count": 5,
"comments": [
{
"id": "uuid",
"body": "Estamos investigando o problema",
"public": true,
"created_at": "2025-01-01T11:00:00Z",
"updated_at": null,
"author": {
"id": "uuid",
"name": "Maria Santos",
"role": "agent",
"email": "[email protected]",
"verified": true
},
"attachments": [
{
"id": "uuid",
"file_name": "screenshot.png",
"content_type": "image/png",
"size": 102400,
"content_url": "https://cdn.pumahelp.com/attachments/abc123"
}
]
}
]
}

Redigir Comentário

PUT https://acme.pumahelp.com/api/v1/tickets/{public_id}/comments/{comment_id}/redact
Authorization: Bearer {token}
Content-Type: application/json

Scope: comment:redact

Request Body:

{
"body": "Comentário <redact>removido</redact>"
}

Response: 204 No Content

aviso

O texto entre as tags é substituído por caracteres especiais como ████████ permanentemente.


Marcar Comentário como Privado

PUT https://acme.pumahelp.com/api/v1/tickets/{public_id}/comments/{comment_id}/make_private
Authorization: Bearer {token}

Scope: comment:update

Response: 204 No Content

observação

Torna o comentário invisível para end-users. Apenas agentes e admins poderão visualizar. dependendo do meio de comunicação, o comentário pode continuar visível para o end-user (cliente).


👥 Usuários

Login

POST https://acme.pumahelp.com/api/v1/users/login
Content-Type: application/json

Rate Limit: IP-based (100 req/10s)

Request Body:

{
"email": "[email protected]",
"password": "sua-senha"
}
CampoTipoObrigatórioDescrição
emailstringSimEmail do usuário
passwordstringSimSenha do usuário

Response: 200 OK

{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"verified": true
}
CampoTipoDescrição
access_tokenstringToken JWT para autenticação (válido por 24h)
refresh_tokenstringToken para renovar o access_token
verifiedbooleanSe o email do usuário foi verificado

Renovar Token (Refresh)

POST https://acme.pumahelp.com/api/v1/users/refresh
Content-Type: application/json

Rate Limit: IP-based (100 req/10s)

Request Body:

{
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Response: 201 Created

{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Logout

POST https://acme.pumahelp.com/api/v1/users/logout
Authorization: Bearer {token}
Content-Type: application/json

Scope: session:delete:own
Rate Limit: IP-based

Request Body:

{
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Response: 204 No Content


Criar Usuário

POST https://acme.pumahelp.com/api/v1/users
Authorization: Bearer {token}
Content-Type: application/json

Scope: user:create

Request Body:

{
"name": "João Silva",
"email": "[email protected]",
"role": "agent",
"external_id": "crm-user-123",
"verified": false,
"group_ids": ["uuid-grupo-1", "uuid-grupo-2"]
}
CampoTipoObrigatórioDescrição
namestringSimNome completo do usuário
emailstringNãoEmail do usuário (opcional)
rolestringSimRole: end-user, agent, admin, owner
external_idstringNãoID externo para integração com outros sistemas
verifiedbooleanNãoSe o email deve ser marcado como verificado (padrão: false)
group_idsarray[uuid]NãoIDs dos grupos aos quais o usuário pertence

Response: 201 Created

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "João Silva",
"email": "[email protected]",
"role": "agent",
"external_id": "crm-user-123",
"verified": false
}

Criar ou Atualizar Usuário (Upsert)

POST https://acme.pumahelp.com/api/v1/users/create_or_update
Authorization: Bearer {token}
Content-Type: application/json

Scope: user:upsert

Request Body:

{
"name": "João Silva",
"email": "[email protected]",
"role": "agent",
"external_id": "crm-user-123",
"verified": false,
"group_ids": ["uuid-grupo-1"]
}

Response: 200 OK ou 201 Created

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "João Silva",
"email": "[email protected]",
"role": "agent",
"external_id": "crm-user-123",
"verified": false
}
dica

Este endpoint verifica se já existe um usuário com o email ou external_id informado. Se existir, atualiza; se não, cria. Ideal para sincronizações com sistemas externos.


Impersonificar Usuário

POST https://acme.pumahelp.com/api/v1/users/impersonate
Authorization: Bearer {token}
Content-Type: application/json

Scope: user:impersonate

Request Body:

{
"email": "[email protected]",
"external_id": "crm-user-123",
"name": "João Silva",
"role": "end-user",
"verified": false,
"group_ids": ["uuid-grupo-1"]
}
CampoTipoObrigatórioDescrição
emailstringCondicionalEmail do usuário (obrigatório se external_id não fornecido)
external_idstringCondicionalID externo (obrigatório se email não fornecido)
namestringNãoNome completo (usado apenas se criar novo usuário)
rolestringNãoRole: end-user, agent, admin, owner (padrão: end-user)
verifiedbooleanNãoSe o email deve ser verificado (apenas para criação)
group_idsarray[uuid]NãoIDs dos grupos (apenas para criação)

Response: 200 OK

{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 86400
}
CampoTipoDescrição
access_tokenstringToken JWT para autenticação em nome do usuário
token_typestringSempre "Bearer"
expires_inintegerTempo de expiração em segundos
dica

Este endpoint permite gerar um access token em nome de um usuário específico. Se o usuário não existir, ele será criado automaticamente. Ideal para implementações de "Login as User" ou integração com sistemas de SSO (Single Sign-On).

aviso

Este é um endpoint sensível que permite assumir a identidade de qualquer usuário. Disponível apenas para roles ADMIN e OWNER, ou via API Key com scope user:impersonate.


Listar Usuários

GET https://acme.pumahelp.com/api/v1/users?page=1&page_size=50
Authorization: Bearer {token}

Scope: user:read

Query Parameters:

ParâmetroTipoDescriçãoPadrão
pageintegerNúmero da página1
page_sizeintegerItens por página (max: 100)25
rolesarray[string]Filtrar por roles (pode enviar múltiplos)-
querystringBusca por nome ou email-
external_idstringFiltrar por ID externo-
group_iduuidFiltrar por grupo-
activebooleanFiltrar por usuários ativos-

Exemplo com múltiplas roles:

GET /v1/users?roles=agent&roles=admin

Response: 200 OK

{
"count": 150,
"users": [
{
"id": "uuid",
"name": "Maria Santos",
"email": "[email protected]",
"role": "agent",
"external_id": "crm-123",
"verified": true
}
]
}

Obter Próprio Perfil

GET https://acme.pumahelp.com/api/v1/users/me
Authorization: Bearer {token}

Scope: profile:read:own

Response: 200 OK

{
"id": "uuid",
"name": "João Silva",
"email": "[email protected]",
"role": "agent",
"notes": "Notas internas sobre o usuário",
"external_id": "crm-123",
"verified": true,
"created_at": "2025-01-01T10:00:00Z",
"updated_at": "2025-01-15T14:30:00Z"
}

Obter Usuário por ID

GET https://acme.pumahelp.com/api/v1/users/{user_id}
Authorization: Bearer {token}

Scope: user:read

Response: 200 OK

{
"id": "uuid",
"name": "João Silva",
"email": "[email protected]",
"role": "agent",
"notes": "Notas internas",
"external_id": "crm-123",
"verified": true,
"created_at": "2025-01-01T10:00:00Z",
"updated_at": "2025-01-15T14:30:00Z"
}

Atualizar Usuário

PUT https://acme.pumahelp.com/api/v1/users/{user_id}
Authorization: Bearer {token}
Content-Type: application/json

Scope: user:update

Request Body:

{
"name": "João Silva Santos",
"role": "admin",
"email": "[email protected]",
"notes": "Promovido a admin em Jan/2025",
"external_id": "crm-456",
"verified": true,
"group_ids": ["uuid-grupo-3"]
}
CampoTipoDescrição
namestringNome do usuário
rolestringNova role do usuário
emailstringNovo email
notesstringNotas internas (não visível para end-users)
external_idstringID externo
verifiedbooleanStatus de verificação de email
group_idsarray[uuid]Grupos do usuário
observação

Todos os campos são opcionais. Apenas os campos enviados serão atualizados.

Response: 200 OK

{
"id": "uuid",
"name": "João Silva Santos",
"email": "[email protected]",
"role": "admin",
"external_id": "crm-456",
"verified": true
}

Deletar Usuário

DELETE https://acme.pumahelp.com/api/v1/users/{user_id}
Authorization: Bearer {token}

Scope: user:delete

Response: 204 No Content


Enviar Email de Verificação

POST https://acme.pumahelp.com/api/v1/users/{user_id}/email/send/verification
Authorization: Bearer {token}

Scope: user:manage

Response: 204 No Content

observação

Envia um email para o usuário com link para verificar o endereço de email.


Verificar Email

POST https://acme.pumahelp.com/api/v1/users/email/verify?token={verification_token}

Rate Limit: IP-based
Sem autenticação necessária

Query Parameters:

  • token - Token de verificação enviado por email

Response: 204 No Content


Reenviar Email de Verificação

POST https://acme.pumahelp.com/api/v1/users/email/resend/verification?email={user_email}

Rate Limit: IP-based
Sem autenticação necessária

Query Parameters:

  • email - Email do usuário que precisa reenviar verificação

Response: 204 No Content


Mesclar Sessão (Merge Session)

POST https://acme.pumahelp.com/api/v1/users/merge-session
Authorization: Bearer {token}
Content-Type: application/json

Scope: session:merge

Request Body:

{
"target_auth_token": "token-jwt-do-usuario-autenticado"
}

Response: 204 No Content

dica

Use este endpoint para mesclar a sessão de um usuário convidado (guest) com um usuário autenticado. Útil quando um visitante cria tickets como guest e depois faz login/cadastro.


👤 Usuários Convidados (Guests)

Usuários convidados permitem criar sessões temporárias para visitantes que ainda não possuem uma conta completa no sistema. Ideal para permitir que potenciais clientes criem tickets sem necessidade de registro completo.

Criar Usuário Convidado

POST https://acme.pumahelp.com/api/v2/guests
X-API-Key: {authorization_token}
Content-Type: application/json

Scope: guest:create

Use Case: Permitir que visitantes do seu site ou aplicativo abram tickets de suporte sem criar uma conta completa, reduzindo fricção no processo de obter ajuda.

Request Body:

{
"name": "Visitante João"
}

Campos do Request:

CampoTipoObrigatórioDescrição
namestringNãoNome do visitante. Se não fornecido, será gerado automaticamente

Response: 201 Created

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Visitante João",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Campos do Response:

CampoTipoDescrição
iduuidID único do usuário convidado
namestringNome do convidado
access_tokenstringToken JWT para autenticar requests em nome deste convidado

Exemplo Completo:

# Criar guest user
curl -X POST https://acme.pumahelp.com/api/v2/guests \
-H "X-API-Key: SEU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Visitante da Landing Page"}'

# Usar o access_token do guest para criar ticket
curl -X POST https://acme.pumahelp.com/api/v1/tickets \
-H "Authorization: Bearer TOKEN_DO_GUEST" \
-H "Content-Type: application/json" \
-d '{
"subject": "Dúvida sobre o produto",
"priority": "normal",
"type": "question",
"comment": {
"body": "Gostaria de saber mais sobre os planos disponíveis",
"public": true
}
}'
dica

Use este recurso para implementar um formulário de contato avançado ou chat widget onde visitantes possam criar tickets sem criar conta.


👥 Grupos

Criar Grupo

POST https://acme.pumahelp.com/api/v1/groups
Authorization: Bearer {token}
Content-Type: application/json

Scope: group:create

Request Body:

{
"name": "Suporte Técnico",
"description": "Equipe responsável por suporte técnico"
}
CampoTipoObrigatórioDescrição
namestringSimNome do grupo
descriptionstringNãoDescrição do grupo

Response: 201 Created

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Suporte Técnico",
"description": "Equipe responsável por suporte técnico",
"default": false
}

Listar Grupos

GET https://acme.pumahelp.com/api/v1/groups?page=1&page_size=50
Authorization: Bearer {token}

Scope: group:read

Query Parameters:

ParâmetroTipoDescriçãoObrigatório
pageintegerNúmero da páginaSim
page_sizeintegerItens por páginaSim
querystringBusca por nome ou descriçãoNão
user_iduuidFiltrar grupos que contêm um usuário específicoNão

Response: 200 OK

{
"count": 3,
"groups": [
{
"id": "uuid",
"name": "Suporte Técnico",
"description": "Equipe responsável por suporte técnico",
"default": false
},
{
"id": "uuid",
"name": "Vendas",
"description": "Equipe de vendas",
"default": true
}
]
}

Listar Grupos com Usuários

GET https://acme.pumahelp.com/api/v1/groups/users?page=1&page_size=50
Authorization: Bearer {token}

Scope: group:read

Query Parameters:

ParâmetroTipoDescriçãoObrigatório
pageintegerNúmero da páginaSim
page_sizeintegerItens por páginaSim
querystringBusca por nome ou descriçãoNão
user_iduuidFiltrar grupos que contêm um usuário específicoNão

Response: 200 OK

{
"count": 3,
"groups": [
{
"id": "uuid",
"name": "Suporte Técnico",
"description": "Equipe responsável por suporte técnico",
"default": false,
"users": [
{
"id": "uuid",
"name": "Maria Santos",
"email": "[email protected]",
"role": "agent",
"external_id": null,
"verified": true
},
{
"id": "uuid",
"name": "João Silva",
"email": "[email protected]",
"role": "agent",
"external_id": null,
"verified": true
}
]
}
]
}
dica

Use este endpoint quando precisar dos membros de cada grupo. Mais eficiente que buscar grupo por grupo.


Obter Grupo por ID

GET https://acme.pumahelp.com/api/v1/groups/{group_id}
Authorization: Bearer {token}

Scope: group:read

Response: 200 OK

{
"id": "uuid",
"name": "Suporte Técnico",
"description": "Equipe responsável por suporte técnico",
"default": false
}

Atualizar Grupo

PUT https://acme.pumahelp.com/api/v1/groups/{group_id}
Authorization: Bearer {token}
Content-Type: application/json

Scope: group:update

Request Body:

{
"name": "Suporte Técnico Nível 2",
"description": "Equipe de suporte avançado"
}
CampoTipoDescrição
namestringNovo nome do grupo
descriptionstringNova descrição
observação

Todos os campos são opcionais. Apenas os campos enviados serão atualizados.

Response: 204 No Content


Deletar Grupo

DELETE https://acme.pumahelp.com/api/v1/groups/{group_id}
Authorization: Bearer {token}

Scope: group:delete

Response: 204 No Content

observação

Para adicionar ou remover usuários de um grupo, use o endpoint de atualização de usuário (PUT /v1/users/{user_id}) enviando o campo group_ids. apenas os grupos padrão não podem ser removidos.


🤖 Macros

Criar Macro

POST https://acme.pumahelp.com/api/v1/macros
Authorization: Bearer {token}
Content-Type: application/json

Scope: macro:create

Request Body:

{
"title": "Resposta Padrão - Problema de Login",
"description": "Resposta automática para problemas de login",
"type": "group",
"group_ids": ["uuid-grupo-1", "uuid-grupo-2"],
"actions": [
{
"field": "status",
"value": "pending"
},
{
"field": "priority",
"value": "high"
},
{
"field": "comment_value",
"value": "Por favor, tente limpar o cache do navegador e fazer login novamente."
}
]
}
CampoTipoObrigatórioDescrição
titlestringSimTítulo da macro
descriptionstringNãoDescrição da macro
typestringSimTipo: user (usuário) ou group (grupo)
group_idsarray[uuid]NãoIDs dos grupos que podem usar esta macro (obrigatório se type=group)
actionsarray[object]SimLista de ações a executar
actions[].fieldstringSimCampo a modificar: comment_value, status, type, priority
actions[].valueanySimValor a aplicar (tipo depende do field)

Response: 201 Created

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "Resposta Padrão - Problema de Login",
"description": "Resposta automática para problemas de login",
"type": "auto",
"group_ids": ["uuid-grupo-1"],
"actions": [
{
"field": "status",
"value": "pending"
},
{
"field": "comment_value",
"value": "Por favor, tente limpar o cache..."
}
],
"created_at": "2025-01-01T10:00:00Z"
}

Listar Macros

GET https://acme.pumahelp.com/api/v1/macros?page=1&page_size=50
Authorization: Bearer {token}

Scope: macro:read

Query Parameters:

ParâmetroTipoDescriçãoObrigatório
pageintegerNúmero da páginaSim
page_sizeintegerItens por páginaSim

Response: 200 OK

{
"count": 5,
"macros": [
{
"id": "uuid",
"title": "Resposta Padrão - Problema de Login",
"description": "Resposta automática para problemas de login",
"type": "user",
"group_ids": [],
"actions": [],
"created_at": "2025-01-01T10:00:00Z"
}
]
}

Obter Macro por ID

GET https://acme.pumahelp.com/api/v1/macros/{macro_id}
Authorization: Bearer {token}

Scope: macro:read

Response: 200 OK

{
"id": "uuid",
"title": "Resposta Padrão - Problema de Login",
"description": "Resposta automática para problemas de login",
"type": "user",
"group_ids": ["uuid-grupo-1"],
"actions": [
{
"field": "status",
"value": "pending"
},
{
"field": "priority",
"value": "high"
}
],
"created_at": "2025-01-01T10:00:00Z"
}

Atualizar Macro

PUT https://acme.pumahelp.com/api/v1/macros/{macro_id}
Authorization: Bearer {token}
Content-Type: application/json

Scope: macro:update

Request Body:

{
"title": "Resposta Padrão - Login Atualizada",
"description": "Nova descrição",
"type": "group",
"group_ids": ["uuid-grupo-3"],
"actions": [
{
"field": "status",
"value": "solved"
}
]
}
observação

Todos os campos são opcionais. Apenas os campos enviados serão atualizados.

Response: 200 OK

{
"id": "uuid",
"title": "Resposta Padrão - Login Atualizada",
"description": "Nova descrição",
"type": "group",
"group_ids": ["uuid-grupo-3"],
"actions": [
{
"field": "status",
"value": "solved"
}
],
"created_at": "2025-01-01T10:00:00Z"
}

Deletar Macro

DELETE https://acme.pumahelp.com/api/v1/macros/{macro_id}
Authorization: Bearer {token}

Scope: macro:delete

Response: 204 No Content


👁️ Visualizações

Visualizações são filtros de tickets salvos com nome — os atalhos da barra lateral do Inbox. Cada uma guarda uma query na mesma sintaxe do filtro de listagem (status:open assignee:me …) e, opcionalmente, uma ordenação.

Existem dois escopos:

EscopoQuem vêQuem cria/edita/apagaLimite
organizationTodos os agentes da organizaçãoOwner e Admin5 por organização
personalApenas quem criouO próprio usuário (Owner, Admin ou Agente)5 por usuário

Toda organização nasce com 5 visualizações padrão no escopo organization ("Seus tickets sem resolução", "Tickets não atribuídos", "Todos os tickets sem resolução", "Tickets resolvidos recentemente" e "Tickets pendentes"). Elas são comuns — podem ser renomeadas, alteradas ou apagadas por Owner/Admin.

Macros como assignee:me são resolvidas para quem está chamando, então uma visualização da organização mostra (e conta) tickets diferentes para cada agente.

Listar Visualizações

GET https://acme.pumahelp.com/api/v1/views
Authorization: Bearer {token}

Scope: view:read

Retorna as visualizações da organização mais as pessoais do usuário autenticado, ordenadas por position.

Response: 200 OK

{
"count": 2,
"views": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Seus tickets sem resolução",
"query": "assignee:me status:new status:open status:pending",
"sort_by": null,
"sort_order": null,
"scope": "organization",
"position": 1,
"created_at": "2025-01-01T10:00:00Z",
"updated_at": null
},
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"name": "Urgentes do meu grupo",
"query": "priority:urgent group:suporte",
"sort_by": "created_at",
"sort_order": "desc",
"scope": "personal",
"position": 1,
"created_at": "2025-01-02T10:00:00Z",
"updated_at": null
}
]
}

Contagem por Visualização

GET https://acme.pumahelp.com/api/v1/views/counts
Authorization: Bearer {token}

Scope: view:read

Retorna quantos tickets cada visualização listaria para o usuário autenticado — a mesma visibilidade da listagem de tickets (agentes veem apenas os tickets dos seus grupos).

Response: 200 OK

{
"counts": [
{ "view_id": "550e8400-e29b-41d4-a716-446655440000", "count": 12 },
{ "view_id": "660e8400-e29b-41d4-a716-446655440001", "count": 3 }
]
}

Criar Visualização

POST https://acme.pumahelp.com/api/v1/views
Authorization: Bearer {token}
Content-Type: application/json

Scope: view:create

Request Body:

{
"name": "Urgentes do meu grupo",
"query": "priority:urgent group:suporte",
"sort_by": "created_at",
"sort_order": "desc",
"scope": "personal"
}
CampoTipoObrigatórioDescrição
namestringSimNome exibido (máx. 80). Único dentro do escopo
querystringNãoFiltro na sintaxe de GET /v1/tickets?query= (máx. 500). Vazio = todos os tickets visíveis
sort_bystringNãostatus, priority, created_at ou updated_at
sort_orderstringNãoasc ou desc
scopestringSimorganization (somente Owner/Admin) ou personal

Response: 201 Created — mesmo formato de um item da listagem.

Erros:

  • 400 — limite de 5 atingido no escopo, nome já usado no escopo, ou campo inválido
  • 401 — agente tentando criar uma visualização organization

Atualizar Visualização

PUT https://acme.pumahelp.com/api/v1/views/{view_id}
Authorization: Bearer {token}
Content-Type: application/json

Scope: view:update

Todos os campos são opcionais; só os enviados são alterados. Envie sort_by: "" para remover a ordenação.

{
"name": "Urgentes",
"query": "priority:urgent status:open"
}

Response: 200 OK — a visualização atualizada.

Erros:

  • 401 — agente tentando alterar uma visualização organization
  • 404 — visualização inexistente ou pessoal de outro usuário

Deletar Visualização

DELETE https://acme.pumahelp.com/api/v1/views/{view_id}
Authorization: Bearer {token}

Scope: view:delete

Mesmas regras de posse do PUT.

Response: 204 No Content


🔗 Webhooks

Criar Webhook

POST https://acme.pumahelp.com/api/v1/webhooks
Authorization: Bearer {token}
Content-Type: application/json

Scope: webhook:create

Request Body:

{
"name": "Notificação Slack",
"url": "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXX",
"events": ["ticket.created", "ticket.updated", "user.created"]
}
CampoTipoObrigatórioDescrição
namestringSimNome do webhook
urlstringSimURL que receberá as notificações
eventsarray[string]SimEventos a monitorar: ticket.created, ticket.updated, user.created, user.updated, user.deleted

Response: 201 Created

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Notificação Slack",
"url": "https://hooks.slack.com/services/...",
"events": ["ticket.created", "ticket.updated", "user.created"],
"created_at": "2025-01-01T10:00:00Z",
"key": "whk_live_a1b2c3d4e5_secretkeyhereXXXXXXXX"
}
observação

Cada organização pode criar no máximo 5 webhooks. Se você atingir esse limite, será necessário deletar um webhook existente antes de criar um novo.

important

A key é gerada automaticamente durante a criação do webhook e é usada para assinar as requisições enviadas. Guarde-a de forma segura, pois será necessária para validar a autenticidade dos webhooks recebidos.


Listar Webhooks

GET https://acme.pumahelp.com/api/v1/webhooks?page=1&page_size=50
Authorization: Bearer {token}

Scope: webhook:read

Query Parameters:

ParâmetroTipoDescriçãoObrigatório
pageintegerNúmero da páginaSim
page_sizeintegerItens por páginaSim

Response: 200 OK

{
"count": 2,
"webhooks": [
{
"id": "uuid",
"name": "Notificação Slack",
"url": "https://hooks.slack.com/services/...",
"events": ["ticket.created", "ticket.updated"],
"created_at": "2025-01-01T10:00:00Z",
"key": "whk_live_a1b2c3d4e5_secretkeyhereXXXXXXXX"
}
]
}

Obter Webhook

GET https://acme.pumahelp.com/api/v1/webhooks/{webhook_id}
Authorization: Bearer {token}

Scope: webhook:read

Response: 200 OK

{
"id": "uuid",
"name": "Notificação Slack",
"url": "https://hooks.slack.com/services/...",
"events": ["ticket.created", "ticket.updated", "user.created"],
"created_at": "2025-01-01T10:00:00Z",
"key": "whk_live_a1b2c3d4e5_secretkeyhereXXXXXXXX"
}

Atualizar Webhook

PUT https://acme.pumahelp.com/api/v1/webhooks/{webhook_id}
Authorization: Bearer {token}
Content-Type: application/json

Scope: webhook:update

Request Body:

{
"name": "Notificação Slack Atualizada",
"url": "https://hooks.slack.com/services/UPDATED",
"events": ["ticket.created", "user.deleted"]
}
CampoTipoDescrição
namestringNovo nome do webhook
urlstringNova URL
eventsarray[string]Novos eventos
observação

Todos os campos são opcionais. Apenas os campos enviados serão atualizados.

Response: 204 No Content


Deletar Webhook

DELETE https://acme.pumahelp.com/api/v1/webhooks/{webhook_id}
Authorization: Bearer {token}

Scope: webhook:delete

Response: 204 No Content


Rotacionar Chave do Webhook

PUT https://acme.pumahelp.com/api/v1/webhooks/rotate/{webhook_id}
Authorization: Bearer {token}

Scope: webhook:rotate

Descrição:
Gera uma nova chave de assinatura para o webhook. A chave antiga é invalidada imediatamente.

aviso

Após rotacionar a chave, atualize imediatamente sua aplicação que recebe os webhooks com a nova chave para validação de assinatura HMAC. Webhooks enviados após a rotação usarão a nova chave.

Response: 200 OK

{
"key": "whk_live_x9y8z7w6v5_newsecretkeyXXXXXXXX"
}
CampoTipoDescrição
keystringNova chave gerada (com prefixo whk_live_)

Exemplo de validação HMAC em Node.js:

const crypto = require('crypto');

function validateWebhook(body, signature, timestamp, webhookKey) {
const stringToSign = `${timestamp}.${JSON.stringify(body)}`;
const expectedSignature = crypto
.createHmac('sha256', webhookKey)
.update(stringToSign)
.digest('base64');

return signature === expectedSignature;
}

🔑 API Keys

Listar API Keys

GET https://acme.pumahelp.com/api/v1/keys?page=1&page_size=50
Authorization: Bearer {token}

Scope: apikey:read

Query Parameters:

ParâmetroTipoDescriçãoObrigatório
pageintegerNúmero da páginaSim
page_sizeintegerItens por páginaSim

Response: 200 OK

{
"count": 3,
"keys": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"prefix": "rk_live_",
"name": "Integration - CRM",
"description": "API Key para sincronização com CRM",
"scopes": ["ticket:create", "ticket:read", "user:upsert"],
"key_lookup": "a1b2c3d4e5f6",
"created_at": "2025-01-01T10:00:00Z"
}
]
}
CampoTipoDescrição
iduuidID da API Key
prefixstringPrefixo da chave para identificação rápida
namestringNome da API Key
descriptionstringDescrição
scopesarray[string]Escopos atribuídos
key_lookupstringLookup hash para busca interna
created_atdatetimeData de criação

Criar API Key

POST https://acme.pumahelp.com/api/v1/keys
Authorization: Bearer {token}
Content-Type: application/json

Scope: apikey:create

Request Body:

{
"name": "Integration - CRM",
"description": "Para sincronização automática de tickets e usuários",
"scopes": ["ticket:create", "ticket:read", "user:upsert"]
}
CampoTipoObrigatórioDescrição
namestringSimNome da API Key
descriptionstringNãoDescrição do uso da chave
scopesarray[string]SimLista de escopos permitidos

Response: 201 Created

{
"key": "rk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"
}
cuidado

A chave completa (key) é exibida apenas uma vez! Guarde-a imediatamente em local seguro. Após esta resposta, você só verá o prefix e key_lookup ao listar as chaves.


Atualizar API Key

PUT https://acme.pumahelp.com/api/v1/keys/{key_id}
Authorization: Bearer {token}
Content-Type: application/json

Scope: apikey:update

Request Body:

{
"name": "Integration - CRM v2",
"description": "Atualizado para nova integração",
"scopes": ["ticket:create", "ticket:read", "ticket:update", "user:upsert"]
}
CampoTipoDescrição
namestringNovo nome
descriptionstringNova descrição
scopesarray[string]Novos escopos
observação

Todos os campos são opcionais. Apenas os campos enviados serão atualizados.

Response: 200 OK

{
"id": "uuid",
"prefix": "rk_live_",
"name": "Integration - CRM v2",
"description": "Atualizado para nova integração",
"scopes": ["ticket:create", "ticket:read", "ticket:update", "user:upsert"],
"key_lookup": "a1b2c3d4e5f6",
"created_at": "2025-01-01T10:00:00Z"
}

Rotacionar API Key

PUT https://acme.pumahelp.com/api/v1/keys/rotate/{key_id}
Authorization: Bearer {token}

Scope: apikey:rotate

Response: 200 OK

{
"key": "rk_live_x9y8z7w6v5u4t3s2r1q0p9o8n7m6l5k4j3i2h1g0"
}
aviso

Ao rotacionar, a chave antiga torna-se inválida imediatamente. Atualize seus sistemas com a nova chave antes de rotacionar!


Deletar API Key

DELETE https://acme.pumahelp.com/api/v1/keys/{key_id}
Authorization: Bearer {token}

Scope: apikey:delete

Response: 204 No Content

cuidado

Esta ação é irreversível. Sistemas usando esta chave perderão acesso imediatamente.


🏢 Organização

Obter Organização

GET https://acme.pumahelp.com/api/v1/organizations
Authorization: Bearer {token}

Scope: organization:read

Response: 200 OK

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Acme Corporation",
"app_name": "acme",
"subscription_status": "active",
"created_at": "2025-01-01T10:00:00Z"
}

📁 Upload de Arquivos

Upload de Arquivo

POST https://acme.pumahelp.com/api/v1/uploads
Authorization: Bearer {token}
Content-Type: multipart/form-data

Scope: file:upload

Limite: 50MB por arquivo

Request Body (multipart):

file: (binary data)

Response: 201 Created

{
"id": "uuid",
"file_name": "screenshot.png",
"content_type": "image/png",
"size": 102400,
"content_url": "https://cdn.pumahelp.com/files/abc123xyz789"
}

Tipos de Arquivo Suportados:

  • Imagens: .jpg, .jpeg, .png, .gif, .webp
  • Documentos: .pdf, .doc, .docx, .xls, .xlsx, .txt
  • Outros: .zip, .rar, .csv

Exemplo:

curl -X POST https://acme.pumahelp.com/api/v1/uploads \
-H "Authorization: Bearer SEU_TOKEN" \
-F "file=@/caminho/para/arquivo.png"

Exemplo com JavaScript:

const formData = new FormData();
formData.append('file', fileInput.files[0]);

fetch('https://acme.pumahelp.com/api/v1/uploads', {
method: 'POST',
headers: {
'Authorization': 'Bearer SEU_TOKEN'
},
body: formData
})
.then(res => res.json())
.then(data => {
console.log('Arquivo enviado:', data.content_url);
});
observação

Após o upload, use o id retornado para anexar o arquivo a um comentário ao criar ou atualizar um ticket.


🔍 Escopos Disponíveis

Listar Escopos

GET https://acme.pumahelp.com/api/v1/scopes?category=ticket
Authorization: Bearer {token}

Scope: Público (qualquer usuário autenticado)

Query Parameters:

ParâmetroTipoDescriçãoObrigatório
categorystringFiltrar por categoriaNão

Response: 200 OK

{
"count": 15,
"categories_count": {
"ticket": 5,
"user": 4,
"group": 3,
"macro": 3
},
"scopes": [
{
"scope": "ticket:read",
"name": "Ler Tickets",
"description": "Permite visualizar tickets",
"category": "ticket"
},
{
"scope": "ticket:create",
"name": "Criar Tickets",
"description": "Permite criar novos tickets",
"category": "ticket"
}
]
}

⚙️ Configurações de Conta

Alterar Email

POST https://acme.pumahelp.com/api/v1/account/email/change
Authorization: Bearer {token}
Content-Type: application/json

Scope: account:update:own

Request Body:

{
"new_email": "[email protected]",
"password": "senha-atual"
}

Response: 204 No Content


Solicitar Redefinição de Senha

POST https://acme.pumahelp.com/api/v1/account/password/forgot
Content-Type: application/json

Rate Limit: IP-based

observação

Endpoint público. Envia email com token de redefinição.

Request Body:

{
"email": "[email protected]"
}

Response: 204 No Content


Redefinir Senha

POST https://acme.pumahelp.com/api/v1/account/password/reset
Content-Type: application/json

Rate Limit: IP-based

Request Body:

{
"token": "token-recebido-por-email",
"new_password": "nova-senha-segura"
}

Response: 204 No Content


Alterar Senha

PUT https://acme.pumahelp.com/api/v1/account/password/change
Authorization: Bearer {token}
Content-Type: application/json

Scope: account:update:own

Request Body:

{
"current_password": "senha-atual",
"new_password": "nova-senha-segura"
}

Response: 204 No Content


❌ Códigos de Erro

A API retorna códigos HTTP padrão:

CódigoSignificadoDescrição
200OKRequisição bem-sucedida
201CreatedRecurso criado com sucesso
204No ContentRecurso excluído com sucesso
400Bad RequestDados inválidos na requisição
401UnauthorizedToken inválido ou ausente
403ForbiddenSem permissão (scope insuficiente)
404Not FoundRecurso não encontrado
409ConflictConflito (ex: email já existe)
422Unprocessable EntityValidação falhou
429Too Many RequestsRate limit excedido
500Internal Server ErrorErro no servidor

Formato de Erro:

{
"errors": [
"O campo 'email' é obrigatório",
"O campo 'password' deve ter no mínimo 8 caracteres"
]
}

Quick Start

# 1. Login (substitua 'acme' pelo seu subdomínio)
curl -X POST https://acme.pumahelp.com/api/v1/users/login \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"password": "senha"
}'

# Response:
# {
# "access_token": "eyJhbGc...",
# "refresh_token": "eyJhbGc..."
# }

# 2. Criar API Key (opcional, para integrações)
curl -X POST https://acme.pumahelp.com/api/v1/keys \
-H "Authorization: Bearer SEU_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Minha Integração",
"scopes": ["ticket:create", "ticket:read"]
}'

# 3. Criar Ticket (com JWT)
curl -X POST https://acme.pumahelp.com/api/v1/tickets \
-H "Authorization: Bearer SEU_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"subject": "Meu primeiro ticket via API",
"priority": "normal",
"type": "question",
"comment": {
"body": "Testando a integração com a API",
"public": true
}
}'

# OU com API Key
curl -X POST https://acme.pumahelp.com/api/v1/tickets \
-H "X-API-Key: SEU_API_KEY_SECRET" \
-H "Content-Type: application/json" \
-d '{...}'

# 4. Listar Tickets
curl -X GET "https://acme.pumahelp.com/api/v1/tickets?page=1&page_size=10" \
-H "Authorization: Bearer SEU_ACCESS_TOKEN"
dica

Substitua acme em todos os exemplos pelo seu App Name configurado no dashboard.


💡 Melhores Práticas

Segurança

  • ✅ Use HTTPS sempre
  • ✅ Nunca exponha secrets no código versionado
  • ✅ Rotacione API Keys regularmente
  • ✅ Use princípio do menor privilégio (scopes mínimos necessários)
  • ✅ Valide assinaturas HMAC dos webhooks

Performance

  • ✅ Use paginação adequada (page_size adequado)
  • ✅ Implemente retry logic com backoff exponencial
  • ✅ Cache dados que mudam pouco
  • ✅ Monitore headers X-RateLimit-*
  • ✅ Use webhooks em vez de polling

Integração

  • ✅ Valide dados antes de enviar
  • ✅ Trate todos os códigos de erro
  • ✅ Implemente logs para auditoria
  • ✅ Teste em ambiente de desenvolvimento primeiro

🔧 Troubleshooting

Erro 401: Unauthorized

Sintoma: Todas as requisições retornam 401 Unauthorized

Causas Comuns:

  1. Token JWT expirado
  2. API Key inválida ou revogada
  3. Header de autorização mal formatado

Soluções:

# Verifique o formato do header
# ✅ Correto:
Authorization: Bearer eyJhbGci...

# ❌ Incorreto:
Authorization: eyJhbGci...
Authorization: bearer eyJhbGci...

# Para API Key:
# ✅ Correto:
X-API-Key: rk_live_abc123...

# Renovar token expirado:
curl -X POST https://acme.pumahelp.com/api/v1/tokens/refresh \
-H "Content-Type: application/json" \
-d '{"refresh_token": "SEU_REFRESH_TOKEN"}'

Erro 403: Forbidden

Sintoma: Request autenticado mas retorna 403 Forbidden

Causas Comuns:

  1. API Key sem o scope necessário
  2. Usuário sem permissão para a ação

Soluções:

# Verificar scopes da sua API Key:
curl -X GET https://acme.pumahelp.com/api/v1/keys \
-H "Authorization: Bearer SEU_TOKEN"

# Verificar scopes disponíveis:
curl -X GET https://acme.pumahelp.com/api/v1/scopes \
-H "Authorization: Bearer SEU_TOKEN"

# Atualizar API Key com scopes necessários ou criar nova

Erro 422: Unprocessable Entity

Sintoma: Request retorna 422 com mensagem de validação

Causas Comuns:

  1. Campos obrigatórios faltando
  2. Formato de dados inválido
  3. Valor fora do range permitido

Exemplo de Response:

{
"errors": [
"O campo 'subject' é obrigatório",
"O campo 'priority' deve ser: low, normal, high ou urgent"
]
}

Soluções:

  • Revise a documentação do endpoint para campos obrigatórios
  • Verifique tipos de dados esperados
  • Valide valores enum (status, priority, type)

Erro 429: Too Many Requests

Sintoma: Requests bloqueados com 429 Too Many Requests

Causa: Rate limit excedido

Solução:

// Implementar retry com backoff exponencial
async function requestWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch(url, options);

if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After') || 10;
const delay = Math.pow(2, i) * 1000; // Backoff exponencial
await new Promise(resolve => setTimeout(resolve, Math.max(delay, retryAfter * 1000)));
continue;
}

return response;
}

throw new Error('Max retries exceeded');
}

Webhook Não Está Sendo Chamado

Sintomas:

  • Webhook criado mas não recebe eventos
  • Eventos não aparecem nos logs

Checklist:

  1. ✅ URL está acessível publicamente (não localhost)?
  2. ✅ URL usa HTTPS?
  3. ✅ Servidor responde com 200 OK em menos de 30 segundos?
  4. ✅ Eventos selecionados estão corretos?

Testar Webhook:

# Verificar status do webhook:
curl -X GET https://acme.pumahelp.com/api/v1/webhooks/{webhook_id} \
-H "Authorization: Bearer SEU_TOKEN"

# Criar evento de teste (criar ticket):
curl -X POST https://acme.pumahelp.com/api/v1/tickets \
-H "X-API-Key: SEU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"subject": "Teste Webhook", "priority": "normal", "type": "question", "comment": {"body": "Teste", "public": true}}'

FAQ

P: Posso usar a mesma API Key em múltiplos ambientes?
R: Não é recomendado. Crie API Keys separadas para desenvolvimento, staging e produção.

P: Quantos webhooks posso criar?
R: Máximo de 5 webhooks por organização. Recomendamos consolidar eventos relacionados em um único endpoint quando possível.

P: Como sei qual scope usar?
R: Consulte GET /v1/scopes para lista completa. Use o princípio do menor privilégio: apenas os scopes necessários.

P: Posso deletar permanentemente um ticket?
R: Não. DELETE arquiva o ticket (soft delete). Isso preserva histórico e auditoria.


Status de Ticket

new
Ticket recém criado, aguardando primeira atribuição ou triagem.

open
Ticket em andamento, sendo trabalhado por um agente.

pending
Aguardando resposta do cliente ou informação externa.

solved
Ticket resolvido. Cliente pode reabrir se necessário.

closed
Ticket fechado. Não pode ser reaberto.


Prioridades

low - Baixa prioridade
normal - Prioridade normal (padrão)
high - Alta prioridade
urgent - Urgente


Tipos de Ticket

question - Pergunta/dúvida
incident - Problema/erro
problem - Problema complexo que afeta múltiplos usuários
task - Tarefa a ser realizada


Roles de Usuário

end-user
Usuário final, cliente. Pode criar tickets e visualizar apenas seus próprios tickets.

agent
Agente de suporte. Pode visualizar, atualizar e resolver tickets.

admin
Administrador. Pode gerenciar usuários, grupos, configurações e tem acesso completo.

owner
Proprietário da organização. Acesso total incluindo configurações de billing e organização.


Eventos de Webhook

ticket.created - Novo ticket criado
ticket.updated - Ticket atualizado (status, prioridade, assignee, etc)
user.created - Novo usuário criado
user.updated - Usuário atualizado
user.deleted - Usuário deletado


Versão da API: v2.1.0
Última Atualização: Dezembro de 2025