Send as a human agent
Past the 24 hour window there is exactly one way to reach a person: a message typed by a named human, sent on the human-agent route, within 7 days of their last message. There is no generic send and there are no message tags.
When to use it#
The route is for a real person following up on a real thread: a delayed answer, a question that needed research, a booking confirmation that came through after hours. It is not a broadcast channel and it cannot be automated, which is a policy HookChat enforces rather than a setting you can change.
window.stateisopen_24h: either route works. Prefer the reply route for automated answers.window.stateishuman_agent_only: only this route works.can_send_as_human_agentis true andcan_replyis false.window.stateisclosed: nothing can be sent. Past 7 days the route refuses withhuman_agent_unavailable.
Send as a named human#
The request is the reply request plus actor_id, the identity of the person who wrote the message. Use whatever identifies them in your own system: a user id, an agent handle, a console session. It is recorded in the audit trail and on the message as actor_id.
import { HookChat } from '@hookchat/node'
const client = new HookChat({ apiKey: process.env.HOOKCHAT_API_KEY!, baseUrl: process.env.HOOKCHAT_BASE_URL! })
// actor_id is required by the type: omitting it does not compile.
const { platform_message_id } = await client.messages.sendAsHumanAgent({
conversation_id: process.env.CONVERSATION_ID!,
text: 'Sorry for the delay, following up personally.',
actor_id: 'agent-7',
})
console.log('sent', platform_message_id)The success shape is identical to the reply route: { ok: true, data: { platform_message_id } }. Attachments and reply_to work the same way too.
Past seven days#
When the conversation is closed the route refuses with human_agent_unavailable (409). Nothing exists past 7 days on any platform; the right move is to wait for the participant to write again, which reopens the 24 hour window.
import { HookChat, HookChatError } from '@hookchat/node'
const client = new HookChat({ apiKey: process.env.HOOKCHAT_API_KEY!, baseUrl: process.env.HOOKCHAT_BASE_URL! })
try {
await client.messages.sendAsHumanAgent({
conversation_id: process.env.CLOSED_CONVERSATION_ID!,
text: 'Checking in',
actor_id: 'agent-7',
})
} catch (error) {
if (error instanceof HookChatError && error.code === 'human_agent_unavailable') {
console.log('refused:', error.code)
} else {
throw error
}
}Why actor_id is required#
A human-agent message is human-sent by definition, so the gateway refuses one that names nobody with missing_actor (409). That is a policy refusal, not a validation error: the body was well formed, the request was simply not allowed. The SDKs make the mistake hard to write: the TypeScript type requires the field, the Python method takes it as a required keyword, and the Go client refuses an empty ActorID locally with the same code.
actor_id to get an automated follow-up through. The route exists because a real person must stand behind an out-of-window message, and the audit trail records who that was.Errors the human-agent route returns#
| code | HTTP | Meaning |
|---|---|---|
invalid_request | 400 | Malformed body, no conversation_id, or no text and no attachments. |
unauthorized | 401 | Missing or invalid bearer key. |
conversation_not_found | 404 | No such conversation. A cross-tenant id is a 404, never a 403. |
missing_actor | 409 | No actor_id. The human who sent it must be named. |
human_agent_unavailable | 409 | The conversation is past the 7 day window. |
rate_limited | 409 | The account's send budget for this category is spent. |
send_failed | 502 | Policy allowed the send and Meta or the network refused it. A retry can help. |