Send & Receive Messages

Send and receive end-to-end encrypted messages between the operator and the customer.

Message Directions

  1. Company → Contact: sent through the API, displayed in the customer's chat.
  2. Contact → Company: sent from the customer's chat, delivered via webhook.

All message content is end-to-end encrypted. The backend only stores and relays ciphertext — it never sees plaintext.

Send a Message (Company)

Send text messages with application/json or media messages with multipart/form-data. For media messages, the content field becomes the encrypted caption.

cURL (text message)
curl -X POST https://api.ixblix.app/api/messages/company \
  -H "X-API-Key: smci_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "conversationId": "conv_xxxx",
    "content": "<base64 AES-GCM ciphertext>",
    "iv": "<base64>",
    "authTag": "<base64>",
    "encryptedKey": "<base64 RSA-OAEP wrapped AES key>",
    "selfEncryptedKey": "<base64 RSA-OAEP wrapped AES key>",
    "keyId": "<operator key id>"
  }'
cURL (media message)
curl -X POST https://api.ixblix.app/api/messages/company \
  -H "X-API-Key: smci_xxxx" \
  -F "conversationId=conv_xxxx" \
  -F "content=<base64 AES-GCM caption>" \
  -F "file=@encrypted_file.bin" \
  -F "iv=<base64>" \
  -F "authTag=<base64>" \
  -F "encryptedKey=<base64>" \
  -F "selfEncryptedKey=<base64>" \
  -F "keyId=<operator key id>"

Request Fields

FieldRequiredDescription
conversationIdYesID of the conversation.
contentYesBase64 AES-256-GCM ciphertext.
ivYesAES-GCM initialization vector (base64).
authTagYesAES-GCM authentication tag (base64).
encryptedKeyYesAES key wrapped to customer's RSA public key.
selfEncryptedKeyYesAES key wrapped to operator's own RSA public key.
keyIdYesIdentifier of the operator's public key.
operatorUuidYesUUID of the operator sending this message. Must be a valid UUID.
replyToIdNoID of the message this replies to.
attachmentsNoEncrypted JSON string with rich message attachments (buttons, vcard, location). The operator identity (name, image, gravatarHash) should be embedded in this JSON by the sender before encryption so the customer client can render the correct avatar per message.
metadataNoOpaque JSON object stored with the message. Use encryptedPushPayload to include an encrypted push notification preview (see below). The backend never inspects the contents.

Send a Message (Contact)

cURL
curl -X POST https://api.ixblix.app/api/messages/contact \
  -H "Content-Type: application/json" \
  -d '{
    "token": "abc123def456",
    "content": "<base64 AES-GCM ciphertext>",
    "iv": "<base64>",
    "authTag": "<base64>",
    "encryptedKey": "<base64>",
    "selfEncryptedKey": "<base64>",
    "keyId": "<customer key id>"
  }'

List Messages

cURL
curl https://api.ixblix.app/api/messages/{conversationId} \
  -H "X-API-Key: smci_xxxx"

Prefer webhooks over polling. SMCI pushes a MESSAGE_RECEIVED webhook whenever a contact sends a message.

Delivery and Read Receipts

Every message moves through three acknowledgement states: sent, delivered and read. read implies delivered — marking a message as read also records its delivery when it had not been delivered yet. Both timestamps are exposed on the message as deliveredAt and readAt.

Mark a customer message as delivered (operator side) as soon as your client receives it:

cURL
curl -X POST https://api.ixblix.app/api/messages/company/delivered \
  -H "X-API-Key: smci_xxxx" \
  -H "Content-Type: application/json" \
  -d '{ "conversationId": "conv_xxxx", "messageId": "msg_xxxx" }'

Mark a customer message as read (operator side) once the operator has actually seen it:

cURL
curl -X POST https://api.ixblix.app/api/messages/company/read \
  -H "X-API-Key: smci_xxxx" \
  -H "Content-Type: application/json" \
  -d '{ "conversationId": "conv_xxxx", "messageId": "msg_xxxx" }'

Only report a read when the chat window is focused and the message is visible in the viewport. Report delivery instead when the message is off-screen or the window is in the background — otherwise the customer sees a false read receipt.

On the customer side, ixblix pushes a MESSAGE_DELIVERED webhook when the customer's device receives your message and a MESSAGE_READ webhook when the customer actually sees it.

Reactions

Reactions are encrypted messages with contentType set to react and replyToId pointing to the target message. Send an empty content to clear a reaction.

Push Notification Previews

When the customer has push notifications enabled, the customer's device shows a notification for each new company message. By default the notification shows a generic "New message" text. To show the actual message content, include an encryptedPushPayload in the message metadata.

The preview is a small JSON object encrypted with the customer's RSA public key (RSA-OAEP/SHA-256):

Push preview plaintext (before encryption)
{
  "text": "Hello, how can I help?",
  "senderName": "Support Agent",
  "thumbnailBase64": "/9j/4AAQ..." // optional, for media messages
}

Encrypt the preview and include it in the message metadata:

Message with push preview
{
  "conversationId": "conv_xxxx",
  "content": "<base64 AES-GCM ciphertext>",
  "iv": "<base64>",
  "authTag": "<base64>",
  "encryptedKey": "<base64>",
  "selfEncryptedKey": "<base64>",
  "keyId": "<operator key id>",
  "metadata": {
    "encryptedPushPayload": "<base64 RSA-OAEP ciphertext of the preview JSON>"
  }
}

The backend never sees the push preview plaintext. It only relays the opaque ciphertext to the customer's device, which decrypts it with its private key.

SDK Helper

The SDK provides encryptPushPreview() to encrypt the preview JSON, and sendEncryptedMessage() accepts a pushPreview option that automatically generates and includes the encrypted payload:

SDK (automatic push preview)
await client.sendEncryptedMessage(conversationId, {
  content: "Hello, how can I help?",
  operatorIdentity: { uuid: "op-1", name: "Support Agent" },
  pushPreview: {
    text: "Hello, how can I help?",
    senderName: "Support Agent",
  },
});