m365_mail kind collects one folder of one Microsoft 365 mailbox through
Microsoft Graph, for example a shared monitoring mailbox that receives alerts,
press releases or newsletters, through the first-party plugin
plugins/m365-mail (step 3). Read the
Connector Instances overview first: creation, credentials, health
and the change feed work the same way for every kind.
What it collects
-
New mail only. An instance collects mail received from its creation, or
from
backfill_sincewhen set (at most 7 days before its first poll). Mail already in the folder before that point is not collected. -
One Record per mail. The Record Key is the mail’s
internetMessageId. When a mail has none, the key isgraph:followed by its immutable Graph id. -
Parts of each Record Version:
Each attachment Part keeps the attachment’s media type and its verified SHA-256 checksum. Its
connector.m365_mail.attachmentextension records the file name, size, inline flag and type (file, oritemfor an attached mail or event stored as MIME). Attachment contents are stored, not extracted: search covers the subject and the body only. -
Headers. The
connector.m365_mailextension (schema version1) holds:from,sender,to,cc;subject,sent_at,received_at;conversation_id,internet_message_id,graph_id,folder;to_countandcc_count: the full recipient counts.toandcckeep at most the first 100 recipients each;attachments_skipped: attachments that were not stored, each with areason. The reasons aretoo_large(over 25 MB, announced or found while downloading),reference_attachment(a link to a cloud file, not a file) andempty.
- What does not create a new Version. Read/unread changes and flags. A genuine change to a mail’s content or attachments becomes a correction.
- What is never withdrawn. Moving or deleting a mail in the mailbox leaves its Record untouched. Withdraw it through the ingestion API if needed.
- Register an application in Microsoft Entra ID
A tenant administrator performs these steps once per organization.
- In the Microsoft Entra admin center, open App registrations > New registration. Choose a descriptive name, single tenant. No redirect URI is needed.
- Note the Directory (tenant) ID and the Application (client) ID.
-
Under Certificates & secrets, add a credential:
- Certificate (recommended). Upload the public certificate. Keep the certificate and its RSA private key in PEM form for step 4.
- Client secret. Copy the secret value when it is shown; it cannot be displayed again.
- Restrict the application to the monitored mailboxes
Quivr needs to read mail, including bodies and attachments. The Graph
permission for that is Mail.Read, which lets an application read every
mailbox in the tenant unless it is scoped. Scope it to the monitored
mailboxes with one of these two methods.
Recommended: RBAC for Applications in Exchange Online. Run these in
Exchange Online PowerShell as an Exchange administrator:
Mail.Read to the application in Entra
ID. Grants from Entra ID and from Exchange RBAC add up, so a tenant-wide
Entra grant would cancel the scope.
Alternative: an application access policy. This older mechanism, which
Microsoft is replacing with RBAC for Applications, works like this:
- Grant the
Mail.Readapplication permission under API permissions. - Grant admin consent.
- Restrict it to a mail-enabled security group containing the monitored
mailboxes:
mailbox_access_denied.
- Deployment settings
The kind comes from the connector.m365_mail plugin: build plugins/m365-mail
(go build), run it beside the worker and pin it in QUIVR_CONFIG
(run a connector plugin). make dev,
make verify and the Railway image pin it already. By default it talks to the
global Microsoft cloud; the pin configuration overrides both endpoints, for
example for a national cloud:
m365 block refuses to start: move its endpoints into the pin configuration.
Instances, checkpoints, credentials and health carry over, with no duplicate
and no second download. Unpinned, instances report unsupported_connector_kind.
- Create the instance
For a certificate, use
certificate_pem (the -----BEGIN CERTIFICATE-----
block) and private_key_pem (an RSA key, PKCS #8 or PKCS #1). Quivr signs a
short-lived client assertion with it and never sends the key.
- Rotate the secret or certificate
- In Entra ID, add a new secret or certificate while the old one is still valid.
-
Deposit it:
-
Once health is
activeagain, delete the old credential in Entra ID.
Health codes and troubleshooting
GET /v0/connectors/{connector_id} reports health. Access problems set
access_error until a later successful poll. Transient problems show only in
last_error.
Other behaviour:
- Retry-After. Short delays (up to 10 seconds) are waited within the run.
- Delta resync. When Graph invalidates the folder’s delta state (
410 Gone,syncStateNotFound), the connector re-reads the folder over the collection window, bounded to the last 7 days, without creating duplicates. - What is logged. Secrets, tokens, addresses and subjects are never logged; logs carry the connector ID and codes only.
Limits
- 25 MB per stored attachment. Larger attachments are listed as
too_large. - One folder per instance, without subfolders. Create one instance per folder, each with its own Source Namespace.
- A run reads up to 10 pages of 10 mails, stores up to 200 MB of attachments and starts no new page after 2 minutes. A larger backlog continues on the next run.
- Attachment text (PDF, Office documents) is not extracted yet.
Optional check against a real tenant
make verify uses a local fake of Graph (scripts/fake_graph.py) and never
contacts Microsoft. To check a real test tenant by hand, register an
application scoped to a test mailbox, run make dev with the plugin pinned to
the public cloud, create an instance, send a mail with an attachment, and
follow GET /v0/changes?corpus_id=… until the Record and its attachment Blob
appear. Keep the tenant ID, mailbox and credential out of the repository.