Get your dream built 10x faster

How to integrate Outlook with OpenClaw

We build custom applications 5x faster and cheaper 🚀

Book a Free Consultation
4.9
Clutch rating 🌟
600+
Happy partners
17+
Countries served
190+
Team members
Matt Graham, CEO of Rapid Developers

Book a call with an Expert

Stuck on an error? Book a 30-minute call with an engineer and get a direct fix + next steps. No pressure, no commitment.

How to integrate Outlook with OpenClaw

This is how to integrate Outlook (Microsoft 365 mail via Microsoft Graph) with OpenClaw in a correct, production-ready way: register an Azure AD app and implement the OAuth 2.0 authorization-code flow and refresh/token storage outside the agent; create and renew Microsoft Graph webhook subscriptions (change notifications) to notify your service; implement a secure webhook endpoint that completes Microsoft’s validation handshake and quickly enqueues notification processing; configure your OpenClaw skill in ClawHub with only the non-sensitive configuration (or references to secrets stored in a vault) so the agent can call your secure service endpoints or short-lived REST functions; and keep durable state (refresh tokens, subscription expiry, queues, background jobs) external to the agent runtime. Below I walk the architecture, concrete steps, security and operational practices, debugging checklist, and minimal, real code examples for the OAuth flow, token refresh, Graph calls, and subscription creation.

 

Architecture overview

 
  • External services (required): an HTTPS server for OAuth callbacks and webhook endpoints, a secure persistent store (database or secret manager) for refresh tokens and subscription metadata, and a queue or background worker to process notifications and long-running tasks.
  • OpenClaw agent/skill (what it does): invokes your service endpoints or performs short-lived Graph API calls using access tokens that your service provides. Agent code should not be relied on for durable storage, long-running subscriptions, or scheduled refresh jobs.
  • Authentication: use Azure AD OAuth 2.0 authorization-code flow for user / tenant consent. Store refresh\_tokens securely outside the agent and refresh access tokens server-side.
  • Notifications: use Microsoft Graph subscriptions (webhooks). The webhook endpoint validates the subscription handshake and then enqueues notifications for asynchronous processing.

 

Step-by-step integration plan

 
  • 1) Register the Azure AD app
    • Register an app in the Azure portal (App registrations). Record client_id, tenant_id, and create a client\_secret (or client certificate) for server-side token exchanges.
    • Set redirect URI(s) to the HTTPS callback endpoint you control (not an agent URL unless it is reachable and secure).
    • Request scopes your integration needs, e.g. Mail.ReadWrite, Mail.Send, offline\_access, plus openid and profile if you need identity info.
  • 2) Implement OAuth 2.0 authorization-code flow (server-side)
    • Send users to the authorize URL so they can sign in and consent: the user will return to your callback with a code.
    • Exchange the code for tokens at the token endpoint. Persist the refresh\_token and associated account metadata in your database; do NOT store secrets only inside the agent runtime.
  • 3) Use and refresh access tokens server-side
    • Use access tokens to call Microsoft Graph from your server or from a short-lived skill invocation that receives the token from your server.
    • When access_token expires, use refresh_token at the token endpoint to get a new access_token and possibly a new refresh_token; update your store.
  • 4) Create and manage Graph subscriptions (webhooks)
    • Create subscriptions via Graph REST API for resources like me/messages or tenant-level resources if using application permissions.
    • Implement the validation handshake: Microsoft will call your notificationUrl with a validation token; your endpoint must return the token as documented by Microsoft Graph.
    • Subscriptions expire; schedule a job outside the agent to renew subscriptions before expiration.
  • 5) Process notifications asynchronously
    • Webhook handler should perform minimal work: validate the request, enqueue a job containing the notification payload or a pointer (resource id), and respond 200 quickly.
    • Background worker dequeues jobs, acquires the appropriate access token (refresh if needed), and calls Graph to retrieve the full resource if necessary.
  • 6) Configure the OpenClaw skill in ClawHub
    • Put only non-sensitive configuration in ClawHub or point the skill at secrets stored in a secret manager (e.g., vault, KMS). Avoid baking client\_secret or refresh tokens into agent runtime images.
    • Skill code should call your server endpoints (e.g., to request an access token for a user or to ask your server to send mail on behalf of a user). The skill should be stateless and short-lived.

 

OAuth REST examples (real, standard endpoints)

 
  • Authorization URL (user sign-in & consent):
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id={CLIENT_ID}
&response_type=code
&redirect_uri={REDIRECT_URI}
&response_mode=query
&scope=offline_access%20openid%20profile%20Mail.ReadWrite
&state={CSRF_STATE}
  • Token exchange (POST to exchange code for tokens):
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

client_id={CLIENT_ID}
&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default
&code={AUTHORIZATION_CODE}
&redirect_uri={REDIRECT_URI}
&grant_type=authorization_code
&client_secret={CLIENT_SECRET}
  • Refresh token request:
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

client_id={CLIENT_ID}
&grant_type=refresh_token
&refresh_token={REFRESH_TOKEN}
&client_secret={CLIENT_SECRET}
  • Call Microsoft Graph with access token:
GET https://graph.microsoft.com/v1.0/me/messages
Authorization: Bearer {ACCESS_TOKEN}
Accept: application/json

 

Creating a subscription (Graph REST example)

 
  • POST to create a subscription:
POST https://graph.microsoft.com/v1.0/subscriptions
Content-Type: application/json
Authorization: Bearer {ACCESS_TOKEN}

{
  "changeType": "created,updated",
  "notificationUrl": "https://your.example.com/webhook",
  "resource": "me/messages",
  "expirationDateTime": "2026-03-10T18:23:45.9356913Z",
  "clientState": "random-secret-for-verification"
}
  • Your webhook endpoint must respond to Microsoft Graph's validation handshake by returning the validation token exactly as Microsoft requests (see Microsoft Graph docs).

 

Minimal server example (Node.js + Express) — OAuth callback, token exchange, webhook handler

 
const express = require('express');
const fetch = require('node-fetch');
const bodyParser = require('body-parser');

const app = express();
app.use(bodyParser.json());

const CLIENT_ID = process.env.CLIENT_ID;
const CLIENT_SECRET = process.env.CLIENT_SECRET;
const TENANT = process.env.TENANT_ID;
const REDIRECT_URI = process.env.REDIRECT_URI;

/b> // Exchange code for tokens (OAuth callback) */
app.get('/oauth/callback', async (req, res) => {
  const code = req.query.code;
  /b> // Exchange code for tokens */
  const params = new URLSearchParams();
  params.append('client_id', CLIENT_ID);
  params.append('scope', 'https://graph.microsoft.com/.default');
  params.append('code', code);
  params.append('redirect_uri', REDIRECT_URI);
  params.append('grant_type', 'authorization_code');
  params.append('client_secret', CLIENT_SECRET);

  const tokenResp = await fetch(`https://login.microsoftonline.com/${TENANT}/oauth2/v2.0/token`, {
    method: 'POST',
    body: params
  });
  const tokenJson = await tokenResp.json();
  /b> // Persist tokenJson.refresh_token securely in your DB keyed to the user/account */
  res.send('OK');
});

/b> // Webhook endpoint: validate and enqueue notifications */
app.post('/webhook', async (req, res) => {
  /b> // Microsoft Graph validation: if validation token present, echo it (see Graph docs) */
  if (req.query && req.query.validationToken) {
    return res.status(200).send(req.query.validationToken);
  }

  /b> // Validate clientState or other fields, then enqueue for async processing */
  const notifications = req.body.value || [];
  for (const note of notifications) {
    /b> // Enqueue note (resource, subscriptionId, tenantId) to your queue for processing */
  }

  res.sendStatus(202);
});

/b> // Example background job: fetching a message with stored refresh token */
async function fetchMessageForUser(userId, messageId) {
  /b> // 1) Lookup refresh_token for userId from DB */
  /b> // 2) Ensure you have a valid access_token (refresh if needed) */
  /b> // 3) Call Graph to GET https://graph.microsoft.com/v1.0/users/{userId}/messages/{messageId} */
}

 

Security and operational best practices

 
  • Never store refresh tokens or client secrets in agent images or local files. Use a secrets manager and give your agent only the minimal permission to read secrets at runtime if necessary.
  • Use HTTPS everywhere; webhook endpoints must be public HTTPS for Microsoft Graph to reach them.
  • Verify webhook notifications using clientState and by optionally calling back to Graph to confirm the resource (defense-in-depth).
  • Renew subscriptions before they expire. Store subscription IDs and expiration times in your DB and run a scheduled job to renew them.
  • Respond to webhooks quickly: do minimal validation, enqueue, return 200/202. Heavy processing should be done in background workers to avoid timeouts and retries.
  • Scope least privilege: request only the Graph scopes you need and document what each scope is used for.

 

What belongs in the OpenClaw skill vs. external service

 
  • Inside the skill/agent: stateless logic that performs a short-lived Graph call when invoked (e.g., fetch a message summary), or code that calls your service’s API to request an action. The skill can be the front-end for agent-driven interactions but should not host webhooks or hold tokens long-term.
  • External: OAuth callback endpoints, long-lived refresh token storage, webhook listener, subscription management, scheduled renewals, queues, and background workers. These require uptime, persistence, and operational tooling that are outside an agent's transient runtime.

 

Debugging and operational checklist

 
  • Check the OAuth flow: ensure redirect\_uri matches exactly what is registered in Azure App Registration.
  • Check token endpoint responses for errors and inspect error\_description.
  • Confirm required scopes were consented to by the user/tenant.
  • Check Graph subscription creation responses and store subscriptionId and expirationDateTime.
  • If no notifications arrive, verify Microsoft Graph’s delivery status (change notification delivery reports) and that your webhook returned the validation token correctly during subscription creation.
  • Inspect webhook logs: validate that the request came from Microsoft IPs or validate with clientState and/or call Graph to confirm the resource.
  • If agent calls fail, log the exact HTTP request/response, including status codes and Graph error bodies (never log secrets), and confirm the access\_token used was still valid and had correct scopes.

Book Your Free 30‑Minute Migration Call

Speak one‑on‑one with a senior engineer about your no‑code app, migration goals, and budget. In just half an hour you’ll leave with clear, actionable next steps—no strings attached.

Book a Free Consultation

Troubleshooting Outlook and OpenClaw Integration

1

OpenClaw Outlook connector failing OAuth 2.0 authentication with "invalid_client" or "consent_required" errors?

Direct answer: "invalid_client" or "consent_required" means the Outlook OAuth app registration or consent flow is misconfigured — usually wrong client_id/client_secret, mismatched redirect URI, missing admin consent, or using the wrong OAuth flow for how the OpenClaw skill is configured. Fix the app registration and consent, ensure OpenClaw skill uses the exact credentials and callback, and re-run the auth flow.

 

Diagnostics & fixes

 

  • Check credentials: verify OUTLOOK_CLIENT_ID and OUTLOOK_CLIENT_SECRET in the skill’s environment vars (exact values from Azure AD).
  • Redirect URI: ensure the skill’s OAuth redirect in ClawHub/OpenClaw matches the app registration URI exactly (scheme, path).
  • Consent & scopes: grant admin consent for required scopes in Azure portal or instruct users to consent; missing consent → consent_required.
  • Flow type: use confidential client (client_secret) for server skills, PKCE for public clients; token endpoint auth must match registration.
  • Logs: inspect skill auth logs, token endpoint responses, and Azure sign-in logs for precise error codes.

2

OpenClaw calendar sync to Outlook missing events or attendees due to Microsoft Graph delegated vs application permission configuration?

 

Direct answer

 

The missing events/attendees usually mean you used the wrong Graph permission type. Delegated tokens act as a user (invitations appear and attendees are populated); app‑only (client credentials) acts as the app and must target a specific mailbox, have the correct Application permissions, and be allowed by an Application Access Policy. Also verify your event payload includes correct attendees.emailAddress.address.

 

Diagnostics & fix steps

 
  • Check token type: delegated vs app‑only and where it was obtained in OpenClaw skill config.
  • Confirm Azure AD scopes: Delegated: Calendars.ReadWrite; Application: Calendars.ReadWrite (app permissions) and App Access Policy for mailbox access.
  • Use correct endpoint: /me/events (delegated) or /users/{id}/events (app‑only).
  • Validate attendee JSON (emailAddress.address and name).
POST https://graph.microsoft.com/v1.0/users/{userId}/events
Authorization: Bearer {token}
Content-Type: application/json
<b>//</b> minimal body with attendees
{
  "subject":"Meeting",
  "start":{"dateTime":"2026-03-10T10:00:00","timeZone":"UTC"},
  "end":{"dateTime":"2026-03-10T11:00:00","timeZone":"UTC"},
  "attendees":[{"emailAddress":{"address":"[email protected]","name":"Alice"},"type":"required"}]
}

3

OpenClaw Microsoft Graph subscription notifications not received for mail/calendar webhooks, showing validation or subscription renewal failures?

 

Direct answer

 

The usual causes are a failed validation handshake (Graph sends a validationToken you must echo as plain text with 200 quickly), an unreachable or non‑TLS public endpoint, incorrect permissions or expired tokens when creating/renewing subscriptions, or renewal calls that fail. Check OpenClaw skill logs, Graph API responses, and subscription objects for expirationDateTime.

 

Troubleshooting steps & example

 
  • Confirm validation: echo validationToken as text/plain within seconds.
  • Ensure public TLS endpoint and correct certificate.
  • Check permissions (delegated/app) and OAuth tokens; renew before expiry.
  • Inspect logs: OpenClaw skill execution, Graph create/renew responses and HTTP 4xx/5xx.
const express = require('express')
const app = express()
app.get('/webhook', (req, res) => {
  <b>//</b> Graph validation uses query param validationToken
  const token = req.query.validationToken
  if(token) return res.type('text/plain').status(200).send(token)
  res.sendStatus(204)
})

4

OpenClaw Outlook integration hitting Microsoft Graph 429 rate limits and producing duplicate items during retries/backoff?

Short answer: handle Graph 429s with proper rate‑limit handling (read Retry‑After and Graph headers), use exponential backoff with jitter, and make operations idempotent by deduplicating retries via client-side operation IDs stored outside the agent (DB) or using stable resource IDs/ETags; move retry and state logic out of transient OpenClaw agents into a hosted worker.

 

Key actions

 
  • Respect Retry-After and x-ms.* headers and back off with jitter.
  • Make requests idempotent — attach an operation ID, persist it, and skip duplicate work.
  • Keep state out of agents (DB/queue) so retries don’t create duplicates.
const opId = generateId();
// // persist opId before calling Graph
await db.save(opId,{status:'pending'});
const res = await fetch(url,{method:'POST',headers:{'Idempotency-Key':opId}});
if(res.status===429){await sleep(retryAfter*1000); /* retry using opId */}
Book a Free Consultation

Still stuck?
Copy this prompt into ChatGPT and get a clear, personalized explanation.

This prompt helps an AI assistant understand your setup and guide you through the fix step by step, without assuming technical knowledge.

AI AI Prompt


Recognized by the best

Trusted by 600+ businesses globally

From startups to enterprises and everything in between, see for yourself our incredible impact.

RapidDev was an exceptional project management organization and the best development collaborators I've had the pleasure of working with.

They do complex work on extremely fast timelines and effectively manage the testing and pre-launch process to deliver the best possible product. I'm extremely impressed with their execution ability.

Arkady
CPO, Praction
Working with Matt was comparable to having another co-founder on the team, but without the commitment or cost.

He has a strategic mindset and willing to change the scope of the project in real time based on the needs of the client. A true strategic thought partner!

Donald Muir
Co-Founder, Arc
RapidDev are 10/10, excellent communicators - the best I've ever encountered in the tech dev space.

They always go the extra mile, they genuinely care, they respond quickly, they're flexible, adaptable and their enthusiasm is amazing.

Mat Westergreen-Thorne
Co-CEO, Grantify
RapidDev is an excellent developer for custom-code solutions.

We’ve had great success since launching the platform in November 2023. In a few months, we’ve gained over 1,000 new active users. We’ve also secured several dozen bookings on the platform and seen about 70% new user month-over-month growth since the launch.

Emmanuel Brown
Co-Founder, Church Real Estate Marketplace
Matt’s dedication to executing our vision and his commitment to the project deadline were impressive. 

This was such a specific project, and Matt really delivered. We worked with a really fast turnaround, and he always delivered. The site was a perfect prop for us!

Samantha Fekete
Production Manager, Media Production Company
The pSEO strategy executed by RapidDev is clearly driving meaningful results.

Working with RapidDev has delivered measurable, year-over-year growth. Comparing the same period, clicks increased by 129%, impressions grew by 196%, and average position improved by 14.6%. Most importantly, qualified contact form submissions rose 350%, excluding spam.

Appreciation as well to Matt Graham for championing the collaboration!

Michael W. Hammond
Principal Owner, OCD Tech

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We’ll discuss your project and provide a custom quote at no cost.Â