> For the complete documentation index, see [llms.txt](https://docs.lingoql.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.lingoql.com/sub0/apis-abi/practical-examples/wallet-backend-realtime-and-oauth.md).

# Wallet backend: Realtime and OAuth

Use this page when your wallet needs live updates or third-party sign-in.

### Best for

Realtime balance changes, connected clients, and secure OAuth callbacks.

### What it shows

* Calling a protected resource over WebSocket
* Broadcasting balance updates to connected users
* Verifying OAuth state before token exchange

### Use this when

You want app updates to feel instant or need to connect external identity providers.

### Pattern 1. Call a protected balance endpoint over WebSocket

Use this when the client should fetch live account data without polling.

```javascript
const uid = "user_12345";
const socket = new WebSocket(`wss://{{YOUR_SUB0_URL}}/ws?uid=${encodeURIComponent(uid)}`, [
  "x-access-token",
  "eyJ0eXAiOiO..."
]);

socket.onopen = () => {
  socket.send(
    JSON.stringify({
      resource: "fetch-balance",
      action: "balance_check",
      payload: {}
    })
  );
};

socket.onmessage = (event) => {
  const message = JSON.parse(event.data);
  console.log(message);
};
```

```json
{
  "id": "wallet-fetch-balance-01",
  "resource": "fetch-balance",
  "tokenize": {
    "type": "JWT",
    "algorithm": "HS256",
    "encryption_key": "$ENV.JWT_SECRET_KEY"
  },
  "protected": {
    "provide_as": "x-access-token",
    "extract_claims": ["id"]
  },
  "actionables": [
    {
      "id": 1,
      "mode": "QUERY",
      "sql_query": {
        "query": "SELECT wallet_balance FROM users WHERE id = $1 AND deleted_at IS NULL",
        "parameters": ["$PROTECTED.id"]
      },
      "returnables": ["wallet_balance"]
    }
  ]
}
```

### Pattern 2. Broadcast a balance update after a write

Use this when one action should immediately update connected clients.

```json
{
  "id": "wallet-broadcast-balance-01",
  "resource": "broadcast-balance-update",
  "actionables": [
    {
      "id": 1,
      "mode": "QUERY",
      "sql_query": {
        "query": "UPDATE users SET wallet_balance = wallet_balance + $1::float8, updated_at = $2::timestamptz WHERE id = $3 RETURNING wallet_balance",
        "parameters": ["wallet_balance", "$DATETIME", "id"]
      },
      "returnables": ["wallet_balance"],
      "broadcast_websocket_message": {
        "broadcast_type": "SINGLE",
        "action": "wallet_balance_update",
        "broadcast_to": "$PAYLOAD.id"
      }
    }
  ]
}
```

### Pattern 3. Issue and verify OAuth state

Use this when you need a CSRF-safe OAuth callback flow.

#### Create a CSRF token

```json
{
  "id": "wallet-oauth-state-01",
  "resource": "get-csrf-token",
  "tokenize": {
    "type": "JWT",
    "algorithm": "HS256",
    "encryption_key": "$ENV.JWT_SECRET_KEY"
  },
  "protected": {
    "provide_as": "x-access-token",
    "extract_claims": ["id"]
  },
  "actionables": [
    {
      "id": 1,
      "mode": "QUERY",
      "sql_query": {
        "query": "INSERT INTO csrf_token_store (id, user_id, csrf_token, created_at, updated_at) VALUES ($1, $2, $3, $4::timestamptz, $5::timestamptz) RETURNING csrf_token",
        "parameters": ["$GENERATOR.KSUID", "$PROTECTED.id", "$GENERATOR.SHORTID", "$DATETIME", "$DATETIME"]
      },
      "returnables": ["csrf_token"]
    }
  ]
}
```

#### Verify the callback and exchange the code

```json
{
  "id": "wallet-verify-github-oauth-01",
  "resource": "verify-github-oauth",
  "tokenize": {
    "type": "JWT",
    "algorithm": "HS256",
    "encryption_key": "$ENV.JWT_SECRET_KEY"
  },
  "protected": {
    "provide_as": "x-access-token",
    "extract_claims": ["id"]
  },
  "actionables": [
    {
      "id": 1,
      "mode": "QUERY",
      "sql_query": {
        "query": "SELECT user_id, csrf_token FROM csrf_token_store WHERE csrf_token = $1 AND user_id = $2",
        "parameters": ["$PAYLOAD.state", "$PROTECTED.id"]
      },
      "must_return_value": true,
      "returnables": ["user_id"]
    },
    {
      "id": 2,
      "mode": "HTTPREQUEST",
      "http": {
        "url": "https://github.com/login/oauth/access_token",
        "method": "POST",
        "headers": {
          "Content-Type": "application/json"
        },
        "request_body": {
          "client_id": "$ENV.YOUR_CLIENT_ID",
          "client_secret": "$ENV.YOUR_CLIENT_SECRET",
          "code": "$PAYLOAD.code",
          "redirect_uri": "$PAYLOAD.redirect_uri",
          "state": "$PAYLOAD.state"
        }
      },
      "returnables": ["access_token"]
    }
  ]
}
```

For the transport details, see [Websockets](/sub0/apis-abi/websockets.md) and [Authentication](/sub0/apis-abi/authentication.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.lingoql.com/sub0/apis-abi/practical-examples/wallet-backend-realtime-and-oauth.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
