CloudSignal Docs
ExtensionsIntegration

REST API bridge

Publish MQTT messages via HTTP REST endpoints - ideal for serverless functions, webhooks, and backends without native MQTT support.

The REST API bridge extension lets you publish MQTT messages using standard HTTP requests. Use it for:

Use caseExamples or notes
Serverless functionsAWS Lambda, Vercel, Cloudflare Workers
No-code platformsBubble.io, Make, Zapier
Backend servicesWithout native MQTT libraries
WebhooksTrigger MQTT messages from external events

This is a paid extension. See pricing below for plan details.

How it works

REST API bridge
Your Backend
CloudSignal
MQTT Clients
POST /api/v1/publish
MQTT Publish
200 OK

Your HTTP request is converted to an MQTT message and delivered to all subscribers.

Getting started

Enable the extension

  1. Go to Dashboard → Extensions
  2. Find REST API Bridge
  3. Click Enable or Subscribe
  4. Choose your plan based on expected request volume

Get your API key

After enabling:

  1. Go to Extensions → REST API Bridge → Settings
  2. Your API Key is displayed (click to copy)
  3. Note the Endpoint URL for your region

Keep your API key secret! Anyone with the key can publish to your MQTT topics.

Make your first request

Publish a message using curl:

curl -X POST https://rest.cloudsignal.io/api/v1/publish \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key-here" \
  -d '{
    "topic": "tasks/task-3/progress",
    "payload": "{\"step\": 4, \"of\": 10, \"status\": \"running\"}",
    "qos": 1
  }'

API reference

Endpoint

POST https://rest.cloudsignal.io/api/v1/publish

Headers

HeaderRequiredDescription
X-API-KeyYesYour REST API Bridge API key
Content-TypeYesMust be application/json

Request body

{
  "topic": "string",      // Required: MQTT topic to publish to
  "payload": "string",    // Required: Message content (usually JSON string)
  "qos": 0,               // Optional: 0, 1, or 2 (default: 0)
  "retain": false         // Optional: Retain message (default: false)
}

Response

Success (200)

{
  "success": true,
  "message_id": "msg_abc123"
}

Error (4xx/5xx)

{
  "success": false,
  "error": "Invalid API key",
  "code": "UNAUTHORIZED"
}

Error codes

CodeHTTP StatusDescription
UNAUTHORIZED401Invalid or missing API key
FORBIDDEN403API key doesn't have permission for this topic
INVALID_REQUEST400Malformed JSON or missing required fields
TOPIC_INVALID400Topic contains invalid characters
PAYLOAD_TOO_LARGE413Payload exceeds maximum size (256KB)
RATE_LIMITED429Too many requests, slow down
QUOTA_EXCEEDED429Monthly request quota exceeded

Code examples

// Node.js / Browser
async function publishMessage(topic, payload) {
  const response = await fetch('https://rest.cloudsignal.io/api/v1/publish', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': process.env.CLOUDSIGNAL_API_KEY
    },
    body: JSON.stringify({
      topic,
      payload: JSON.stringify(payload),
      qos: 1
    })
  });

  if (!response.ok) {
    throw new Error(`Publish failed: ${response.status}`);
  }

  return response.json();
}

// Usage
await publishMessage('agents/agent-01/inbox', { task: 'summarize-thread' });
import os
import json
import requests

def publish_message(topic: str, payload: dict, qos: int = 0):
    response = requests.post(
        'https://rest.cloudsignal.io/api/v1/publish',
        headers={
            'Content-Type': 'application/json',
            'X-API-Key': os.environ['CLOUDSIGNAL_API_KEY']
        },
        json={
            'topic': topic,
            'payload': json.dumps(payload),
            'qos': qos
        }
    )
    response.raise_for_status()
    return response.json()

# Usage
publish_message('presence/room-9', {'online': 12, 'typing': 2})
package main

import (
    "bytes"
    "encoding/json"
    "net/http"
    "os"
)

type PublishRequest struct {
    Topic   string `json:"topic"`
    Payload string `json:"payload"`
    QoS     int    `json:"qos"`
}

func publishMessage(topic string, payload interface{}) error {
    payloadBytes, _ := json.Marshal(payload)
    
    reqBody, _ := json.Marshal(PublishRequest{
        Topic:   topic,
        Payload: string(payloadBytes),
        QoS:     1,
    })

    req, _ := http.NewRequest("POST", 
        "https://rest.cloudsignal.io/api/v1/publish",
        bytes.NewBuffer(reqBody))
    
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-API-Key", os.Getenv("CLOUDSIGNAL_API_KEY"))

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    
    return nil
}
<?php
function publishMessage(string $topic, array $payload, int $qos = 0): array {
    $ch = curl_init('https://rest.cloudsignal.io/api/v1/publish');
    
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            'Content-Type: application/json',
            'X-API-Key: ' . getenv('CLOUDSIGNAL_API_KEY')
        ],
        CURLOPT_POSTFIELDS => json_encode([
            'topic' => $topic,
            'payload' => json_encode($payload),
            'qos' => $qos
        ])
    ]);

    $response = curl_exec($ch);
    curl_close($ch);

    return json_decode($response, true);
}

// Usage
publishMessage('alerts/system', ['level' => 'warning', 'message' => 'CPU high']);
# Basic publish
curl -X POST https://rest.cloudsignal.io/api/v1/publish \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $CLOUDSIGNAL_API_KEY" \
  -d '{
    "topic": "notifications/user-123",
    "payload": "{\"title\": \"New message\", \"body\": \"Hello!\"}",
    "qos": 1
  }'

# With retain flag
curl -X POST https://rest.cloudsignal.io/api/v1/publish \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $CLOUDSIGNAL_API_KEY" \
  -d '{
    "topic": "agents/agent-42/state",
    "payload": "{\"status\": \"idle\"}",
    "retain": true
  }'

Use cases

Serverless functions

Publish from AWS Lambda, Vercel Edge Functions, or Cloudflare Workers:

// Vercel Edge Function
export default async function handler(req) {
  const { userId, message } = await req.json();

  await fetch('https://rest.cloudsignal.io/api/v1/publish', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': process.env.CLOUDSIGNAL_API_KEY
    },
    body: JSON.stringify({
      topic: `users/${userId}/notifications`,
      payload: JSON.stringify({ message, timestamp: Date.now() })
    })
  });

  return new Response('Notification sent');
}

No-code platforms (Bubble.io)

Use Bubble's API Connector plugin:

  1. Add a new API: CloudSignal REST API
  2. Authentication: None (we use headers)
  3. Add shared header: X-API-Key = your key
  4. Create call: POST to https://rest.cloudsignal.io/api/v1/publish
  5. Body type: JSON with topic and payload fields

Webhooks (Stripe, GitHub, and others)

Forward webhook events to MQTT subscribers:

// Express.js webhook handler
app.post('/webhooks/stripe', async (req, res) => {
  const event = req.body;

  // Forward to MQTT subscribers
  await fetch('https://rest.cloudsignal.io/api/v1/publish', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': process.env.CLOUDSIGNAL_API_KEY
    },
    body: JSON.stringify({
      topic: `webhooks/stripe/${event.type}`,
      payload: JSON.stringify(event)
    })
  });

  res.sendStatus(200);
});

Dashboard features

Overview tab

View your usage at a glance:

MetricDescription
Requests today / this monthTrack against your quota
Success ratePercentage of successful publishes
Average latencyTime to process requests

Settings tab

Manage your API key:

ActionDescription
View / copy API keyClick to copy
Regenerate keyInvalidates the old key immediately
Endpoint URLYour region's REST endpoint

Logs tab

Debug failed requests:

FieldDescription
TimestampWhen the request was made
TopicWhich topic was targeted
StatusSuccess or error code
LatencyProcessing time in milliseconds

Filter logs by status, topic, or date range.

Topic permissions

The REST API Bridge uses your organization's ACL rules. Your API key can only publish to topics that match your configured ACL patterns.

By default, the REST API user has publish access to all topics (#). You can restrict this in Dashboard → ACL Rules.

Rate limits

PlanRequests/minuteRequests/month
Starter6010,000
Professional300100,000
Scale1,0001,000,000

Exceeding rate limits returns a 429 status code. Implement exponential backoff:

async function publishWithRetry(topic, payload, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const response = await fetch('https://rest.cloudsignal.io/api/v1/publish', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': process.env.CLOUDSIGNAL_API_KEY
      },
      body: JSON.stringify({ topic, payload: JSON.stringify(payload) })
    });

    if (response.status !== 429) return response;

    // Exponential backoff: 1s, 2s, 4s
    await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));
  }
  throw new Error('Rate limited after retries');
}

Pricing

PlanMonthly PriceRequests/monthFeatures
Starter$910,000Basic publishing, 7-day logs
Professional$29100,000Higher rate limits, 30-day logs
Scale$991,000,000Priority support, 90-day logs

Enable the extension from Dashboard → Extensions → REST API Bridge.

Security best practices

PracticeWhy
Never expose your API key in client-side codeUse only from server-side
Use environment variablesDon't hardcode keys
Restrict ACL rulesLimit which topics the REST API can publish to
Monitor logsWatch for unusual patterns or failed requests
Regenerate keys periodicallyRotate keys as part of security hygiene

Troubleshooting

"Invalid API key" error

  • Verify the key is copied correctly (no extra spaces)
  • Check the key hasn't been regenerated
  • Ensure the extension is still enabled

"Topic invalid" error

Topics cannot contain:

  • Null characters
  • + or # (these are wildcard characters for subscriptions only)

Messages not reaching subscribers

  1. Check the topic matches subscriber patterns
  2. Verify ACL rules allow the REST API user to publish
  3. Check subscriber is connected and subscribed

High latency

  • Use the endpoint closest to your servers
  • Reduce payload size if possible
  • Check your network connectivity

Next steps

On this page