Skip to main content

Microsoft Outlook ExecModule

Overview

MicrosoftOutlookModule connects ValkyrAI workflows to Outlook mail and calendars through the fixed Microsoft Graph v1.0 API. Its 13 operations cover message discovery, governed email delivery, draft lifecycle, message moves, calendar discovery, expanded event windows, and guarded event changes. The module uses ValkyrAI's native map I/O ExecModule ABI and keeps the OAuth access token inside one READY IntegrationAccount.

The module builds only https://graph.microsoft.com/v1.0 production requests, supports delegated me routing and explicit mailbox-email routing for app-only access, asks Graph for immutable IDs, bounds requests to 512 KiB and responses to 5 MiB, exposes one page per execution, retries transient failures only for GET, and never automatically retries mail or calendar writes.

The official n8n Microsoft Outlook v2 node informed the resource selection: calendars, drafts, events, folders, messages, and attachments. This ValkyrAI version prioritizes the composable mail/draft/event core and adds strict IntegrationAccount handling, payload allowlists, OData bounds, immutable-ID normalization, destructive confirmation, concurrency headers, and credential-safe errors.

Usage

  1. Register or select a Microsoft Entra application for the intended delegated or app-only access model.
  2. Grant only the mail and calendar permissions needed by the selected operations.
  3. Put the resulting OAuth access token in the encrypted apiKey or password field of a ValkyrAI IntegrationAccount and set the account to READY.
  4. Bind the account through ExecModuleConfig.authConfig.integrationAccount.
  5. Use mailbox: "me" for delegated OAuth, or a valid mailbox email for app-only routing.
  6. Choose one operation and supply only its conditional inputs.
  7. When hasMore is true, pass nextCursor unchanged to the next execution.

Inputs

NameTypeRequiredDescriptionConstraints
operationstringYesOne of the 13 operations below.Exact allowlist.
mailboxstringNoMailbox selector.me or a valid email; defaults to IntegrationAccount.accountId, then me.
resourceIdstringConditionalMessage, draft, or event ID.Non-empty, no control characters, at most 2,048 characters.
folderIdstringNoFolder for list_messages.ID or well-known name; default inbox.
destinationFolderIdstringConditionalFolder for move_message.ID or well-known name.
calendarIdstringNoCalendar for event operations.primary or an opaque calendar ID.
payloadobjectConditionalMessage or event fields.Non-empty, at most 30 top-level fields and 512 KiB; operation-specific allowlist; credential-like fields rejected recursively.
saveToSentItemsbooleanNoSave send_message in Sent Items.Default true.
confirmDeletebooleanConditionalDestructive guard.Must be true for delete_event.
etagstringNoOptimistic concurrency value.Sent as If-Match for event update/delete.
filterstringNoOData $filter.At most 4,096 characters; no control characters.
searchstringNoOutlook $search.list_messages only; at most 2,048 characters.
orderBystringNoOData $orderby.At most 1,024 characters.
selectstringNoResponse projection.Up to 50 comma-separated Graph field names.
timeMin / timeMaxstringConditionalExpanded recurring-event window.Both required together; RFC3339 with offsets; timeMax after timeMin.
cursorstringNoModule-issued continuation.token: or skip: form returned as nextCursor; at most 4,096 characters.
limitintegerNoGraph page size.1–1,000; default 100.

Message create/send payload fields are subject, body, toRecipients, ccRecipients, bccRecipients, replyTo, importance, categories, isRead, and inferenceClassification. send_message requires a bounded subject, a Text or HTML body, and 1–100 valid toRecipients. Draft create/update accepts partial message composition while still rejecting unknown or credential-like fields.

Event payload fields are subject, body, start, end, location, locations, attendees, recurrence, reminderMinutesBeforeStart, isReminderOn, responseRequested, sensitivity, showAs, categories, allowNewTimeProposals, transactionId, isOnlineMeeting, and onlineMeetingProvider. Create requires a subject plus Graph start and end objects containing bounded dateTime and timeZone values.

Outputs

NameTypeWhen presentDescription
statusstringAlwayssuccess or error.
operationstringAlwaysNormalized operation name.
resourcestringAfter validationmessages, drafts, calendars, or events.
dataobjectSingle-resource or accepted no-content successGraph object or bounded acceptance evidence.
idstringGraph object successProvider resource ID.
itemsarrayList successOne bounded page from Graph's value array.
countintegerList successNumber of returned items.
hasMorebooleanList successWhether Graph returned a supported continuation.
nextCursorstringWhen another page existsOpaque token: or skip: continuation.
httpStatusintegerProvider successGraph response status.
attemptsintegerProvider call or errorHTTP attempts consumed.
errorobjectFailureRedacted code, message, and retryable.

Mail and calendar outputs are classified restricted. Tokens, provider response bodies, subjects, recipients, and event details are never copied into progress logs.

IntegrationAccount Requirements

SettingRequirement
ProviderMicrosoft Graph Outlook API v1.0
AuthenticationOAuth 2.0 bearer access token in encrypted apiKey or password
StatusExactly READY
accountIdOptional default mailbox email for app-only routing
RelationshipExecModuleConfig.authConfig.integrationAccount

Least-privilege delegated permissions are normally Mail.Read for read-only message workflows, Mail.ReadWrite for draft/move workflows, Mail.Send for delivery, Calendars.Read for read-only calendar workflows, and Calendars.ReadWrite for event changes. App-only permissions require administrator consent and can affect any mailbox allowed by the tenant's application-access policy; bind a deliberately constrained Entra application. Token acquisition, refresh, tenant consent, and application-access-policy administration remain outside the module.

Configuration

Illustrative persisted configuration:

{
"version": "1.0.0",
"authConfig": {
"authStrategy": 1,
"integrationAccount": "integration-account:microsoft-outlook-sales"
},
"payloadConfig": {
"parameters": "{\"mailbox\":\"me\",\"operation\":\"list_messages\",\"folderId\":\"inbox\",\"filter\":\"isRead eq false\",\"select\":\"id,subject,from,receivedDateTime\",\"limit\":100}"
}
}

The relationship is symbolic. The generated IntegrationAccount owns the encrypted token; never persist it in payload, filters, examples, WorkflowState, or logs.

Operations

OperationGraph routeBehavior
list_messagesGET /{mailbox}/mailFolders/{folderId}/messagesBounded OData page and continuation.
get_messageGET /{mailbox}/messages/{resourceId}Reads one immutable-ID message.
send_messagePOST /{mailbox}/sendMailSends one message; no automatic retry.
create_draftPOST /{mailbox}/messagesCreates one draft.
update_draftPATCH /{mailbox}/messages/{resourceId}Updates allowlisted draft fields.
send_draftPOST /{mailbox}/messages/{resourceId}/sendSends an existing draft once.
move_messagePOST /{mailbox}/messages/{resourceId}/moveMoves the message and accepts Graph's new immutable ID.
list_calendarsGET /{mailbox}/calendarsLists calendars.
list_eventsGET /{mailbox}/calendar/events or calendarViewLists events; a time window expands recurring instances.
get_eventGET /{mailbox}/calendar/events/{resourceId}Reads one event.
create_eventPOST /{mailbox}/calendar/eventsCreates one event.
update_eventPATCH /{mailbox}/calendar/events/{resourceId}Updates one event, optionally with If-Match.
delete_eventDELETE /{mailbox}/calendar/events/{resourceId}Requires confirmDelete=true; optionally uses If-Match.

/{mailbox} is /me for delegated access or /users/{email} for an explicit mailbox. A non-primary calendar uses /calendars/{calendarId} instead of /calendar.

Errors and Failure Modes

FailureCauseRecovery
VALIDATION_ERRORMissing/invalid field, unsafe payload key, malformed recipient, bad mailbox/time/cursor, exceeded bound, delete not confirmed, or response-shape mismatch.Correct the request; validation failures send no provider call.
INTEGRATION_ACCOUNT_REQUIREDNo secure account is bound.Bind the intended Outlook account.
INTEGRATION_ACCOUNT_NOT_READYAccount status is not READY.Repair consent/token state before executing.
MICROSOFT_GRAPH_HTTP_400/404Graph rejected a field, filter, folder, calendar, or ID.Correct the provider resource or payload.
MICROSOFT_GRAPH_HTTP_401/403Token expired, consent is missing, mailbox routing is disallowed, or permissions are insufficient.Refresh the account and grant only the required permission.
MICROSOFT_GRAPH_HTTP_409/412Conflict or stale ETag.Read the resource again and intentionally reconcile.
MICROSOFT_GRAPH_HTTP_429Graph throttling.Reads honor bounded Retry-After; writes remain single-attempt.
MICROSOFT_GRAPH_HTTP_5xx / NETWORK_ERRORProvider or transport failure.GET retries up to three attempts. Reconcile mail/draft/event state before manually retrying a write.
RESPONSE_TOO_LARGEResponse exceeded 5 MiB.Reduce limit, select, filter scope, or event window.
Unsupported continuationGraph returned a next link without $skiptoken or numeric $skip.Narrow the query and report the provider compatibility issue.

Provider errors are bounded to 500 characters, have the exact token removed, and pass through ValkyrAI's sensitive-data redactor. A timeout after a write is ambiguous; the module never assumes the write failed.

Example

Send one approved HTML follow-up:

{
"operation": "send_message",
"mailbox": "me",
"saveToSentItems": true,
"payload": {
"subject": "Your ValkyrAI workspace is ready",
"body": {
"contentType": "HTML",
"content": "<p>Your onboarding workspace is ready. Reply with a preferred review time.</p>"
},
"toRecipients": [
{
"emailAddress": {
"address": "buyer@example.com"
}
}
],
"importance": "normal"
}
}

Expected normalized result:

{
"status": "success",
"operation": "send_message",
"resource": "messages",
"data": {
"accepted": true
},
"httpStatus": 202,
"attempts": 1
}

Notes

  • Pagination: each execution returns one provider page. Reuse nextCursor exactly; the module does not follow arbitrary URLs or silently exhaust a mailbox.
  • Rate limits: Graph throttles per app, tenant, mailbox, and resource. Numeric Retry-After is bounded to five seconds per safe-read attempt; writes remain single-attempt.
  • API limits: page size is capped at 1,000, request JSON at 512 KiB, response JSON at 5 MiB, recipients at 100 per supported array, and projection fields at 50.
  • Idempotency: only GET is automatically retried. Message send, draft create/update/send, move, and event create/update/delete can complete ambiguously and must be reconciled by immutable ID, Sent Items, or calendar read-back.
  • Destructive behavior: delete_event permanently removes the event and can notify attendees according to Graph behavior, so it requires explicit confirmation. Message deletion, mail-folder mutation, contact mutation, attachments, replies, webhooks, subscriptions, delta queries, online-meeting lifecycle, and automatic token refresh are deferred.
  • Search: $search is limited to message listing. Callers are responsible for a Graph-valid expression; local bounds prevent control characters and oversized queries.
  • Calendar views: supplying both timeMin and timeMax switches to calendarView, expanding recurring occurrences in that bounded window.
  • External verification: deterministic tests cover routing, bearer authentication, payload construction, recipient validation, pagination, calendar windows, event concurrency/delete guards, safe-read retries, write non-retry, redaction, response identities, and metadata discovery. Live Outlook execution is deferred until a separately authorized Microsoft account is supplied.

See Microsoft's Outlook mail API overview, Outlook calendar API overview, Graph throttling guidance, and the official n8n Microsoft Outlook v2 node for upstream behavior and current provider limits.