Tannur

SDK Reference

Tannur provides type-safe SDKs for every major platform — TypeScript, React, React Native, Python, Go, PHP, and SvelteKit. Each SDK wraps the REST API with ergonomic helpers, smart retries, and automatic tenant resolution.

TypeScript

npm install tannur

React

npm install @tannur/react

Python

pip install tannur-sdk

Go

go get github.com/tannur/go-sdk

PHP

composer require tannur/php-sdk

SvelteKit

npm install @tannur/sveltekit

React Native

npm install @tannur/react-native

CLI

npm install -g @tannur/cli

Universal TypeScript SDK

TypeScript

The core JavaScript/TypeScript package. Works in Node.js, edge workers (Cloudflare, Deno), and browsers alike.

$npm install tannur
Initialize & Emit an EventTypeScript
import { createClient } from 'tannur';

const tannur = createClient({
  apiKey: process.env.TANNUR_API_KEY,
});

// Emit an event into a named stream
const res = await tannur.emit('user.signup', {
  userId: 'usr_9kx2m',
  email: 'alice@example.com',
  plan: 'pro',
});
console.log(res.eventId); // evt_7xk29m
Fetch Materialized Stream StateTypeScript
// Fetch the current materialised state of a stream
const state = await tannur.getState('user_stream_usr_9kx2m');
console.log(state);
// {
//   totalEvents: 12,
//   lastEvent: 'user.profile_updated',
//   updatedAt: '2026-06-30T12:00:00Z'
// }
List Raw Events with PaginationTypeScript
// List raw events (with pagination)
const { events, total } = await tannur.listEvents('orders', {
  limit: 50,
  offset: 0,
});
events.forEach(e => console.log(e.type, e.data));
Configure Retries & TimeoutTypeScript
// Built-in retry + custom timeout
const tannur = createClient({
  apiKey: process.env.TANNUR_API_KEY,
  retry: { maxAttempts: 3, backoffMs: 500 },
  timeoutMs: 10_000,
});

React SDK

React

Purpose-built for React. Provides useStream() and useEmit() hooks that re-render your components automatically when stream state changes.

$npm install @tannur/react
Wrap Your App with TannurProviderTSX
// Wrap your app with TannurProvider
import { TannurProvider } from '@tannur/react';

export default function App({ children }) {
  return (
    <TannurProvider apiKey={process.env.NEXT_PUBLIC_TANNUR_KEY}>
      {children}
    </TannurProvider>
  );
}
useStream & useEmit HooksTSX
'use client';
import { useStream, useEmit } from '@tannur/react';

export function OrderDashboard() {
  // Reactive — re-renders when the stream updates
  const { state, loading, error } = useStream('orders');
  const { emit, pending } = useEmit();

  const placeOrder = async () => {
    await emit('order.created', {
      items: ['SKU-001'],
      amount: 49.99,
      currency: 'NGN',
    });
  };

  if (loading) return <p>Loading stream…</p>;
  if (error)   return <p>Error: {error.message}</p>;

  return (
    <div>
      <h2>Total Orders: {state?.totalOrders}</h2>
      <button onClick={placeOrder} disabled={pending}>
        {pending ? 'Placing…' : 'Place Order'}
      </button>
    </div>
  );
}

React Native SDK

React Native

The same hook API as @tannur/react, but optimised for iOS and Android. Works with Expo and bare React Native projects.

$npm install @tannur/react-native
useStream in a React Native ComponentTSX
import { useStream, useEmit } from '@tannur/react-native';
import { View, Text, TouchableOpacity } from 'react-native';

export function ActivityFeed() {
  const { state, loading } = useStream('user_activity');
  const { emit } = useEmit();

  const trackTap = () =>
    emit('ui.button_tapped', { screen: 'Home', button: 'CTA' });

  return (
    <View>
      <Text>Events: {state?.totalEvents ?? 0}</Text>
      <TouchableOpacity onPress={trackTap}>
        <Text>Track Tap</Text>
      </TouchableOpacity>
    </View>
  );
}

Python SDK

Python

Full-featured sync and async clients for Python 3.9+. Great for Django, Flask, FastAPI, and data-science pipelines.

$pip install tannur-sdk
Sync Client — Emit & Get StatePython
import tannur

client = tannur.Client(api_key="tnr_live_xxxx")

# Emit an event
result = client.emit(
    stream="payments",
    event_type="payment.completed",
    data={"amount": 9999, "currency": "NGN", "user_id": "u_abc"},
)
print(result["eventId"])

# Fetch stream state
state = client.get_state("payments")
print(state["totalRevenue"])
Async Client — Batch Emit with asyncioPython
import asyncio
import tannur

async def main():
    async with tannur.AsyncClient(api_key="tnr_live_xxxx") as client:
        # Batch emit
        events = [
            {"type": "page.viewed", "data": {"page": "/home"}},
            {"type": "page.viewed", "data": {"page": "/pricing"}},
        ]
        await asyncio.gather(*[
            client.emit("analytics", e["type"], e["data"])
            for e in events
        ])

asyncio.run(main())

Go SDK

Go

A high-performance, idiomatic Go SDK for fast backend services and edge-native connectors. Fully context-aware.

$go get github.com/tannur/go-sdk@latest
Emit & Get State in GoGo
package main

import (
    "context"
    "fmt"
    "log"

    tannur "github.com/tannur/go-sdk"
)

func main() {
    client, err := tannur.NewClient(tannur.Options{
        APIKey: "tnr_live_xxxx",
    })
    if err != nil {
        log.Fatal(err)
    }

    ctx := context.Background()

    // Emit an event
    event, err := client.Emit(ctx, tannur.EmitInput{
        Stream: "inventory",
        Type:   "stock.updated",
        Data: map[string]any{
            "sku":      "PROD-001",
            "quantity": 250,
        },
    })
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Event ID:", event.ID)

    // Fetch state
    state, err := client.GetState(ctx, "inventory")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("State: %+v\n", state)
}

PHP SDK

PHP

A PSR-compatible PHP SDK that works with Laravel, WordPress, Symfony, and raw PHP. Install via Composer.

$composer require tannur/php-sdk
Basic Usage (Emit & Get State)PHP
<?php

require 'vendor/autoload.php';

use Tannur\Client;

$client = new Client(['api_key' => 'tnr_live_xxxx']);

// Emit an event
$result = $client->emit('subscriptions', 'subscription.renewed', [
    'user_id' => 'u_9xk2',
    'plan'    => 'pro',
    'amount'  => 2999,
]);
echo $result['eventId'] . PHP_EOL;

// Get stream state
$state = $client->getState('subscriptions');
echo "Active subs: " . $state['activeCount'] . PHP_EOL;
Laravel Service Provider + ControllerPHP
<?php
// Laravel service provider — bind once, use anywhere
use Tannur\Client;
use Illuminate\Support\ServiceProvider;

class TannurServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->singleton(Client::class, fn () =>
            new Client(['api_key' => config('services.tannur.key')])
        );
    }
}

// In a controller:
class OrderController extends Controller
{
    public function store(Request $request, Client $tannur)
    {
        $tannur->emit('orders', 'order.placed', $request->validated());
        return response()->json(['ok' => true], 201);
    }
}

SvelteKit SDK

SvelteKit

A first-class SvelteKit integration with server-side load helpers and reactive Svelte stores for real-time stream subscriptions.

$npm install @tannur/sveltekit
Server-side Load (page.server.ts)TypeScript
// +page.server.ts — server-side load
import { createServerClient } from '@tannur/sveltekit/server';
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async ({ locals }) => {
    const tannur = createServerClient(process.env.TANNUR_API_KEY!);
    const state = await tannur.getState('dashboard_metrics');
    return { metrics: state };
};
Reactive Svelte Store (page.svelte)Svelte
<!-- +page.svelte — reactive client store -->
<script lang="ts">
    import { useStream } from '@tannur/sveltekit';
    export let data; // from load()

    // Subscribe to live updates
    const stream = useStream('dashboard_metrics');
</script>

<h1>Total Revenue</h1>

{#if $stream.loading}
    <p>Loading…</p>
{:else}
    <!-- Use server-loaded value, updated by live store -->
    <p class="text-4xl font-bold">
        ₦{($stream.state?.totalRevenue ?? data.metrics.totalRevenue).toLocaleString()}
    </p>
{/if}
Ctrl+I