Skip to content

Email API

The BKR Email API (bkr-email-api) is a FastAPI service that gives the Mansa agents safe, scoped access to the BKR Capital shared mailbox (pipeline@bkrcapital.ca). It wraps Microsoft Graph API behind a clean REST interface with API key authentication.

Layer Technology
Framework FastAPI (Python 3.13+)
Auth Azure AD / MSAL (Microsoft Authentication Library)
HTTP client httpx (async)
Data validation Pydantic v2
Deployment Azure Container Apps

The API uses a two-tier API key system via the X-API-Key request header:

  • Master key (API_KEY): full access to all endpoints
  • Scoped keys (API_KEYS): restricted by scope

Available scopes:

Scope Grants access to
send:email POST /send
download:attachment GET /emails/{id}/attachments/{attachment_id}
Method Path Description
GET /emails List inbox emails. Supports unread_only, past_day, include_labeled, and pagination via next_link
GET /emails/by-category/{category} List emails with a specific category label
GET /emails/search Full-text search across the mailbox (?q=text)
GET /emails/{message_id} Get full email details including body, recipients, and attachments
POST /emails/{message_id}/read Mark an email as read
GET /emails/{message_id}/attachments/{attachment_id} Download an attachment (requires download:attachment scope)
Method Path Description
POST /emails/{message_id}/label Apply a category label (ai-processed or ai-misc)
Method Path Description
GET /drafts List drafts in the Drafts folder
POST /drafts/reply Create a reply-all draft for a specific message
Method Path Description
POST /send Send an email (requires send:email scope)
Method Path Description
GET /health Health check that validates required settings are present

Emails are categorised with Outlook categories to track processing state:

  • ai-processed: the agent handled this email (created/updated Attio records; drafted a pitch deck request if none was included)
  • ai-misc: the agent saw this email but it was not a relevant lead

By default, GET /emails excludes emails that already carry either label. Set include_labeled=true to override. This ensures agents don’t reprocess the same email across runs.

Email bodies are stored as HTML in Outlook. The API converts them to Markdown before returning them to agents, because Markdown is far more token-efficient in a Claude context window. It strips layout-only tables (common in HTML email templates) and falls back to the original HTML if the conversion yields nothing.

Recipients are restricted to @bkrcapital.ca addresses. This prevents the agent from accidentally sending emails to founders or external parties from the send endpoint (founder reply drafts go through the draft flow instead).

Every sent email automatically CCs pipeline@bkrcapital.ca so the full team can see what was sent. A DEBUG_ADDRESS env var can CC a second address during testing.

All list endpoints support top (1–100, default 25) and next_link pagination. The next_link value is an OData cursor returned by Microsoft Graph. When set, other query parameters are ignored and the cursor drives the next page.

The API runs on Azure Container Apps in BKR Capital’s Azure account.

App: mansa-email-api
Region: Canada Central (canadacentral)

Deployment is done manually using the scripts in bkr-email-api/scripts/. Environment variables are managed as Container Apps secrets. See .env.example for the full list of required variables.

The app can be accessed directly in the Azure Portal.

  • 40+ unit tests using pytest + respx (async httpx mocking)
  • End-to-end suite targeting ai-test-labelled emails in the real mailbox (requires live Azure credentials)
Terminal window
# Unit tests
pytest tests/ -v
# E2E tests (requires .env with live credentials)
pytest tests/e2e/ -v -m e2e