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:
It now also shows an explicit tenant/account-type hint. This is especially important when Microsoft returns unauthorized_client: that usually means the Azure app registration exists, but its supported account types do not match the selected tenant mode.
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.
Preferred shared Microsoft environment values:
MICROSOFT_TENANT=common
MICROSOFT_CLIENT_ID=
MICROSOFT_CLIENT_SECRET=
MICROSOFT_REDIRECT_URI=
MICROSOFT_DEFAULT_SCOPES="offline_access openid profile User.Read Tasks.ReadWrite"
Legacy Microsoft To Do-specific aliases are still accepted too:
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/callback/oauth/microsoft-todo/callbackThe web page now also shows a direct clickable auth-start link for browser testing/copying when the platform app is complete. That link uses:
/oauth/microsoft/start-link/oauth/microsoft-todo/start-linkoauth.microsoft_todo.start/oauth/microsoft-todo/startPOSTauth:weboauth.microsoft.start_link/oauth/microsoft/start-linkoauth.microsoft_todo.start_link/oauth/microsoft-todo/start-linkGETauth:webThis route exists so the Microsoft To Do integration page can show one normal clickable shared Microsoft auth link in addition to the POST form button. Each visit creates a fresh signed OAuth state and then redirects directly to Microsoft.
oauth.microsoft.callback/oauth/microsoft/callbackoauth.microsoft_todo.callback/oauth/microsoft-todo/callbackGETThe callback can now still finish the connection even if the browser no longer has an active Tools web session, as long as the signed Microsoft state matches the original user/transaction. When that happens, Tools now recreates the matching web session from the signed state before it redirects back to the Microsoft To Do integration page, so the user does not land on a fresh login wall after a successful callback.
GET /api/microsoft/auth/statusReturns the generic Microsoft auth/platform-app status for the authenticated user.
Intended use:
The response can include additive fields such as:
provider="microsoft"platform="microsoft_graph"auth_readycallback_urlcallback_legacy_urlsupported_integrations[]planned_integrations[]platform_app.supports_personal_accountsplatform_app.supports_work_or_school_accountsplatform_app.account_support_hintThese 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_urlrecommended_callback_legacy_urlrecommended_start_link_urldefault_scopesaccount_support_hintsupports_personal_accountssupports_work_or_school_accountsmissing_fields[]status_messageExample response:
{
"ok": true,
"connected": true,
"app_available": true,
"connect_web_url": "/settings/integrations/microsoft-todo",
"connect_api_url": "/api/microsoft/oauth/start",
"connect_api_legacy_url": "/api/microsoft-todo/oauth/start",
"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
}
}
GET /api/microsoft/oauth/startBuilds one Microsoft authorization URL for the currently authenticated user.
Compatibility note:
GET /api/microsoft-todo/oauth/start path still works/oauth/microsoft/callback, while /oauth/microsoft-todo/callback remains available as a legacy aliasIntended use:
Example response:
{
"ok": true,
"authorization_url": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?...",
"callback_url": "https://tools.tornevall.com/oauth/microsoft-todo/callback",
"connect_web_url": "/settings/integrations/microsoft-todo",
"connect_api_url": "/api/microsoft/oauth/start",
"connect_api_legacy_url": "/api/microsoft-todo/oauth/start"
}
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/settings/integrations/microsoft-todo.GET /api/microsoft/oauth/start and then open that URL in a browser surface. The older GET /api/microsoft-todo/oauth/start path remains available as a compatibility alias.GET /api/microsoft/auth/status instead of starting from the To Do-specific status endpoint.common or consumers and make sure the Azure app registration itself allows personal accounts (MSA).organizations (or a specific tenant GUID/domain). The status/auth diagnostics now also report whether the current tenant mode supports work/school accounts.