Run Apus as a named delegate: an agent with its own identity that acts "on behalf of" people in an organization. The agent never impersonates a human - it sends, reads, and schedules under its own account with explicit delegation permissions.

This extends Multi-Agent Routing from personal use into organizational deployments.

What is a delegate

A delegate is an Apus agent that:

This maps to how executive assistants work: their own credentials, mail sent "on behalf of" their principal, and a defined scope of authority.

Why delegates

Apus's simplest mode is one human, one agent. Delegates extend this to organizations:

Personal mode Delegate mode
Agent uses your credentials Agent has its own credentials
Replies come from you Replies come from the delegate, on your behalf
One principal One or many principals
Trust boundary = you Trust boundary = organization policy

Delegates solve two problems:

  1. Accountability: messages sent by the agent are clearly from the agent, not a human.
  2. Scope control: the identity provider enforces what the delegate can access, independent of Apus's own tool policy.

Capability tiers

Start with the lowest tier that meets your needs; escalate only when the use case demands it.

Tier 1: Read-Only + Draft

Reads organizational data and drafts messages for human review. Nothing sends without approval.

Requires only read permissions from the identity provider. The agent never writes to a mailbox or calendar - drafts and proposals go to chat for a human to act on.

Tier 2: Send on Behalf

Sends messages and creates calendar events under its own identity. Recipients see "Delegate Name on behalf of Principal Name."

Requires send-on-behalf (or delegate) permissions.

Tier 3: Proactive

Operates autonomously on a schedule, executing standing orders without per-action human approval. Humans review output asynchronously.

Combines Tier 2 permissions with Cron Jobs and Standing Orders.

Tier 3 requires hard blocks configured first: actions the agent must never take regardless of instruction. Complete the prerequisites below before granting any identity provider permissions.

Prerequisites: isolation and hardening

Do this first. Lock down the delegate's boundaries before granting credentials or identity provider access. Establish what the agent cannot do before giving it the ability to do anything.

Hard blocks (non-negotiable)

Define these in the delegate's SOUL.md and AGENTS.md before connecting any external accounts:

These rules load every session - the last line of defense regardless of what instructions the agent receives.

Tool restrictions

Use per-agent tool policy to enforce boundaries at the Gateway level, independent of the agent's personality files - even if the agent is instructed to bypass its rules, the Gateway blocks the tool call:

{
  id: "delegate",
  workspace: "~/.apus/workspace-delegate",
  tools: {
    allow: ["read", "exec", "message", "cron"],
    deny: ["write", "edit", "apply_patch", "browser", "canvas"],
  },
}

Sandbox isolation

For high-security deployments, sandbox the delegate agent so it cannot reach the host filesystem or network beyond its allowed tools:

{
  id: "delegate",
  workspace: "~/.apus/workspace-delegate",
  sandbox: {
    mode: "all",
    scope: "agent",
  },
}

See Sandboxing and Multi-Agent Sandbox & Tools.

Audit trail

Configure logging before the delegate handles any real data:

All delegate actions flow through Apus's session store. For compliance, retain and review these logs.

Setting up a delegate

With hardening in place, grant the delegate its identity and permissions.

1. Create the delegate agent

apus agents add delegate --workspace ~/.apus/workspace-delegate

This creates:

Configure the delegate's personality in its workspace files:

2. Configure identity provider delegation

Give the delegate its own account in your identity provider with explicit delegation permissions. Apply least privilege - start with Tier 1 (read-only) and escalate only when the use case demands it.

Microsoft 365

Create a dedicated user account for the delegate (for example delegate@[organization].org).

Send on Behalf (Tier 2):

# Exchange Online PowerShell
Set-Mailbox -Identity "principal@[organization].org" `
  -GrantSendOnBehalfTo "delegate@[organization].org"

Read access (Graph API with application permissions):

Register a Microsoft Entra ID (formerly Azure AD) application with Mail.Read and Calendars.Read application permissions. Before using the application, scope access with an application access policy to restrict it to only the delegate and principal mailboxes:

New-ApplicationAccessPolicy `
  -AppId "<app-client-id>" `
  -PolicyScopeGroupId "<mail-enabled-security-group>" `
  -AccessRight RestrictAccess

Without an application access policy, Mail.Read application permission grants access to every mailbox in the tenant. Create the access policy before the application reads any mail. Test by confirming the app returns 403 for mailboxes outside the security group.

Google Workspace

Create a service account and enable domain-wide delegation in the Admin Console. Delegate only the scopes you need:

https://www.googleapis.com/auth/gmail.readonly    # Tier 1
https://www.googleapis.com/auth/gmail.send         # Tier 2
https://www.googleapis.com/auth/calendar           # Tier 2

The service account impersonates the delegate user (not the principal), preserving the "on behalf of" model.

Domain-wide delegation lets the service account impersonate any user in the domain. Restrict scopes to the minimum required, and limit the service account's client ID to only the scopes above in the Admin Console (Security > API controls > Domain-wide delegation). A leaked service account key with broad scopes grants full access to every mailbox and calendar in the organization. Rotate keys on a schedule and monitor the Admin Console audit log for unexpected impersonation events.

3. Bind the delegate to channels

Route inbound messages to the delegate agent using Multi-Agent Routing bindings:

{
  agents: {
    ownership: "explicit",
    defaults: {
      heartbeat: { agentId: "main" },
      systemAgent: { agentId: "main" },
    },
    entries: {
      main: { workspace: "~/.apus/workspace" },
      delegate: {
        workspace: "~/.apus/workspace-delegate",
        tools: {
          allow: ["read", "exec", "message", "cron"],
          deny: ["write", "edit", "apply_patch", "browser", "canvas"],
        },
      },
    },
  },
  bindings: [
    // Route a specific channel account to the delegate
    {
      agentId: "delegate",
      match: { channel: "whatsapp", accountId: "org" },
    },
    // Route a Discord guild to the delegate
    {
      agentId: "delegate",
      match: { channel: "discord", guildId: "123456789012345678" },
    },
    // Other traffic on these channels goes to the main personal agent.
    { agentId: "main", match: { channel: "whatsapp", accountId: "*" } },
    { agentId: "main", match: { channel: "discord", accountId: "*" } },
  ],
  talk: { agentId: "main" },
}

4. Add credentials to the delegate agent

Agents read shared auth profiles without copying them. To give the delegate an independent provider account, sign in on the Gateway host for that agent:

apus models auth login --provider <providerId> --agent delegate

The login writes the delegate's SQLite auth store at ~/.apus/agents/delegate/agent/apus-agent.sqlite; its local profiles override the shared read-through base. Never share the main agent's agentDir with the delegate. See Auth credential semantics and Multi-Agent Routing for auth isolation details.

Example: organizational assistant

A complete delegate configuration handling email, calendar, and social media:

{
  agents: {
    ownership: "explicit",
    defaults: {
      heartbeat: { agentId: "main" },
      systemAgent: { agentId: "main" },
    },
    entries: {
      main: { workspace: "~/.apus/workspace" },
      "org-assistant": {
        name: "[Organization] Assistant",
        workspace: "~/.apus/workspace-org",
        agentDir: "~/.apus/agents/org-assistant/agent",
        identity: { name: "[Organization] Assistant" },
        tools: {
          allow: ["read", "exec", "message", "cron", "sessions_list", "sessions_history"],
          deny: ["write", "edit", "apply_patch", "browser", "canvas"],
        },
      },
    },
  },
  bindings: [
    {
      agentId: "org-assistant",
      match: { channel: "signal", peer: { kind: "group", id: "[group-id]" } },
    },
    { agentId: "org-assistant", match: { channel: "whatsapp", accountId: "org" } },
    { agentId: "main", match: { channel: "whatsapp", accountId: "*" } },
    { agentId: "main", match: { channel: "signal", accountId: "*" } },
  ],
  talk: { agentId: "main" },
}

The delegate's AGENTS.md defines its autonomous authority - what it may do without asking, what needs approval, and what is forbidden. Cron Jobs drive its daily schedule.

If you grant sessions_history, it is a bounded, safety-filtered recall view, not a raw transcript dump. Apus redacts credential/token-like text, truncates long content, and strips internal scaffolding (thinking-block signatures, <relevant-memories> scaffolding tags, tool-call XML tags such as <tool_call>/<function_calls>, and similar leaked provider control tokens) from assistant recall. Oversized rows can be replaced with [sessions_history omitted: message too large] instead of returning the raw content. Use nextOffset when present to page backward through older transcript windows.

Scaling pattern

  1. Create one delegate agent per organization.
  2. Harden first - tool restrictions, sandbox, hard blocks, audit trail.
  3. Grant scoped permissions via the identity provider (least privilege).
  4. Define standing orders for autonomous operations.
  5. Schedule cron jobs for recurring tasks.
  6. Review and adjust the capability tier as trust builds.

Multiple organizations can share one Gateway server using multi-agent routing - each org gets its own isolated agent, workspace, and credentials.

Related