🎉 Tickety V3 has now been released! Read more →
APIHTTP EventsTicket Events

Close

Triggered when a ticket is closed

Event Type

"ticket.close"

Description

This event is triggered when a ticket is closed by any means - staff command, button, auto-close, or other methods.

Payload Structure

Prop

Type

Participant

Prop

Type

Bots are excluded, so messages from Tickety itself and from other bots never appear.

Response times are measured per side of the conversation. A staff member's response time is the gap between the last message from a ticket user and their reply, with the first one measured from the moment the ticket was opened. A ticket user's response time is the gap between the last staff message and their reply, so their opening message has nothing to measure against. Only the first reply after the other side speaks counts, so a burst of follow up messages does not lower the average.

Example Payload

{
  "type": "ticket.close",
  "payload": {
    "ticketId": "TKT-1234",
    "panelId": "support-panel",
    "channel": {
      "id": "1140972530400776294",
      "name": "ticket-0001"
    },
    "creatorId": "713115896805064856",
    "closerId": "280158289667555328",
    "closeReason": "Issue resolved!",
    "openDate": "2024-01-18T15:30:00.000Z",
    "closeDate": "2024-01-18T16:45:00.000Z",
    "transcript": "https://tickety.top/transcripts/GdaWKdWF6A0m1Hs7W9P",
    "participants": [
      {
        "id": "713115896805064856",
        "messages": 14,
        "averageResponseTime": 182.4,
        "staff": false
      },
      {
        "id": "280158289667555328",
        "messages": 9,
        "averageResponseTime": 46.75,
        "staff": true
      },
      {
        "id": "451230862009171968",
        "messages": 1,
        "averageResponseTime": null,
        "staff": true
      }
    ]
  }
}

Example Usage

app.post('/webhook', (req, res) => {
  const { type, payload } = req.body;

  if (type === 'ticket.close') {
    const duration = new Date(payload.closeDate) - new Date(payload.openDate);
    const hours = Math.floor(duration / (1000 * 60 * 60));
    
    console.log(`Ticket ${payload.ticketId} closed`);
    console.log(`Duration: ${hours} hours`);
    console.log(`Reason: ${payload.closeReason || 'No reason provided'}`);
    
    if (payload.transcript) {
      console.log(`Transcript: ${payload.transcript}`);
      // Archive transcript to your system
      await archiveTranscript(payload.transcript, payload.ticketId);
    }

    // Record how much each staff member handled and how fast they replied
    for (const participant of payload.participants.filter(p => p.staff)) {
      console.log(`${participant.id}: ${participant.messages} messages`);
      await recordStaffActivity({
        userId: participant.id,
        ticketId: payload.ticketId,
        messages: participant.messages,
        averageResponseTime: participant.averageResponseTime,
      });
    }
    
    // Update ticket status in your database
    await updateTicketStatus(payload.ticketId, 'closed');
  }

  res.status(200).send('OK');
});

Use Cases

  • Archive transcripts to your own storage
  • Calculate ticket resolution times
  • Update ticket status in external systems
  • Send closure notifications
  • Generate analytics on close reasons
  • Track staff workload and response speed per ticket
  • Spot tickets where the creator went quiet or replied slowly

On this page