← Back to docs

Microsoft To Do

Microsoft To Do

Tools now includes a personal Microsoft To Do integration for authenticated users.

What it does

  • Connects your Microsoft account to Tools through Microsoft OAuth.
  • Mirrors your Microsoft To Do lists into Tools.
  • Mirrors your Microsoft To Do tasks into Tools.
  • Pushes changes made in Tools back to Microsoft To Do.
  • Pulls remote changes from Microsoft To Do back into Tools.
  • Reuses one shared Microsoft / Microsoft Graph auth app that is meant to stay reusable for broader Microsoft / Entra-backed integrations later too.
  • Exposes authenticated JSON endpoints under /api/microsoft-todo/* for apps and other clients.

Web UI

  • Path: /todo (short alias) and /settings/integrations/microsoft-todo
  • Auth: auth:web

From this page a user can:

  • connect their Microsoft account,
  • run a manual sync,
  • create, rename, and delete lists,
  • create, edit, complete, and delete tasks,
  • disconnect the local Microsoft To Do connection.

Configuration

The shared Microsoft app is configured at host/admin level (typically through environment values). The user-facing To Do page is intentionally per-user and focuses on connecting/disconnecting each user's own Microsoft account.

If you want to reuse one Entra app across both Copilot and Microsoft To Do, the config now also accepts shared fallback aliases:

ENTRA_TENANT_ID=
ENTRA_CLIENT_ID=
ENTRA_CLIENT_SECRET=

MICROSOFT_* keys remain the preferred explicit keys for this integration, but when they are empty Tools can now fall back to ENTRA_* (and then COPILOT_*) for tenant/client credentials.

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

The 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 routes

Start OAuth flow

  • Route name: oauth.microsoft.start
  • Path: /oauth/microsoft/start
  • Method: POST
  • Auth: auth:web

Direct auth-start link

  • Route name: oauth.microsoft.start_link
  • Path: /oauth/microsoft/start-link
  • Method: GET
  • Auth: auth:web

This 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 callback

  • Route name: oauth.microsoft.callback
  • Path: /oauth/microsoft/callback
  • Method: GET
  • Auth: public callback URL (the original signed OAuth state is still required)

The 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/status

Returns the generic Microsoft auth/platform-app status for the authenticated user.

Intended use:

  • clients that want one shared Microsoft auth readiness/status surface,
  • native/mobile callers that need callback/start URLs and tenant/account-type diagnostics,
  • future Microsoft / Graph integrations that should not start from a To Do-specific status contract.

The response can include additive fields such as:

  • provider="microsoft"
  • platform="microsoft_graph"
  • auth_ready
  • callback_url
  • callback_legacy_url
  • supported_integrations[]
  • planned_integrations[]
  • platform_app.supports_personal_accounts
  • platform_app.supports_work_or_school_accounts
  • platform_app.account_support_hint

API endpoints

These endpoints accept either:

  • a normal logged-in web session, or
  • a JWT bearer token from POST /api/account/login.

GET /api/microsoft-todo/status

Returns 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_via
  • env_managed
  • client_id_configured
  • client_secret_configured
  • tenant
  • redirect_uri
  • recommended_callback_url
  • recommended_callback_legacy_url
  • recommended_start_link_url
  • default_scopes
  • account_support_hint
  • supports_personal_accounts
  • supports_work_or_school_accounts
  • missing_fields[]
  • status_message

Example 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/start

Builds one Microsoft authorization URL for the currently authenticated user.

Compatibility note:

  • the older GET /api/microsoft-todo/oauth/start path still works
  • the new generic path is now the recommended API entrypoint when the same Microsoft app registration is intended to serve the wider Tools platform and not only the To Do UI wording
  • the returned callback now points at the generic browser callback /oauth/microsoft/callback

Intended use:

  • native/mobile clients that authenticate against Tools first,
  • browser-based helpers that need the current OAuth start URL from the API,
  • clients that want to open the Microsoft consent page in a WebView/Custom Tab instead of relying only on the Tools web form.

Example response:

{
  "ok": true,
  "authorization_url": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?...",
  "callback_url": "https://tools.tornevall.com/oauth/microsoft/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/sync

Runs 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/lists

Returns local mirrored lists. By default tasks are included.

Optional query parameters:

  • include_tasks=1 (default)
  • include_tasks=0

POST /api/microsoft-todo/lists

Create 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}/tasks

Returns the tasks for one local mirrored list.

POST /api/microsoft-todo/lists/{listId}/tasks

Create 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.

Sync behavior

The synchronization model is intentionally simple and predictable:

  1. Local dirty lists are pushed first.
  2. Local dirty tasks are pushed second.
  3. The remote Microsoft To Do snapshot is pulled back into Tools.
  4. Remote deletes are reflected locally when the local row is not dirty.
  5. Local dirty rows are not overwritten by the pull step.

A scheduled sync also runs automatically every 15 minutes through:

  • php artisan microsoft-todo:sync

Notes

  • Web users can still start the connect flow from /settings/integrations/microsoft-todo.
  • API/native clients can now request the current Microsoft OAuth URL through 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.
  • Clients that need one generic Microsoft auth readiness surface can now also call GET /api/microsoft/auth/status instead of starting from the To Do-specific status endpoint.
  • If you expect personal Microsoft accounts, use tenant common or consumers and make sure the Azure app registration itself allows personal accounts (MSA).
  • If you expect Microsoft Entra work/school accounts only, use organizations (or a specific tenant GUID/domain). The status/auth diagnostics now also report whether the current tenant mode supports work/school accounts.
  • If Microsoft token refresh fails, the user may need to reconnect.