Getting Started

omnibase is a complete backend platform built on PostgreSQL. It gives you a database, authentication, file storage, realtime subscriptions, and auto-generated APIs — plus extras like Redis caching, CDN, monitoring, and workflow automation.

Everything runs on dedicated infrastructure in the EU (Hetzner, Germany). Your data never leaves European jurisdiction.

1. Access your dashboard

Log in at dashboard.omnibase.live with the email you signed up with. Your dashboard shows your project overview, usage, and service links.

2. Get your API keys

Every project has two keys:

KeyPurposeSafe to expose?
anon (public)Used in frontend apps. Respects Row Level Security (RLS).Yes
service_role (secret)Bypasses RLS. Used only in server-side code.Never

Find them in Settings → API Keys in your dashboard.

3. Install the client library

# JavaScript / TypeScript
npm install @supabase/supabase-js

# Python
pip install supabase

# Dart / Flutter
dart pub add supabase_flutter

4. Initialize the client

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

const supabase = createClient(
  'https://your-project.omnibase.live',
  'your-anon-key'
)

💡 omnibase is fully compatible with the Supabase client libraries. Any Supabase tutorial or example code works with omnibase — just swap the URL and key.

Database

Your project includes a full PostgreSQL 15 database with no row limits — you're only limited by your plan's disk allocation.

Creating tables

Use the Studio SQL editor or any PostgreSQL client. Example:

CREATE TABLE products (
  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  name text NOT NULL,
  description text,
  price decimal(10,2) NOT NULL DEFAULT 0,
  in_stock boolean DEFAULT true,
  created_at timestamptz DEFAULT now()
);

-- Enable Row Level Security
ALTER TABLE products ENABLE ROW LEVEL SECURITY;

Row Level Security (RLS)

RLS lets you control which rows each user can see or modify. Always enable it on tables that hold user data.

-- Allow anyone to read products
CREATE POLICY "Products are viewable by everyone"
  ON products FOR SELECT
  USING (true);

-- Only authenticated users can insert
CREATE POLICY "Authenticated users can insert products"
  ON products FOR INSERT
  TO authenticated
  WITH CHECK (true);

-- Users can only update their own products
CREATE POLICY "Users can update own products"
  ON products FOR UPDATE
  USING (auth.uid() = user_id);

⚠️ If you enable RLS on a table without adding any policies, no one can access the data (including the anon key). This is a safe default.

Querying from your app

// Fetch all products
const { data, error } = await supabase
  .from('products')
  .select('*')

// Insert a product
const { data, error } = await supabase
  .from('products')
  .insert({ name: 'Widget', price: 9.99 })
  .select()

// Update
const { data, error } = await supabase
  .from('products')
  .update({ price: 12.99 })
  .eq('id', 1)

// Delete
const { error } = await supabase
  .from('products')
  .delete()
  .eq('id', 1)

Joins and relationships

// Fetch orders with their products (foreign key relationship)
const { data } = await supabase
  .from('orders')
  .select(`
    id,
    created_at,
    order_items (
      quantity,
      products ( name, price )
    )
  `)

Database functions

// Create a server-side function
CREATE OR REPLACE FUNCTION get_total_revenue()
RETURNS decimal AS $$
  SELECT COALESCE(SUM(amount), 0) FROM orders WHERE status = 'paid';
$$ LANGUAGE sql SECURITY DEFINER;

// Call it from your app
const { data } = await supabase.rpc('get_total_revenue')

REST API

Every table and view in your database automatically gets a RESTful API via PostgREST. No code generation needed — create a table and it's instantly available.

Base URL

https://your-project.omnibase.live/rest/v1/

Authentication

Include your API key in every request:

curl https://your-project.omnibase.live/rest/v1/products \
  -H "apikey: YOUR_ANON_KEY" \
  -H "Authorization: Bearer YOUR_ANON_KEY"

Filtering

OperatorURL syntaxMeaning
eq?column=eq.valueEquals
neq?column=neq.valueNot equals
gt / lt?column=gt.100Greater / less than
gte / lte?column=gte.100Greater/less than or equal
like?column=like.*widget*Pattern match (case-sensitive)
ilike?column=ilike.*widget*Pattern match (case-insensitive)
in?column=in.(a,b,c)In list
is?column=is.nullIs null / is true / is false

Pagination

# Get rows 0-9
GET /rest/v1/products?limit=10&offset=0

# With ordering
GET /rest/v1/products?order=created_at.desc&limit=10

Inserting and updating

# Insert
curl -X POST https://your-project.omnibase.live/rest/v1/products \
  -H "apikey: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Widget", "price": 9.99}'

# Update (PATCH)
curl -X PATCH https://your-project.omnibase.live/rest/v1/products?id=eq.1 \
  -H "apikey: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"price": 12.99}'

# Upsert (insert or update)
curl -X POST https://your-project.omnibase.live/rest/v1/products \
  -H "apikey: YOUR_KEY" \
  -H "Prefer: resolution=merge-duplicates" \
  -H "Content-Type: application/json" \
  -d '{"id": 1, "name": "Widget Pro", "price": 14.99}'

💡 Your interactive API docs with live testing are available at /docs (Swagger UI).

GraphQL

Every table is also available through GraphQL via pg_graphql. The endpoint mirrors your database schema automatically.

Endpoint

https://your-project.omnibase.live/graphql/v1

Query example

query {
  productsCollection(filter: { in_stock: { eq: true } }, first: 10) {
    edges {
      node {
        id
        name
        price
      }
    }
  }
}

Mutation example

mutation {
  insertIntoProductsCollection(objects: [
    { name: "New Widget", price: 19.99 }
  ]) {
    records {
      id
      name
    }
  }
}

GraphQL respects the same RLS policies as the REST API. Use the same API keys for authentication.

Authentication

Built-in authentication supports email/password, magic links, one-time passwords (OTP), and OAuth providers. Powered by GoTrue.

Email and password

// Sign up
const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'securepassword123'
})

// Sign in
const { data, error } = await supabase.auth.signInWithPassword({
  email: 'user@example.com',
  password: 'securepassword123'
})

// Sign out
await supabase.auth.signOut()

Magic links (passwordless)

const { error } = await supabase.auth.signInWithOtp({
  email: 'user@example.com'
})
// User receives an email with a login link

OAuth providers

const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'google',  // or 'github', 'discord', 'apple', etc.
  options: {
    redirectTo: 'https://yourapp.com/callback'
  }
})

Configure OAuth providers in Studio → Authentication → Providers.

Get current user

// Get the logged-in user
const { data: { user } } = await supabase.auth.getUser()

// Listen for auth state changes
supabase.auth.onAuthStateChange((event, session) => {
  console.log(event, session)
  // SIGNED_IN, SIGNED_OUT, TOKEN_REFRESHED, etc.
})

Using auth in RLS policies

-- auth.uid() returns the current user's ID
CREATE POLICY "Users see own data"
  ON profiles FOR SELECT
  USING (auth.uid() = id);

-- auth.jwt() returns the full JWT claims
CREATE POLICY "Admin access"
  ON admin_data FOR ALL
  USING (auth.jwt() ->> 'role' = 'admin');

Storage

S3-compatible file storage powered by MinIO. Upload images, documents, videos — any file type. Files are served globally through Bunny CDN.

Creating buckets

Create buckets in Studio → Storage, or via the API. Buckets can be public (anyone can read) or private (requires auth).

Upload files

const { data, error } = await supabase.storage
  .from('avatars')
  .upload('user123/photo.jpg', file, {
    contentType: 'image/jpeg',
    upsert: true  // overwrite if exists
  })

Download and get URLs

// Public URL (for public buckets)
const { data } = supabase.storage
  .from('avatars')
  .getPublicUrl('user123/photo.jpg')
// → https://cdn.omnibase.live/storage/v1/object/public/avatars/user123/photo.jpg

// Signed URL (for private buckets, expires)
const { data, error } = await supabase.storage
  .from('private-docs')
  .createSignedUrl('report.pdf', 3600)  // expires in 1 hour

List and delete

// List files in a folder
const { data, error } = await supabase.storage
  .from('avatars')
  .list('user123', { limit: 100 })

// Delete
const { error } = await supabase.storage
  .from('avatars')
  .remove(['user123/photo.jpg'])

Image transforms

// Resize on the fly
const { data } = supabase.storage
  .from('avatars')
  .getPublicUrl('photo.jpg', {
    transform: { width: 200, height: 200, resize: 'cover' }
  })

💡 Files in public buckets are automatically served through Bunny CDN (120+ edge locations) for fast global delivery.

Realtime

Subscribe to database changes, broadcast messages between clients, and track user presence — all over WebSockets.

Listen to database changes

// Subscribe to all inserts on the 'messages' table
const channel = supabase
  .channel('messages-feed')
  .on('postgres_changes',
    { event: 'INSERT', schema: 'public', table: 'messages' },
    (payload) => {
      console.log('New message:', payload.new)
    }
  )
  .subscribe()

// Listen to specific rows
.on('postgres_changes',
  { event: 'UPDATE', schema: 'public', table: 'orders', filter: 'id=eq.42' },
  (payload) => console.log('Order updated:', payload.new)
)

Broadcast (client-to-client)

// Send a message to all subscribers
const channel = supabase.channel('room-1')

channel.on('broadcast', { event: 'cursor' }, (payload) => {
  console.log('Cursor moved:', payload)
})

await channel.subscribe()

// Send
channel.send({
  type: 'broadcast',
  event: 'cursor',
  payload: { x: 100, y: 200 }
})

Presence (who's online)

const channel = supabase.channel('online-users')

channel.on('presence', { event: 'sync' }, () => {
  const state = channel.presenceState()
  console.log('Online:', Object.keys(state).length)
})

await channel.subscribe(async (status) => {
  if (status === 'SUBSCRIBED') {
    await channel.track({ user_id: 'abc', name: 'Alice' })
  }
})

Edge Functions

Server-side TypeScript/JavaScript functions that run on Deno. Use them for webhooks, scheduled tasks, third-party API calls, or any logic that shouldn't run on the client.

Creating a function

// supabase/functions/hello/index.ts
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'

serve(async (req) => {
  const { name } = await req.json()
  return new Response(
    JSON.stringify({ message: `Hello, ${name}!` }),
    { headers: { 'Content-Type': 'application/json' } }
  )
})

Calling from your app

const { data, error } = await supabase.functions.invoke('hello', {
  body: { name: 'World' }
})

Using with database

// Inside an edge function, create a Supabase client
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'

const supabase = createClient(
  Deno.env.get('SUPABASE_URL')!,
  Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
)

const { data } = await supabase.from('orders').select('*')

Redis Cache

Every omnibase project includes a Redis (Valkey) instance for caching, session storage, and rate limit counters. No setup required.

Use cases

Use caseExample
Application cacheCache API responses, computed results
Session storageStore user sessions server-side
Rate limitingCount requests per IP per minute
Job queuesSimple pub/sub for background tasks
LeaderboardsSorted sets for rankings

Connecting

// Node.js
import Redis from 'ioredis'
const redis = new Redis(process.env.REDIS_URL)

// Set and get
await redis.set('user:123:name', 'Alice', 'EX', 3600)  // expires in 1hr
const name = await redis.get('user:123:name')

// Counter
await redis.incr('page:views:homepage')

// JSON
await redis.set('config', JSON.stringify({ theme: 'dark' }))
const config = JSON.parse(await redis.get('config'))
PlanRedis memory
Starter ($12/mo)50MB
Pro ($25/mo)256MB
Flex ($10+/mo)128MB
Dedicated add-on1GB ($7/mo)

CDN & Edge Caching

All files and API responses are served through Bunny CDN with 120+ global edge locations. Average response time: ~24ms worldwide.

How it works

Storage files are automatically served through cdn.omnibase.live. No configuration needed.

API responses can be cached at the edge with smart invalidation — when data changes in PostgreSQL, a trigger automatically purges the CDN cache for affected endpoints.

Smart invalidation

Uses pg_net to call the Bunny CDN purge API on writes. When you INSERT, UPDATE, or DELETE a row, the relevant cached responses are purged automatically. Reads stay fast, data is never stale.

💡 Edge caching is included on all plans at no extra cost. Most platforms charge for this or don't offer it at all.

Rate Limiting

Built-in per-endpoint, per-IP rate limiting via Kong. Protects your APIs from abuse out of the box. No Cloudflare or custom middleware needed.

Default limits

EndpointLimit
Authentication (/auth)30 requests/minute
REST API (/rest)100 requests/minute
GraphQL (/graphql)100 requests/minute
Storage (/storage)60 requests/minute
Edge Functions (/functions)60 requests/minute

When a limit is exceeded, the API returns 429 Too Many Requests with a Retry-After header.

💡 Need custom limits? Contact support and we can adjust per-endpoint rates for your project.

Monitoring

Full server monitoring via Grafana with 7 pre-configured dashboard panels. Available at grafana.omnibase.live.

What's monitored

MetricSource
CPU usagePrometheus + node-exporter
RAM usagePrometheus + node-exporter
Disk usage and I/OPrometheus + node-exporter
Network trafficPrometheus + node-exporter
System loadPrometheus + node-exporter
Container healthDocker health checks
Service uptimeUptime Kuma (9 monitors)

Metrics are stored for 30 days. Uptime Kuma sends Telegram alerts when services go down.

Public status page: uptime.omnibase.live

Analytics

Privacy-friendly web analytics via Umami. GDPR-compliant, no cookies, no personal data collection.

Adding to your site

<!-- Add to your HTML head -->
<script
  async
  src="https://umami.omnibase.live/script.js"
  data-website-id="YOUR_WEBSITE_ID"
></script>

What you get

Page views, unique visitors, bounce rate, session duration, referrers, browser/OS/device breakdown, geographic distribution — all without cookies or consent banners.

Dashboard: umami.omnibase.live

💡 Unlike Google Analytics, Umami doesn't track users across sites, sell data, or require cookie consent. Fully GDPR compliant by design.

Backups

Automated daily database backups to Bunny Storage, geo-replicated to Frankfurt and London. Runs at 4:00 AM UTC daily.

PlanBackup retention
Starter ($12/mo)7 days
Pro ($25/mo)30 days
Flex ($10+/mo)14 days

Restore

Contact support for one-click restore from any backup point within your retention window. Restore typically takes 2-5 minutes depending on database size.

💡 Most platforms charge extra for daily backups. On omnibase, they're included on every plan.

Push Notifications

Real-time push notifications via self-hosted ntfy.sh. Send alerts to mobile and desktop without third-party services.

Free tier (all plans)

Service outage alerts, backup status notifications, and usage warnings (at 80% of limits).

Advanced ($5/mo add-on)

Custom triggers: new user signups, webhook failures, database events, and any custom condition you define.

Sending a notification

# From your app or server
curl -X POST https://ntfy.omnibase.live/your-topic \
  -H "Title: New Order" \
  -H "Priority: high" \
  -H "Tags: shopping_cart" \
  -d "Order #1234 received — $49.99"

Subscribe on your phone with the ntfy app (iOS / Android) or any HTTP client.

Workflow Automation

Visual workflow automation via n8n with 400+ integrations. Build automations without code — connect your database to Slack, email, CRMs, payment processors, and more.

Availability

Plann8n access
Starter ($12/mo)$10/mo add-on
Pro ($25/mo)Included
Flex ($10+/mo)$10/mo add-on

Example workflows

New row in database → send Slack notification. New user signup → add to Mailchimp list. Daily at 9am → generate report and email CSV. Stripe webhook → update subscription status in database.

Dashboard: n8n.omnibase.live

💡 n8n replaces Zapier ($20+/mo), Make, and similar tools. Each client gets their own isolated container.

AI Gateway

Unified proxy for OpenAI, Anthropic, and DeepSeek APIs with built-in Redis caching, cost tracking, and rate limiting. You bring your own API keys — zero AI cost risk for us.

How it works

// Instead of calling OpenAI directly:
// POST https://api.openai.com/v1/chat/completions

// Call through the omnibase AI Gateway:
POST https://ai.omnibase.live/v1/chat/completions
Authorization: Bearer YOUR_OPENAI_KEY

Benefits

Response caching (identical prompts return cached results instantly), per-key cost tracking in PostgreSQL, rate limiting per client, and a single endpoint that routes to multiple providers.

PlanAI Gateway
All plans$5-10/mo add-on

Postgres Extensions

10 PostgreSQL extensions pre-installed and ready to use. No setup required.

ExtensionPurpose
pgvectorVector embeddings for AI/ML similarity search
pgmqLightweight message queue (pub/sub) inside Postgres
pg_cronSchedule recurring SQL jobs (like cron, but in the database)
PostGISGeospatial queries — distances, polygons, GPS coordinates
pg_stat_statementsQuery performance stats — find slow queries
pgsodiumEncryption functions — encrypt columns, generate keys
pg_jsonschemaValidate JSON columns against JSON Schema
pg_hashidsGenerate short, YouTube-like IDs from integers
pgauditAudit logging — log which RLS policies fire on writes
pg_netMake HTTP requests from inside Postgres (used for CDN cache purging)

Example: Vector search with pgvector

-- Create a table with embeddings
CREATE TABLE documents (
  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  content text,
  embedding vector(1536)  -- OpenAI ada-002 dimensions
);

-- Find similar documents
SELECT content, 1 - (embedding <=> '[0.1, 0.2, ...]') as similarity
FROM documents
ORDER BY embedding <=> '[0.1, 0.2, ...]'
LIMIT 5;

Example: Scheduled jobs with pg_cron

-- Delete expired sessions every hour
SELECT cron.schedule('clean-sessions', '0 * * * *',
  $$DELETE FROM sessions WHERE expires_at < now()$$
);

-- Generate daily report at midnight
SELECT cron.schedule('daily-report', '0 0 * * *',
  $$INSERT INTO reports (date, total_users)
    SELECT current_date, count(*) FROM auth.users$$
);

Example: Message queue with pgmq

-- Create a queue
SELECT pgmq.create('email_queue');

-- Send a message
SELECT pgmq.send('email_queue', '{"to": "user@example.com", "subject": "Welcome!"}');

-- Read and process (locks message for 30 seconds)
SELECT * FROM pgmq.read('email_queue', 30, 1);

-- Delete after processing
SELECT pgmq.delete('email_queue', msg_id);

Feature Flags

Simple key-value feature toggles stored in your PostgreSQL database. No third-party service needed.

Setting flags

-- In Studio or via SQL
INSERT INTO feature_flags (key, enabled, description)
VALUES ('dark_mode', true, 'Enable dark mode for all users');

-- Update
UPDATE feature_flags SET enabled = false WHERE key = 'dark_mode';

Checking in your app

const { data } = await supabase
  .from('feature_flags')
  .select('enabled')
  .eq('key', 'dark_mode')
  .single()

if (data?.enabled) {
  // Show dark mode
}

💡 The feature_flags table has anon read access and service_role write access by default. Your frontend can check flags, but only your backend can change them.

Webhook Logs

Every outgoing webhook call is logged with status code, response time, retry count, and payload. Useful for debugging integrations.

Querying logs

// Find failed webhooks in the last 24 hours
const { data } = await supabase
  .from('webhook_logs')
  .select('*')
  .neq('status', 200)
  .gte('created_at', new Date(Date.now() - 86400000).toISOString())
  .order('created_at', { ascending: false })

The table is indexed by status and created_at for fast filtering.

Custom Domains

Use your own domain instead of the default omnibase subdomain. Available on Pro and Flex plans.

Setup

1. Add a CNAME record pointing to omnibase.live

2. Go to Settings → Custom Domain in your dashboard

3. Enter your domain and verify

SSL is automatically provisioned via Caddy (Let's Encrypt). Your API, auth, and storage endpoints all work under your custom domain.

Migrate from Supabase

omnibase uses the same open-source stack as Supabase. Migration is straightforward — your code, queries, and client libraries work without changes.

Step 1: Export your database

# From your Supabase project
pg_dump -h db.YOUR_PROJECT.supabase.co -U postgres -d postgres \
  --no-owner --no-acl > backup.sql

Step 2: Import to omnibase

# Into your omnibase database
psql -h your-project.omnibase.live -U postgres -d postgres < backup.sql

Step 3: Update your app

// Change two lines in your app:
const supabase = createClient(
  'https://your-project.omnibase.live',  // was: https://xxx.supabase.co
  'your-new-anon-key'                     // from your omnibase dashboard
)

Step 4: Migrate storage

Download files from Supabase Storage and re-upload to your omnibase bucket. We can help with bulk migration — contact support.

💡 Moving from Supabase? First 3 months at 50% off. We'll help you migrate for free.

FAQ

Will my project be paused for inactivity?

No. Unlike Supabase's free tier which pauses after 7 days of inactivity, omnibase never pauses your project on any plan.

Can I get unexpected bills?

No. Fixed plans have hard limits with no overages. Flex plans have a spending cap you set — you'll never be charged more than your cap.

Where is my data stored?

Hetzner data center in Germany (EU). Your data is subject to EU jurisdiction only — no US CLOUD Act exposure.

Is it compatible with Supabase client libraries?

Yes, 100%. Use the same @supabase/supabase-js, Python, Dart, or any other Supabase client. Just change the URL and key.

What happens when I hit my plan limit?

The API returns a clear 429 error with a message to upgrade. You get email warnings at 80% and 95% of any limit. No surprise charges, no data loss.

Do you support SOC2/ISO 27001?

Not yet (those certifications require scale to justify the cost). However, your data is encrypted at rest and in transit, backups are geo-replicated, and we use industry-standard security practices.

Can I access my database directly?

Yes. You get full PostgreSQL access — connect with psql, pgAdmin, DBeaver, or any SQL tool. Connection details are in Settings → Database.

What if omnibase goes down?

Your data is always yours. Daily backups are stored on external geo-replicated storage. In a worst case, you can restore to any PostgreSQL host. We're built on open source — no vendor lock-in.

Limits & Quotas

ResourceStarter ($12/mo)Pro ($25/mo)Flex ($10+/mo)
Database size5 GB20 GB2 GB + $1/GB
File storage10 GB50 GB5 GB + $0.50/GB
Bandwidth100 GB500 GB30 GB + $0.10/GB
Auth users10,000UnlimitedUnlimited
Realtime connections100500200
Redis cache50 MB256 MB128 MB
pgmq queues3UnlimitedUnlimited
Backup retention7 days30 days14 days
Custom domain
n8n automation$10/mo add-onIncluded$10/mo add-on
SupportEmail (48hr)Priority (24hr)Email (48hr)

API rate limits

EndpointRequests per minute
Auth30
REST API100
GraphQL100
Storage60
Edge Functions60

At the limit

Fixed plans: hard block with clear error. Flex plans: hard block at spending cap. Both: email warnings at 80% and dashboard indicators at 80% (yellow) and 95% (red).

Need help? Email support@omnibase.live

© 2026 Omnium Labs Ltd. All rights reserved.