Skip to main content
Running OpenMail for many customers — a multi-tenant SaaS, an agency, an AI agent platform? Give each tenant its own pod and a pod-scoped API key. The pod keeps the tenant’s inboxes separate. The scoped key locks their integration to that pod: it returns 403 on everything else and cannot manage pods or mint keys. This guide walks the full onboarding flow — pod, domain (optional), inbox, scoped key — then inbound routing and offboarding.

Prerequisites

  • An OpenMail account with your account-wide API key. You use it to provision tenants; it never leaves your side.
  • A stable tenant identifier from your own system (a user ID, org ID, or workspace ID) to use as the pod’s clientId.

Use this pre-built prompt to get started faster.

Open in Cursor

Step 1: Create a pod per tenant

When a tenant signs up, create a pod and pass your own tenant ID as clientId. From then on you can address the pod by that ID everywhere — no mapping table on your side.
clientId must be unique within your account. Reuse your own primary key (tenant ID, workspace ID) and you never have to store OpenMail’s pod ID.

Step 2: Add a tenant domain (optional)

For white-label setups, scope a custom domain to the pod so the tenant’s inboxes live on their own domain (e.g. support@meridian.io). Add and verify the domain in the dashboard under Settings → Domains, then scope it to the pod — or use the pod’s Domains tab. A pod-scoped domain can only be used by inboxes in that same pod. Skip this step to put the tenant’s inboxes on your account default domain, or on an account-wide domain every pod shares.

Step 3: Create the tenant’s inboxes

Create inboxes directly in the pod by passing podId (the pod ID or your clientId). An inbox belongs to exactly one pod and cannot be moved later, so pick the pod at creation.

Step 4: Mint a pod-scoped API key

Mint a key scoped to the tenant’s pod. This is the key the tenant’s integration will use. It can read and send only from this pod’s inboxes.
The full token is returned once, in this response, and can never be retrieved again. Store it as that tenant’s secret immediately. Minting requires your account-wide key.
Each pod has a cap on active keys. Hitting it returns api_key_limit_reached (422) — list the pod’s keys with GET /v1/pods/{id}/api-keys and revoke an unused one first. Listing returns only a masked tokenPrefix and last4, never the token.

Step 5: Hand the scoped key to the tenant’s integration

Give the scoped token to whatever runs on the tenant’s behalf — a per-tenant worker, an agent container, or the tenant’s own environment. The key cannot cross pods or manage pods, so it safely confines the tenant to their own inboxes:

Handle inbound email

Every inbound event carries the inbox_id it arrived on. Map that to the tenant and deliver — the same routing you’d use for any per-inbox integration. See Handle inbound email in the API integration guide for the full WebSocket and webhook handlers.
Subscribe with your account-wide key to receive events across every pod, then fan out by inbox_id. A pod-scoped key’s subscription is filtered to its own pod, so a per-tenant worker only sees its tenant’s events. One caveat: a pod-scoped subscribe-all covers the pod’s inboxes at subscribe time — re-subscribe after creating an inbox to stream its events.

Full onboarding flow

Putting the steps together — one function that provisions a tenant from scratch and returns the scoped key to store:

Offboarding a tenant

Revoke the tenant’s key first, then clean up their pod. Revocation is immediate — the key returns 401 on its next request.
A pod must be empty and own no pod-scoped domains before it can be deleted. See Lifecycle in the pods concept page for the full teardown order.

Reference

WhatEndpointKey required
Create a tenant’s podPOST /v1/podsAccount-wide
Create inboxes in the podPOST /v1/inboxes (with podId)Account-wide
Mint the tenant’s scoped keyPOST /v1/pods/{id}/api-keysAccount-wide
List a pod’s keys (masked)GET /v1/pods/{id}/api-keysAccount-wide
Revoke a keyDELETE /v1/pods/{id}/api-keys/{keyId}Account-wide
Send / receive within a podPOST /v1/inboxes/{id}/send, etc.Pod-scoped or account-wide

Pods

The full pods model: isolation, clientId, patterns, and lifecycle.

Authentication

Account-wide vs. pod-scoped keys and the token-once rule.

Custom domains

Scope a domain to one pod for white-label tenant addresses.

API integration

Per-inbox provisioning, skill files, and inbound routing.