Klicke hier um ein unverbindliches Erstgespräch buchen!

Microsoft Loop zu HTML Export — Technical Guide

So exportiert man alle Microsoft Loop Workspace-Seiten als HTML Files über die Microsoft Graph API.


Einleitung

Microsoft Loop ist ein attraktives Tool für kollaboratives Note-Taking. Es ist in den meisten Microsoft 365 Plans ohne Aufpreis enthalten, integriert sich nativ in Teams und Outlook und erledigt seinen Job für Meeting Notes, Projekt-Docs und Knowledge Bases. Für kleine und mittelgroße Organisationen, die ohnehin für M365 zahlen, ist es schwer, die Kosten für ein dediziertes Tool wie Notion oder Confluence zu rechtfertigen, wenn Loop einfach da ist.

Bis man raus will.

Das Problem

Loop hat keine Export API. Es gibt keinen "Export Workspace" Button. Keinen Bulk Download. Keinen dokumentierten programmatischen Zugriff auf die eigenen Inhalte. Als wir unseren Workspace migrieren wollten — über 1.000 Loop Pages, angesammelt über zwei Jahre täglicher Nutzung — wurde schnell klar, dass die offensichtlichen Wege alle Sackgassen sind:

  • Manuelles Copy-Paste: Nicht machbar bei 1.000+ Seiten. Selbst bei 2 Minuten pro Seite sind das über 33 Stunden stumpfe Arbeit.

  • PDF Export: Loop bietet einen "Export to PDF" pro Seite an. Der Output verliert sämtliche Struktur, Links und Formatierung, die ihn in einem Zielsystem brauchbar machen würden. Für eine Migration ist PDF ein Format zum Sterben, nicht zum Starten.

  • Power Automate: Diverse Community Posts schlagen Power Automate Flows vor, um auf Loop Content zuzugreifen. Wir haben das ausprobiert — die Flows bestätigten, dass Loop Files programmatisch über SharePoints Storage Layer erreichbar sind, aber Power Automates Per-File Trigger Model und Throttling Limits machen es für Bulk Exports unpraktikabel. Was es uns aber gegeben hat, war der entscheidende Hint: Loop Pages werden in SharePoint Embedded Containern gespeichert und sind über die Microsoft Graph API zugänglich.

Dieser Hint hat uns in einen Rabbit Hole aus Entra ID App Registrations, undokumentierten Permission Models und PowerShell Cmdlets geschickt, die nur auf Windows funktionieren. Dieser Guide ist das, was wir auf der anderen Seite gefunden haben.

Acknowledgments

Wir haben das nicht from Scratch herausgefunden. Entscheidende Hinweise kamen aus der Community:

  • Marco De Tonis Frage auf Microsoft Q&A hat bestätigt, dass der ?format=html Parameter der Graph API für Loop Files in SharePoint Embedded funktioniert — das entscheidende Puzzlestück, das einen sauberen Export überhaupt erst möglich macht.

  • Diverse Blog Posts zu SharePoint Embedded Container Types und dem FileStorageContainer.Selected Permission Model haben uns durch den ansonsten undokumentierten SPE Guest Registration Prozess navigiert.

Falls ihr über Loops interne Storage Architecture oder SPE Permissions geschrieben habt: Danke. Eure Breadcrumbs haben uns Tage an Rätselraten erspart.

Export Script

Das komplette Export Script (inklusive Shared Library) ist als GitHub Gist verfügbar: export-loop.sh


Overview

Microsoft Loop speichert Workspace Pages in SharePoint Embedded (SPE) Containern. Das sind keine regulären SharePoint Sites — sie nutzen ein separates Permission Model, das eine explizite App Registration auf dem Loop Container Type erfordert.

Architektur

Discovery:  Search API (/search/query)     <- Client Credentials Token
                                              (Files.Read.All Application Permission)

Download:   Drive API  (/drives/{id}/...)   <- Client Credentials Token
                                              (FileStorageContainer.Selected + SPE Guest Registration)

Export:     ?format=html Query Parameter    <- Graph API konvertiert .loop -> HTML on the fly

Key Insight

  • Die Search API kann Loop Files über SPE Container hinweg mit nur Files.Read.All discovern

  • Aber direkter Drive/Item Access (Metadata, Content Download) erfordert, dass die App als Guest Application auf Loops Container Type via Set-SPOApplicationPermission registriert ist

  • Files.Read.All allein reicht nicht für SPE Container Content — das ist eine dokumentierte Microsoft Limitation


Prerequisites

Komponente

Zweck

Azure AD App Registration

Client Credentials Auth für Graph API

Global Admin Access (einmalig)

App auf Loop Container Type registrieren

Windows PC mit PowerShell (einmalig)

SharePoint Online Management Shell

macOS/Linux mit curl, jq, bash

Export Script ausführen


Step 1: App Registration in Entra ID

Neue App Registration anlegen (oder eine bestehende verwenden):

  • Name: z.B. "Loop Export"

  • Supported account types: Single Tenant ("My organization only")

  • Platform: "Mobile and desktop applications" Redirect URI hinzufügen (erforderlich für Public Client Flows)

API Permissions (Microsoft Graph — Application)

Permission

Type

Description

Admin Consent

Files.Read.All

Application

Read files in all site collections

Yes

Sites.Read.All

Application

Read items in all site collections

Yes

FileStorageContainer.Selected

Application

Access selected file storage containers

Yes

Admin Consent für alle drei Permissions erteilen.

Client Secret

Unter "Certificates & secrets" ein Client Secret erstellen. Den Value notieren — er kann danach nicht mehr abgerufen werden.

Allow Public Client Flows

Unter "Authentication" > "Advanced settings":

  • "Allow public client flows" auf Yes setzen

Das wird benötigt, falls man Device Code Flow für Delegated Access nutzen möchte. Für den reinen Client Credentials Export ist es optional, aber empfohlen.

Werte notieren

Application (client) ID:  <your-client-id>
Directory (tenant) ID:    <your-tenant-id>
Client Secret Value:      <your-secret>

In einer .env Datei speichern:

LOOP_CLIENT_ID=<your-client-id>
LOOP_CLIENT_SECRET=<your-secret>
LOOP_TENANT_ID=<your-tenant-id>

Step 2: SPE Guest App Registration (erfordert Global Admin)

Das ist der kritische Step. Ohne ihn kann die App Loop Files via Search discovern, aber deren Content nicht downloaden.

Warum das nötig ist

Loop nutzt SharePoint Embedded (SPE) Container mit der Container Type ID a187e399-0c36-4b98-8f04-1edc167a0996. Das ist ein Microsoft-eigener Container Type. Third-Party Apps müssen explizit als "Guest Applications" registriert werden, um auf Content in diesen Containern zugreifen zu können.

Vorgehen

Auf einem Windows PC (die SharePoint Online Management Shell funktioniert auf macOS nicht zuverlässig):

# 1. Modul installieren (einmalig)
Install-Module Microsoft.Online.SharePoint.PowerShell

# 2. Als Global Admin connecten
Connect-SPOService -Url "https://<tenant>-admin.sharepoint.com"

# 3. App als Guest auf Loops Container Type registrieren
Set-SPOApplicationPermission -OwningApplicationId "a187e399-0c36-4b98-8f04-1edc167a0996" -GuestApplicationId "<your-client-id>" -PermissionAppOnly "readcontent"

Wichtige Hinweise

  • -OwningApplicationId: Immer a187e399-0c36-4b98-8f04-1edc167a0996 (Loops Container Type, identisch für alle Tenants)

  • -GuestApplicationId: Die Client ID der App aus Step 1

  • -PermissionAppOnly: Lowercase "readcontent" verwenden (nicht "ReadContent")

  • Delegated Permissions: Werden von SPE aktuell nicht unterstützt — -PermissionDelegated Parameter weglassen

  • Benötigte Rolle: Global Admin. SharePoint Admin allein reicht für dieses Cmdlet nicht

  • Einmalige Operation: Muss nur einmal pro App Registration durchgeführt werden

Häufige Fehler

Error

Ursache

Fix

(403) Forbidden bei Connect-SPOService

User ist kein SharePoint Admin oder Global Admin

Korrekte Rolle in Entra ID zuweisen

Attempted to perform an unauthorized operation

User ist SharePoint Admin, aber nicht Global Admin

Global Admin Account verwenden

Invalid AppOnlyPermissions: ["ReadContent"]

Falsche Schreibweise

Lowercase verwenden: "readcontent"

Delegated permissions not supported

SPE Limitation

-PermissionDelegated Parameter weglassen

Backtick Line Continuation funktioniert nicht

PowerShell Parsing Issue

Als einzelne Zeile einfügen


Step 3: Export ausführen

So funktioniert das Export Script

1. Authenticate via Client Credentials (OAuth2 client_credentials Grant)
2. Alle .loop/.fluid Files via POST /search/query discovern
   - Nutzt den Region Parameter (z.B. "EUR") — erforderlich für Application Permissions
   - Paginiert durch alle Results (25 pro Page)
3. Für jedes File:
   a. HTML via GET /drives/{driveId}/items/{itemId}/content?format=html downloaden
   b. Metadata (Author, Dates, Permissions) als JSON speichern
   c. Fallback auf Raw .loop Download falls HTML Conversion fehlschlägt
4. manifest.json mit komplettem Inventory schreiben

Drive ID Encoding

Loop Drive IDs enthalten ! (z.B. b!a2IBG9x...). In Graph API URLs muss das ! als %21 URL-encoded werden:

GET /drives/b%21a2IBG9x.../items/{itemId}/content

Das --path-as-is curl Flag verhindert, dass curl die URL normalisiert.

Usage

# Dry Run — Files discovern ohne Download
./tools/export-loop.sh --dry-run

# Full Export (idempotent — überspringt bereits exportierte Files)
./tools/export-loop.sh

# Force Re-Export aller Files
./tools/export-loop.sh --force

Output Struktur

exports/loop/
  manifest.json          # Komplettes Inventory mit Status pro File
  pages/
    Filename__itemId.html   # HTML Export jeder Loop Page
  meta/
    Filename__itemId.json   # Metadata (Author, Dates, Permissions)

Token Refresh

Das Script refresht den Client Credentials Token automatisch alle 45 Minuten. Bei Exports mit 700+ Files passiert das transparent während des Runs.


API Details

Search API (Discovery)

POST https://graph.microsoft.com/v1.0/search/query
Authorization: Bearer <client-credentials-token>
Content-Type: application/json

{
  "requests": [{
    "entityTypes": ["driveItem"],
    "query": { "queryString": "filetype:loop OR filetype:fluid" },
    "from": 0,
    "size": 25,
    "region": "EUR"
  }]
}
  • region: Erforderlich für Application Permissions. "EUR" für europäische Tenants, "NAM" für Nordamerika, etc.

  • Pagination: from + size verwenden. moreResultsAvailable in der Response checken.

  • Returns: File Name, driveId, itemId, Size, Dates, Authors, webUrl, parentReference

HTML Download

GET https://graph.microsoft.com/v1.0/drives/{driveId}/items/{itemId}/content?format=html
Authorization: Bearer <client-credentials-token>
  • Der ?format=html Parameter triggert serverseitige Conversion von .loop Format zu HTML

  • Folgt Redirects (mit curl -L)

  • Gibt vollständiges HTML mit Inline Styles zurück

Raw Download (Fallback)

GET https://graph.microsoft.com/v1.0/drives/{driveId}/items/{itemId}/content
Authorization: Bearer <client-credentials-token>
  • Downloadt das Raw .loop File (Fluid Framework Format)

  • Nützlich falls die HTML Conversion für bestimmte Files fehlschlägt


Troubleshooting

"403 Forbidden" bei Drive/Item Access

Die App ist nicht als SPE Guest registriert. Step 2 durchführen.

Search liefert Files, aber Download schlägt fehl

Selbe Ursache — die Search API hat speziellen Cross-Container Access, der SPE Restrictions umgeht, aber direkter Drive Access nicht.

"Resource not found for segment '!...'"

Das ! in Drive IDs wird URL-decoded. Sicherstellen, dass es als %21 encoded ist.

"File name too long" beim Export

Manche Loop Page Names überschreiten Filesystem Limits (255 Bytes auf macOS). Das Export Script kürzt Filenames auf 100 Zeichen.

Client Credentials Token funktioniert für Search, aber nicht für Downloads

Sicherstellen, dass alle drei Permissions granted sind:

  1. Files.Read.All (Application) — für Search API

  2. FileStorageContainer.Selected (Application) — für SPE Container

  3. SPE Guest Registration via Set-SPOApplicationPermission — verknüpft die Permission mit Loops Container Type

Device Code Flow schlägt fehl mit "client_assertion or client_secret required"

Azure AD unterstützt keinen Device Code Flow für Confidential Clients (Apps mit Secrets). Stattdessen Client Credentials Flow verwenden, der nach SPE Guest Registration funktioniert.


Was NICHT funktioniert

Diese Ansätze wurden getestet und als nicht funktionierend für Loop SPE Container bestätigt:

Ansatz

Ergebnis

Files.Read.All (Application) ohne SPE Registration

Search geht, Drive Access 403

Files.Read.All (Delegated via Device Code)

Azure AD lehnt Device Code für Confidential Clients ab

az login Token für SharePoint Admin REST API

Falscher Scope (user_impersonation nicht an Azure CLI granted)

Connect-SPOService auf macOS

"Object reference not set to an instance of an object"

Set-SPOApplication PowerShell Cmdlet

Hat keinen -ApplicationId Parameter — falsches Cmdlet

Graph API /admin/sharepoint/settings/containerTypes/

Endpoint existiert nicht (weder v1.0 noch beta)

Container Listing via /storage/fileStorage/containers

403 ohne SPE Registration


Referenzen