Guide

Provision Agent Accounts at Scale

One Nylas application can hold hundreds of agent accounts, which makes per-tenant agent inboxes practical for a B2B product. The hard part isn't the API call — it's doing it repeatably: bulk-create from a source-of-truth list, skip accounts that already exist, apply the same guardrails to each, keep an inventory, and tear down on churn. This guide scripts the whole loop from the CLI, so a tenant signing up or leaving maps to one idempotent provisioning run.

Written by Caleb Geene Director, Site Reliability Engineering

VerifiedCLI 3.1.16 · Nylas managed · last tested June 7, 2026

What does provisioning agent accounts at scale mean?

Provisioning at scale means treating agent accounts as fleet infrastructure: a script — not a person — creates, configures, and retires them in bulk against a source-of-truth list. A single Nylas application can hold hundreds of agent accounts, so a B2B product can give every customer its own agent inbox. The work is repeatable CLI calls wrapped in the safety rails any provisioning system needs: idempotency, inventory, and teardown.

The pattern that makes this practical is one account per tenant. Each account is a separate grant with its own workspace, so tenant A's agent can't see tenant B's mail, and retiring a customer is a single deletion. The diagram below shows the fan-out: one application key at the top, one isolated account per tenant beneath it.

One application provisions many agent accounts, each with its own grant and workspaceOne applicationone API keytenant-agrant + workspacetenant-bgrant + workspacetenant-cgrant + workspaceone account per tenant, each created in under 2 seconds

How do I bulk-create accounts from a tenant list?

Bulk creation is a loop over your tenant list calling nylas agent account create. Each call returns in under 2 seconds, so provisioning 100 tenants finishes in a few minutes even run serially. Use a stable naming convention — the tenant slug in the local part of the address — so an address always maps back to a tenant:

#!/usr/bin/env bash
set -euo pipefail

# tenants.txt: one tenant slug per line
while IFS= read -r tenant || [ -n "$tenant" ]; do
  [ -z "$tenant" ] && continue
  nylas agent account create "support-${tenant}@yourapp.nylas.email"
done < tenants.txt

Keeping the tenant slug in the address is what makes the fleet self-describing: a glance at support-acme@yourapp.nylas.email tells you which customer it serves. Run the loop serially for predictable rate behavior, or background each call when you need to provision a large batch faster.

How do I make provisioning idempotent?

A provisioning script reruns — on a new deploy, after a partial failure, on every tenant sync. It has to be safe to run twice, which means checking whether an account exists before creating it. Probe with nylas agent account get and skip the create when it succeeds:

while IFS= read -r tenant || [ -n "$tenant" ]; do
  [ -z "$tenant" ] && continue
  addr="support-${tenant}@yourapp.nylas.email"
  if nylas agent account get "$addr" --quiet >/dev/null 2>&1; then
    echo "skip: $addr already exists"
  else
    nylas agent account create "$addr"
    echo "created: $addr"
  fi
done < tenants.txt

The get probe returns non-zero when the account doesn't exist, so the if branch only creates what's missing. This converts the script from "create everything" — which errors on the second run — into "reconcile to the desired state," the property every provisioning system needs.

How do I apply shared guardrails to every tenant?

Every tenant's agent should run under the same policy and rules: the same send cap, the same blocked domains, the same spam handling. Create the policy and rules once with nylas agent policy create and nylas agent rule create, then apply them as part of each tenant's provisioning step so the guardrails are identical across the fleet:

# Define the shared policy once (daily send cap, attachment limits)
nylas agent policy create --data '{
  "name": "Tenant agent baseline",
  "limits": {
    "limit_count_daily_message_per_grant": 500,
    "limit_attachment_count_limit": 10
  }
}'

Applying a policy and rules to each account is the per-account half of the workflow — the full attachment mechanism, including every policy setting and rule trigger, is in Agent Rules and Policies. The point at scale is consistency: the same baseline on every tenant means one place to change a limit, and a fleet that behaves predictably. Because rules live on the workspace, a tenant's agent can't prompt its way past them.

How do I inventory the fleet?

You can't manage what you can't list. The nylas agent account list --json command is the fleet inventory — every account in the application as structured data. Pipe it through jq to count accounts, find broken ones, or reconcile against your tenant list:

# Total accounts
nylas agent account list --json | jq length

# Any account not healthy
nylas agent account list --json \
  | jq -r '.[] | select(.grant_status != "valid") | .email'

# Provisioned addresses vs the tenant list (find drift)
nylas agent account list --json | jq -r '.[].email' | sort > provisioned.txt

Diffing provisioned.txt against your expected list surfaces both orphans (accounts with no tenant) and gaps (tenants with no account). A nightly run of this check is how a fleet of 200 accounts stays consistent without anyone watching it. The per-account operations behind each entry are in the agent account lifecycle guide.

How do I tear down a tenant's account?

Churn is part of the lifecycle: when a tenant leaves, its agent account should go with them. Deletion takes the account ID, which get --quiet resolves from the address, and --yes skips the prompt for unattended teardown:

offboard_tenant() {
  local tenant="${1:?usage: offboard_tenant <tenant>}"
  local addr="support-${tenant}@yourapp.nylas.email"
  if ! nylas agent account get "$addr" --quiet >/dev/null 2>&1; then
    echo "not found: $addr"; return 0
  fi
  local id; id=$(nylas agent account get "$addr" --quiet)
  nylas agent account delete "$id" --yes
  echo "deleted: $addr"
}

offboard_tenant acme

Wiring teardown into your tenant-offboarding flow guarantees a departed customer leaves no working mailbox behind — the grant is gone, so the address stops sending and receiving immediately. That clean revocation is the scale benefit of one account per tenant: removing a customer is one deletion, not an audit of shared access.

One app, many accounts: what to know

A single application is the right container for a fleet because all accounts share one API key, one region, and one managed connector — the connector is auto-created on the first account and reused for the rest. The trade-off to design around is isolation versus sharing: one account per tenant maximizes isolation, while a smaller pool of shared accounts trades that for fewer identities to manage.

ModelIsolationBest for
One account per tenantStrong — per-grant separationB2B products, per-customer inboxes
One account per agent roleMedium — separation by functionA few internal agents (support, billing)
Shared accountLow — one identity, many callersPrototypes and test automation

For per-customer inboxes specifically — addressing, routing replies, and keeping tenants separate — the agent account architecture explains how the grant and workspace give each tenant its own isolated identity. Provisioning at scale is just that model, multiplied and scripted.

Next steps