Appearance
Send a message
Delivers one customer message into the pipeline, with its attachments in the same request.
POST {receiver}/web-chat/message
Authorization: Basic base64(client_id:client_secret)This goes to the receiver, not the API
The host is the messages_webhook value shown on the channel in the Genudo Console — not {api}.
Returns as soon as the message is queued. The AI reply arrives asynchronously on your callback URL, not in this response.
multipart/form-data (recommended)
Text fields plus the binary files, in one request:
bash
curl -X POST "https://receiver.genudo.ai/web-chat/message" \
-u "wc_YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
-F "contact_id=user-abc-123" \
-F "message=Here are the files you asked for" \
-F "files=@./photo.png" \
-F "files=@./invoice.pdf"js
const form = new FormData()
form.append('contact_id', 'user-abc-123')
form.append('message', 'Here are the files you asked for')
form.append('files', new Blob([fs.readFileSync('./photo.png')]), 'photo.png')
await fetch('https://receiver.genudo.ai/web-chat/message', {
method: 'POST',
headers: {
Authorization: 'Basic ' + Buffer.from(`${clientId}:${clientSecret}`).toString('base64'),
// Do NOT set Content-Type — fetch sets the multipart boundary for you.
},
body: form,
})application/json
Text-only messages, or files you already pushed to files_upload_url:
bash
curl -X POST "https://receiver.genudo.ai/web-chat/message" \
-u "wc_YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{ "contact_id": "user-abc-123", "message": "Do you ship to Cairo?" }'Request fields
| Field | Type | Rules |
|---|---|---|
contact_id | string | Required, max 255 — the same value on every message in a conversation |
message | string | Required. May be "" when sending attachments only. |
files | file[] | multipart only. Field name files or files[]. Max 10 files, 5 MB each. |
attachments | object[] | Optional — { type, url, name? } for files already hosted at a public URL. In multipart, send as a JSON string. |
No contact identity here
This endpoint does not accept name, email or phone. Set those once with Initiate. Unknown fields are silently stripped rather than rejected, so sending them here fails quietly.
Any file part under another field name is ignored rather than rejected.
Response
json
{
"status": true,
"message": "Message received",
"data": { "message_id": "6a9d596d9b739b3683fbe126" }
}Different envelope
This is the receiver's own shape — it does not use the console's {status, code, validation, message, data} envelope.
message_id is the Mongo id of the stored inbound message. The reply's callback carries its own, different message_id.
What gets stored
Both content types produce identical files[] entries on the message:
json
"files": [
{
"path": "uploads/wc_YOUR_CLIENT_ID/01M1VAAK85ZQ61JNJQHMC7WKYF.png",
"mimetype": "image/png",
"type": "image",
"size": 46,
"name": "photo.png"
}
]type is derived from the MIME type: image, video, audio, document, or file. Files are stored privately under your own uploads/{client_id}/ prefix.
Errors
| Code | Cause |
|---|---|
400 | Validation error, or Invalid file path on a JSON files[].path outside uploads/{client_id}/ |
401 | Invalid client credentials — bad Basic auth, or the connector is inactive |
403 | Origin not allowed / IP not allowed — adjust the allowlists on the channel in the Console |
413 | File too large — a part exceeded 5 MB |