curl --request POST \
--url https://public-api.workchats.com/v1/messages \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--data '
{
"blocks": [
{
"text": "New lead from Meta Lead Ads",
"type": "header"
},
{
"fields": [
{
"label": "Campaign",
"value": "Meta Lead Ads (Paid)"
}
],
"type": "fields"
}
],
"metadata": {
"lead_id": "lead_mul0oe5wo0dgol",
"source": "leadey"
},
"text": "New lead: Sarah Chen (Acme Ltd) from Meta Lead Ads",
"thread_id": null,
"to": {
"id": "chn_c41a2b3c-4d5e-4f60-8a7b-9c0d1e2f3a4b",
"type": "conversation"
},
"unfurl_links": false
}
'const options = {
method: 'POST',
headers: {
'Idempotency-Key': '<idempotency-key>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
blocks: [
{text: 'New lead from Meta Lead Ads', type: 'header'},
{fields: [{label: 'Campaign', value: 'Meta Lead Ads (Paid)'}], type: 'fields'}
],
metadata: {lead_id: 'lead_mul0oe5wo0dgol', source: 'leadey'},
text: 'New lead: Sarah Chen (Acme Ltd) from Meta Lead Ads',
thread_id: null,
to: {id: 'chn_c41a2b3c-4d5e-4f60-8a7b-9c0d1e2f3a4b', type: 'conversation'},
unfurl_links: false
})
};
fetch('https://public-api.workchats.com/v1/messages', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://public-api.workchats.com/v1/messages"
payload = {
"blocks": [
{
"text": "New lead from Meta Lead Ads",
"type": "header"
},
{
"fields": [
{
"label": "Campaign",
"value": "Meta Lead Ads (Paid)"
}
],
"type": "fields"
}
],
"metadata": {
"lead_id": "lead_mul0oe5wo0dgol",
"source": "leadey"
},
"text": "New lead: Sarah Chen (Acme Ltd) from Meta Lead Ads",
"thread_id": None,
"to": {
"id": "chn_c41a2b3c-4d5e-4f60-8a7b-9c0d1e2f3a4b",
"type": "conversation"
},
"unfurl_links": False
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"message": {
"conversation_id": "dm_77a1b2c3-d4e5-4f60-8a7b-9c0d1e2f3a4b",
"created_at": "2026-09-28T11:05:12Z",
"id": "dmsg_4e2d1c0b-9a8f-4e7d-8c6b-5a4f3e2d1c0b",
"thread_id": null
},
"ok": true
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}Send a message
Sends a message as the bot: a direct message to a person (to.type: user), or a post in a group or channel the bot is a member of (to.type: conversation). People see text; blocks are stored and shown from a later release. Needs messages:write.
Idempotency. Every send needs an Idempotency-Key, unique per message and destination: <event_id>:<destination_id> works when one event goes to several people. Keys are kept for 24 hours. Repeating a key with the same body returns the original 201 and posts nothing; with a different body, 422 idempotency_key_reused. After 24 hours, a repeat to the same destination still posts nothing: it returns the original 201 while the message is unchanged, and 422 idempotency_key_reused once it was edited or deleted through this API. A key is 1 to 255 visible ASCII characters, with no spaces.
Who can receive a DM. People with an active or onboarding account. A deactivated or removed person is 409 user_deactivated; a guest, or someone who blocked the App, is 403 cannot_dm_user.
Rate limits. 5 sends a second per installation with bursts of 20, and 1 a second per destination with bursts of 5.
curl --request POST \
--url https://public-api.workchats.com/v1/messages \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--data '
{
"blocks": [
{
"text": "New lead from Meta Lead Ads",
"type": "header"
},
{
"fields": [
{
"label": "Campaign",
"value": "Meta Lead Ads (Paid)"
}
],
"type": "fields"
}
],
"metadata": {
"lead_id": "lead_mul0oe5wo0dgol",
"source": "leadey"
},
"text": "New lead: Sarah Chen (Acme Ltd) from Meta Lead Ads",
"thread_id": null,
"to": {
"id": "chn_c41a2b3c-4d5e-4f60-8a7b-9c0d1e2f3a4b",
"type": "conversation"
},
"unfurl_links": false
}
'const options = {
method: 'POST',
headers: {
'Idempotency-Key': '<idempotency-key>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
blocks: [
{text: 'New lead from Meta Lead Ads', type: 'header'},
{fields: [{label: 'Campaign', value: 'Meta Lead Ads (Paid)'}], type: 'fields'}
],
metadata: {lead_id: 'lead_mul0oe5wo0dgol', source: 'leadey'},
text: 'New lead: Sarah Chen (Acme Ltd) from Meta Lead Ads',
thread_id: null,
to: {id: 'chn_c41a2b3c-4d5e-4f60-8a7b-9c0d1e2f3a4b', type: 'conversation'},
unfurl_links: false
})
};
fetch('https://public-api.workchats.com/v1/messages', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://public-api.workchats.com/v1/messages"
payload = {
"blocks": [
{
"text": "New lead from Meta Lead Ads",
"type": "header"
},
{
"fields": [
{
"label": "Campaign",
"value": "Meta Lead Ads (Paid)"
}
],
"type": "fields"
}
],
"metadata": {
"lead_id": "lead_mul0oe5wo0dgol",
"source": "leadey"
},
"text": "New lead: Sarah Chen (Acme Ltd) from Meta Lead Ads",
"thread_id": None,
"to": {
"id": "chn_c41a2b3c-4d5e-4f60-8a7b-9c0d1e2f3a4b",
"type": "conversation"
},
"unfurl_links": False
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"message": {
"conversation_id": "dm_77a1b2c3-d4e5-4f60-8a7b-9c0d1e2f3a4b",
"created_at": "2026-09-28T11:05:12Z",
"id": "dmsg_4e2d1c0b-9a8f-4e7d-8c6b-5a4f3e2d1c0b",
"thread_id": null
},
"ok": true
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}{
"error": {
"code": "token_revoked",
"message": "The token has been revoked."
},
"ok": false,
"request_id": "F7p0mJcXk3tq2x0AAAAB"
}Authorizations
Bot token (wc_bot_live_… or wc_bot_test_…) from the OAuth install
Headers
Unique per message and destination: 1 to 255 visible ASCII characters, no spaces
1 - 255^[\x21-\x7E]+$"evt_88231:usr_9a1"
Body
The message
What people see, and the push notification's text. Always send it.
1 - 4000user with a usr_ id sends a direct message; conversation with a grp_ or chn_ id from GET /v1/conversations posts there
Show child attributes
Show child attributes
Rich layout, at most 16 KB. Stored now and shown from a later release.
50Show child attributes
Show child attributes
Your own data, at most 4 KB, stored with the message. Never shown in the app, but not secret: do not put credentials in it.
A message in the same destination to reply to. It is quoted above the new message.
Accepted and ignored: App messages never unfurl links