---
sidebar_position: 4
---

# Publishing from your server

Your server sends events to the site's channels. Every browser subscribed to them gets each one.

```php
$sent = wordplus_realtime()->publish( 'private-orders-42', 'updated', array( 'status' => 'paid' ) );

if ( is_wp_error( $sent ) ) {
	error_log( 'realtime: ' . $sent->get_error_code() );   // not_connected, not_included, bad_signature …
}
```

## `publish( $channels, $event, $data = null, $args = array() )`

- **`$channels`:** one channel name, or up to 100.
- **`$event`:** its name, up to 200 characters. A name starting with `realtime:` is refused, since those are the page's own.
- **`$data`:** anything JSON can carry, up to 10 KB.
- **`$args`:**
  - `except`: the tab whose action made the event, which then doesn't get it. A browser knows its tab as `rt.tab`, and can send it with the request that triggers the publish.
  - `seal`: `false` sends the data in plain. By default a private or presence channel's data goes sealed with its key, each such channel getting its own copy, and public channels' in plain ([encryption](encryption.md)).
  - `blocking`: `false` sends without waiting for the answer. The answer is then always `true`.
  - `timeout`: seconds to wait, 5 by default.

It returns `true`, or a `WP_Error` whose code is the server's reason ([errors](errors.md)).

```js
// The browser that made the change skips its own echo.
fetch( '/wp-json/my-shop/v1/orders/42/pay', { method: 'POST', headers: { 'X-Realtime-Tab': rt.tab } } );
```

```php
wordplus_realtime()->publish( 'private-orders-42', 'updated', $data, array( 'except' => $request->get_header( 'x_realtime_tab' ) ) );
```

## `publish_batch( $events, $args = array() )`

Up to 10 events, each an array as `publish()` takes them: `channels`, `event`, `data`, `except`, and `seal`. Sealed copies for several private channels may take more than one request.

```php
wordplus_realtime()->publish_batch( array(
	array( 'channels' => 'private-orders-42', 'event' => 'updated', 'data' => array( 'status' => 'paid' ) ),
	array( 'channels' => 'orders', 'event' => 'count', 'data' => array( 'open' => 12 ) ),
) );
```

## `channel_info( $channel )`

A channel as the server holds it: `subscriptions`, the connections on it, and for a presence channel its `members` (`count`, and `list` of `id` and `info`). It returns an array, or a `WP_Error`.

## How it is signed

Every request is signed as the site, with a key derived from its secret: an HMAC-SHA256 over the timestamp, the method, the path and the body's SHA-256, accepted within 5 minutes of its timestamp. The site's clock must be right within those 5 minutes, or the server answers `stale_signature`. You never handle the secret yourself.

## Where it goes

To `https://rest.wordplus.cloud/` by default. The filter `wordplus_realtime_api_server` names another server ([self-hosted](self-hosted.md)).
