REST API
Send messages, reply in conversations, manage contacts and read history with an API key.
Rasayel Platform
One API for every conversation: send and receive on WhatsApp, email, Telegram, web chat and your own channels. From first call to production.
curl -X POST https://rasayel.net/api/v1/messages \
-H "Authorization: Bearer $RASAYEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channelId": "ch_01J9Z3",
"to": "+201001234567",
"text": "Hello from Rasayel 👋"
}'Platform
Send messages, reply in conversations, manage contacts and read history with an API key.
Receive every incoming message and delivery update, from every channel, at one signed URL.
Make your own app a channel: post your customers' messages in, and get your team's replies back.
Developer journey
Get started
channelId to send from.Get started
https://rasayel.net/api/v1curl https://rasayel.net/api/v1/channels \
-H "Authorization: Bearer $RASAYEL_API_KEY"curl -X POST https://rasayel.net/api/v1/messages \
-H "Authorization: Bearer $RASAYEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channelId": "ch_01J9Z3",
"to": "+201001234567",
"text": "Hello from Rasayel 👋"
}'Guide
POST /messages messages any phone number, email address or Telegram chat on a channel. If a conversation with them exists it continues; otherwise one is started and the contact is created.
To answer inside a conversation you already have, use POST /conversations/{id}/messages, which also takes "type": "note" for internal notes. Every message sent costs credit; see Usage in the app.
curl -X POST https://rasayel.net/api/v1/conversations/cv_01J9Z4/messages \
-H "Authorization: Bearer $RASAYEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "Your order is on its way."
}'Guide
PUT /webhook (or in Settings → API). Rasayel posts message.received, message.sent and message.updated from every channel, signed with your secret. Answer with any 2xx within 10 seconds.Guide
X-Rasayel-Signature against the raw body before parsing it, and reject anything older than five minutes.curl -X PUT https://rasayel.net/api/v1/webhook \
-H "Authorization: Bearer $RASAYEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/rasayel/events"
}'import { createHmac, timingSafeEqual } from "node:crypto";
// header: X-Rasayel-Signature: t=<unix seconds>,v1=<hex>
export function verify(secret, rawBody, header) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
return expected.length === parts.v1?.length &&
timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}Guide
POST /files, then send the ids with the message in fileIds. Up to 10 files per message; the text may be empty. Links in responses are signed and work for 7 days.curl -X POST https://rasayel.net/api/v1/files \
-H "Authorization: Bearer $RASAYEL_API_KEY" \
-F file=@invoice.pdfGuide
nextCursor.| Status | Code | What it means |
|---|---|---|
| 400 | invalid_request | Something in the request is missing or wrong; the message says what. |
| 401 | unauthenticated | No key, or a revoked or unknown one. |
| 403 | forbidden | Not allowed: a disabled channel, or starting conversations is turned off. |
| 404 | not_found | Not in your organization. |
| 409 | conflict | It already exists. |
| 429 | rate_limited | Over 600 requests a minute; wait for Retry-After seconds. |
| 503 | unavailable | A temporary problem on our side; retry with backoff. |
{
"error": {
"code": "invalid_request",
"message": "`to` is required."
}
}API reference
https://rasayel.net/api/v1Message any number or address on one of your channels.
POST/messages
Continues the conversation with to on that channel, or starts one (the contact is created) while the platform allows starting conversations from the API; otherwise 403 for an address with no conversation yet. to is a phone number for WhatsApp (+201001234567), an email address for email, a chat id for Telegram.
channelIdstringrequiredtostringrequiredtextstringoptionalUp to 10000 characters
namestringoptionalThe contact's name, when the contact is new.
subjectstringoptionalEmail subject for a new conversation.
fileIdsstring[]optionalIds from POST /files. Send text, fileIds or both.
Up to 10 items
curl -X POST https://rasayel.net/api/v1/messages \
-H "Authorization: Bearer $RASAYEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channelId": "ch_01J9Z3",
"to": "+201001234567",
"text": "Your order is on its way.",
"fileIds": [
"fl_01J9Z6"
]
}'{
"data": {
"id": "01J9Z3K7Q2X4",
"conversationId": "cv_01J9Z4",
"direction": "inbound",
"type": "text",
"text": "Your order is on its way.",
"attachments": [
{
"name": "invoice.pdf",
"mimeType": "application/pdf",
"size": 48213,
"fileId": "fl_01J9Z6",
"href": "https://rasayel.net/api/files/…"
}
],
"status": "open",
"createdAt": "2026-10-27T10:15:00.000Z"
},
"conversationId": "cv_01J9Z4"
}Read threads, reply, add internal notes, assign and resolve.
GET/conversations
statusenumoptionalopenpendingresolvedall
Default "open"
assigneestringoptionalA user id, or unassigned
channelIdstringoptionalcursorstringoptionallimitintegeroptionalcurl https://rasayel.net/api/v1/conversations \
-H "Authorization: Bearer $RASAYEL_API_KEY"{
"data": [
{
"id": "01J9Z3K7Q2X4",
"status": "open",
"channel": {
"id": "01J9Z3K7Q2X4",
"name": "Ada Lovelace",
"type": "whatsapp",
"status": "open",
"address": "201001234567"
},
"contact": {
"id": "01J9Z3K7Q2X4",
"name": "Ada Lovelace",
"email": "ada@example.com",
"phone": "+201001234567",
"tags": [
null
],
"attributes": {},
"lastSeenAt": "2026-10-27T10:15:00.000Z",
"createdAt": "2026-10-27T10:15:00.000Z"
},
"assigneeId": null,
"lastMessageAt": "2026-10-27T10:15:00.000Z",
"unreadCount": 0
}
],
"nextCursor": null
}GET/conversations/{id}
idstringrequiredcurl https://rasayel.net/api/v1/conversations/cv_01J9Z4 \
-H "Authorization: Bearer $RASAYEL_API_KEY"PATCH/conversations/{id}
idstringrequiredstatusenumoptionalopenpendingresolved
assigneeIdstring | nulloptionalcurl -X PATCH https://rasayel.net/api/v1/conversations/cv_01J9Z4 \
-H "Authorization: Bearer $RASAYEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "open",
"assigneeId": null
}'GET/conversations/{id}/messages
idstringrequiredcursorstringoptionalcurl https://rasayel.net/api/v1/conversations/cv_01J9Z4/messages \
-H "Authorization: Bearer $RASAYEL_API_KEY"{
"data": [
{
"id": "01J9Z3K7Q2X4",
"conversationId": "cv_01J9Z4",
"direction": "inbound",
"type": "text",
"text": "Your order is on its way.",
"attachments": [
{
"name": null,
"mimeType": null,
"size": null,
"fileId": null,
"href": null
}
],
"status": "open",
"createdAt": "2026-10-27T10:15:00.000Z"
}
],
"nextCursor": null
}POST/conversations/{id}/messages
idstringrequiredtextstringoptionaltypeenumoptionalreplynote
Default "reply"
fileIdsstring[]optionalIds from POST /files. text may be empty when files are attached (replies only).
Up to 10 items
curl -X POST https://rasayel.net/api/v1/conversations/cv_01J9Z4/messages \
-H "Authorization: Bearer $RASAYEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "Your order is on its way.",
"fileIds": [
"fl_01J9Z6"
]
}'{
"data": {
"id": "01J9Z3K7Q2X4",
"conversationId": "cv_01J9Z4",
"direction": "inbound",
"type": "text",
"text": "Your order is on its way.",
"attachments": [
{
"name": "invoice.pdf",
"mimeType": "application/pdf",
"size": 48213,
"fileId": "fl_01J9Z6",
"href": "https://rasayel.net/api/files/…"
}
],
"status": "open",
"createdAt": "2026-10-27T10:15:00.000Z"
}
}The people you talk to, across every channel: their record, timeline and notes.
GET/contacts
searchstringoptionalcursorstringoptionalcurl https://rasayel.net/api/v1/contacts \
-H "Authorization: Bearer $RASAYEL_API_KEY"{
"data": [
{
"id": "01J9Z3K7Q2X4",
"name": "Ada Lovelace",
"email": "ada@example.com",
"phone": "+201001234567",
"tags": [
"tag"
],
"attributes": {},
"lastSeenAt": "2026-10-27T10:15:00.000Z",
"createdAt": "2026-10-27T10:15:00.000Z"
}
],
"nextCursor": null
}GET/contacts/{id}
The summary counts messages each way, conversations open and in total, and the median first response for this person.
idstringrequiredcurl https://rasayel.net/api/v1/contacts/ct_01J9Z5 \
-H "Authorization: Bearer $RASAYEL_API_KEY"{
"data": {
"id": "01J9Z3K7Q2X4",
"name": "invoice.pdf",
"email": "ada@example.com",
"phone": "+201001234567",
"tags": [
"tag"
],
"attributes": {},
"lastSeenAt": "2026-10-27T10:15:00.000Z",
"createdAt": "2026-10-27T10:15:00.000Z",
"identities": [
{
"channelType": "channelType",
"address": "201001234567",
"displayName": "displayName",
"createdAt": "2026-10-27T10:15:00.000Z"
}
],
"conversations": [
{
"id": "01J9Z3K7Q2X4",
"status": "open",
"channel": {
"id": null,
"name": null,
"type": null,
"status": null,
"address": null
},
"contact": {
"id": null,
"name": null,
"email": null,
"phone": null,
"tags": null,
"attributes": null,
"lastSeenAt": null,
"createdAt": null
},
"assigneeId": null,
"lastMessageAt": "2026-10-27T10:15:00.000Z",
"unreadCount": 0
}
],
"summary": {
"conversations": 1,
"active": 1,
"received": 1,
"sent": 1,
"notes": null,
"firstContactAt": "2026-10-27T10:15:00.000Z",
"lastReceivedAt": "2026-10-27T10:15:00.000Z",
"lastSentAt": "2026-10-27T10:15:00.000Z",
"firstResponseMedian": 1,
"channels": [
"channel"
]
}
}
}PATCH/contacts/{id}
Changes are recorded in the contact's history. notes is kept for older integrations: text sent there is added as a new note.
idstringrequirednamestring | nulloptionalemailstring | nulloptionalphonestring | nulloptionaltagsstring[]optionalattributesobjectoptionalnotesstring | nulloptionalcurl -X PATCH https://rasayel.net/api/v1/contacts/ct_01J9Z5 \
-H "Authorization: Bearer $RASAYEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Ada Lovelace",
"email": "ada@example.com"
}'{
"data": {
"id": "01J9Z3K7Q2X4",
"name": "Ada Lovelace",
"email": "ada@example.com",
"phone": "+201001234567",
"tags": [
"tag"
],
"attributes": {},
"lastSeenAt": "2026-10-27T10:15:00.000Z",
"createdAt": "2026-10-27T10:15:00.000Z"
}
}DEL/contacts/{id}
Deletes the contact and everything about them: identities, conversations, messages, notes, history and the files they sent. It cannot be undone.
idstringrequiredcurl -X DELETE https://rasayel.net/api/v1/contacts/ct_01J9Z5 \
-H "Authorization: Bearer $RASAYEL_API_KEY"{
"data": {
"id": "01J9Z3K7Q2X4",
"deleted": true,
"messages": 1,
"files": 1
}
}GET/contacts/{id}/timeline
Everything that happened with a contact, newest first: messages on every channel, internal notes, status and assignment changes, notes about them, and profile changes. Pass nextCursor back as before for older items.
idstringrequiredbeforestringoptionallimitintegeroptionalDefault 50
typesstringoptionalchannelstringoptionalcurl https://rasayel.net/api/v1/contacts/ct_01J9Z5/timeline \
-H "Authorization: Bearer $RASAYEL_API_KEY"{
"data": [
{
"kind": "message",
"id": "01J9Z3K7Q2X4",
"at": "2026-10-27T10:15:00.000Z",
"message": {
"id": "01J9Z3K7Q2X4",
"conversationId": "cv_01J9Z4",
"direction": "inbound",
"type": "text",
"text": "Your order is on its way.",
"attachments": [
null
],
"status": "open",
"createdAt": "2026-10-27T10:15:00.000Z"
},
"channel": {
"id": "01J9Z3K7Q2X4",
"name": "Ada Lovelace",
"type": "whatsapp"
},
"authorName": "authorName",
"note": {
"id": "01J9Z3K7Q2X4",
"body": "body",
"pinned": false,
"author": {
"id": null,
"name": null
},
"source": "user",
"edited": false,
"createdAt": "2026-10-27T10:15:00.000Z",
"updatedAt": "2026-10-27T10:15:00.000Z"
},
"event": {
"kind": "kind",
"actorName": "actorName",
"source": "source",
"detail": {},
"createdAt": "2026-10-27T10:15:00.000Z"
}
}
],
"nextCursor": null
}GET/contacts/{id}/notes
idstringrequiredbeforestringoptionalcurl https://rasayel.net/api/v1/contacts/ct_01J9Z5/notes \
-H "Authorization: Bearer $RASAYEL_API_KEY"{
"data": [
{
"id": "01J9Z3K7Q2X4",
"body": "body",
"pinned": false,
"author": {
"id": "01J9Z3K7Q2X4",
"name": "Ada Lovelace"
},
"source": "user",
"edited": false,
"createdAt": "2026-10-27T10:15:00.000Z",
"updatedAt": "2026-10-27T10:15:00.000Z"
}
],
"nextCursor": null
}POST/contacts/{id}/notes
idstringrequiredbodystringrequiredUp to 10000 characters
pinnedbooleanoptionalDefault false
curl -X POST https://rasayel.net/api/v1/contacts/ct_01J9Z5/notes \
-H "Authorization: Bearer $RASAYEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"body": "body"
}'{
"data": {
"id": "01J9Z3K7Q2X4",
"body": "body",
"pinned": false,
"author": {
"id": "01J9Z3K7Q2X4",
"name": "Ada Lovelace"
},
"source": "user",
"edited": false,
"createdAt": "2026-10-27T10:15:00.000Z",
"updatedAt": "2026-10-27T10:15:00.000Z"
}
}PATCH/contacts/{id}/notes/{noteId}
A key can change the text of notes written through the API, and pin or unpin any note.
idstringrequirednoteIdstringrequiredbodystringoptionalUp to 10000 characters
pinnedbooleanoptionalcurl -X PATCH https://rasayel.net/api/v1/contacts/ct_01J9Z5/notes/nt_01J9Z6 \
-H "Authorization: Bearer $RASAYEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"body": "body",
"pinned": false
}'{
"data": {
"id": "01J9Z3K7Q2X4",
"body": "body",
"pinned": false,
"author": {
"id": "01J9Z3K7Q2X4",
"name": "Ada Lovelace"
},
"source": "user",
"edited": false,
"createdAt": "2026-10-27T10:15:00.000Z",
"updatedAt": "2026-10-27T10:15:00.000Z"
}
}DEL/contacts/{id}/notes/{noteId}
idstringrequirednoteIdstringrequiredcurl -X DELETE https://rasayel.net/api/v1/contacts/ct_01J9Z5/notes/nt_01J9Z6 \
-H "Authorization: Bearer $RASAYEL_API_KEY"Your WhatsApp numbers, inboxes, bots, web chat and API channels.
GET/channels
curl https://rasayel.net/api/v1/channels \
-H "Authorization: Bearer $RASAYEL_API_KEY"{
"data": [
{
"id": "01J9Z3K7Q2X4",
"name": "Ada Lovelace",
"type": "whatsapp",
"status": "open",
"address": "201001234567"
}
]
}POST/channels/{id}/messages
idstringrequiredcontactobjectrequiredidnameemailphone
textstringoptionalUp to 10000 characters
externalIdstringoptionalYour id for the message; retries with the same id are stored once.
fileIdsstring[]optionalIds from POST /files. Send text, fileIds or both.
Up to 10 items
curl -X POST https://rasayel.net/api/v1/channels/ch_01J9Z3/messages \
-H "Authorization: Bearer $RASAYEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contact": {
"id": "01J9Z3K7Q2X4",
"name": "Ada Lovelace",
"email": "ada@example.com",
"phone": "+201001234567"
},
"text": "Your order is on its way.",
"fileIds": [
"fl_01J9Z6"
]
}'{
"data": {
"id": "01J9Z3K7Q2X4",
"conversationId": "cv_01J9Z4",
"direction": "inbound",
"type": "text",
"text": "Your order is on its way.",
"attachments": [
{
"name": "invoice.pdf",
"mimeType": "application/pdf",
"size": 48213,
"fileId": "fl_01J9Z6",
"href": "https://rasayel.net/api/files/…"
}
],
"status": "open",
"createdAt": "2026-10-27T10:15:00.000Z"
},
"conversationId": "cv_01J9Z4"
}Upload attachments, then send them by id.
POST/files
filestringrequiredcurl -X POST https://rasayel.net/api/v1/files \
-H "Authorization: Bearer $RASAYEL_API_KEY" \
-F file=@invoice.pdf{
"data": {
"id": "01J9Z3K7Q2X4",
"name": "invoice.pdf",
"mimeType": "application/pdf",
"size": 48213,
"url": "https://example.com/rasayel/events"
}
}Where your system receives messages from every channel.
GET/webhook
curl https://rasayel.net/api/v1/webhook \
-H "Authorization: Bearer $RASAYEL_API_KEY"PUT/webhook
The signing secret is returned once, when it is first created or rotated.
urlstringrequiredrotateSecretbooleanoptionalDefault false
curl -X PUT https://rasayel.net/api/v1/webhook \
-H "Authorization: Bearer $RASAYEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/rasayel/events"
}'{
"data": {
"url": "https://example.com/rasayel/events",
"secret": "whsec_…"
}
}DEL/webhook
curl -X DELETE https://rasayel.net/api/v1/webhook \
-H "Authorization: Bearer $RASAYEL_API_KEY"API reference
message.receivedA contact wrote to you, on any channel.
message.sentA message went out: from Messenger, the API or automation.
message.updatedA sent message changed status: delivered, read or failed.
Headers: X-Rasayel-Signature, X-Rasayel-Event, Content-Type: application/json.
{
"type": "message.received",
"createdAt": "2026-10-27T10:15:00.000Z",
"data": {
"channelId": "ch_01J9Z3",
"channelType": "whatsapp",
"conversationId": "cv_01J9Z4",
"message": {
"id": "ms_01J9Z7",
"direction": "inbound",
"senderType": "contact",
"type": "text",
"text": "Is my order shipped?",
"status": "received",
"attachments": [],
"createdAt": "2026-10-27T10:15:00.000Z"
},
"contact": {
"id": "ct_01J9Z5",
"address": "201001234567",
"name": "Ada Lovelace",
"phone": "+201001234567",
"email": null
}
}
}API reference
idstringnamestringtypeenumwhatsappemailtelegramchatformapi
statusstringaddressstring | nullidstringnamestring | nullemailstring | nullphonestring | nulltagsstring[]attributesobjectlastSeenAtstring | nullcreatedAtstringidstringstatusstringchannelChannelcontactContactassigneeIdstring | nulllastMessageAtstringunreadCountintegeridstringconversationIdstringdirectionenuminboundoutbound
typeenumtextmedianoteevent
textstring | nullattachmentsAttachment[]statusstringcreatedAtstringnamestringmimeTypestringsizeinteger | nullfileIdstring | nullhrefstring | nullSigned link, valid for 7 days
idstringnamestring | nullemailstring | nullphonestring | nulltagsstring[]attributesobjectlastSeenAtstring | nullcreatedAtstringidentitiesobject[]conversationsConversation[]summaryobjectconversationsactivereceivedsentnotesfirstContactAtlastReceivedAtlastSentAtfirstResponseMedianchannels
idstringbodystringpinnedbooleanauthorobject | nullidname
sourceenumuserapisystem
editedbooleancreatedAtstringupdatedAtstringkindenummessagenoteprofileidentitycreated
idstringatstringmessageMessagechannelobjectidnametype
authorNamestring | nullnoteNoteeventobjectkindactorNamesourcedetailcreatedAt
idstringnamestringmimeTypestringsizeintegerurlstring