Quickstart

Three steps: install the SDK, verify a webhook delivery, send a reply. All examples use @hookchat/node, the official TypeScript SDK.

1. Install and construct the client#

Shell
npm install @hookchat/node
TypeScript
import { HookChat } from '@hookchat/node'

const hookchat = new HookChat({
  apiKey: process.env.HOOKCHAT_API_KEY, // hookchat_live_... or hookchat_test_...
  baseUrl: process.env.HOOKCHAT_BASE_URL ?? 'https://hookchat.dev', // the API origin, https://hookchat.dev by default
})

Create an API key in the console; the secret is shown exactly once. Register a webhook endpoint there too: its signing secret (whs_...) is also shown exactly once.

2. Receive and verify an event#

Every delivery is HMAC-SHA256 signed over the exact transmitted bytes. Verify against the raw request body. If you parse the JSON and encode it again, the bytes change and the check fails now and then. verifyWebhook checks the signature, rejects a timestamp more than 300 seconds (five minutes) from your clock, and returns the parsed, typed event.

TypeScript
import { verifyWebhook, HookChatSignatureError } from '@hookchat/node'

app.post('/hook', async (req, res) => {
  const raw = await readRawBody(req) // the EXACT bytes: do not re-serialise
  try {
    const event = verifyWebhook(raw, req.headers, process.env.HOOKCHAT_WEBHOOK_SECRET)
    switch (event.type) {
      case 'message.received':
        // event.data.message.text, .conversation_id, .platform, ...
        break
      case 'message.sent':
        // event.data.message.direction: 'outbound' (sent through the API)
        // or 'echo' (sent from the Instagram or Messenger app)
        break
      case 'message.failed':
      case 'test.event':
      case 'account.connected':
      case 'account.disconnected':
        break
      case 'participant.updated':
        // event.data.participant.name, .username, .picture: who the person is
        break
    }
    res.status(200).end()
  } catch (e) {
    if (e instanceof HookChatSignatureError) return res.status(401).end()
    throw e
  }
})
Delivery is at-least-once and unordered. Dedupe on the HookChat-Event-Id header, and sort by the envelope created time where order matters. See Webhooks and events.

3. Send a reply#

Reply inside the conversation 24 hour window with one call. The conversation id is the only target you need: there is no ?tenant on the send routes.

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

const hookchat = new HookChat({
  apiKey: process.env.HOOKCHAT_API_KEY,
  baseUrl: process.env.HOOKCHAT_BASE_URL ?? 'https://hookchat.dev',
})

try {
  const { platform_message_id } = await hookchat.messages.reply({
    conversation_id: process.env.CONVERSATION_ID, // from the event: event.data.message.conversation_id
    text: 'Thanks for the message, on it now.',
  })
  console.log('sent', platform_message_id)
} catch (e) {
  if (e instanceof HookChatError) {
    // Branch on the stable code, never the message.
    if (e.code === 'window_closed') {
      // Out of the 24h window: offer the human-agent path instead.
    }
  }
}

On success the promise resolves with { platform_message_id }. On a policy refusal it rejects with a HookChatError carrying a stable code. The full send model and every error code are on the Sending messages page.