We build custom applications 5x faster and cheaper 🚀
Book a Free Consultation
Stuck on an error? Book a 30-minute call with an engineer and get a direct fix + next steps. No pressure, no commitment.
You can integrate Outlook (Microsoft Graph) with OpenClaw by registering an Azure AD application, choosing the right permission model (delegated vs application), implementing OAuth token exchange and secure token storage outside the agent, exposing an external webhook/receiver for Graph change notifications, and then wiring an OpenClaw skill (configured in ClawHub) to call Graph REST APIs using those credentials. Keep long‑lived state and inbound webhook endpoints outside the agent runtime, refresh tokens securely, handle subscription renewal and missed-notification reconciliation (delta queries), and debug via Graph API responses and logs.
<b>//</b> Express route to handle OAuth callback and exchange code for tokens
const express = require('express');
const axios = require('axios');
const app = express();
app.get('/auth/callback', async (req, res) => {
<b>//</b> req.query.code is the authorization code from Azure AD
const code = req.query.code;
try {
const params = new URLSearchParams();
params.append('client_id', process.env.AZURE_CLIENT_ID);
params.append('scope', 'https://graph.microsoft.com/.default offline_access');
params.append('redirect_uri', process.env.AZURE_REDIRECT_URI);
params.append('grant_type', 'authorization_code');
params.append('code', code);
params.append('client_secret', process.env.AZURE_CLIENT_SECRET); <b>//</b> for confidential clients
const tokenResp = await axios.post(
'https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token',
params.toString(),
{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
);
const { access_token, refresh_token, expires_in } = tokenResp.data;
<b>//</b> Persist tokens in a secure store (key vault, database encrypted)
// saveTokensForUser(userId, { access_token, refresh_token, expires_at: Date.now() + expires_in*1000 });
res.send('Authentication successful. You can close this window.');
} catch (err) {
console.error('token exchange', err.response?.data || err.message);
res.status(500).send('Token exchange failed');
}
});
<b>//</b> Example: create a webhook subscription for the signed-in user's mail
curl -X POST "https://graph.microsoft.com/v1.0/subscriptions" \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"changeType": "created,updated,deleted",
"notificationUrl": "https://your-public-service.example.com/webhook",
"resource": "me/messages",
"expirationDateTime": "2026-03-10T11:23:00.000Z",
"clientState": "expected_client_state"
}'
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.
1
The direct cause is either bad client credentials (wrong/rotated secret, wrong tenant, or certificate) producing invalid_client, or missing/ungranted application permissions in Azure AD producing insufficient privileges. OpenClaw’s Admin Portal only sends what you configure — it won’t auto-grant consent or fix secrets.
Quick checks:
Run a direct token request to isolate OpenClaw config:
```bash // request app-only token curl -X POST https://login.microsoftonline.com/
2
The short answer: Microsoft Graph sends a validationToken request to the subscription's public HTTPS notificationUrl and expects an immediate 200 with the token echoed as plain text. Subscriptions created by the OpenClaw Sync Agent fail because Microsoft cannot successfully reach or get the exact response from the agent's webhook receiver (URL not public, TLS/certificate issues, wrong response format, or timeout), so validation never completes and notifications are never delivered.
3
Direct answer: Duplicate or missing messages happen when the deltaLink (the state token Graph returns) your OpenClaw Sync Agent uses is out of sync with the mailbox state — typically from race conditions, non-atomic persistence, wrong scope (folder vs mailbox), parallel workers sharing a token, or failure to handle delta reset responses — causing replays or skipped changes.
Common reasons:
// fetch changes const resp = await fetch(deltaUrl); // process items... // then atomically store newDeltaLink await storeDeltaLink(mailboxId, resp['@odata.deltaLink']);
4
Direct answer: Mailbox mapping usually fails because the OAuth token and permission type don't match how Exchange exposes shared/delegated mailboxes. Mail.Read and Mail.Read.Shared are delegated scopes (require a user present and that user's access to the shared mailbox) and won't let an app-only client access a shared mailbox. Also the shared mailbox often needs explicit Exchange-level full_access delegation or an application access policy plus admin consent.
Common causes:
This prompt helps an AI assistant understand your setup and guide you through the fix step by step, without assuming technical knowledge.
From startups to enterprises and everything in between, see for yourself our incredible impact.
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.Â