DOCS/SUBSCRIPTIONS

WebSocket Subscriptions

Subscribe to real-time blockchain events using GraphQL subscriptions over WebSocket. Get instant notifications with minimal latency — perfect for live dashboards, block explorers, and trading applications.

Connection

Connect to the WebSocket endpoint:

wss://api.chainvue.io/graphql

We use the graphql-ws protocol (not the older subscriptions-transport-ws). Include your API key as a connection parameter.

Available Subscriptions

newBlock

Subscribe to new blocks as they are confirmed:

subscription NewBlocks {
  newBlock(chainId: "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq") {
    height
    hash
    time
    txCount
    difficulty
    size
    miner
  }
}

newTransaction

Subscribe to new confirmed transactions:

subscription NewTransactions {
  newTransaction(chainId: "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq") {
    txid
    blockHeight
    blockHash
    timestamp
    valueIn
    valueOut
    fee
    isCoinbase
  }
}

addressActivity

Watch specific addresses for incoming or outgoing transactions:

subscription WatchAddresses {
  addressActivity(
    chainId: "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq"
    addresses: [
      "RXL3YXG2ceaB6C5hfJcN4fvmLH2C34knhA",
      "mike@"
    ]
  ) {
    address
    txid
    type
    amount
    currencyId
    currencyName
    blockHeight
    timestamp
  }
}

Tip: You can watch both regular addresses and VerusID names.

identityUpdates

Subscribe to VerusID changes:

subscription IdentityUpdates {
  identityUpdates(
    chainId: "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq"
    names: ["mike", "alice"]
  ) {
    name
    fullyQualifiedName
    identityAddress
    operation
    version
    blockHeight
    txid
    primaryAddresses
  }
}

currencyPriceUpdates

Subscribe to price changes for basket currencies:

subscription PriceUpdates {
  currencyPriceUpdates(
    chainId: "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq"
    currencyIds: ["iBridgeCurrencyId..."]
  ) {
    currencyId
    currencyName
    price
    priceChange24h
    supply
    blockHeight
    timestamp
  }
}

largeTransactions

Get notified of whale movements:

subscription WhaleAlert {
  largeTransactions(
    chainId: "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq"
    minValue: 10000
    currencyId: "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq"
  ) {
    txid
    value
    currencyName
    fromAddresses
    toAddresses
    blockHeight
    timestamp
  }
}

JavaScript Implementation

Using graphql-ws (Recommended)

import { createClient } from 'graphql-ws';

const client = createClient({
  url: 'wss://api.chainvue.io/graphql',
  connectionParams: {
    authorization: 'Bearer YOUR_API_KEY',
  },
  // Automatic reconnection with exponential backoff
  retryAttempts: Infinity,
  shouldRetry: () => true,
  // Keepalive ping every 30 seconds
  keepAlive: 30000,
});

// Subscribe to new blocks
const unsubscribe = client.subscribe(
  {
    query: `
      subscription {
        newBlock(chainId: "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq") {
          height
          hash
          txCount
          time
        }
      }
    `,
  },
  {
    next: (data) => {
      const block = data.data.newBlock;
      console.log(`Block #${block.height}: ${block.txCount} transactions`);
    },
    error: (err) => {
      console.error('Subscription error:', err);
    },
    complete: () => {
      console.log('Subscription complete');
    },
  }
);

// Later: unsubscribe to clean up
// unsubscribe();

React Hook Example

import { useEffect, useState, useCallback } from 'react';
import { createClient, Client } from 'graphql-ws';

// Create a singleton client
let client: Client | null = null;

function getClient() {
  if (!client) {
    client = createClient({
      url: 'wss://api.chainvue.io/graphql',
      connectionParams: {
        authorization: `Bearer ${process.env.NEXT_PUBLIC_CHAINVUE_API_KEY}`,
      },
    });
  }
  return client;
}

// Custom hook for subscriptions
function useSubscription<T>(query: string, variables?: Record<string, any>) {
  const [data, setData] = useState<T | null>(null);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    const client = getClient();

    const unsubscribe = client.subscribe(
      { query, variables },
      {
        next: (result) => setData(result.data as T),
        error: (err) => setError(err as Error),
        complete: () => console.log('Subscription complete'),
      }
    );

    return () => unsubscribe();
  }, [query, JSON.stringify(variables)]);

  return { data, error };
}

// Usage in a component
function BlockMonitor() {
  const { data, error } = useSubscription<{ newBlock: Block }>(`
    subscription {
      newBlock(chainId: "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq") {
        height
        hash
        txCount
      }
    }
  `);

  if (error) return <div>Error: {error.message}</div>;
  if (!data) return <div>Waiting for blocks...</div>;

  return (
    <div>
      <h2>Latest Block: #{data.newBlock.height}</h2>
      <p>Transactions: {data.newBlock.txCount}</p>
    </div>
  );
}

Node.js (Server-side)

import { createClient } from 'graphql-ws';
import WebSocket from 'ws';

const client = createClient({
  url: 'wss://api.chainvue.io/graphql',
  webSocketImpl: WebSocket,
  connectionParams: {
    authorization: 'Bearer YOUR_API_KEY',
  },
});

// Multiple subscriptions on one connection
const subscriptions = [
  // Watch blocks
  client.subscribe(
    {
      query: `subscription { newBlock(chainId: "...") { height hash } }`,
    },
    {
      next: (data) => processBlock(data.data.newBlock),
      error: console.error,
    }
  ),
  // Watch addresses
  client.subscribe(
    {
      query: `subscription {
        addressActivity(chainId: "...", addresses: ["..."]) {
          address txid amount
        }
      }`,
    },
    {
      next: (data) => processActivity(data.data.addressActivity),
      error: console.error,
    }
  ),
];

// Cleanup on shutdown
process.on('SIGINT', () => {
  subscriptions.forEach(unsub => unsub());
  process.exit();
});

Connection Management

Keepalive

WebSocket connections are kept alive with automatic ping/pong messages. Configure keepAlive in your client (default: 30 seconds).

Reconnection

The graphql-ws client automatically reconnects on disconnect. Configure retry behavior:

const client = createClient({
  url: 'wss://api.chainvue.io/graphql',
  connectionParams: { authorization: 'Bearer ...' },

  // Retry configuration
  retryAttempts: 10,
  retryWait: async (retries) => {
    // Exponential backoff: 1s, 2s, 4s, 8s, max 30s
    const delay = Math.min(1000 * Math.pow(2, retries), 30000);
    await new Promise(resolve => setTimeout(resolve, delay));
  },

  // Custom retry logic
  shouldRetry: (errOrCloseEvent) => {
    // Don't retry on auth errors
    if (errOrCloseEvent?.code === 4401) return false;
    return true;
  },

  // Called on successful connection
  on: {
    connected: () => console.log('Connected!'),
    closed: () => console.log('Connection closed'),
    error: (err) => console.error('Connection error:', err),
  },
});

Error Handling

Common error codes:

4400 — Bad request (invalid query)
4401 — Unauthorized (invalid API key)
4429 — Rate limited (too many subscriptions)
4500 — Server error

Connection Limits

PlanConnectionsSubscriptions per Connection
Free15
Developer520
Business2550
EnterpriseUnlimitedUnlimited

Best Practices

  • Reuse connections — Create one client, use for multiple subscriptions
  • Handle reconnection — Your subscriptions auto-resume on reconnect
  • Clean up subscriptions — Always unsubscribe when components unmount
  • Request only needed fields — Reduces bandwidth and latency
  • Use variables — Makes queries reusable and type-safe

Related