Visão Geral

O endpoint /api/externalAPIs/public/tickets/updateTicket permite atualizar campos de tickets existentes e movê-los entre diferentes estágios do pipeline, com rastreamento automático de eventos de auditoria.

Este endpoint suporta duas operações: atualização de campos OU movimentação de estágio, ou ambas combinadas. A movimentação é executada após a atualização quando ambas são solicitadas.

Características Principais

  • Operações Flexíveis: Atualização de campos, movimentação de estágio, ou ambas
  • Campos Completos: Todos os campos do ticket podem ser atualizados
  • Movimentação Inteligente: Move tickets entre estágios com rastreamento de motivos
  • Eventos Automáticos: Cria automaticamente eventos de auditoria para todas as mudanças
  • Integração HubSpot: Sincroniza estágios quando habilitado

Casos de Uso

  • Atualizar informações do ticket durante o progresso do workflow
  • Mover tickets entre estágios do pipeline
  • Atribuir tickets a usuários específicos
  • Fechar/abrir tickets com documentação adequada
  • Operações automatizadas com workflows

Endpoint

POST /api/externalAPIs/public/tickets/updateTicket

Autenticação

  • Header: Authorization: Bearer <token>
  • Content-Type: application/json
  • Middleware: tolkyAuthMiddleware (injeta hostId/hostSlug automaticamente)

Parâmetros

Obrigatórios

ParâmetroTipoDescrição
ticketIdstring (UUID)ID único do ticket a ser atualizado

Campos de Atualização (Opcionais)

ParâmetroTipoDescrição
subjectstringAssunto do ticket
descriptionstringDescrição detalhada do problema
definitionOfDonestringDefinição de pronto (DOD)
statusobjectStatus do ticket
status.labelstringNome do status
status.levelnumberNível numérico do status
status.tickets_levels_idstring (UUID)ID do nível (preferencial)
importanceobjectImportância do ticket
importance.importance_levelnumberNível de importância (1=baixa, 4=urgente)
importance.importance_labelstringLabel da importância
assignedUserobjectUsuário responsável
assignedUser.hostPeopleIdstring (UUID)ID do usuário no host
assignedUser.namestringNome do usuário (opcional, para histórico)
closedbooleanStatus de fechamento do ticket

Campos de Movimentação (Opcionais)

ParâmetroTipoDescrição
newLevelstring | UUIDNome/nível ou ID do nível de destino
reasonstringMotivo da movimentação (cria evento “Motivo”)
conversationIdstring (UUID)ID da conversa (para eventos no chat)
isAutomaticMovebooleanIndica se é movimentação automática (default: false)

Contexto/Segurança (Opcionais)

ParâmetroTipoDescrição
authUserIdstring (UUID)ID do usuário autenticado (para vincular eventos)
globalDataobjectContexto adicional da requisição

Regras de Negócio

  • Obrigatório: ticketId deve ser fornecido
  • Operação Mínima: Pelo menos uma operação (atualização OU movimentação)
  • Ordem de Execução: Se ambas operações forem solicitadas, a atualização é executada primeiro, depois a movimentação
  • Permissões: Respeita permissões do host via tolkyAuthMiddleware
  • Prioridade de Nível: status.tickets_levels_id é preferencial para trocar status

Exemplos de Requisição

Apenas Atualização

{
  "ticketId": "b8c9f8c4-0b6a-4b2a-8f8f-0c9d9e2f5a1a",
  "subject": "Atualizar orçamento",
  "description": "Cliente solicitou revisão do escopo",
  "importance": { 
    "importance_level": 3, 
    "importance_label": "Alta" 
  },
  "assignedUser": { 
    "hostPeopleId": "4a6b9f6e-0f2f-4e2b-9a77-98b0d64c4d11" 
  }
}

Apenas Movimentação

{
  "ticketId": "b8c9f8c4-0b6a-4b2a-8f8f-0c9d9e2f5a1a",
  "newLevel": "3fb2a9f0-5aa3-4f6d-8d37-c2a0b1d7b9a2",
  "reason": "Aprovado pelo gerente",
  "isAutomaticMove": false
}

Atualização + Movimentação

{
  "ticketId": "b8c9f8c4-0b6a-4b2a-8f8f-0c9d9e2f5a1a",
  "subject": "Orçamento aprovado",
  "newLevel": "Concluído",
  "reason": "Entregue ao cliente",
  "authUserId": "d7b5d8a9-3f4c-4ed3-9c77-22f577d25f10"
}

Atualização de Status com ID Específico

{
  "ticketId": "b8c9f8c4-0b6a-4b2a-8f8f-0c9d9e2f5a1a",
  "status": {
    "label": "Em Progresso",
    "level": 2,
    "tickets_levels_id": "3fb2a9f0-5aa3-4f6d-8d37-c2a0b1d7b9a2"
  },
  "closed": false
}

Resposta de Sucesso

{
  "success": true,
  "message": "Ticket updated and moved successfully",
  "data": {
    "ticketId": "b8c9f8c4-0b6a-4b2a-8f8f-0c9d9e2f5a1a",
    "operationsPerformed": { 
      "updated": true, 
      "moved": true 
    },
    "updateResult": {
      "id": "b8c9f8c4-0b6a-4b2a-8f8f-0c9d9e2f5a1a",
      "protocol": "ABCD-1234",
      "subject": "Orçamento aprovado",
      "description": "Cliente solicitou revisão do escopo",
      "status": {
        "name": "Em Progresso",
        "level": 2,
        "color": "#F6AD55",
        "isClosed": false
      },
      "importance": 3,
      "importanceColor": "#F6AD55",
      "assignedUser": {
        "id": "4a6b9f6e-0f2f-4e2b-9a77-98b0d64c4d11",
        "name": "João Silva"
      }
    },
    "moveResult": {
      "data": {
        "ticketId": "b8c9f8c4-0b6a-4b2a-8f8f-0c9d9e2f5a1a",
        "previousLevel": { 
          "name": "Em Progresso", 
          "level": 2, 
          "id": "3fb2a9f0-5aa3-4f6d-8d37-c2a0b1d7b9a2" 
        },
        "newLevel": { 
          "name": "Concluído", 
          "level": 3, 
          "id": "4fb2a9f0-5aa3-4f6d-8d37-c2a0b1d7b9a3",
          "color": "#68D391", 
          "external_id": "completed" 
        },
        "updatedTicket": { ...registro completo do ticket... }
      }
    },
    "finalTicketState": { ...estado final do ticket... }
  }
}

Códigos de Erro

CódigoErroDescriçãoSolução
400MissingTicketIdTicket ID não fornecidoIncluir ticketId na requisição
400NoOperationSpecifiedNenhuma operação especificadaFornecer pelo menos um campo para atualizar ou newLevel
403Invalid credentialsCredenciais inválidasVerificar token de autenticação
500UpdateTicketErrorErro na atualizaçãoContatar suporte técnico
500MoveTicketErrorErro na movimentaçãoContatar suporte técnico

Exemplo com cURL

curl -X POST "$BASE_URL/api/externalAPIs/public/tickets/updateTicket" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "ticketId": "b8c9f8c4-0b6a-4b2a-8f8f-0c9d9e2f5a1a",
    "newLevel": "Concluído",
    "reason": "Entregue ao cliente"
  }'

Exemplo de Integração Completa

class TicketUpdater {
  constructor(apiToken, baseUrl) {
    this.apiToken = apiToken;
    this.baseUrl = baseUrl;
  }

  async updateTicket(ticketId, updates) {
    try {
      const response = await fetch(`${this.baseUrl}/api/externalAPIs/public/tickets/updateTicket`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${this.apiToken}`
        },
        body: JSON.stringify({
          ticketId,
          ...updates
        })
      });

      if (!response.ok) {
        const error = await response.json();
        throw new Error(`Erro ${response.status}: ${error.message}`);
      }

      return await response.json();
    } catch (error) {
      console.error('Erro ao atualizar ticket:', error);
      throw error;
    }
  }

  // Atualizar apenas campos
  async updateFields(ticketId, fields) {
    return await this.updateTicket(ticketId, fields);
  }

  // Mover apenas estágio
  async moveToLevel(ticketId, newLevel, reason, isAutomatic = false) {
    return await this.updateTicket(ticketId, {
      newLevel,
      reason,
      isAutomaticMove: isAutomatic
    });
  }

  // Atualizar e mover
  async updateAndMove(ticketId, fields, newLevel, reason) {
    return await this.updateTicket(ticketId, {
      ...fields,
      newLevel,
      reason
    });
  }

  // Fechar ticket
  async closeTicket(ticketId, reason, authUserId) {
    return await this.updateTicket(ticketId, {
      closed: true,
      newLevel: "Concluído",
      reason,
      authUserId
    });
  }

  // Atribuir ticket
  async assignTicket(ticketId, hostPeopleId, userName) {
    return await this.updateTicket(ticketId, {
      assignedUser: {
        hostPeopleId,
        name: userName
      }
    });
  }
}

// Uso da classe
const ticketUpdater = new TicketUpdater('seu_token_aqui', 'https://api.tolky.to');

// Exemplos de uso
async function examples() {
  try {
    // Atualizar apenas campos
    await ticketUpdater.updateFields('b8c9f8c4-0b6a-4b2a-8f8f-0c9d9e2f5a1a', {
      subject: 'Novo assunto',
      importance: { importance_level: 4, importance_label: 'Urgente' }
    });

    // Mover apenas estágio
    await ticketUpdater.moveToLevel('b8c9f8c4-0b6a-4b2a-8f8f-0c9d9e2f5a1a', 'Concluído', 'Aprovado pelo cliente');

    // Atualizar e mover
    await ticketUpdater.updateAndMove('b8c9f8c4-0b6a-4b2a-8f8f-0c9d9e2f5a1a', 
      { subject: 'Orçamento finalizado' }, 
      'Concluído', 
      'Entregue ao cliente'
    );

    // Fechar ticket
    await ticketUpdater.closeTicket('b8c9f8c4-0b6a-4b2a-8f8f-0c9d9e2f5a1a', 'Resolvido', 'd7b5d8a9-3f4c-4ed3-9c77-22f577d25f10');

  } catch (error) {
    console.error('Erro:', error.message);
  }
}

Considerações Importantes

Movimentação de Níveis

  • newLevel aceita:
    • UUID de tickets_levels.id (preferido)
    • Nome/nível resolvido por host (fallback)
  • status.tickets_levels_id é o caminho preferencial para trocar status

Eventos Automáticos

  • Eventos de histórico são criados automaticamente (status, info, motivo se enviado)
  • Eventos são vinculados ao authUserId quando fornecido
  • Eventos podem ser escritos no chat via conversationId

Integração HubSpot

  • Se habilitada no avatar/host, a movimentação sincroniza estágio automaticamente
  • Sincronização ocorre quando aplicável ao estágio de destino

Importante: Sempre forneça o ticketId válido. Tentativas de atualização com IDs inexistentes resultarão em erro 404.

Dica: Use status.tickets_levels_id para mudanças de status mais precisas, especialmente em integrações automatizadas.

Nota: A movimentação é executada após a atualização quando ambas operações são solicitadas na mesma requisição.