Send test events

A test event is the fastest way to prove an endpoint works: it is signed by the same code, delivered on the same path and retried on the same schedule as a real message, and it carries nothing sensitive.

What a test event looks like#

The envelope is the one every event shares. The id is evt_test_ plus a fresh nonce each time, so a test is always a new delivery and is never deduped against an earlier one.

test.event
{
  "id": "evt_test_5f1c9a2b7d3e",
  "type": "test.event",
  "created": 1756900000,
  "data": {
    "test": {
      "message": "HookChat test event",
      "tenant": "acme",
      "nonce": "5f1c9a2b7d3e"
    }
  }
}

Send one#

The route returns the new delivery id straight away; the delivery itself runs asynchronously. It is retried only on a 429 by the SDKs, so calling it twice sends two events.

TypeScript
import { HookChat } from '@hookchat/node'

const client = new HookChat({ apiKey: process.env.HOOKCHAT_API_KEY!, baseUrl: process.env.HOOKCHAT_BASE_URL! })

const { delivery_id } = await client.webhooks.test(process.env.WEBHOOK_ID!)
console.log('queued test delivery', delivery_id)

Confirm it arrived#

Two places show the outcome: your receiver's log, and the delivery row, which moves from pending through attempting to delivered, or to retrying, failed or dlq when the receiver did not answer 2xx. This sample sends a test event and polls its row until it settles.

TypeScript
import { HookChat } from '@hookchat/node'

const client = new HookChat({ apiKey: process.env.HOOKCHAT_API_KEY!, baseUrl: process.env.HOOKCHAT_BASE_URL! })
const endpointId = process.env.WEBHOOK_ID!

const { delivery_id } = await client.webhooks.test(endpointId)
for (let i = 0; i < 20; i++) {
  const page = await client.webhooks.deliveries.list(endpointId, { limit: 20 })
  const row = page.deliveries.find((d) => d.delivery_id === delivery_id)
  if (row) {
    console.log('test delivery', row.delivery_id, row.status, 'attempt', row.attempt_number)
    if (row.status === 'delivered' || row.status === 'failed' || row.status === 'dlq') break
  }
  await new Promise((resolve) => setTimeout(resolve, 500))
}

Verify a captured delivery offline#

If your receiver stores the raw body and headers, the CLI can verify a delivery after the fact. The input is {"headers": {...}, "body": "<raw body>"} with lowercased header names, which is what a capture receiver stores. A mismatch exits 9; an old fixture needs --tolerance because the timestamp check applies offline too.

Shell
hookchat verify --secret "$WEBHOOK_SECRET" --file delivery.json

Test scope and real endpoints#

Two different things carry the word test. A test.event is the diagnostic above and is always delivered to the endpoint you aimed it at. Events produced under a hookchat_test_ key (a mock send, for example) are marked test-scoped and are not delivered to endpoints unless an endpoint has opted in, so production consumers never see sandbox traffic by default.

Next#