Webhooks API

The Webhooks API allows you to receive real-time notifications about events in your TidySupport workspace. You can subscribe to conversation, message, contact, and signal events and process them in your own systems.

Webhooks send HTTP POST requests to your specified URL whenever an event happens.

You can also inspect delivery analytics for each webhook with a 7-day Tinybird-backed delivery summary.

Events

Webhooks can be configured to listen for the following events:

  • CONVERSATION_CREATED - A new conversation was created
  • CONVERSATION_UPDATED - A conversation was updated
  • CONVERSATION_ASSIGNED - A conversation was assigned
  • CONVERSATION_CLOSED - A conversation was closed
  • CONVERSATION_REOPENED - A conversation was reopened
  • MESSAGE_CREATED and MESSAGE_UPDATED - Message activity inside a conversation
  • CONTACT_CREATED - A new contact was created
  • SIGNAL_CREATED and SIGNAL_UPDATED - AI support signals were created or updated

TESTING_CONNECTION is reserved for endpoint verification and health checks. TidySupport sends it when testing or validating a webhook endpoint.

Endpoints

GET/v1/webhooks

Lists all webhooks for the authenticated workspace.

Authentication

Requires a valid API key with api_user scope.

Request

curl "https://api.tidysupport.com/v1/webhooks" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "webhooks": {
    "items": [
      {
        "id": "wh_123456789",
        "url": "https://example.com/webhooks/app",
        "displayName": "example.com",
        "status": "ACTIVE",
        "events": ["CONVERSATION_CREATED", "MESSAGE_CREATED"],
        "createdAt": "2023-03-01T10:00:00Z",
        "updatedAt": "2023-03-01T10:00:00Z",
        "lastAttemptedAt": null,
        "lastErrorAt": null,
        "lastErrorMessage": null
      },
      {
        "id": "wh_987654321",
        "url": "https://example.com/webhooks/signals",
        "displayName": "example.com",
        "status": "PAUSED",
        "events": ["SIGNAL_CREATED", "SIGNAL_UPDATED"],
        "createdAt": "2023-04-10T14:30:00Z",
        "updatedAt": "2023-04-10T14:30:00Z",
        "lastAttemptedAt": "2023-04-11T09:15:00Z",
        "lastErrorAt": "2023-04-11T09:15:00Z",
        "lastErrorMessage": "{"message":"connect ETIMEDOUT"}"
      }
    ],
    "hasMore": false,
    "startAt": null
  }
}

GET/v1/webhooks/{webhookId}

Retrieves details of a specific webhook.

Authentication

Requires a valid API key with api_user scope.

Request

curl "https://api.tidysupport.com/v1/webhooks/wh_123456789" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "webhook": {
    "id": "wh_123456789",
    "url": "https://example.com/webhooks/app",
    "displayName": "example.com",
    "status": "ACTIVE",
    "events": ["CONVERSATION_CREATED", "MESSAGE_CREATED"],
    "token": "wht_a1b2c3d4e5f6g7h8i9j0",
    "createdAt": "2023-03-01T10:00:00Z",
    "updatedAt": "2023-03-01T10:00:00Z",
    "workspaceId": "wrk_ENGTK86JEXXckyDVJthKkPer",
    "lastAttemptedAt": null,
    "lastErrorAt": null,
    "lastErrorMessage": null
  }
}

POST/v1/webhooks

Creates a new webhook.

Authentication

Requires a valid API key with api_user scope.

Request Body

{
  "url": "https://example.com/webhooks/app",
  "events": ["CONVERSATION_CREATED", "MESSAGE_CREATED"],
  "token": "optional_secret_token"
}

Request

curl -X POST "https://api.tidysupport.com/v1/webhooks" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks/app",
    "events": ["CONVERSATION_CREATED", "MESSAGE_CREATED"],
    "token": "optional_secret_token"
  }'

Response

{
  "webhook": {
    "id": "wh_123456789",
    "url": "https://example.com/webhooks/app",
    "displayName": "example.com",
    "status": "ACTIVE",
    "events": ["CONVERSATION_CREATED", "MESSAGE_CREATED"],
    "token": "wht_a1b2c3d4e5f6g7h8i9j0",
    "createdAt": "2023-04-16T15:45:00Z",
    "updatedAt": "2023-04-16T15:45:00Z",
    "workspaceId": "wrk_ENGTK86JEXXckyDVJthKkPer",
    "lastAttemptedAt": null,
    "lastErrorAt": null,
    "lastErrorMessage": null
  }
}

POST/v1/webhooks/{webhookId}

Updates a webhook.

Authentication

Requires a valid API key with api_user scope.

Request Body

{
  "events": ["CONVERSATION_CREATED", "MESSAGE_CREATED", "SIGNAL_CREATED"]
}

Request

curl -X POST "https://api.tidysupport.com/v1/webhooks/wh_123456789" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["CONVERSATION_CREATED", "MESSAGE_CREATED", "SIGNAL_CREATED"]
  }'

Response

{
  "webhook": {
    "id": "wh_123456789",
    "url": "https://example.com/webhooks/app",
    "displayName": "example.com",
    "status": "ACTIVE",
    "events": ["CONVERSATION_CREATED", "MESSAGE_CREATED", "SIGNAL_CREATED"],
    "token": "wht_a1b2c3d4e5f6g7h8i9j0",
    "createdAt": "2023-03-01T10:00:00Z",
    "updatedAt": "2023-03-02T12:15:00Z",
    "workspaceId": "wrk_ENGTK86JEXXckyDVJthKkPer",
    "lastAttemptedAt": null,
    "lastErrorAt": null,
    "lastErrorMessage": null
  }
}

DELETE/v1/webhooks/{webhookId}

Deletes a webhook.

Authentication

Requires a valid API key with api_user scope.

Request

curl -X DELETE "https://api.tidysupport.com/v1/webhooks/wh_123456789" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "webhook": {
    "id": "wh_123456789",
    "url": "https://example.com/webhooks/app",
    "displayName": "example.com",
    "status": "ACTIVE",
    "events": ["CONVERSATION_CREATED", "MESSAGE_CREATED"],
    "token": "wht_a1b2c3d4e5f6g7h8i9j0",
    "createdAt": "2023-03-01T10:00:00Z",
    "updatedAt": "2023-03-02T12:15:00Z",
    "workspaceId": "wrk_ENGTK86JEXXckyDVJthKkPer",
    "lastAttemptedAt": "2023-03-05T11:30:00Z",
    "lastErrorAt": null,
    "lastErrorMessage": null
  },
  "isDeleted": true
}

GET/v1/webhooks/{webhookId}/delivery-analytics

Returns Tinybird-backed delivery totals plus a day-by-day timeseries for the last 7 days.

Authentication

Requires a valid API key with api_user scope.

Request

curl "https://api.tidysupport.com/v1/webhooks/wh_123456789/delivery-analytics" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "timeseries": [
    {
      "day": "2023-04-10",
      "successes": 12,
      "failures": 1
    },
    {
      "day": "2023-04-11",
      "successes": 8,
      "failures": 0
    },
    {
      "day": "2023-04-12",
      "successes": 15,
      "failures": 2
    }
  ],
  "stats": {
    "totalSuccesses": 35,
    "totalFailures": 3,
    "totalAttempts": 38,
    "lastAttemptedAt": "2023-04-12T18:15:00Z",
    "lastErrorAt": "2023-04-12T18:12:00Z"
  }
}

Webhook Payloads

When an event occurs, TidySupport will send a POST request to your webhook URL with a JSON payload containing an event name, payload object, version, and timestamp.

Example payload for MESSAGE_CREATED:

{
  "event": "MESSAGE_CREATED",
  "version": 1,
  "timestamp": "2023-04-16T15:45:00.000Z",
  "payload": {
    "message": {
      "id": "msg_h1s2JkLmNoPqRsTuVwXyZaBc",
      "conversationId": "cnv_q1w2e3r4t5y6u7i8o9p0as",
      "workspaceId": "wrk_ENGTK86JEXXckyDVJthKkPer",
      "direction": "INBOUND",
      "senderType": "CONTACT",
      "contentText": "I need help with my billing plan.",
      "contentPreview": "I need help with my billing plan.",
      "createdAt": "2023-04-16T15:45:00.000Z"
    },
    "conversation": {
      "id": "cnv_q1w2e3r4t5y6u7i8o9p0as",
      "workspaceId": "wrk_ENGTK86JEXXckyDVJthKkPer",
      "status": "OPEN",
      "subject": "Billing question",
      "source": "WIDGET",
      "lastMessageAt": "2023-04-16T15:45:00.000Z",
      "lastMessagePreview": "I need help with my billing plan.",
      "createdAt": "2023-04-16T15:44:30.000Z"
    }
  }
}

Example payload for TESTING_CONNECTION:

{
  "event": "TESTING_CONNECTION",
  "version": 1,
  "timestamp": "2023-04-16T16:30:00.000Z",
  "payload": {
    "message": "Testing your webhook endpoint. Respond with a 200 status code."
  }
}

Delivery Headers

TidySupport includes the following headers on each webhook request:

  • x-tidysupport-webhook-token - The webhook token you configured. Use it to authenticate the request.
  • x-tidysupport-dedup-key - A unique delivery ID you can store for idempotency and deduplication.

Example in Node.js:

const express = require('express');
const app = express();

app.use(express.json());

const WEBHOOK_TOKEN = 'YOUR_WEBHOOK_TOKEN';

app.post('/webhooks/app', (req, res) => {
  const token = req.headers['x-tidysupport-webhook-token'];
  const dedupKey = req.headers['x-tidysupport-dedup-key'];

  if (token !== WEBHOOK_TOKEN) {
    return res.status(401).send('Unauthorized');
  }

  console.log('Webhook verified:', req.body.event, dedupKey);

  switch (req.body.event) {
    case 'CONTACT_CREATED':
      console.log(req.body.payload.contact.id);
      break;
    case 'CONVERSATION_CREATED':
      console.log(req.body.payload.conversation.id);
      break;
    case 'MESSAGE_CREATED':
      console.log(req.body.payload.message.id);
      break;
    default:
      console.log('Unhandled event type:', req.body.event);
    }
  }
  return res.status(200).send('Webhook received');
});