# Connecting

> Parklio pushes results and status changes over a [Socket.IO](https://socket.io/docs/v4/)
> connection. Connect once from your server, keep the connection open, and listen for the
> [events](/realtime/events) you need.

## Prerequisites

- An API token; see [Authentication](/api/authentication). Realtime uses the same token
  as the REST API.
- A Socket.IO **v4** client: `socket.io-client` 4 for Node.js, or `python-socketio` 5
  for Python.

## Connection settings

| Setting | Value |
| --- | --- |
| URL | `https://api.parklio.com` |
| Namespace | `/v2/realtime` |
| Path | `/socketio` |
| Auth | your API token in the handshake query parameter `token` |

## Connect

**Node.js**

```ts title="connect.ts"
import { io } from 'socket.io-client';

const socket = io('https://api.parklio.com/v2/realtime', {
  path: '/socketio',
  query: { token: process.env.PARKLIO_API_TOKEN },
});

socket.on('connect', () => console.log('Connected'));
socket.on('authorized', () => console.log('Token accepted'));
socket.on('connect_error', (err) => console.error('Could not connect:', err.message));
socket.on('disconnect', (reason) => console.warn('Disconnected:', reason));

// Every payload is a JSON string: parse it before reading fields.
socket.on('lot_change', (raw: string) => {
  const event = JSON.parse(raw);
  console.log('lot_change', event.lot_id, event.event_id, event.data.status);
});
```

**Python**

```python title="connect.py"
import json
import os

import socketio

NS = "/v2/realtime"
sio = socketio.Client()  # reconnects automatically


@sio.event(namespace=NS)
def connect():
    print("Connected")


@sio.on("authorized", namespace=NS)
def on_authorized(_):
    print("Token accepted")


@sio.event(namespace=NS)
def connect_error(data):
    print("Could not connect:", data)


@sio.on("lot_change", namespace=NS)
def on_lot_change(raw):
    event = json.loads(raw)  # every payload is a JSON string
    print("lot_change", event["lot_id"], event.get("event_id"), event["data"]["status"])


sio.connect(
    f"https://api.parklio.com?token={os.environ['PARKLIO_API_TOKEN']}",
    namespaces=[NS],
    socketio_path="socketio",
)
sio.wait()
```

## Authentication

The server checks the token while the connection is being set up:

- **Accepted:** it sends an `authorized` event (with an empty payload), and the
  connection completes. Your client's `connect` fires.
- **Rejected** (missing, invalid or revoked token): the connection is refused and your
  client gets `connect_error`. Don't retry in a loop with the same token; fix it first.

Either `authorized` or `connect` tells you you're in. `authorized` can arrive just before
`connect`.

The server adds your connection to its rooms (below) a moment **after** that, once it
has looked up your token. Events sent in that moment don't reach you, so read the state
you track over REST once you're connected, not before.

## What you receive

There are no subscribe messages, and the server doesn't accept any messages from you:
the events you receive are fixed when you connect, by the kind of token. If a user is
assigned to another lot later, their connection gets that lot's events only after it
reconnects.

| Token | You receive events for |
| --- | --- |
| API token | everything in your **account** |
| User token | that **user**, plus every **lot** the user is assigned to |

Use an API token for integrations. Events for things not attached to a lot (for example
a product with no lot) aren't sent at all.

Your connection also receives events nobody on your side requested: changes made from
the Connect app, by other users, or by Parklio itself. Filter on what you care about.

## Payloads are JSON strings

Every event's payload is a **string** of JSON, not an object. Call `JSON.parse` (Node)
or `json.loads` (Python) first. The [event catalog](/realtime/events) shows the parsed
objects.

## Reconnecting

Both clients reconnect automatically, with backoff, and the server puts you back in the
same rooms when you're back. But **events sent while you were disconnected are lost**:
there's no replay. After every reconnect, re-read what you track over REST. See
[Patterns](/realtime/patterns#resync-after-a-reconnect).

## Connect before you send commands

Results of remote-control commands arrive only as realtime events. Connect, and wait
until you're connected, **before** calling
[`POST /v2/products/{id}/state`](/api/endpoints/products-change-state); otherwise you
can miss the result.

## Next

- [Events](/realtime/events): the catalog.
- [Patterns](/realtime/patterns): correlating results, deduplicating, resyncing.
