Show who you are talking to
Each message from Meta names its sender by an id and nothing else. HookChat looks up the person's name, username and picture for you, stores them on the conversation, and tells you when they are ready.
How it works#
- A person sends your account a message. HookChat stores it and sends
message.receivedat once. The message carriesparticipant.idonly. - Just after, HookChat asks Meta for the person's profile. This never holds up the message.
- When Meta shares a name, username or picture, HookChat stores it on the conversation and sends one
participant.updatedevent.
The lookup runs once per conversation. If Meta shares nothing, it is tried again on the person's next message.
It also runs for a thread your team starts from the Instagram or Messenger app. That thread's first event is message.sent with direction: "echo", and participant_profile goes from pending to complete or unavailable the same way, so a participant.updated event can follow an echo. Key the profile by conversation_id, not by the first message.received.
Read the profile from the conversation#
Every conversation carries the profile and where the lookup stands:
| field | what it holds |
|---|---|
participant_handle | The display name, or null. |
participant_username | The Instagram username, without the @, or null. Always null on Messenger. |
participant_picture | Meta's picture URL, or null. It expires; download it if you need to keep it. |
participant_profile | pending, complete or unavailable. See below. |
| participant_profile | means | what to do |
|---|---|---|
pending | The lookup has not run yet. You fetched the conversation straight after its first message. | Show the id for now. A participant.updated event follows if Meta shares a profile. |
complete | A name, username or picture is stored, and participant.updated went out. | Show it. |
unavailable | The lookup ran and Meta shared nothing. | Show the id. The lookup is tried again on the person's next message. |
import { HookChat } from '@hookchat/node'
const client = new HookChat({ apiKey: process.env.HOOKCHAT_API_KEY!, baseUrl: process.env.HOOKCHAT_BASE_URL! })
const { conversation } = await client.conversations.get(process.env.CONVERSATION_ID!)
const name = conversation.participant_handle ?? conversation.participant_id
console.log('profile', conversation.participant_profile, name, conversation.participant_picture)Update your records when the profile arrives#
Subscribe your endpoint to participant.updated and store what it carries. It is sent once per conversation, when the profile is first stored. Events are not delivered in order, so it can arrive before or after the message.received that caused it; key both by conversation_id.
{
"id": "evt_6b1f0c2a9d4e8b7a5c3f1e0d",
"type": "participant.updated",
"created": 1790807556,
"data": {
"participant": {
"id": "6021573449812077",
"conversation_id": "CONV#acme#instagram#17841405309211844#6021573449812077",
"tenant": "acme",
"platform": "instagram",
"account": { "external_id": "17841405309211844", "handle": "acme.studio" },
"name": "Ada Lovelace",
"username": "ada.inks",
"picture": "https://scontent.cdninstagram.com/v/ada.jpg"
}
}
}name, username, picture and account.handle are each a string or null. At least one of name, username and picture is set.
import { createServer } from 'node:http'
import { verifyWebhook, HookChatSignatureError } from '@hookchat/node'
const secret = process.env.WEBHOOK_SECRET!
createServer((req, res) => {
const chunks: Buffer[] = []
req.on('data', (chunk: Buffer) => chunks.push(chunk))
req.on('end', () => {
try {
const event = verifyWebhook(Buffer.concat(chunks), req.headers, secret)
if (event.type === 'participant.updated') {
const p = event.data.participant
console.log('profile', p.conversation_id, p.name ?? p.username ?? p.id, p.picture)
}
res.writeHead(200).end()
} catch (error) {
if (error instanceof HookChatSignatureError) {
res.writeHead(401).end()
return
}
throw error
}
})
}).listen(Number(process.env.PORT))Good to know#
- The profile is looked up once per conversation. A later change to the person's name on Instagram or Facebook is not sent.
- An endpoint with no event list gets every type,
participant.updatedincluded. One set to a fixed list does not get it until you add it. Edit the endpoint in the console, or callPATCH /v1/webhooks/{id}with the new list. - Meta's picture URLs expire. Download the image when the event arrives if you need it later.
- Erasing a person's data also erases the
participant.updateddeliveries that named them. See data deletion.