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

# Subscriptions

> Real-time event streams over WebSockets. Subscribe to inserts, updates, and deletes on any table.

GraphQL subscriptions push events to a connected client whenever data changes. Archie publishes an event when a row is inserted, updated, or deleted on a watched [table](/features/backend/data-model/overview); a subscribed client receives the event over a WebSocket.

You configure subscriptions in two stages:

1. **Define the subscription** — what to watch — by calling a mutation under the `system` namespace.
2. **Connect to the WebSocket endpoint** with the subscription's id; events flow over that connection until you disconnect.

Subscriptions use the `wss://` protocol. The endpoint is per-environment — see [Environments](/features/backend/environments/overview).

```text theme={null}
wss://<your-graphql-endpoint>/subscriptions?project_id=<projectId>
```

## Defining a subscription

Subscriptions are first-class records. Create them with `system { createSubscription }`, update them with `system { updateSubscription }`, and remove them with `system { deleteSubscription }`.

### Create

```graphql theme={null}
mutation CreateSubscription($input: SubscriptionInput!) {
  system {
    createSubscription(input: $input) {
      id
      name
      active
    }
  }
}
```

```json theme={null}
{
  "input": {
    "name": "students_create",
    "description": "Subscribe to new student records",
    "active": true,
    "tables": [
      {
        "table": "students",
        "operations": ["CREATE"],
        "fields": ["id", "first_name", "email"]
      }
    ]
  }
}
```

The response contains the subscription `id`. Hold on to it — that's how you reference the subscription on a WebSocket connection and how you update or delete it later.

### Update

```graphql theme={null}
mutation UpdateSubscription($input: SubscriptionInput!) {
  system {
    updateSubscription(input: $input) {
      id
      name
    }
  }
}
```

```json theme={null}
{
  "input": {
    "id": "id_subscription",
    "name": "students_all",
    "active": true,
    "tables": [
      {
        "table": "students",
        "operations": ["CREATE", "UPDATE", "DELETE"],
        "fields": ["id", "first_name"]
      }
    ]
  }
}
```

### Delete

```graphql theme={null}
mutation DeleteSubscription($id: String!) {
  system {
    deleteSubscription(id: $id)
  }
}
```

## Watching multiple tables

A single subscription can watch any number of tables, each with its own operations and field set:

```json theme={null}
{
  "input": {
    "name": "orders_and_payments",
    "active": true,
    "tables": [
      {
        "table": "orders",
        "operations": ["CREATE", "UPDATE"],
        "fields": ["id", "status", "total"]
      },
      {
        "table": "payments",
        "operations": ["CREATE"],
        "fields": ["id", "amount", "order_id"]
      }
    ]
  }
}
```

## Filtered subscriptions

To receive events only for records matching a condition, attach `conditions` to a table block. Conditions stack with logical AND.

| Operator     | Example                    |
| ------------ | -------------------------- |
| `EQ`         | `status = "active"`        |
| `NEQ`        | `type != "admin"`          |
| `GT`         | `age > 18`                 |
| `LT`         | `price < 100`              |
| `GTE`        | `score >= 80`              |
| `LTE`        | `attempts <= 3`            |
| `CONTAINS`   | `name contains "john"`     |
| `STARTSWITH` | `email startsWith "admin"` |
| `ENDSWITH`   | `domain endsWith ".com"`   |

```json theme={null}
{
  "input": {
    "name": "students_25_to_50",
    "active": true,
    "tables": [
      {
        "table": "students",
        "operations": ["CREATE"],
        "fields": ["id", "first_name", "email", "age"],
        "conditions": [
          { "field": "age", "operator": "GTE", "value": "25" },
          { "field": "age", "operator": "LTE", "value": "50" }
        ]
      }
    ]
  }
}
```

You can mix multiple tables with their own conditions in the same subscription:

```json theme={null}
{
  "input": {
    "name": "active_business_events",
    "active": true,
    "tables": [
      {
        "table": "students",
        "operations": ["CREATE"],
        "fields": ["id", "first_name", "age"],
        "conditions": [
          { "field": "age", "operator": "GTE", "value": "30" }
        ]
      },
      {
        "table": "courses",
        "operations": ["UPDATE"],
        "fields": ["id", "code", "price"],
        "conditions": [
          { "field": "price", "operator": "LTE", "value": "500" }
        ]
      }
    ]
  }
}
```

## On the WebSocket side

Once the subscription exists, connect to the WebSocket endpoint and reference the subscription id. Most GraphQL client libraries (Apollo, urql, graphql-ws) handle the connection lifecycle for you — you provide the WebSocket URL and an authentication token, and they deliver events to a callback.

Events arrive as JSON payloads matching the `fields` declared on each table block, plus the operation type (`CREATE`, `UPDATE`, `DELETE`). Toggle `active: false` to pause delivery without deleting the subscription.

## Permissions

Subscriptions honor the read permissions defined in [Role-Based Access](/features/backend/app-services/role-based-access). A connected client only receives events for records and fields it's allowed to read; everything else is filtered out before delivery.

## Authentication

WebSocket connections require the same authentication tokens as queries and mutations — issued by an authentication provider configured in [App Services → Authentication Providers](/features/backend/app-services/authentication-providers/overview). Pass the token through the connection params your GraphQL client supports.

## FAQ

<AccordionGroup>
  <Accordion title="When should I use a subscription instead of polling?">
    Use a subscription when you need event-driven UI updates — collaborative editing, live dashboards, chat — and the upstream change rate is low to moderate. For high-volume or batched changes, polling at a controlled interval is often simpler and cheaper.
  </Accordion>

  <Accordion title="Do subscriptions work in browsers without WebSocket support?">
    Modern browsers all support WebSockets. If you're targeting an environment without WebSocket support, fall back to polling the [REST API](/features/backend/rest-api-explorer/overview) or use [webhooks](/features/backend/rest-api-explorer/webhooks) for server-to-server delivery.
  </Accordion>

  <Accordion title="What's the difference between a subscription and a webhook?">
    A subscription pushes events over a WebSocket to a long-lived client connection. A [webhook](/features/backend/rest-api-explorer/webhooks) pushes events over HTTP to a server URL you control. Subscriptions are best for client UIs; webhooks are best for backend systems integrating with your data.
  </Accordion>

  <Accordion title="Can I subscribe to changes on a related table?">
    Add a separate table block to the same subscription with its own operations, fields, and conditions. Subscriptions across tables run independently within the same subscription record.
  </Accordion>

  <Accordion title="What happens if I miss events while disconnected?">
    Subscriptions are not durable queues — events fire while the WebSocket is connected. For at-least-once delivery to a backend, use [webhooks](/features/backend/rest-api-explorer/webhooks), which retry on failure.
  </Accordion>
</AccordionGroup>
