> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Wallet-Eigentumsnachweis (Travel Rule) – Didit Widget

> Didit-Widget: Kunden weisen die Kontrolle über ihre Krypto-Wallet nach per Signatur, Satoshi-Test, Screenshot oder Selbstauskunft – für die Travel Rule.

Das Wallet-Eigentumsnachweis-Widget ist eine gehostete Seite, die belegt, dass einer Ihrer Kunden eine bestimmte Krypto-Wallet-Adresse kontrolliert. Nach der EU-TFR muss ein VASP den Eigentumsnachweis für eine selbstverwahrte (ungehostete) Wallet erbringen, bevor Gelder oberhalb des Sunrise-Schwellenwerts an diese Wallet oder von ihr freigegeben werden. Das Widget ist der Weg, auf dem Didit diesen Nachweis erfasst, ohne dass Sie eigene Signaturabläufe entwickeln müssen.

Ein verifizierter Nachweis markiert den passenden [Adressbucheintrag für Wallets](https://docs.didit.me/de/transaction-monitoring/travel-rule.md#wallet-adressbuch) mit `is_ownership_verified: true`. Dadurch kann eine verknüpfte [Travel-Rule-Transaktion](https://docs.didit.me/de/transaction-monitoring/travel-rule.md) aus dem Status `UNCONFIRMED_OWNERSHIP` voranschreiten.

## Wann Didit die Sitzung für Sie erstellt

Sie müssen selten selbst eine Sitzung erstellen.
Wenn `auto_wallet_verification` in Ihren [Travel-Rule-Einstellungen](https://docs.didit.me/de/transaction-monitoring/travel-rule.md#einrichtung) aktiviert ist (Standardeinstellung), erstellt Didit bei der Anlage der Transaktion automatisch eine Widget-Sitzung, sobald eine Transaktion den Nachweis der Wallet-Kontrolle durch den Endbenutzer erfordert:

* Die Transaktion befindet sich im Status `UNCONFIRMED_OWNERSHIP`, weil die Gegenpartei-Wallet bei der Einreichung als [selbstverwahrt](https://docs.didit.me/de/transaction-monitoring/travel-rule.md#deklarierte-selbstverwahrte-wallets) deklariert wurde.
* Die Transaktion ist ausgehend und befindet sich im Status `COUNTERPARTY_VASP_NOT_FOUND`. Es konnte kein VASP der Gegenpartei ermittelt werden, daher ist der Eigentumsnachweis der verbleibende Weg zum Abschluss.

Die automatisch erstellte Sitzung wird im Block `action_required` der Transaktion zurückgegeben (`type: "wallet_ownership"`, mit der zu öffnenden `url`). Die SDKs starten sie bei der [clientseitigen Übermittlung](https://docs.didit.me/de/transaction-monitoring/sdk-transaction-submission.md#erforderliche-benutzeraktionen) automatisch.
Eingehende Transaktionen, die Didit für Sie anlegt (eine Sunrise-Registrierung, eine eingehende `TRP`-Anfrage oder der eingehende Abschnitt der `INTERNAL`-Schiene), erhalten niemals eine automatisch erstellte Sitzung, auch wenn sie im Status `UNCONFIRMED_OWNERSHIP` landen.
Erstellen Sie die Sitzung manuell mit `POST /v3/travel-rule/widget-session/`, wenn Sie eine Wallet vor dem Bestehen einer Transaktion vorab verifizieren möchten, wenn `auto_wallet_verification` deaktiviert ist oder wenn es um eine der genannten eingehenden Transaktionen geht.

## Was es belegt

Das Widget bietet bis zu vier Nachweismethoden. Welche davon angezeigt werden, hängt davon ab, wie Sie die Sitzung erstellt haben, und von Ihren [Travel-Rule-Einstellungen](https://docs.didit.me/de/transaction-monitoring/travel-rule.md#einrichtung):

| Methode | So funktioniert es | Wann sie angeboten wird |
| - | - | - |
| `MESSAGE_SIGNING` | Der Kunde signiert mit seiner Wallet eine von Didit ausgestellte Challenge-Zeichenfolge. Unterstützt werden EVM-, Solana-, Bitcoin- und Tron-Wallets (bei Bitcoin: Legacy-, Nested-SegWit-, Native-SegWit- und Taproot-Adressen). Didit prüft die Signatur gegen die Adresse. Wird mit einem anderen Konto als dem zu verifizierenden signiert, wird die Signatur mit einer klaren Aufforderung zum Kontowechsel abgelehnt. Auch EVM-Smart-Contract-Konten (ERC-4337 Smart Wallets) können signieren: Didit fragt den Vertrag des Kontos selbst, auf der Chain, auf der die Wallet hinterlegt ist, ob die Signatur gültig ist (ERC-1271). Bei einem noch nicht bereitgestellten Konto prüft Didit die ERC-6492-Signatur, die die Wallet zurückgibt, ohne das Konto bereitzustellen oder eine Transaktion zu senden. Smart-Contract-Konten werden auf jeder unterstützten EVM-Chain geprüft. Siehe unten [Vertragsprüfung bei Nachrichtensignierung](#vertragsprüfung-bei-nachrichtensignierung). Wallets, die im Browser des Kunden installiert sind, werden über die Protokolle EIP-6963 und Wallet Standard erkannt, sodass jede Erweiterung als eigene Signaturoption erscheint. Das Koppeln einer separaten mobilen Wallet-App, etwa per QR-Code, ist derzeit nicht verfügbar. | Immer, für unterstützte Chain-Familien. |
| `SATOSHI_TEST` | Der Kunde sendet einen winzigen Betrag On-Chain von der Wallet an eine Einzahlungsadresse, die Sie kontrollieren. Der Watcher von Didit prüft den Absender (bei einem EVM-Smart-Contract-Konto das Konto selbst, auch wenn ein ERC-4337-Bundler die Transaktion einreicht), den Betrag und die On-Chain-Bestätigung der Einzahlung. Sobald sie bestätigt ist, schließt er den Nachweis automatisch ab. Siehe unten [Vertragsprüfung beim Satoshi-Test](#vertragsprüfung-beim-satoshi-test). | Nur, wenn Sie beim Erstellen einen `satoshi_deposit_address` übergeben und die Chain eine EVM-, Bitcoin- oder Solana-Chain ist. Niemals bei einer automatisch erstellten Sitzung. |
| `SCREENSHOT` | Der Kunde lädt einen Screenshot der Wallet hoch, auf dem die Adresse zu sehen ist (JPEG/PNG/WebP, bis zu 10 MB). Anders als bei den anderen Methoden wird er nie automatisch verifiziert. Ihr Compliance-Team prüft ihn in der Warteschlange für Screenshot-Prüfungen ([`GET /v3/travel-rule/wallet-proofs/`](https://docs.didit.me/de/management-api/travel-rule/list-wallet-proofs)) und genehmigt oder lehnt ihn ab ([`POST .../decision/`](https://docs.didit.me/de/management-api/travel-rule/decide-wallet-proof)). Alternativ bestätigt es den Transfer auf dem Tab **Travel Rule** der Transaktion in der Konsole oder über [`POST .../travel-rule/ownership/`](https://docs.didit.me/de/management-api/travel-rule/confirm-ownership) (bei einem Transfer im Status `UNCONFIRMED_OWNERSHIP` oder einem eingehenden Transfer in Wartestellung). | Nur, wenn `allow_screenshot_proof` in Ihren Einstellungen auf `true` gesetzt ist. |
| `SELF_DECLARATION` | Der Kunde bestätigt die Inhaberschaft ohne kryptografischen Nachweis. Es ist die letzte Option, dort, wo die Rechtsordnung sie zulässt. | Nur, wenn `allow_self_declaration` auf `true` gesetzt ist und Ihr [`ownership_validation_mode`](https://docs.didit.me/de/transaction-monitoring/travel-rule#modi-der-eigentumsvalidierung) eine Erklärung für den verknüpften Transfer akzeptiert: niemals im Modus `FULL`, im Modus `SIMPLIFIED` nur unterhalb von `simplified_validation_threshold`. Wird eine Erklärung initiiert, die der Modus nicht akzeptiert, antwortet die API mit `403`. |

### Vertragsprüfung bei Nachrichtensignierung

Die Übermittlung der Signatur liefert `200` mit `status: "VERIFIED"`, sobald sie die Kontrolle über die Wallet belegt.
Sie liefert `400` und lässt den Nachweis offen, damit der Kunde erneut signieren kann, mit einem dieser `code`-Werte:

| `code` | Bedeutung |
| - | - |
| `SIGNATURE_INVALID` | Die Signatur belegt die Wallet nicht: Ein anderes Konto oder ein anderer Schlüssel hat signiert, oder die Nachricht weicht ab. Bei einem Smart-Contract-Konto hat dessen Vertrag geantwortet, dass die Signatur nicht von ihm stammt. |
| `SIGNATURE_UNSUPPORTED_SCHEME` | Die Wallet hat in einem Format signiert, das Didit nicht prüfen kann, etwa einer Bitcoin-Multisig oder einer vollständigen BIP-322-Signatur. |
| `SIGNATURE_LOOKUP_FAILED` | Die Wallet könnte ein Smart-Contract-Konto sein, und seine Chain konnte im Moment nicht geprüft werden. Bei dieser Antwort wird der Nachweis nie verifiziert. Erneutes Signieren prüft ihn erneut. |

### Vertragsprüfung beim Satoshi-Test

Die Verifizierung einer Satoshi-Test-Einzahlung erfolgt asynchron, da die Transaktion On-Chain sichtbar sein und anschließend bestätigt werden muss.
Die Übermittlung der `txid` der Einzahlung liefert `200` mit `status: "PENDING"`, solange die Einzahlung On-Chain noch nicht bestätigt ist. Die `txid` wird dabei gespeichert und nicht verworfen.
Sobald die Einzahlung bestätigt ist, liefert sie `200` mit `status: "VERIFIED"`.
Bei einer tatsächlich falschen Einzahlung liefert sie `400` mit `status: "REJECTED"`: wenn sie von einer anderen Wallet stammt, nicht den erwarteten Betrag an die Einzahlungsadresse sendet, älter als die Challenge ist oder die Transaktion bereits den Besitz einer anderen Wallet nachgewiesen hat. Der Nachweis wird dann geschlossen, und der Kunde kann einen neuen starten, solange die Sitzung noch Versuche übrig hat.
Eine Einzahlung, die noch nicht indexiert ist, liefert ebenfalls `200` mit `status: "PENDING"`. Schlägt eine Abfrage auf Seiten von Didit fehl, liefert sie `400` mit dem Code `DEPOSIT_LOOKUP_FAILED`. In beiden Fällen bleibt der Nachweis offen, und Didit prüft weiter.
Sie liefert `410`, wenn das Testfenster abläuft, bevor eine gültige Einzahlung erkannt wurde.

Sobald eine `txid` übermittelt wurde, prüft ein Hintergrundjob die ausstehende Einzahlung alle fünf Minuten erneut und schließt den Nachweis ab, nachdem die Transaktion bestätigt wurde. Der Kunde muss die `txid` daher nicht erneut übermitteln.
Wenn Sie eine `satoshi_deposit_address` übergeben, wird der eigene `expires_at`-Wert der Widget-Sitzung auf mindestens das 24-Stunden-Fenster des Satoshi-Tests verlängert. So hat die Einzahlung Zeit zur Bestätigung, bevor die Sitzung selbst abläuft.

## Ablauf

**1. Widget-Sitzung erstellen**

Rufen Sie `POST /v3/travel-rule/widget-session/` mit dem Wallet auf, das verifiziert werden muss. Optional können Sie die Sitzung mit einer Transaktion verknüpfen, damit die Transaktion automatisch voranschreitet, sobald der Besitz nachgewiesen ist.

**2. Die zurückgegebene URL öffnen**

Senden Sie Ihren Kunden an die `url` aus der Antwort. Dort erbringt er einen Nachweis, ohne dass ein Didit-Konto oder ein API-Schlüssel erforderlich ist. Das Token in der URL dient als Authentifizierung.

**3. Abschluss erkennen**

Wenn ein Nachweis verifiziert wurde, markiert das Widget den Adressbucheintrag als verifiziert und schreitet die Transaktion voran, sofern die Sitzung mit einer Transaktion verknüpft ist. Achten Sie auf `travel_rule.status.updated` (bei verknüpften Transaktionen) oder fragen Sie den Widget-Kontext ab.

### Widget-Sitzung erstellen

Dieses Beispiel verknüpft die Sitzung mit der [deklarierten selbstverwahrten Transaktion](https://docs.didit.me/de/transaction-monitoring/travel-rule.md#deklarierte-selbstverwahrte-wallets) aus dem Travel-Rule-Leitfaden. Daher ist `wallet_address` die Ziel-Wallet dieser Transaktion.
Eine mit einer Transaktion verknüpfte Sitzung akzeptiert nur Nachweise für die Wallet der Gegenpartei dieser Transaktion.

```bash theme={null}
curl -X POST https://verification.didit.me/v3/travel-rule/widget-session/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "wallet_address": "0xd3b62acb76fb27a7f8961ec40fa11746b113aca9",
    "chain": "ETH",
    "holder_name": "Jane Doe",
    "vendor_data": "user-042",
    "transaction_id": "99999999-aaaa-bbbb-cccc-dddddddddddd",
    "satoshi_deposit_address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
    "callback_url": "https://yourapp.example/wallet-verified",
    "expires_in_minutes": 60
  }'
```

| Feld | Typ | Beschreibung |
| - | - | - |
| `wallet_address` | string | Die zu verifizierende Adresse. |
| `chain` | string | Chain-Kennung für die Adresse (zum Beispiel `ETH`, `BTC`, `SOL`). Verwenden Sie dasselbe Token, das Ihre Transaktionen tragen (`ETH`, nicht `ethereum`): Das Widget speichert den Adressbucheintrag mit dieser `chain`, und eine Transaktion auf einer anderen Chain passt nicht dazu. Die Nachrichtensignatur erfordert eine Chain, die Didit erkennt. |
| `holder_name` | string | Name des Wallet-Inhabers, der für den Abgleich des Begünstigtennamens verwendet wird. |
| `vendor_data` | string | Ihre interne Kennung für den Inhaber, die in den angelegten Adressbucheintrag übernommen wird. |
| `transaction_id` | string (uuid) | Optional. Didit-Transaktions-UUID einer Travel-Rule-Transaktion, die nach erfolgreicher Verifizierung des Nachweises vorangebracht wird. Eine unbekannte UUID liefert `404`, und ein Nachweis für eine andere Wallet als die Wallet der Gegenpartei der Transaktion wird mit `400` abgelehnt. Lassen Sie das Feld weg, um eine Wallet eigenständig zu verifizieren. |
| `satoshi_deposit_address` | string | Optional. Eine Einzahlungsadresse, die Ihnen gehört. Wenn Sie sie angeben, wird die Methode `SATOSHI_TEST` aktiviert. |
| `callback_url` | string | Optional. Das Ziel, an das das Widget den Kunden nach Abschluss zurückleitet. |
| `expires_in_minutes` | integer | Optional. Gültigkeitsdauer des erstellten Links in Minuten, von `5` bis `10080` (Standard: `60`). Wenn Sie eine `satoshi_deposit_address` übergeben, wird sie automatisch auf mindestens das 24-Stunden-Testfenster von Satoshi angehoben. |

```json theme={null}
{
  "widget_session_id": "77777777-8888-9999-aaaa-bbbbbbbbbbbb",
  "token": "3s9x...urlsafe",
  "url": "https://verify.didit.me/wallet-ownership/3s9x...urlsafe",
  "expires_at": "2026-07-07T13:00:00Z"
}
```

Öffnen Sie die in der Antwort zurückgegebene `url` immer unverändert. Erstellen Sie sie nicht selbst.

### Die zurückgegebene URL öffnen

> [!WARNING]
>
> **Das Widget ist in dieser Version nur im Web verfügbar. Öffnen Sie die `url` im Systembrowser oder in einem Custom Tab, niemals in einer Webview eines Didit-Mobile-SDKs.**
>
> Die Nachrichtensignatur funktioniert mit Wallets, die im Browser des Kunden installiert sind. Das Widget erkennt diese über die Protokolle EIP-6963 und Wallet Standard.
> Das Koppeln einer separaten mobilen Wallet-App, beispielsweise per QR-Code, ist derzeit nicht verfügbar. Daher übergibt das Widget den Vorgang nicht an eine andere App.
> Eine In-App-Webview, etwa die Webview des Didit Web/Flutter/React Native/iOS/Android SDKs, **kann die Browser-Wallets des Kunden nicht erreichen**. Ein dort geöffnetes Widget lässt dem Kunden daher keine Möglichkeit zu signieren.
>
> Auf mobilen Geräten starten Sie die `url` mit dem Browser des Betriebssystems (`SFSafariViewController` / Chrome Custom Tabs oder der Standardbrowser), statt sie in einer Webview darzustellen. Im Web ist ein normaler Tab oder ein Popup ausreichend.

### Abschluss erkennen

Das Widget stellt einen öffentlichen Lese-Endpoint bereit, der über das Token adressiert wird. Fragen Sie ihn ab, um den Fortschritt zu sehen, oder verlassen Sie sich auf Webhooks, wenn die Sitzung mit einer Transaktion verknüpft ist:

```bash theme={null}
curl https://verification.didit.me/v3/travel-rule/widget/{token}/
```

```json theme={null}
{
  "vasp_name": "Origin CASP SL",
  "wallet_address": "0xd3b62acb76fb27a7f8961ec40fa11746b113aca9",
  "chain": "ETH",
  "holder_name": "Jane Doe",
  "allowed_methods": ["MESSAGE_SIGNING", "SCREENSHOT", "SATOSHI_TEST", "SELF_DECLARATION"],
  "transfer_status": "UNCONFIRMED_OWNERSHIP",
  "callback_url": "https://yourapp.example/wallet-verified",
  "expires_at": "2026-07-07T13:00:00Z",
  "completed_at": null,
  "proofs": [
    {
      "proof_id": "88888888-9999-aaaa-bbbb-cccccccccccc",
      "method": "SATOSHI_TEST",
      "status": "PENDING",
      "wallet_address": "0xd3b62acb76fb27a7f8961ec40fa11746b113aca9",
      "chain": "ETH",
      "deposit_address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
      "expected_amount": "0.000042817305521963",
      "expires_at": "2026-07-07T13:05:00.412731+00:00",
      "created_at": "2026-07-06T13:05:00.412731Z"
    }
  ],
  "customization": null
}
```

* `completed_at` ist nicht mehr null, sobald ein Nachweis verifiziert wurde.
* Jeder Eintrag in `proofs` enthält die Felder, die seine Methode benötigt: Ein `SATOSHI_TEST`-Nachweis ergänzt `deposit_address`, `expected_amount` und sein eigenes `expires_at`. Ein `MESSAGE_SIGNING`-Nachweis ergänzt `challenge`, die Zeichenfolge, die der Kunde signiert.
* Ein `SATOSHI_TEST`-Nachweis kann den Status `status: "PENDING"` haben, während seine Einzahlung On-Chain erkannt, aber noch nicht bestätigt ist. Dafür müssen weder Sie noch der Kunde etwas tun. Siehe oben den [Verifizierungsvertrag für den Satoshi-Test](#vertragsprüfung-beim-satoshi-test).
* Wurde die Sitzung mit einer `transaction_id` erstellt, schreitet die verknüpfte Transaktion voran, sobald der Nachweis verifiziert ist. In einer Live-Anwendung wird außerdem der Webhook [`travel_rule.status.updated`](https://docs.didit.me/de/integration/webhooks#ereignistypen) ausgelöst.
  Bevorzugen Sie den Webhook gegenüber Polling.
  Eine eingehende `TRP`-Anfrage wird genehmigt und an den Absender zurückgesendet und wechselt zu `COMPLETED`, sobald diese Freigabe zugestellt ist.
  Jede andere Transaktion in `UNCONFIRMED_OWNERSHIP` (oder eine eingehende Transaktion in `ON_HOLD`) wechselt zu `COMPLETED`. Stimmt der Name des Begünstigten nicht mit dem Wallet-Inhaber überein, wechselt sie stattdessen zu `COUNTERPARTY_MISMATCHED_DATA`. Eine abgewickelte Sunrise-Einzahlung, die abgeschlossen wird, wechselt zu `FINISHED`.
  Eine ausgehende Transaktion in `COUNTERPARTY_VASP_NOT_FOUND` wechselt zu `COMPLETED`.
* Ein `SCREENSHOT`-Nachweis behält in diesem Kontext den Status `status: "PENDING"`, auch nachdem Sie den Besitz bestätigt haben.
  Sie genehmigen ihn mit [`POST .../travel-rule/ownership/`](https://docs.didit.me/de/transaction-monitoring/travel-rule#bestätigung-des-wallet-eigentums). Dieser Endpoint akzeptiert nur eine Transaktion, die auf die Bestätigung des Besitzes wartet.
  Ein Screenshot hat daher keinen Genehmigungsweg in einer Sitzung ohne verknüpfte Transaktion oder in einer Sitzung mit einer ausgehenden Transaktion in `COUNTERPARTY_VASP_NOT_FOUND`. Deaktivieren Sie `allow_screenshot_proof`, wenn solche Sitzungen diesen Nachweistyp nicht anbieten dürfen.
* Nach einem abgelehnten Nachweis erhält die Sitzung einen neuen Versuch, solange noch Versuche übrig sind.
  `max_attempts`, `attempts_used` und `attempts_remaining` geben die Anzahl an. Ein abgelehnter Nachweis enthält `rejection_reason`. Ist `attempts_remaining` gleich `0`, liefert der Start eines weiteren Nachweises die Antwort `409`, und Sie erstellen eine neue Sitzung.
* Der Kontext-Endpoint liefert `404` für ein unbekanntes Token und `410`, sobald die Sitzung abgelaufen ist, ohne abgeschlossen worden zu sein.

> [!NOTE]
>
> Hier besteht keine Abhängigkeit von einmaligen Vorgängen: Das Widget und Ihr Backend greifen beide auf denselben dauerhaften Zustand zu (den verifizierten Adressbucheintrag und den Transaktionsstatus). Der Vorgang lässt sich daher nach einem verlorenen Webhook oder einem geschlossenen Tab immer wieder fortsetzen, indem Sie die Transaktion oder den Widget-Kontext erneut abrufen.

## Lokalisierung und Branding

Das Widget wird in **54 Sprachen** angezeigt und folgt dabei dem Sprachpfad des Links (oder der Browser-Standardsprache).

Wenn White-Label für Ihre Anwendung aktiviert ist, übernimmt das Widget automatisch Ihr Branding des Verifizierungsablaufs: Logo, Markenfarben, Gestaltung von Schaltflächen und Panels, benutzerdefinierte Schriftart, Favicon und den öffentlichen Namen Ihrer App im Titel.
Dafür ist keine separate Konfiguration nötig: Es gilt dieselbe Anpassung, die Sie für KYC-Sitzungen eingerichtet haben.

Ein Benutzer, der zu einem bereits abgeschlossenen Link zurückkehrt, sieht erneut den Abschlussbildschirm. Haben Sie eine `callback_url` übergeben, leitet das Widget ihn dorthin weiter.
Abgelaufene Links zeigen einen gebrandeten Ablaufbildschirm an, auf dem der Benutzer aufgefordert wird, einen neuen Link anzufordern.
Erstellen Sie für ihn eine neue Sitzung mit `POST /v3/travel-rule/widget-session/`: Das erneute Senden eines Transfers mit `action: "resend"` erstellt keine Sitzung.

## Nächste Schritte

> [card] [Travel Rule](https://docs.didit.me/de/transaction-monitoring/travel-rule.md)
>
> Der vollständige Lebenszyklus des Austauschs, Zahlungswege, Einstellungen und Empfangsabläufe.

> [card] [Wallet-Adressbuch](https://docs.didit.me/de/transaction-monitoring/travel-rule.md#wallet-adressbuch)
>
> Registrieren Sie die Wallets, die Sie kontrollieren, damit eingehende Transfers ihnen zugeordnet werden.

> [card] [Webhooks](https://docs.didit.me/de/integration/webhooks.md)
>
> Abonnieren Sie travel\_rule.status.updated, um zu reagieren, sobald ein Nachweis einen Transfer weiterbringt.

> [card] [Native SDKs](https://docs.didit.me/de/integration/native-sdks/overview.md)
>
> Öffnen Sie die Widget-URL im System-Browser und nicht in der WebView des SDK.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.