Tools now includes a personal Microsoft To Do integration for authenticated users.
/api/microsoft-todo/* for apps and other clients./settings/integrations/microsoft-todoauth:webFrom this page a user can:
The shared platform Azure app can now be handled in two ways:
MICROSOFT_TODO_* values, or/settings/integrations/microsoft-todo by an acknowledged admin.Environment values still take precedence when they are present.
The page now shows a configuration diagnostics panel with:
When the platform app is not environment-managed, acknowledged admins can save/update the shared Microsoft To Do platform app directly from that page through an AJAX form. The client secret is never echoed back; the page only reports whether a secret is already stored.
Environment values remain:
MICROSOFT_TODO_TENANT=common
MICROSOFT_TODO_CLIENT_ID=
MICROSOFT_TODO_CLIENT_SECRET=
MICROSOFT_TODO_REDIRECT_URI=
MICROSOFT_TODO_DEFAULT_SCOPES="offline_access openid profile User.Read Tasks.ReadWrite"
The callback URL for the current environment is shown on the web page and is also available via the route:
/oauth/microsoft-todo/callbackoauth.microsoft_todo.start/oauth/microsoft-todo/startPOSTauth:weboauth.microsoft_todo.callback/oauth/microsoft-todo/callbackGETauth:webThese endpoints accept either:
POST /api/account/login.GET /api/microsoft-todo/statusReturns connection state, account display name, local counts, and whether the platform app is available.
The response now also includes additive platform_app diagnostics with non-secret configuration metadata such as:
managed_viaenv_managedclient_id_configuredclient_secret_configuredtenantredirect_urirecommended_callback_urldefault_scopesmissing_fields[]status_messageExample response:
{
"ok": true,
"connected": true,
"app_available": true,
"connect_web_url": "/settings/integrations/microsoft-todo",
"connection": {
"status": "connected",
"status_label": "Connected",
"status_description": "Connected to Microsoft account \"user@example.com\".",
"provider_account_name": "user@example.com",
"expires_at": "2026-03-30T13:15:00+00:00",
"last_connected_at": "2026-03-30T12:58:00+00:00"
},
"counts": {
"lists": 4,
"tasks": 22,
"dirty_lists": 0,
"dirty_tasks": 1
}
}
POST /api/microsoft-todo/syncRuns an immediate push/pull synchronization.
Example response:
{
"ok": true,
"message": "Microsoft To Do sync complete.",
"sync": {
"ok": true,
"lists_pushed": 1,
"tasks_pushed": 2,
"lists_pulled": 4,
"tasks_pulled": 22,
"errors": []
}
}
GET /api/microsoft-todo/listsReturns local mirrored lists. By default tasks are included.
Optional query parameters:
include_tasks=1 (default)include_tasks=0POST /api/microsoft-todo/listsCreate a list and sync it to Microsoft To Do.
Request body:
{
"display_name": "Work"
}
PATCH /api/microsoft-todo/lists/{listId}Rename a synced list.
Request body:
{
"display_name": "Work Projects"
}
DELETE /api/microsoft-todo/lists/{listId}Delete the list both locally and remotely.
GET /api/microsoft-todo/lists/{listId}/tasksReturns the tasks for one local mirrored list.
POST /api/microsoft-todo/lists/{listId}/tasksCreate a task in the given list.
Request body:
{
"title": "Follow up with customer",
"body_text": "Remember to attach the report.",
"importance": "high",
"status": "notStarted",
"due_at": "2026-04-02 09:00:00",
"reminder_at": "2026-04-02 08:30:00"
}
PATCH /api/microsoft-todo/tasks/{taskId}Update a task.
Request body:
{
"title": "Follow up with customer",
"body_text": "Report sent, waiting for reply.",
"importance": "normal",
"status": "inProgress",
"due_at": "2026-04-03 09:00:00",
"reminder_at": null
}
DELETE /api/microsoft-todo/tasks/{taskId}Delete a task both locally and remotely.
The synchronization model is intentionally simple and predictable:
A scheduled sync also runs automatically every 15 minutes through:
php artisan microsoft-todo:sync/api/microsoft-todo/* endpoints.