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

# Realtime

> How to receive realtime events with the PHP SDK over WebSocket

Use `$vzaps->events()` to receive instance events in realtime without exposing a public URL.

For HTTP callbacks, see [Webhooks](/en/sdk/php/webhooks).

<Note>
  In PHP, realtime should run in CLI processes, workers, daemons, Laravel Commands, Symfony Console commands, or queue consumers. Do not keep a WebSocket open inside a normal HTTP request lifecycle.
</Note>

## Common events

| Event                     | Description                            |
| ------------------------- | -------------------------------------- |
| `Message`                 | New incoming message or message event. |
| `ReadReceipt`             | Read/delivery update.                  |
| `Presence`                | User presence.                         |
| `ChatPresence`            | Chat presence.                         |
| `HistorySync`             | History sync.                          |
| `Connected`               | Instance connected to WhatsApp.        |
| `Disconnected`            | Instance disconnected.                 |
| `GroupParticipantsAdd`    | Participants added to a group.         |
| `GroupParticipantsRemove` | Participants removed from a group.     |
| `All`                     | Every subscribed event.                |

## Install WebSocket transport

```bash theme={null}
composer require textalk/websocket
```

You can also pass `webSocketFactory` to `VZapsClient` for tests or a custom transport.

## Subscribe to realtime

```php theme={null}
use VZaps\Sdk\Models\Realtime\EventSubscribeRequest;
use VZaps\Sdk\Models\Realtime\VZapsEventType;

$subscription = $vzaps->events()->subscribe(new EventSubscribeRequest(
    instanceId: 'VZ...',
    instanceToken: 'instance-token',
    events: [
        VZapsEventType::Message,
        VZapsEventType::ReadReceipt,
        VZapsEventType::Connected,
        VZapsEventType::Disconnected,
    ],
    reconnect: true,
    maxRetries: 10,
    retryDelayMs: 1000,
));
```

**Return:** `EventSubscription` — object with `on()`, `close()`, and automatic reconnect when configured.

Options:

| Field           | Type                             | Required | Description                                                     |
| --------------- | -------------------------------- | -------- | --------------------------------------------------------------- |
| `instanceId`    | `string`                         | Yes      | Instance to monitor.                                            |
| `instanceToken` | `string`                         | Yes      | Instance token.                                                 |
| `events`        | `VZapsEventType[]` or `string[]` | No       | Event list. If omitted, uses events subscribed on the instance. |
| `reconnect`     | `boolean`                        | No       | Reconnect automatically. Default: `true`.                       |
| `maxRetries`    | `int`                            | No       | Maximum retry attempts.                                         |
| `retryDelayMs`  | `int`                            | No       | Delay between attempts.                                         |
| `lastEventId`   | `string`                         | No       | Resume cursor.                                                  |

## Register handlers

```php theme={null}
$subscription->on(VZapsEventType::Message, function ($event): void {
    echo $event->id . PHP_EOL;
    echo $event->instanceId . PHP_EOL;
    print_r($event->data);
});

$subscription->on(VZapsEventType::All, function ($event): void {
    echo 'Received event: ' . $event->type . PHP_EOL;
});

$subscription->onError(function ($error): void {
    error_log('Realtime error: ' . $error->getMessage());
});
```

## Close subscription

```php theme={null}
$subscription->close();
```

**Return:** `Promise<void>` after the WebSocket closes.

In CLI processes:

```php theme={null}
if (function_exists('pcntl_signal')) {
    pcntl_signal(SIGINT, function () use ($subscription): void {
        $subscription->close();
        exit(0);
    });
}

$subscription->awaitClose();
```

## Event envelope

Each event received by the SDK has this shape:

```json theme={null}
{
  "id": "evt_01J...",
  "type": "Message",
  "instance_id": "VZ...",
  "created_at": "2026-06-23T22:57:17.000Z",
  "data": {
    "type": "Message",
    "media_url": "https://cdn.example.com/media/image.jpg"
  }
}
```

Fields:

| Field            | Description                                                                          |
| ---------------- | ------------------------------------------------------------------------------------ |
| `id`             | Event identifier. Use for deduplication.                                             |
| `type`           | Event type.                                                                          |
| `instance_id`    | Source instance in the original payload.                                             |
| `created_at`     | Event creation time in the original payload.                                         |
| `data`           | Event payload.                                                                       |
| `data.media_url` | Media URL when the incoming event contains media and the platform provides the file. |

On the PHP `VZapsEvent` object, the main shortcuts are `$event->id`, `$event->type`, `$event->instanceId`, `$event->createdAt`, `$event->data`, and `$event->raw`.

## Delivery and ack

Delivery is **at-least-once**. Your app should process events idempotently.

After the handler finishes, the SDK sends ack automatically.

Recommendations:

* save `event.id` if your automation performs external side effects;
* ignore events that were already processed;
* use `lastEventId` when reconnecting if you want to reduce gaps;
* keep handlers fast and move long-running work to your own queue.

## Realtime or webhook?

| Scenario                                          | Recommendation |
| ------------------------------------------------- | -------------- |
| Bot, dashboard, or app with immediate consumption | Realtime       |
| Backend with public URL and HTTP pipeline         | Webhook        |
| You do not want to expose a public URL            | Realtime       |
| You want to reprocess deliveries via logs         | Webhook        |
