Inbox
An Inbox is a routable email address with a processing policy. Each inbox belongs to one workspace and may bind a single agent for automated processing.
Purpose
- Provide a unique email address for a use-case (support triage, invoice intake, contract review, etc.).
- Define filters that decide whether an inbound email is
approvedorrejected. - Control attachment policy (count, per-file size, total size).
- Optionally route every approved email to an
Agentfor LLM processing.
Important: provisioning is database-only
Creating an inbox does not call AWS. AWS SES inbound matches by domain — with the Domain Identity verified and MX records pointed, every address *@inbox.{env}.kobana.com.br reaches SES regardless of whether it exists in our database. The inbox row is what makes the address routable downstream; without it the email is dropped at process-email with rejection_reason=recipient_unknown.
Schema
| Field | Type | Description |
|---|---|---|
id | UUID | Internal id. |
external_id | UUID | Stable id from the source system. Unique. |
workspace_id | UUID | Owning workspace. |
agent_id | UUID | null | Optional agent for approved emails. |
label | string | Human label. |
email | string | The routable address. Globally unique. |
status | enum | active or inactive. |
allowed_recipients | string[] | When non-empty, only emails whose to includes one of these addresses are approved. Wildcards (*@acme.com) supported. |
filters | JSON | Array of filter rules (see below). |
attachments_allow | bool | Whether attachments are allowed at all. |
attachments_max_file_number | int | Max attachments per email. Default 10. |
attachments_max_file_size | int | Bytes. Default 10485760 (10 MiB). |
attachments_max_total_size | int | Bytes. Default 41943040 (40 MiB). |
attachments_allow_rules | array|null | Allow list of attachments. null processes every attachment. See Attachment allow rules. |
custom_metadata | object | Free-form. |
metadata | object | System-controlled. |
created_at / updated_at / deleted_at | datetime |
Filters
The filters field is a JSON array. Each element is a rule like:
{ "field": "from", "op": "matches", "value": "^.*@acme\\.com$" }
field | op | value |
|---|---|---|
from, subject, header.<name> | equals, contains, matches (regex) | string |
headers.list-id | present, absent | (no value) |
body.text | contains | string |
Rules are AND'd. If any rule fails the email is rejected with rejection_reason=filters.
Attachment allow rules
attachments_allow_rules decides which attachments of an e-mail are processed. Each rule may carry:
| Key | Type | Meaning |
|---|---|---|
extensions | string[] | Extensions without the dot, case-insensitive: ["pdf", "xml"]. |
filename_pattern | string | JavaScript regular expression tested against the filename, up to 200 characters: ^GUIA_. Nested quantifiers are refused. |
case_sensitive | bool | Applies to filename_pattern. Default false. |
A rule needs extensions, filename_pattern or both; inside a rule every key given must match. An attachment is processed when it matches any rule. Up to 20 rules.
[{ "extensions": ["pdf"], "filename_pattern": "^GUIA_" }]
processes only the PDFs whose name starts with GUIA_ (any case).
An attachment outside the list is ignored, never a reason to reject the e-mail:
- the agent does not see it — the other attachments keep their number (
Anexo 1,Anexo 3); attachments_max_*count only the attachments that passed;- a workflow step that references it (
${{email.attachments.N}}) fails before any request is sent; - the e-mail records it in
metadata.ignoredAttachments, as{ "index": N, "reason": "not_allowed" }(0-based, theNof${{email.attachments.N}}). A reprocess decides the list again under the current rules; when it rejects the e-mail, the key is removed.
An attachment without a filename (an inline signature logo, say) never matches a rule. When no attachment passes, the agent still runs on the body. null or [] processes every attachment.
Example
{
"id": "b3c4d5e6-7a89-4b0c-9d1e-2f3a4b5c6d7e",
"external_id": "00000000-0000-0000-0000-00000000001a",
"workspace_id": "a8b2c1d4-9e7f-4a3b-8c5d-1f2e3a4b5c6d",
"agent_id": "c4d5e6f7-8a9b-4c0d-9e1f-2a3b4c5d6e7f",
"label": "Support — Triage",
"email": "triage-3f4a@inbox.kobana.com.br",
"status": "active",
"allowed_recipients": ["support@acme.com"],
"filters": [{ "field": "from", "op": "matches", "value": ".*@(acme|partner)\\.com$" }],
"attachments_allow": true,
"attachments_max_file_number": 10,
"attachments_max_file_size": 10485760,
"attachments_max_total_size": 41943040,
"attachments_allow_rules": null,
"custom_metadata": { "team": "support" },
"metadata": {},
"created_at": "2026-01-04T12:00:00.000Z",
"updated_at": "2026-01-04T12:00:00.000Z",
"deleted_at": null
}
Lifecycle
POST /inboxes— create (no AWS side effects).PATCH /inboxes/{id}— update label, filters, allowed recipients, attachment policy, bound agent.DELETE /inboxes/{id}— soft-delete. Future emails to the address are rejected withrecipient_unknown.
Setting status: "inactive" keeps the row but rejects new emails with recipient_unknown — useful for temporary suspensions where you plan to reactivate.