Skip to Content
RunbooksRunbook: Agent Identity Provisioning

Runbook: Agent Identity Provisioning

Owner: Platform Team Backup owner: On-call engineer Last validated: 2026-05-15 Validation method: Phase 0 dogfood on curate-me.ai tenant Severity trigger: SEV3 (feature inoperable, not data-loss) Customer impact: Agent emails fall back to noreply@curate-me.ai; no outage Required access: M365 admin center, SSH (VPS), MongoDB Related services: curateme-backend-gateway, Microsoft Graph API Time to complete: ~10 minutes (first provision); ~2 minutes (subsequent orgs)


Agent identities let each org’s AI assistant send email as curate@<org>.curate-me.ai instead of the shared noreply@curate-me.ai address. The assistant appears in the Microsoft 365 directory as a real user — Teams presence, display name, avatar, and all 12 capability aliases routing to the same mailbox.

This runbook covers Phase 0 provisioning (operator creates the M365 user manually, then the platform verifies and activates it via Graph API). Wave A4 will automate user creation through Graph.


Prerequisites

Before starting, confirm you have:

  • M365 admin access — Global Admin or User Admin role in the target tenant
  • Entra ID app registration with these Application permissions granted:
    • Mail.Send
    • User.Read.All
  • Business Basic license available ($6/mo per agent identity)
  • Environment variables set on the VPS (see Step 3)

Step 1: Create the M365 user

  1. Open M365 Admin Center  and navigate to Users > Active users > Add a user
  2. Fill in:
    • First name: Curate-Me
    • Last name: Assistant
    • Display name: Curate-Me Assistant
    • Username: curate@<org-subdomain>.curate-me.ai
  3. Assign a Microsoft 365 Business Basic license
  4. Under Optional settings > Profile:
    • Job title: AI Agent
    • Department: AI Workforce
  5. Click Finish adding and note the Object ID from the user’s profile page (you’ll need it for Step 3)

Verification: The new user should appear in the Microsoft 365 directory within 60 seconds. Search for “Curate-Me” in the admin center to confirm.


Step 2: Add capability aliases

Still in M365 Admin Center, edit the user you just created:

  1. Go to Mail > Manage email aliases
  2. Add each alias (only the ones the org needs; dev-team is the minimum for dogfood):
    • dev-team@<org-subdomain>.curate-me.ai
    • cfo@<org-subdomain>.curate-me.ai
    • security@<org-subdomain>.curate-me.ai
    • (up to 12 total; see the full list in provisioning_service.py)
  3. Save

Verification: Send a test email to dev-team@<org-subdomain>.curate-me.ai from any external address. It should arrive in the user’s mailbox within a few minutes.


Step 3: Set environment variables

On the VPS, add or update these variables in .env.production:

# Entra ID app registration (one-time setup) MICROSOFT_GRAPH_TENANT_ID=<your-tenant-id> MICROSOFT_GRAPH_CLIENT_ID=<your-app-client-id> MICROSOFT_GRAPH_CLIENT_SECRET=<your-app-client-secret> # Per the user you just created M365_AGENT_USER_ID=<object-id-from-step-1> M365_DEFAULT_LICENSE_SKU_ID=<business-basic-sku-id>

To find the license SKU ID:

# Via Graph Explorer or curl: curl -H "Authorization: Bearer $TOKEN" \ "https://graph.microsoft.com/v1.0/subscribedSkus" \ | jq '.value[] | select(.skuPartNumber == "O365_BUSINESS_ESSENTIALS") | .skuId'

Restart the gateway service after updating env vars:

ssh curateme@178.105.8.25 cd deploy && docker compose restart gateway

Step 4: Provision via dashboard

  1. Open Dashboard > Settings > Agent Identity
  2. Set Identity mode to “Curate-Me hosted”
  3. Enter the org subdomain (must match what you used in M365)
  4. Click Provision identity

The system will:

  • Call Graph API to verify the M365 user exists
  • Record the m365_user_id in MongoDB
  • Mark the identity as ACTIVE
  • Log an agent_identity.provisioned audit event

If the identity shows ERROR status, check the error message — most common cause is the M365 user hasn’t finished provisioning yet (wait 2-5 minutes and retry).


Step 5: Verify end-to-end

  1. Still on the Agent Identity settings page, enter your email in the “Send test” field
  2. Click Send test
  3. Check your inbox for an email from curate@<org>.curate-me.ai

What to look for:

  • From address shows the org’s subdomain, not noreply@curate-me.ai
  • Signature includes “Why you got this:” line with the request ID
  • The “Audit:” link in the signature resolves to a real audit log entry (not a 404)

Troubleshooting

Provision returns ERROR immediately

SymptomCauseFix
RuntimeError: user not foundM365 user not yet provisionedWait 2-5 min, retry
RuntimeError: MICROSOFT_GRAPH_* env vars not configuredMissing env vars on VPSSet vars per Step 3, restart gateway
403 Insufficient privilegesGraph app missing permissionsGrant Mail.Send + User.Read.All in Entra ID
401 Invalid client secretClient secret expiredRotate secret in Entra ID, update VPS env

Test email not received

  1. Check spam/junk folder
  2. Verify the alias exists in M365 admin center
  3. Check gateway logs for Graph sendMail errors:
ssh curateme@178.105.8.25 docker logs curateme-gateway --since 5m 2>&1 | grep -i "graph_send_mail\|agent_identity"
  1. Confirm the identity status is ACTIVE in MongoDB:
mongosh "mongodb://localhost:27017/curateme_b2b" --eval \ 'db.agent_identities.findOne({org_id: "<org-id>", template_id: null})'

Identity stuck in PENDING

The provisioning call was interrupted. Safe to retry — POST /agent-identity/provision is idempotent.

Deprovisioning

Phase 0 does not delete the M365 user automatically. To fully remove:

  1. In the dashboard, the identity will show as DEPROVISIONED (soft delete)
  2. Manually delete the user in M365 Admin Center
  3. The identity record stays in MongoDB for audit trail compliance

Cost

  • $6/mo per agent identity (Microsoft 365 Business Basic license)
  • Promoted specialists (Growth tier) add $6/mo each
  • No additional Graph API costs (included in the license)