Multi-tenancy is a first-class concept in Programmable Inbox. This post explains how to structure your integration when you're building a SaaS product and need isolated email inboxes per customer.
Organizations and inboxes
The data model is:
Organization (your tenant)
└── Inbox (one or more per org)
└── Thread
└── MessageEach organization has its own API key, and API calls are scoped to that organization. You can't accidentally read another org's messages.
Provisioning inboxes on signup
When a new customer signs up for your product, create an inbox for them:
async function onCustomerSignup(customer: Customer) {
const res = await fetch('https://api.programmableinbox.dev/orgs', {
method: 'POST',
headers: { Authorization: `Bearer ${MASTER_API_KEY}` },
body: JSON.stringify({ name: customer.id }),
})
const org = await res.json()
await fetch(`https://api.programmableinbox.dev/orgs/${org.id}/inboxes`, {
method: 'POST',
headers: { Authorization: `Bearer ${MASTER_API_KEY}` },
body: JSON.stringify({ name: 'support', domain: customer.domain }),
})
// Store org.apiKey in your database — scoped to this customer
await db.customers.update({ id: customer.id, inboxApiKey: org.apiKey })
}Scoped API keys
Once you've stored the per-org API key, use it for all inbox operations on behalf of that customer. It cannot access any other org's data, so you can safely expose it to your frontend if needed.
Webhook routing
If you want a single webhook endpoint to handle all organizations, include the org ID in the webhook URL:
curl -X POST .../orgs/{orgId}/inboxes/{id}/webhooks \
-d '{"url": "https://your-app.com/api/email?orgId={orgId}"}'Or register a global webhook at the master API level that receives events from all orgs, with the org ID included in the payload.