Mobility 2.0 API Documentation

Back to Dashboard

Mobility 2.0 Graph Sync API

REST API for synchronizing Microsoft 365 data (Mail, Calendar, Contacts, To Do, Planner) via Microsoft Graph API with delta sync support.

BASE URL http://localhost:4000
GET Read data POST Create / trigger actions PATCH Partial update PUT Replace / configure DELETE Remove data
GET /api/search Global search across all entities

Searches across Users, Messages, Calendar Events, Contacts, and Planner Tasks in parallel. Returns grouped results limited per entity.

Query Parameters
NameTypeDescription
q requiredstringSearch term (matched via LIKE against entity fields)
limit optionalintegerMax results per entity (default: 5, max: 20)
Response (200)
{ "users": [ { "Id": "abc-123", "DisplayName": "John Doe", "Mail": "john@company.com" } ], "messages": [ { "GraphId": "AAMk...", "UserId": "abc-123", "Subject": "Meeting" } ], "events": [ { "GraphId": "AAMk...", "UserId": "abc-123", "Subject": "Standup" } ], "contacts": [ { "GraphId": "AAMk...", "UserId": "abc-123", "DisplayName": "Jane" } ], "plannerTasks": [ { "GraphId": "task-123", "Title": "Design mockups", "PercentComplete": 50 } ] }
Authentication
GET /auth/login Redirect to Microsoft login

Redirects the user to Microsoft's OAuth 2.0 authorization endpoint. After login, Microsoft redirects back to /auth/callback.

Query Parameters
NameTypeDescription
state optionalstringOpaque value for CSRF protection
Response

302 Redirect to Microsoft login page

GET /auth/callback OAuth callback handler

Handles the OAuth 2.0 callback from Microsoft. Exchanges the authorization code for tokens, creates/updates the user in the database, and stores tokens.

Response (200)
{ "message": "Authentication successful", "userId": "abc-123-def", "displayName": "John Doe" }
GET /auth/status/:userId Check token status for a user

Returns the current authentication/token status for a user.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
Response (200)
{ "authenticated": true, "isValid": true, "hasRefreshToken": true, "expiresAt": "2025-01-15T10:30:00.000Z", "scope": "User.Read Mail.Read ..." }
POST /auth/logout/:userId Soft-delete user tokens

Soft-deletes the stored tokens for a user (marks them inactive).

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
Response (200)
{ "message": "Tokens removed", "userId": "abc-123-def" }
Users
GET /api/users List all synced users

Returns all Microsoft 365 users that have been synced to the local database.

Response (200)
[ { "Id": "abc-123", "DisplayName": "John Doe", "Mail": "john@company.com", "JobTitle": "Developer", "Department": "Engineering", "AccountEnabled": true } ]
GET /api/users/:userId Get single user profile

Returns complete profile for a single user.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
Response (200)
{ "Id": "abc-123", "DisplayName": "John Doe", "GivenName": "John", "Surname": "Doe", "Mail": "john@company.com", "UserPrincipalName": "john@company.onmicrosoft.com", "JobTitle": "Developer", "Department": "Engineering", "MobilePhone": "+1234567890", "City": "San Juan", "Country": "Puerto Rico", "AccountEnabled": true }
GET /api/users/lookup/:email Find user by email (auto-fetch from Graph)

Searches for a user by email in the local database. If not found, fetches the user from Microsoft Graph API and saves them locally. Returns the user with a source field indicating where the data came from.

Path Parameters
NameTypeDescription
email requiredstringEmail address or UserPrincipalName
Response (200)
{ "Id": "abc-123", "DisplayName": "John Doe", "Mail": "john@company.com", "JobTitle": "Developer", "Department": "Engineering", "source": "local" // or "graph" if fetched from Microsoft }
Error (404)
{ "error": true, "message": "User not found: john@company.com" }
Sync
POST /api/sync/all Sync all users

Triggers a full sync for all registered users. Syncs mail, calendar, contacts, and to-do for each user.

Response (200)
{ "message": "Bulk sync completed", "results": { "user-id-1": { "mail": { ... }, "calendar": { ... } }, "user-id-2": { "error": "Token expired" } } }
POST /api/sync/user/:userId Full sync for a user

Triggers a full sync of all entities (mail, calendar, contacts, todo) for a single user. Uses delta links if available.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
Response (200)
{ "message": "Sync completed", "userId": "abc-123", "results": { ... } }
POST /api/sync/user/:userId/:entity Sync specific entity

Triggers sync for a specific entity type for a user.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
entity requiredstringEntity type: mail, calendar, contacts, todo
Response (200)
{ "message": "Entity sync completed", "userId": "abc-123", "entity": "mail", "results": { ... } }
GET /api/sync/status/:userId Get sync status for a user

Returns the sync status for each entity type, including last sync timestamp and whether a delta link exists.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
Response (200)
{ "userId": "abc-123", "syncStatus": { "messages": { "lastSyncedAt": "2025-01-15T10:30:00.000Z", "hasDeltaLink": true }, "events": { "lastSyncedAt": "2025-01-15T10:30:00.000Z", "hasDeltaLink": false } } }
DELETE /api/sync/delta/:userId/:entity Delete delta link (force full re-sync)

Deletes the stored delta link for a specific entity. The next sync will be a full sync instead of an incremental delta sync.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
entity requiredstringEntity type: messages, events, contacts, todoTasks
Response (200)
{ "message": "Delta link deleted — next sync will be a full sync", "userId": "abc-123", "entity": "messages" }
POST /api/sync/discover-users Discover all tenant users from Azure AD

Discovers all users from Azure AD using app-only (Client Credentials) flow. No user OAuth required. Creates/updates user records in the local database.

Response (200)
{ "message": "Users discovered from Azure AD", "results": { "synced": 12 } }
POST /api/sync/contacts/all Sync contacts for all users (app-only)

Discovers users and syncs all contacts using app-only flow. No user OAuth required.

Response (200)
{ "message": "Contacts synced for all users (app-only)", "results": { "contacts": { "user-1": 45, "user-2": 23 } } }
Mail
GET /api/mail/:userId/folders List mail folders

Returns all synced mail folders for a user (Inbox, Sent Items, Drafts, etc.).

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
Response (200)
[ { "GraphId": "AAMk...", "DisplayName": "Inbox", "TotalItemCount": 142, "UnreadItemCount": 5 } ]
GET /api/mail/:userId/messages List messages (paginated)

Returns synced messages for a user with optional filtering and pagination.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
Query Parameters
NameTypeDescription
folderId optionalstringFilter by folder Graph ID
isRead optionalbooleanFilter by read status
offset optionalintegerPagination offset (default: 0)
limit optionalintegerPage size (default: 50)
Response (200)
[ { "GraphId": "AAMk...", "Subject": "Meeting Tomorrow", "FromName": "Jane Smith", "FromAddress": "jane@company.com", "ReceivedDateTime": "2025-01-15T10:30:00Z", "IsRead": false, "HasAttachments": true, "Importance": "normal" } ]
GET /api/mail/:userId/messages/:graphId Message detail with recipients

Returns a single message with its recipients (To, Cc, Bcc).

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
graphId requiredstringMessage Graph ID
Response (200)
{ "GraphId": "AAMk...", "Subject": "Meeting Tomorrow", "BodyPreview": "Hi team, let's discuss...", "recipients": [ { "RecipientType": "to", "Name": "Bob", "EmailAddress": "bob@company.com" } ] }
GET /api/mail/:userId/messages/:graphId/attachments List message attachments

Returns all attachments for a specific message.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
graphId requiredstringMessage Graph ID
Response (200)
[ { "Name": "document.pdf", "ContentType": "application/pdf", "Size": 24576 } ]
POST /api/mail/:userId/sendMail Send a new email

Sends a new email via Microsoft Graph on behalf of the user.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
Request Body
{ "to": "jane@company.com", "cc": "bob@company.com", "subject": "Meeting notes", "body": "<p>Here are the notes...</p>", "contentType": "html", "importance": "normal" }
Response (200)
{ "message": "Mail sent successfully" }
POST /api/mail/:userId/messages/:graphId/reply Reply to a message

Sends a reply to an existing message.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
graphId requiredstringMessage Graph ID
Request Body
{ "comment": "Thanks, I'll review this." }
POST /api/mail/:userId/messages/:graphId/forward Forward a message

Forwards an existing message to new recipients.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
graphId requiredstringMessage Graph ID
Request Body
{ "to": "bob@company.com", "comment": "FYI — see below." }
PATCH /api/mail/:userId/messages/:graphId Update message (read/unread, importance)

Updates message properties like read status or importance via Microsoft Graph.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
graphId requiredstringMessage Graph ID
Request Body
{ "isRead": true, "importance": "high" }
Calendar
GET /api/calendar/:userId/calendars List calendars

Returns all synced calendars for a user.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
Response (200)
[ { "GraphId": "AAMk...", "Name": "Calendar", "Color": "auto", "IsDefaultCalendar": true } ]
GET /api/calendar/:userId/events List events (paginated, filterable)

Returns synced calendar events with optional date range filtering and pagination.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
Query Parameters
NameTypeDescription
start optionaldateStart date filter (ISO format)
end optionaldateEnd date filter (ISO format)
offset optionalintegerPagination offset (default: 0)
limit optionalintegerPage size (default: 50)
Response (200)
[ { "GraphId": "AAMk...", "Subject": "Team Standup", "StartDateTime": "2025-01-15T09:00:00Z", "EndDateTime": "2025-01-15T09:30:00Z", "LocationDisplayName": "Room 101", "IsAllDay": false } ]
GET /api/calendar/:userId/events/:graphId Event detail with attendees

Returns a single event with its attendees list.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
graphId requiredstringEvent Graph ID
Response (200)
{ "GraphId": "AAMk...", "Subject": "Team Standup", "OrganizerName": "John Doe", "IsOnlineMeeting": true, "attendees": [ { "Name": "Jane Smith", "EmailAddress": "jane@company.com", "Type": "required", "ResponseStatus": "accepted" } ] }
POST /api/calendar/:userId/events Create a calendar event

Creates a new calendar event via Microsoft Graph, with optional attendees.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
Request Body
{ "subject": "Team Meeting", "start": "2025-02-01T09:00:00", "end": "2025-02-01T10:00:00", "timeZone": "America/Puerto_Rico", "location": "Room 101", "body": "Agenda: Q1 review", "isOnlineMeeting": true, "attendees": "jane@company.com, bob@company.com" }
Contacts
GET /api/contacts/:userId/folders List contact folders

Returns all synced contact folders for a user.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
Response (200)
[ { "GraphId": "AAMk...", "DisplayName": "Contacts" } ]
GET /api/contacts/:userId List contacts (with search)

Returns synced contacts with optional search and pagination.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
Query Parameters
NameTypeDescription
search optionalstringSearch by name or email
offset optionalintegerPagination offset (default: 0)
limit optionalintegerPage size (default: 50)
Response (200)
[ { "GraphId": "AAMk...", "DisplayName": "Jane Smith", "CompanyName": "Acme Corp", "JobTitle": "Manager", "Department": "Sales" } ]
GET /api/contacts/:userId/:graphId Contact detail with emails & phones

Returns a single contact with their email addresses and phone numbers.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
graphId requiredstringContact Graph ID
Response (200)
{ "GraphId": "AAMk...", "DisplayName": "Jane Smith", "CompanyName": "Acme Corp", "emails": [ { "Type": "work", "Address": "jane@acme.com" } ], "phones": [ { "Type": "mobile", "Number": "+1234567890" } ] }
POST /api/contacts/:userId Create a new contact

Creates a new contact in the user's default contact folder via Microsoft Graph.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
Request Body
{ "givenName": "Jane", "surname": "Smith", "emailAddresses": [{ "address": "jane@acme.com" }], "companyName": "Acme Corp", "jobTitle": "Manager", "mobilePhone": "+1234567890" }
POST /api/contacts/:userId/:graphId/ai-summary Generate AI summary of contact relationship

Generates a professional AI summary of the relationship with a contact based on recent emails, calendar events, and Planner tasks. Uses Claude CLI for the AI generation.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
graphId requiredstringContact Graph ID
Response (200)
{ "summary": "Jane Smith is a key contact at Acme Corp...", "contact": { "DisplayName": "Jane Smith", ... }, "dataUsed": { "emails": 5, "events": 3, "tasks": 2 } }
To Do
GET /api/todo/:userId/lists List task lists

Returns all synced To Do task lists for a user.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
Response (200)
[ { "GraphId": "AAMk...", "DisplayName": "My Tasks", "IsOwner": true, "IsShared": false } ]
GET /api/todo/:userId/lists/:listId/tasks List tasks in a list

Returns tasks within a specific task list with optional status filtering.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
listId requiredstringTask list Graph ID
Query Parameters
NameTypeDescription
status optionalstringFilter: notStarted, inProgress, completed
offset optionalintegerPagination offset (default: 0)
limit optionalintegerPage size (default: 50)
Response (200)
[ { "GraphId": "AAMk...", "Title": "Review PR #42", "Status": "inProgress", "Importance": "high", "DueDateTime": "2025-01-20T00:00:00Z" } ]
GET /api/todo/:userId/tasks/:graphId Task detail with checklist & linked resources

Returns a single task with its checklist items and linked resources.

Path Parameters
NameTypeDescription
userId requiredstringGraph user ID
graphId requiredstringTask Graph ID
Response (200)
{ "GraphId": "AAMk...", "Title": "Review PR #42", "Status": "inProgress", "Body": "Check the auth module changes", "checklistItems": [ { "DisplayName": "Review code", "IsChecked": true }, { "DisplayName": "Run tests", "IsChecked": false } ], "linkedResources": [ { "DisplayName": "PR #42", "ApplicationName": "GitHub", "WebUrl": "https://github.com/..." } ] }
Planner
GET /api/planner/groups List M365 groups

Returns all synced M365 Unified Groups from the tenant. Planner plans belong to groups.

Response (200)
[ { "GraphId": "abc-123", "DisplayName": "Marketing Team", "Description": "Marketing department group", "Mail": "marketing@company.com", "Visibility": "Public" } ]
GET /api/planner/groups/:groupId/plans Plans for a group

Returns all plans belonging to a specific M365 group.

Path Parameters
NameTypeDescription
groupId requiredstringM365 Group Graph ID
Response (200)
[ { "GraphId": "plan-123", "Title": "Q1 Campaign", "GroupGraphId": "abc-123", "CreatedDateTime": "2025-01-10T08:00:00Z" } ]
GET /api/planner/plans All plans (with optional filter)

Returns all synced plans, optionally filtered by group.

Query Parameters
NameTypeDescription
groupId optionalstringFilter by M365 Group Graph ID
Response (200)
[ { "GraphId": "plan-123", "Title": "Q1 Campaign", "GroupGraphId": "abc-123", "Owner": "abc-123", "CreatedDateTime": "2025-01-10T08:00:00Z" } ]
GET /api/planner/plans/:planId/buckets Buckets for a plan

Returns all buckets (columns) in a plan, ordered by their order hint.

Path Parameters
NameTypeDescription
planId requiredstringPlan Graph ID
Response (200)
[ { "GraphId": "bucket-123", "Name": "To Do", "PlanGraphId": "plan-123", "OrderHint": "8585..." } ]
GET /api/planner/plans/:planId/tasks Tasks for a plan (filterable, paginated)

Returns tasks in a plan with optional filtering by bucket and completion status.

Path Parameters
NameTypeDescription
planId requiredstringPlan Graph ID
Query Parameters
NameTypeDescription
bucketId optionalstringFilter by bucket Graph ID
status optionalstringFilter: notStarted, inProgress, completed
offset optionalintegerPagination offset (default: 0)
limit optionalintegerPage size (default: 50)
Response (200)
[ { "GraphId": "task-123", "Title": "Design mockups", "BucketGraphId": "bucket-123", "PercentComplete": 50, "Priority": 1, "DueDateTime": "2025-02-15T00:00:00Z" } ]
GET /api/planner/tasks/:taskId Task detail with assignments & checklist

Returns a single Planner task with its assignments and checklist items.

Path Parameters
NameTypeDescription
taskId requiredstringTask Graph ID
Response (200)
{ "GraphId": "task-123", "Title": "Design mockups", "PercentComplete": 50, "Priority": 1, "DescriptionContent": "Create wireframes for the new dashboard", "DueDateTime": "2025-02-15T00:00:00Z", "assignments": [ { "UserId": "user-abc-123", "OrderHint": "8585..." } ], "checklistItems": [ { "ItemId": "item-1", "Title": "Desktop layout", "IsChecked": true }, { "ItemId": "item-2", "Title": "Mobile layout", "IsChecked": false } ] }
PATCH /api/planner/tasks/:taskId Update task status or assignees

Updates a Planner task's status (percentComplete) or assignees via Microsoft Graph. Requires the task's ETag for concurrency control (fetched automatically).

Path Parameters
NameTypeDescription
taskId requiredstringTask Graph ID
Request Body
{ "percentComplete": 100, "assignees": ["jane@company.com", "bob@company.com"] }
POST /api/planner/tasks Create a new Planner task

Creates a new task in a Planner plan, optionally assigning it to a bucket and users.

Request Body
{ "planId": "plan-123", "title": "Design mockups", "bucketId": "bucket-123", "assignees": ["jane@company.com"], "dueDateTime": "2025-02-15" }
POST /api/sync/planner/all Full Planner sync (app-only)

Triggers a full sync of all Planner data: M365 Groups, Plans, Buckets, Tasks (with assignments and checklist items). Uses app-only (Client Credentials) flow — no user OAuth required. Planner does not support delta sync.

Response (200)
{ "message": "Planner sync completed (app-only)", "results": { "groups": 5, "plans": 12, "buckets": 36, "tasks": 148 } }
Subscriptions (Webhooks)
GET /api/subscriptions List all active subscriptions

Returns all active Graph webhook subscriptions. Optionally filter by user.

Query Parameters
NameTypeDescription
userId optionalstringFilter subscriptions by user ID
Response (200)
{ "subscriptions": [ { "SubscriptionId": "sub-abc-123", "Resource": "/users/abc/messages", "ResourceType": "mail", "ChangeType": "created,updated,deleted", "ExpirationDateTime": "2025-01-17T10:00:00Z", "UserId": "abc-123" } ], "count": 1, "webhookUrl": "https://abc123.ngrok.io/api/subscriptions/notifications" }
POST /api/subscriptions/create-all Subscribe to all services

Creates Graph webhook subscriptions for all resource types (mail, calendar, contacts, chat messages) for one or all users. Automatically starts an ngrok tunnel if no webhook URL is configured. Existing subscriptions are renewed instead of duplicated.

Request Body optional
{ "userId": "abc-123", // optional — omit for all users "notificationUrl": "https://..." // optional — override webhook URL }
Response (200)
{ "message": "Subscription creation completed", "notificationUrl": "https://abc.ngrok.io/api/subscriptions/notifications", "results": { "created": [{ "user": "John Doe", "type": "Mail", "subscriptionId": "sub-123" }], "skipped": [{ "user": "Jane", "type": "Mail", "action": "renewed" }], "errors": [] } }
POST /api/subscriptions/create/:resourceType Subscribe to a specific service

Creates subscriptions for a specific resource type only.

Path Parameters
NameTypeDescription
resourceType requiredstringOne of: mail, calendar, contacts, chatMessages
Request Body optional
{ "userId": "abc-123" } // optional — omit for all users
POST /api/subscriptions/renew-all Renew all subscriptions

Extends the expiration of all active subscriptions by 2 days. Expired subscriptions that fail renewal are automatically cleaned up.

Response (200)
{ "message": "Subscription renewal completed", "results": { "renewed": 8, "failed": 0, "expired": 0 } }
DELETE /api/subscriptions/:subscriptionId Delete a specific subscription

Deletes a subscription from both Microsoft Graph and the local database.

Path Parameters
NameTypeDescription
subscriptionId requiredstringGraph subscription ID
DELETE /api/subscriptions Delete all subscriptions

Deletes all subscriptions from Graph and the local database. Optionally filter by user.

Query Parameters
NameTypeDescription
userId optionalstringDelete only subscriptions for this user
GET /api/subscriptions/webhook-url Get configured webhook URL

Returns the currently configured webhook notification URL and its source (custom or ngrok).

Response (200)
{ "webhookUrl": "https://my-domain.com/api/subscriptions/notifications", "ngrokUrl": null, "source": "custom" // "custom", "ngrok", or "none" }
PUT /api/subscriptions/webhook-url Set or clear webhook URL

Configures a custom HTTPS webhook URL. Send null to clear and fall back to ngrok auto-tunnel.

Request Body
{ "webhookUrl": "https://my-domain.com/api/subscriptions/notifications" }
Clear webhook (use ngrok):
{ "webhookUrl": null }
GET /api/subscriptions/tunnel-status Check ngrok tunnel status

Returns the current state of the ngrok HTTPS tunnel and configured webhook URL.

Response (200)
{ "tunnelUrl": "https://abc123.ngrok.io", "active": true, "webhookUrl": "https://abc123.ngrok.io/api/subscriptions/notifications", "usingCustomUrl": false, "source": "ngrok" }
POST /api/subscriptions/notifications Webhook callback (receives Graph notifications)

Webhook endpoint called by Microsoft Graph when subscribed resources change. Handles both the initial validation handshake (returns validationToken) and incoming change notifications. Do not call this directly — it is invoked by Microsoft.

Validation (query param)
// Graph sends: POST /api/subscriptions/notifications?validationToken=abc123 // Server responds: 200 "abc123" (text/plain)
Notification payload
{ "value": [ { "subscriptionId": "sub-abc-123", "changeType": "created", "resource": "users/abc/messages/AAMk...", "clientState": "secret-token" } ] }
Mobility 2.0 Graph Sync API — 48 Endpoints — Built with Express.js + Microsoft Graph