Docs / Contact / getting-started/sending-messages

Sending Messages

Send messages from your entity to Contact over HTTP. For MQTT, see MQTT Protocol. For SDK examples, see SDKs. To send a file instead of a JSON message, see File Transfer.

Endpoint

code

POST https://app.tendrl.com/api/entities/message

Authenticate with the entity's API key in a Bearer header.

Message body

Field Required Description
msg_type Yes One of publish, heartbeat, state_new, state_update
data Yes Your payload (arbitrary key-value pairs)
context.tags No Routing tags, required if you want to trigger connectors or Strand workflows
dest No Send to another entity by name
timestamp No Defaults to server time
bash

curl -X POST https://app.tendrl.com/api/entities/message \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_ENTITY_API_KEY" \
  -d '{
    "msg_type": "publish",
    "data": {"temperature": 23.5, "humidity": 65},
    "context": {"tags": ["sensor"]}
  }'

Success returns 200 OK with {"code": 200, "content": "<message id>"}.

If the request failed

Status reason What it means
400 Message type is required msg_type is missing.
400 Invalid message format Malformed JSON, or data isn't an object.
400 Message size exceeds the 5KB per message limit. Per-message cap, on every plan. Trim the payload or send a file.
401 Access Denied Key wrong or revoked, or Bearer missing.
401 Only entities can write messages You used an account key. Messages need the entity's own key.
401 Unauthorized: Missing required permission 'entity:WriteMessages' The entity's role can't publish.
403 Monthly data limit exceeded Plan data cap reached for this billing period.
A message that fails validation still succeeds

Validation is passive. A rule violation returns 200 and stores the message marked validation_status: "failed" — it does not return 400. If you expected a rejection and got a success, that's why. See Validation below.

Batch sending

Send multiple messages in one request via POST /api/entities/messages (plural). Body is a JSON array of messages.

Receiving Messages

Messages addressed to your entity (via dest, or fanned out from an fanout) are queued until the entity retrieves them. An HTTP-only device receives them by polling:

code

GET https://app.tendrl.com/api/entities/check_messages?limit=10

Authenticate with the receiving entity's API key. The response is an object with a messages array of pending messages:

json

{
  "messages": [
    {
      "msg_type": "command",
      "data": {"action": "reboot"},
      "tags": ["fleet-a"],
      "source": "100:us-east:entity:controller-1",
      "timestamp": "2026-06-15T10:30:00Z"
    }
  ]
}
Param Default Description
limit 1 Maximum number of pending messages to return
Delivery is once-only

Each returned message is atomically marked as delivered, so it is never handed out twice, even if multiple clients poll concurrently. Poll on an interval (or use MQTT for real-time push) to drain the queue. If there are no pending messages, messages is an empty array.

Validation

If the entity is assigned a service, publish messages run through that service's validation rules. Results are attached as actionResults in the stored message and forwarded to the destination entity when the service has Forward Validation enabled. Validation is passive: a message that fails is still stored (marked validation_status: "failed"), not rejected. See Validation Rules.

Next steps