# API- und Nachrichten-Referenz

> HTTP-Endpunkte mit Request und Response, Trigger-Bindungen, Schema der Service-Bus-Nachrichten und Fehlerverhalten.

---

Site [llms.txt](/llms.txt)

---

## Authentifizierung

Alle HTTP-Functions verwenden `AuthorizationLevel.Function`. Der Funktionsschlüssel wird
als Query-Parameter `code` oder Header `x-functions-key` übergeben. Es gibt keine
Easy-Auth- oder Entra-Konfiguration im Deployment. Timer-, Service-Bus- und
Durable-Trigger haben keinen HTTP-Endpunkt.

Routen ohne `Route`-Attribut folgen der Konvention `/api/<FunctionName>`.

## HTTP-Endpunkte

### `GET /api/GetInfo`

Liefert die Assembly-Version, keine Gesundheitsprüfung.

```json
{ "Version": "2.3.2026.0926" }
```

### `POST /api/ExecuteJob`

Stößt einen Job aus der Tabelle `jobs` manuell an (Erkennungsphase).

```json
{ "JobName": "<RowKey des Jobs>" }
```

Ablauf: `JobName` prüfen → `EnsureInitializedAsync` → `ProcessJobForRetrieval`.
Antwort `200 OK`; Fehler propagieren als 500. Der Aufruf läuft ohne Job-Locking parallel
zum Timer, siehe [Datenflüsse → Watermark](../datenfluesse/#watermark-logik).

### `POST /api/ExecuteHttpRequest`

Diagnose-Endpunkt: setzt eine rohe HTTP-GET-Anfrage mit eigenem `HttpClient` ab,
unabhängig von den konfigurierten Connectoren.

| Feld | Default | Beschreibung |
|---|---|---|
| `BaseAddress` | erforderlich | Basis-URL |
| `Url` | erforderlich | relativer Pfad inkl. Query |
| `Method` | `GET` | nur GET implementiert |
| `Authentication` | `NTLM` | `NTLM` mit `UserName`/`Password`, sonst ohne Credentials |
| `ODataMaxVersion`, `ODataVersion` | `4.0` | OData-Header |
| `Prefer` | `odata.include-annotations="*"` | Prefer-Header |
| `MediaTypeWithQuality` | `application/json` | Accept-Header |

Antwort: `200 OK` mit dem rohen Antwort-Body des Zielservers.

### `GET /api/CheckList`

`EnsureInitializedAsync` → `InitializationService.CheckAsync()`. Prüft Connections, Job-Connections,
eindeutige `Order`, Mapping-Existenz sowie Tabellen und Felder gegen die Live-Metadaten
der Connectoren. Antwort `200 OK` mit serialisiertem `CheckResponse`, auch wenn Befunde
vorliegen.

### `GET /api/Validate`

Erzwingt die Initialisierung. `200 OK` bei Erfolg, sonst `500`; Body `text/plain` mit
`InitializationService.GetErrorLogs()`.

### `GET /api/job`, `GET /api/job/{rowKey}`

Geladene Jobs (alle bzw. einer). `404`, wenn der RowKey unbekannt ist.

### `GET /api/mapping`, `GET /api/mapping/{name}`

Geladene Mappings. Die Suche läuft über den Mapping-Namen (PartitionKey), Fallback
RowKey.

### `POST /api/mcp`

Streamable-HTTP-Endpunkt des MCP-Servers. `GET /api/mcp` antwortet mit `405`, weil kein
SSE-Stream angeboten wird. Tools und Nutzung: [MCP-Server](/docs/mcp-server/).

### `GET /api/StartGetChangesOrchestration`

Startet die Durable-Orchestrierung. Antwort `202 Accepted` mit der Instance-ID und den
Standard-Status-URLs des Durable-Clients.

## Trigger-Bindungen

| Function | Bindung | Konfiguration |
|---|---|---|
| `GetChanges` | Timer | `%GetChangesTimerExpression%`, Bicep-Default `*/15 * * * * *`; `RunOnStartup` nur im Debug-Build |
| `ProcessStandardQueueItems` | Service Bus | Queue `sbqexport`, Connection `ExportQueueServiceBus`, `AutoCompleteMessages = false` |
| `ProcessHighPriorityQueueItems` | Service Bus | Queue `sbqhighexport` |
| `ProcessLowPriorityQueueItems` | Service Bus | Queue `sbqlowexport` |
| `GetChangesOrchestrator` | Orchestration | Durable |
| `GetChangesActivity` | Activity | `MaxBatchSize = 3` |
| `JobState` | Entity | `Add`, `Remove`, `InProgress`, `Reset` |

Die Datei `sample.local.settings.json` setzt lokal `* */2 * * * *`. Effektiv gilt immer
der App-Setting-Wert der jeweiligen Umgebung.

## Nachrichtenschema (Service Bus)

Body: JSON-serialisiertes `QueueMessage` (Newtonsoft.Json).

| Feld | Typ | Beschreibung |
|---|---|---|
| `Id` | string | Schlüssel des Quell-Datensatzes |
| `JobName` | string | erzeugender Job |
| `ActionCode` | `JobActionType` | `NewUpdate`, `Delete`, `TimeStamp`, `DeleteOnUpdate` oder ein `InvokeAction`-Name |
| `Attributes` | `Dictionary<string, QueueMessageProperty>` | gemappte Werte als `Value` (string) + `TypeName` |
| `ModifiedOn` | datetime? | Änderungszeitpunkt der Quelle, Basis für `SkipOutdatedMessages` |
| `ChangeTracker` | string | `[JsonIgnore]`, nur für die Watermark während der Erkennung |
| `NeedToIgnore` | bool | `[JsonIgnore]` |

Application Properties:

| Property | Wann gesetzt |
|---|---|
| `JobName`, `SourceKey`, `Timestamp` | beim Senden |
| `ErrorMessage`, `ErrorTimeStamp`, `ErrorStackTrace`, `OperationId` | beim Abandon nach einem Fehler |

`SourceKey` nutzt der `DeadLetterMover` zur Deduplizierung. `OperationId` ist die
W3C-Root-ID und erlaubt die Korrelation mit Application Insights.

> **Typisierung der Attribute**
>
> Alle Werte werden heute als String plus Typname transportiert. Eine Umstellung auf echte
> JSON-Typen mit symmetrischen Resolvern ist entworfen, aber nicht umgesetzt. Sie wäre ein
> Breaking Change für bereits gequeute Nachrichten.



## Fehlerverhalten

| Situation | Verhalten |
|---|---|
| `ExecuteJob` ohne `JobName` | Exception → 500 |
| Initialisierungsfehler | `Validate` liefert 500 mit Log; Timer- und Queue-Trigger setzen die Initialisierung zurück und versuchen es beim nächsten Aufruf erneut |
| Verarbeitungsfehler in `ProcessQueueItems` | Abandon mit Fehler-Properties; nach `maxDeliveryCount` in die Dead-Letter-Subqueue |
| `TransitiveException` (HTTP 409 im Ziel) | Nachricht wird in die Transitive-Queue der Priorität kopiert und im Original abgeschlossen; kein Trigger auf der Transitive-Queue |
| Veraltete Nachricht bei `SkipOutdatedMessages` | Kopie nach `sbqignored`, Original abgeschlossen |
| Wiederholte Zustellung | `ProcessQueueMessageService` wartet `DeliveryCount² · 1000 ms` vor dem Versuch |
| `EndpointNotFoundException`, `CommunicationException` in `GetChanges` | Reset der Initialisierung, nächster Timer-Lauf versucht erneut |
