Skip to main content

Overview

In addition to the WebSocket-based SDK, Brook provides a REST API for publishing messages. This is useful for:
  • Server-side applications
  • One-way message publishing (no subscription needed)
  • Integration with services that don’t support WebSockets
  • Serverless functions and cron jobs

Endpoint

POST https://connect.aptly.cloud/realtime

Authentication

Aptly uses API keys for authentication. Include your API key in the x-api-key header.
Use Server Keys (sk_) for REST API requests. Public keys (pk_) cannot publish messages.

Required Headers

HeaderValueRequiredNotes
x-api-keyYour server key✅ YesMust start with sk_
Content-Typeapplication/json✅ YesRequest body format
OriginYour domain❌ NoNot required for server keys

Server Keys vs Public Keys

Authentication Guide

Learn when to use public keys vs server keys

Request Format

Body

{
  "channel": "channel-name",
  "message": {
    // Your message data (any JSON-serializable object)
  }
}

Examples

Using cURL

  • Simple Message
  • Complex Message
  • With Pretty Print
curl -X POST https://connect.aptly.cloud/realtime \
  -H "Content-Type: application/json" \
  -H "x-api-key: your-api-key" \
  -H "Origin: https://your.allowed.website \
  -d '{
    "channel": "my-topic",
    "message": {
      "text": "Hello from cURL!"
    }
  }'

Using Fetch (Browser/Node.js)

  • Browser
  • Node.js
  • With Retry Logic
async function publishMessage(channel, message) {
  const response = await fetch('https://connect.aptly.cloud/realtime', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-api-key': 'your-api-key'
      // Note: Origin header is automatically sent by the browser
    },
    body: JSON.stringify({
      channel,
      message
    })
  });

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

  const data = await response.json();
  console.log('Published:', data);
  return data;
}

// Usage
publishMessage('my-topic', {
  text: 'Hello from browser!',
  timestamp: Date.now()
});

Using Axios

  • Basic
  • With Instance
  • With Interceptors
import axios from 'axios';

async function publishMessage(channel, message) {
  try {
    const response = await axios.post(
      'https://connect.aptly.cloud/realtime',
      {
        channel,
        message
      },
      {
        headers: {
          'Content-Type': 'application/json',
          'x-api-key': 'your-api-key',
          'Origin': 'https://your.allowed.website'
        }
      }
    );

    console.log('Published:', response.data);
    return response.data;
  } catch (error) {
    console.error('Error:', error.response?.data || error.message);
    throw error;
  }
}

// Usage
publishMessage('chat-room', {
  user: 'alice',
  text: 'Hello from axios!'
});

Response Format

Success Response

{
  "success": true,
  "offset": 12345,
  "channel": "my-topic",
  "timestamp": "2024-01-15T10:30:00.000Z"
}
FieldTypeDescription
successbooleanAlways true for successful publishes
offsetnumberMessage offset/sequence number in the channel
channelstringThe channel name
timestampstringISO timestamp when message was published

Error Response

{
  "error": "Error message description",
  "success": false
}
Status CodeDescription
200Success
401Unauthorized (invalid API key)
400Bad request (invalid payload)
429Too many requests (rate limited)
500Server error

Use Cases

Serverless Functions

  • AWS Lambda
  • Vercel/Next.js
  • Cloudflare Workers
// AWS Lambda function
export const handler = async (event) => {
  const response = await fetch('https://connect.aptly.cloud/realtime', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-api-key': process.env.BROOK_API_KEY,
      'Origin': 'https://your.allowed.website'
    },
    body: JSON.stringify({
      channel: 'lambda-events',
      message: {
        event: event.eventName,
        time: event.time,
        data: event.data
      }
    })
  });

  const data = await response.json();

  return {
    statusCode: 200,
    body: JSON.stringify(data)
  };
};

Webhooks

// Express.js webhook endpoint
app.post("/webhook", async (req, res) => {
  try {
    // Forward webhook data to Brook
    const response = await fetch("https://connect.aptly.cloud/realtime", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-api-key": process.env.BROOK_API_KEY,
        Origin: "https://your.allowed.website",
      },
      body: JSON.stringify({
        channel: "webhooks",
        message: {
          source: req.headers["x-webhook-source"],
          event: req.body.event,
          data: req.body,
          receivedAt: new Date().toISOString(),
        },
      }),
    });

    const data = await response.json();
    res.json({ success: true, brook: data });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

Scheduled Tasks

// Cron job / scheduled task
import cron from "node-cron";

// Run every hour
cron.schedule("0 * * * *", async () => {
  console.log("Publishing hourly update...");

  await fetch("https://connect.aptly.cloud/realtime", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": process.env.BROOK_API_KEY,
      Origin: "https://your.allowed.website",
    },
    body: JSON.stringify({
      channel: "system-events",
      message: {
        type: "hourly-check",
        timestamp: new Date().toISOString(),
        status: "ok",
      },
    }),
  });
});

Best Practices

Use environment variables for your API key to keep it secure.
Implement retry logic for critical messages to handle temporary network issues.
Rate limits apply - avoid sending too many requests in a short period.
REST API is one-way - use the WebSocket SDK if you need to subscribe to messages.

Error Handling

async function safePublish(channel, message) {
  try {
    const response = await fetch("https://connect.aptly.cloud/realtime", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-api-key": process.env.BROOK_API_KEY,
        Origin: "https://your.allowed.website",
      },
      body: JSON.stringify({ channel, message }),
    });

    const data = await response.json();

    if (!response.ok) {
      // Handle specific errors
      switch (response.status) {
        case 401:
          console.error("Invalid API key");
          break;
        case 429:
          console.error("Rate limited - retry later");
          break;
        case 500:
          console.error("Server error");
          break;
        default:
          console.error("Unknown error:", data);
      }

      throw new Error(data.error || "Publish failed");
    }

    return data;
  } catch (error) {
    console.error("Network error:", error);
    throw error;
  }
}

Next Steps