Agents, people, and keys

Decide who can read, send, and administer a mailbox, and confine agents with mailbox-scoped keys and namespaces.

Every action on a mailbox is taken by a principal, and Samva records which one. A principal is a person with a grant, a mailbox-scoped key, or an installed extension. Each principal reaches only the mailboxes you give it, and none can do more than the person behind it.

People and grants

A grant shares one mailbox with one organization member. It carries a role and a send mode. The person who creates a mailbox receives an admin grant.

RoleReads the mailboxEdits threads, labels, drafts, and notesSends and repliesApproves sends, reads and releases quarantine, configures the mailbox
viewerYesNoNoNo
collaboratorYesYesYesNo
adminYesYesYesYes

The send mode is send or approval; it defaults to send. With approval, each of the person's sends waits for a different person. See Approve sends.

Managing grants needs admin. Grant a member access with POST /v1/mailboxes/{id}/grants:

grant-access.ts
import { createClient } from "samva";

const samva = createClient({ apiKey: process.env.SAMVA_API_KEY! });

const grant = await samva.mailboxes.createGrant({
  id: "mbx_...",
  userId: "user_...",
  role: "collaborator",
  sendMode: "approval",
});
curl -X POST https://api.samva.dev/v1/mailboxes/$MAILBOX_ID/grants \
  -H "X-API-Key: $SAMVA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "userId": "user_...", "role": "collaborator", "sendMode": "approval" }'

The member must already belong to the organization; anyone else is a validation error. Granting someone who already holds a grant is a 409, so granting never widens access by accident. Change an existing grant instead:

change-grant.ts
await samva.mailboxes.updateGrant({
  id: "mbx_...",
  grantId: grant.id,
  role: "admin",
  sendMode: "send",
});

PATCH /v1/mailboxes/{id}/grants/{grantId} takes role, sendMode, or both, and keeps any field you leave out. Naming neither is a validation error. The change applies to the person's next request on every surface. DELETE on the same path revokes the grant.

A mailbox always keeps at least one admin. Revoking the last one, or changing its role, is a 409: hand the mailbox to a teammate first.

Mailbox-scoped keys

An agent should not hold a key that reaches your whole organization. Create a restricted key with mailboxes access instead. The key acts as itself, named by the key's name, so every message it sends and every thread it changes is attributed to that key. A full-access key has no identity of its own: it acts as its owner.

create-agent-key.ts
const key = await samva.apiKeys.create({
  name: "Support agent",
  access: {
    mode: "restricted",
    resources: {},
    mailboxes: {
      permissions: ["read", "send"],
      mailboxIds: ["mbx_..."],
      sendMode: "send",
    },
  },
});
FieldMeaning
permissionsOne or more of read, update, send, quarantine.read.
mailboxIdsThe mailboxes the key may act on. Omit it to reach every mailbox its owner reaches.
sendModesend (the default) or approval.
namespaceIdOn access, confines the key to one namespace.
PermissionLets the key
readRead mailboxes, threads, messages, attachments, and the mailbox's actions, events, and policies.
updateChange thread state, labels, drafts, and notes.
sendSend, reply, reply to all, forward, and send drafts.
quarantine.readList held messages as metadata.

A key is capped by the member who owns it. It reaches only mailboxes that member reaches, with at most that member's current role, and it stops working if the member leaves the organization. A restricted key with mailboxes access cannot create, configure, pause, or delete a mailbox or manage grants; a full-access key acts as its owner and can, which is why setup uses one. No key, full-access or restricted, can approve or deny a send or release or discard held mail: those need a person signed in. A key scoped to listed mailboxes or a namespace holds mailbox access only.

Revoke a key with DELETE /v1/api-keys/{id}. It stops authenticating at once, on every surface, and the mail it handled keeps its attribution. Deleting a mailbox removes it from every key scope and webhook filter that named it, and a key or endpoint left with no mailbox is disabled.

Change a key's send mode

Switching an agent between sending directly and sending for approval does not need a new key. A person sets the key's mailboxes.sendMode with PATCH /v1/api-keys/{id}:

require-approval.ts
await samva.apiKeys.update({
  id: "key_...",
  mailboxes: { sendMode: "approval" },
});

Only a person can change it, and only on a key that holds mailbox access. The change applies to the key's next request.

Namespaces

A namespace is a sub-tenant inside your organization. Use one per customer when you resell mailboxes or run agents on behalf of customers. A mailbox belongs to a namespace when you create it with namespaceId, and a key created with access.namespaceId reaches only that namespace's mailboxes, so one customer's agent can never read another's mail. Webhook endpoints take the same namespaceId filter.

curl -X POST https://api.samva.dev/v1/namespaces \
  -H "X-API-Key: $SAMVA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Acme Corp", "metadata": { "customerId": "cus_123" } }'

Names are unique within the organization. metadata is yours: Samva stores it and never reads it. A namespace that still holds mailboxes or keys cannot be deleted; remove or move them first.

Next steps

Related documentation

On this page