Client-Side API

You can use the SendHub API directly from the browser or a mobile client. The user authenticates themselves (via Google or email), so you don’t need an API secret key at all. This is how the SendHub dashboard works.

How It Works

import { createClient } from '@supabase/supabase-js'

const sendhub = createClient(
  'https://noddeqgxbyujhpqxezbg.supabase.co',
  'sb_publishable_YVA7_HVuhhguR-OVA-LZPg_ecJF2CY2'
)

// User signs in — you never handle credentials
await sendhub.auth.signInWithOAuth({
  provider: 'google',
  options: { redirectTo: window.location.origin }
})

// After redirect, the session is set automatically
// Every query is scoped to this user's role and permissions
const { data } = await sendhub
  .from('conversations')
  .select('*, contacts(name, phone)')
  .order('last_message_at', { ascending: false })

No API secret key. No secrets. The user’s own session is the auth. RLS enforces what they can see based on their role — an agent only sees assigned businesses, an admin sees everything in the org.

Real-Time Subscriptions

Client-side is where real-time really shines — push new messages to the UI as they arrive:

sendhub
  .channel('inbox')
  .on('postgres_changes', {
    event: 'INSERT',
    schema: 'public',
    table: 'messages',
  }, (payload) => {
    // RLS filters automatically — user only gets messages they can access
    addMessageToUI(payload.new)
  })
  .subscribe()

Send a Message

await sendhub.from('messages').insert({
  conversation_id: convoId,
  business_id: workspaceId,
  direction: 'outbound',
  sender_type: 'agent',
  sender_id: teamMemberId, // attributed to the signed-in user
  content: 'Your order has shipped!'
})

Client-Side vs Server-Side API

Client-Side (User Session)Server-Side (API Secret Key)
AuthUser signs in via Google/email — no secrets in your codeAPI secret key stored in your backend
PermissionsAutomatic per-user ACL — agents see only their businesses, admins see allFull org access regardless of who triggers the action
AttributionActions are automatically tied to the signed-in userAll actions attributed to “API” (unless you set sender_id manually)
Real-timeNative — subscribe to changes and push to UIPossible, but typically polled from a backend
SecretsNone — safe to ship in a browser or mobile appAPI secret key must be kept secret on your server
Session managementHandled by Supabase — auto-refresh, expiry, logoutLong-lived key (1 year), no session to manage
Best forDashboards, internal tools, customer portals, mobile appsAutomations, cron jobs, webhooks, CI/CD, MCP

When to use client-side

  • You’re building a dashboard or internal tool where team members sign in
  • You want per-user permissions enforced automatically by the database
  • You need real-time updates pushed to the browser
  • You don’t want to manage secret keys in frontend code

When to use server-side

  • Automations that run without a user present (cron, webhooks, queue workers)
  • Integrations with external systems (Shopify, CRM, helpdesk)
  • MCP server or CLI tools where there’s no browser
  • You need org-wide access that isn’t tied to a specific user’s role

Using both

Many setups use both. For example: a React dashboard with client-side auth for the UI, plus a backend worker with an API secret key for automated replies and webhook processing. Both hit the same database, same RLS, same tables.