> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aptly.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# REST API

> Publish messages to Brook using HTTP/REST API

## 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.

<Note>
  Use **Server Keys (sk\_)** for REST API requests. Public keys (pk\_) cannot publish messages.
</Note>

### Required Headers

| Header         | Value              | Required | Notes                        |
| -------------- | ------------------ | -------- | ---------------------------- |
| `x-api-key`    | Your server key    | ✅ Yes    | Must start with `sk_`        |
| `Content-Type` | `application/json` | ✅ Yes    | Request body format          |
| `Origin`       | Your domain        | ❌ No     | Not required for server keys |

### Server Keys vs Public Keys

<Tabs>
  <Tab title="Server Key (sk_) - Recommended">
    Server keys are designed for backend services and have no origin restrictions.

    ```bash theme={null}
    curl -X POST https://connect.aptly.cloud/realtime \
      -H "x-api-key: sk_xyz789..." \
      -H "Content-Type: application/json" \
      -d '{"channel": "events", "message": {"type": "update"}}'
    ```

    <Tip>
      No `Origin` header needed. Perfect for serverless functions, APIs, and backend services.
    </Tip>
  </Tab>

  <Tab title="Public Key (pk_) - Client Only">
    Public keys are for client-side subscriptions only and cannot publish via REST API.

    <Warning>
      Publishing with public keys is not supported via REST API. Use the JavaScript SDK for client-side publishing.
    </Warning>

    For client-side real-time subscriptions, see the [JavaScript SDK](/javascript-sdk) or [React SDK](/react-sdk) documentation.
  </Tab>
</Tabs>

<Card title="Authentication Guide" icon="shield" href="/authentication">
  Learn when to use public keys vs server keys
</Card>

## Request Format

### Body

```json theme={null}
{
  "channel": "channel-name",
  "message": {
    // Your message data (any JSON-serializable object)
  }
}
```

## Examples

### Using cURL

<Tabs>
  <Tab title="Simple Message">
    ```bash theme={null}
    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!"
        }
      }'
    ```
  </Tab>

  <Tab title="Complex Message">
    ```bash theme={null}
    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": "notifications",
        "message": {
          "type": "alert",
          "severity": "high",
          "title": "Server Down",
          "description": "Production server is unreachable",
          "timestamp": 1699564800000,
          "metadata": {
            "server": "prod-01",
            "region": "us-east-1"
          }
        }
      }'
    ```
  </Tab>

  <Tab title="With Pretty Print">
    ```bash theme={null}
    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!"}
      }' \
      | jq
    ```
  </Tab>
</Tabs>

### Using Fetch (Browser/Node.js)

<Tabs>
  <Tab title="Browser">
    ```javascript theme={null}
    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()
    });
    ```
  </Tab>

  <Tab title="Node.js">
    ```javascript theme={null}
    // Node.js 18+ (native fetch)
    async function publishMessage(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) {
          throw new Error(data.error || 'Failed to publish');
        }

        return data;
      } catch (error) {
        console.error('Publish error:', error);
        throw error;
      }
    }

    // Usage
    publishMessage('notifications', {
      type: 'email',
      to: 'user@example.com',
      subject: 'Welcome!'
    })
      .then(result => console.log('Success:', result))
      .catch(error => console.error('Error:', error));
    ```
  </Tab>

  <Tab title="With Retry Logic">
    ```javascript theme={null}
    async function publishWithRetry(channel, message, maxRetries = 3) {
      for (let i = 0; i < maxRetries; i++) {
        try {
          const response = await fetch('https://connect.aptly.cloud/realtime', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
              'x-api-key': 'your-api-key',
              'Origin': 'https://your.allowed.website'
            },
            body: JSON.stringify({ channel, message })
          });

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

          return await response.json();
        } catch (error) {
          console.log(`Attempt ${i + 1} failed:`, error);

          if (i === maxRetries - 1) {
            throw error;
          }

          // Wait before retrying (exponential backoff)
          await new Promise(resolve =>
            setTimeout(resolve, Math.pow(2, i) * 1000)
          );
        }
      }
    }
    ```
  </Tab>
</Tabs>

### Using Axios

<Tabs>
  <Tab title="Basic">
    ```javascript theme={null}
    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!'
    });
    ```
  </Tab>

  <Tab title="With Instance">
    ```javascript theme={null}
    import axios from 'axios';

    // Create a configured instance
    const brookApi = axios.create({
      baseURL: 'https://connect.aptly.cloud',
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': process.env.BROOK_API_KEY,
        'Origin': 'https://your.allowed.website'
      }
    });

    async function publish(channel, message) {
      const { data } = await brookApi.post('/realtime', {
        channel,
        message
      });
      return data;
    }

    // Usage
    publish('notifications', {
      type: 'info',
      text: 'System update completed'
    });
    ```
  </Tab>

  <Tab title="With Interceptors">
    ```javascript theme={null}
    import axios from 'axios';

    const brookApi = axios.create({
      baseURL: 'https://connect.aptly.cloud',
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': process.env.BROOK_API_KEY,
        'Origin': 'https://your.allowed.website'
      }
    });

    // Request interceptor
    brookApi.interceptors.request.use(
      (config) => {
        console.log('Publishing to:', config.data.channel);
        return config;
      },
      (error) => Promise.reject(error)
    );

    // Response interceptor
    brookApi.interceptors.response.use(
      (response) => {
        console.log('Published successfully:', response.data);
        return response;
      },
      (error) => {
        console.error('Publish failed:', error.response?.data);
        return Promise.reject(error);
      }
    );

    async function publish(channel, message) {
      const { data } = await brookApi.post('/realtime', {
        channel,
        message
      });
      return data;
    }
    ```
  </Tab>
</Tabs>

## Response Format

### Success Response

```json theme={null}
{
  "success": true,
  "offset": 12345,
  "channel": "my-topic",
  "timestamp": "2024-01-15T10:30:00.000Z"
}
```

| Field       | Type      | Description                                   |
| ----------- | --------- | --------------------------------------------- |
| `success`   | `boolean` | Always `true` for successful publishes        |
| `offset`    | `number`  | Message offset/sequence number in the channel |
| `channel`   | `string`  | The channel name                              |
| `timestamp` | `string`  | ISO timestamp when message was published      |

### Error Response

```json theme={null}
{
  "error": "Error message description",
  "success": false
}
```

| Status Code | Description                      |
| ----------- | -------------------------------- |
| `200`       | Success                          |
| `401`       | Unauthorized (invalid API key)   |
| `400`       | Bad request (invalid payload)    |
| `429`       | Too many requests (rate limited) |
| `500`       | Server error                     |

## Use Cases

### Serverless Functions

<Tabs>
  <Tab title="AWS Lambda">
    ```javascript theme={null}
    // 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)
      };
    };
    ```
  </Tab>

  <Tab title="Vercel/Next.js">
    ```javascript theme={null}
    // pages/api/notify.js
    export default async function handler(req, res) {
      if (req.method !== 'POST') {
        return res.status(405).json({ error: 'Method not allowed' });
      }

      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: 'notifications',
            message: req.body
          })
        });

        const data = await response.json();
        res.status(200).json(data);
      } catch (error) {
        res.status(500).json({ error: error.message });
      }
    }
    ```
  </Tab>

  <Tab title="Cloudflare Workers">
    ```javascript theme={null}
    export default {
      async fetch(request, env) {
        const response = await fetch('https://connect.aptly.cloud/realtime', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'x-api-key': env.BROOK_API_KEY,
            'Origin': 'https://your.allowed.website'
          },
          body: JSON.stringify({
            channel: 'worker-events',
            message: {
              timestamp: Date.now(),
              url: request.url
            }
          })
        });

        return response;
      }
    };
    ```
  </Tab>
</Tabs>

### Webhooks

```javascript theme={null}
// 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

```javascript theme={null}
// 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

<Tip>**Use environment variables** for your API key to keep it secure.</Tip>

<Tip>
  **Implement retry logic** for critical messages to handle temporary network
  issues.
</Tip>

<Warning>
  **Rate limits apply** - avoid sending too many requests in a short period.
</Warning>

<Note>
  **REST API is one-way** - use the WebSocket SDK if you need to subscribe to
  messages.
</Note>

## Error Handling

```javascript theme={null}
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

<CardGroup cols={2}>
  <Card title="JavaScript SDK" icon="js" href="/javascript-sdk">
    Use the full SDK for bidirectional communication
  </Card>

  <Card title="React SDK" icon="react" href="/react-sdk">
    Integrate Brook into React applications
  </Card>
</CardGroup>
