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
# Distribution is internal (git) for now: there is no npm publish yet. npm install @hookchat/node
import { HookChat } from '@hookchat/node'
const hookchat = new HookChat({
apiKey: process.env.HOOKCHAT_API_KEY, // hookchat_live_… / hookchat_test_…
baseUrl: 'https://hookchat.dev', // staging: https://stg.hookchat.dev
})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. Re-serialising parsed JSON changes the digest and fails intermittently. verifyWebhook checks the signature, enforces a timestamp tolerance of about 300 seconds, and returns the parsed, typed event.
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':
case 'message.failed':
case 'test.event':
case 'account.connected':
case 'account.disconnected':
break
}
res.status(200).end()
} catch (e) {
if (e instanceof HookChatSignatureError) return res.status(401).end()
throw e
}
})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's 24-hour window with one call. The conversation id is the only target you need: there is no ?tenant on the send routes.
import { HookChatError } from '@hookchat/node'
try {
const { platform_message_id } = await hookchat.messages.reply({
conversation_id: event.data.message.conversation_id,
text: 'Thanks for the message, on it now.',
})
} 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.