Manage API keys

An API key is a bearer token bound to one workspace and carrying one scope. The secret is shown once, at creation; everything after that works with the key id and a display prefix.

Scopes#

ScopePrefixSendsReads
livehookchat_live_Go to Meta and reach real people.See live rows.
testhookchat_test_Route through a mock connection and never leave HookChat.Write rows that other reads only show with include_test.

A key inherits the caller's scope unless you pass one, and a test key can never mint a live key (that is a 403 forbidden). Events produced under a test key are not delivered to your endpoints unless an endpoint opts in, so a colleague's sandbox traffic never reaches production.

Create a key#

The response carries id, prefix, scope, label, created_at and, once only, secret. Store the secret immediately; no read path returns it again.

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

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

const created = await client.keys.create({ scope: 'test', label: 'docs' })
console.log('id', created.id)
console.log('prefix', created.prefix, 'scope', created.scope)
// created.secret is the plaintext key. Store it now and never log it.

List keys#

Listing returns every key under the tenant with its prefix, scope, label, status and creation time. Revoked keys stay in the list with status revoked. The list is not paginated.

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

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

const keys = await client.keys.list()
for (const key of keys) console.log(key.id, key.prefix, key.scope, key.status, key.label)
console.log(keys.length, 'keys')

Revoke a key#

Revocation is immediate and idempotent: revoking an already revoked key still succeeds. An unknown or cross-tenant id is a 404 key_not_found, never a 403, so ids do not leak across workspaces. The sample mints a throwaway key and revokes it.

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

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

const temp = await client.keys.create({ scope: 'test', label: 'docs-temp' })
const { revoked } = await client.keys.revoke(temp.id)
console.log('revoked', revoked, temp.id)

Operator keys and tenants#

A key minted in the console or through the API is bound to one workspace and needs no tenant parameter. An operator key, issued to a partner that serves several workspaces, must name the tenant on every call: ?tenant= on the API, the tenant client option or per-call parameter in the SDKs, --tenant or HOOKCHAT_TENANT in the CLI. Omitting it is a 400 tenant_required; naming a workspace the key is not allowed on is a 403 forbidden. The send routes take no tenant at all: the conversation id already scopes the send.

Where to keep the secret#

Treat a key like a password. Keep it in a secret manager or an environment variable, give each worker its own labelled key so one can be revoked without touching the rest, and never ship a key in a browser bundle: the API allows browser calls only from the console's own origin.

Every key action (create, revoke) is written to the audit trail with the actor that performed it. hookchat audit list or client.audit.list() shows it.

Next#