Руководство: мультитенантность

Поды, ограниченные ключи и маршрутизация событий для ваших клиентов.

Если вы строите платформу, где у каждого клиента должна быть своя почтовая инфраструктура, схема такая: один Pod на клиента, выданный ему ограниченный API-ключ и вебхуки, направленные в нужный endpoint.

Pod = изоляция тенанта

У каждого тенанта свой Pod. Все ресурсы (ящики, домены, цепочки, черновики) живут внутри него и изолированы от других подов. Подробности — на странице Поды.

1from agentinbox import Agentinbox
2
3client = Agentinbox()
4
5# Use client_id to map to your internal tenant ID so
6# you don't need to maintain a separate mapping table
7pod = client.pods.create(client_id="tenant-acme-123")

Дальше создаёте ресурсы клиента:

1inbox = client.pods.inboxes.create(
2 pod.pod_id,
3 username="support",
4 display_name="Acme Support"
5)
6
7domain = client.pods.domains.create(pod.pod_id, domain="acme.com")

API-ключи с областью действия

По умолчанию ключи на уровне организации видят всё во всех подах. Ограниченные (scoped) ключи привязаны к одному поду или одному ящику: ключ для пода Acme трогает только ресурсы Acme.

Так можно выдать ключ сервису или агенту клиента, не открывая всю организацию.

Ключи на уровне пода

Доступ ко всем ресурсам пода (ящики, цепочки, черновики, домены).

1# Create a key that can only access Acme's pod
2scoped_key = client.pods.api_keys.create(
3 pod.pod_id,
4 name="acme-service-key"
5)
6
7# This is the only time you'll see the full key, so store it
8print(scoped_key.api_key)

Ключи на уровне ящика

Ещё уже: только один ящик и связанные с ним цепочки, сообщения и черновики. Подходит, когда агенту или интеграции нужен один адрес.

1# Create a key that can only access the support inbox
2inbox_key = client.inboxes.api_keys.create(
3 inbox.inbox_id,
4 name="support-inbox-key"
5)
6
7print(inbox_key.api_key)

Полный ключ возвращается один раз при создании. Потеряли — удалите ключ и создайте новый.

Список и удаление ограниченных ключей для пода или ящика:

1# Pod-scoped keys
2keys = client.pods.api_keys.list(pod.pod_id)
3client.pods.api_keys.delete(pod.pod_id, scoped_key.api_key_id)
4
5# Inbox-scoped keys
6inbox_keys = client.inboxes.api_keys.list(inbox.inbox_id)
7client.inboxes.api_keys.delete(inbox.inbox_id, inbox_key.api_key_id)

Маршрутизация вебхуков

Один endpoint на всех клиентов обычно не нужен. При создании Webhook можно ограничить область списком pod_ids или inbox_ids, чтобы события приходили только по нужным ресурсам.

1# Only fires for events in Acme's pod
2webhook = client.webhooks.create(
3 url="https://your-server.com/webhooks/acme",
4 event_types=["message.received", "message.sent"],
5 pod_ids=[pod.pod_id]
6)
7
8# Or narrow it down to specific inboxes
9webhook = client.webhooks.create(
10 url="https://your-server.com/webhooks/acme-support",
11 event_types=["message.received"],
12 inbox_ids=[inbox.inbox_id]
13)

Полный сценарий онбординга

Как выглядит подключение нового клиента от начала до конца:

1from agentinbox import Agentinbox
2
3client = Agentinbox()
4
5def onboard_tenant(tenant_id: str, domain_name: str):
6 # Create isolated pod
7 pod = client.pods.create(client_id=tenant_id)
8
9 # Provision inbox + domain
10 inbox = client.pods.inboxes.create(
11 pod.pod_id,
12 username="support",
13 display_name=f"{tenant_id} Support"
14 )
15 domain = client.pods.domains.create(pod.pod_id, domain=domain_name)
16
17 # Pod-scoped key for the tenant
18 key = client.pods.api_keys.create(pod.pod_id, name=f"{tenant_id}-key")
19
20 # Inbox-scoped key for the support inbox
21 inbox_key = client.inboxes.api_keys.create(
22 inbox.inbox_id, name=f"{tenant_id}-support-key"
23 )
24
25 # Webhook for their events
26 webhook = client.webhooks.create(
27 url=f"https://your-server.com/webhooks/{tenant_id}",
28 event_types=["message.received"],
29 pod_ids=[pod.pod_id]
30 )
31
32 return {
33 "pod_id": pod.pod_id,
34 "inbox_id": inbox.inbox_id,
35 "pod_api_key": key.api_key, # deliver securely to tenant
36 "inbox_api_key": inbox_key.api_key,
37 "webhook_id": webhook.webhook_id,
38 }