# Webhooks

Statt auf den Abschluss einer Assembly und die Antwort auf deren API-Anfrage zu warten, können Sie auch einen Webhook konfigurieren, der ebenfalls als Assembly Notification bezeichnet wird. Sobald eine Assembly beendet ist, sendet das System einen vollständigen Bericht per POST-Anfrage an eine URL Ihrer Wahl.

## Warum Webhooks verwenden?

Mit Webhooks bieten Sie Ihren Endnutzerinnen und Endnutzern ein reibungsloseres Erlebnis, da sie nur warten müssen, bis die Datei-Uploads abgeschlossen sind, bevor sie das Browserfenster schließen. Ohne Datei-Uploads könnten sie das Browserfenster sogar sofort schließen.

## Wie aktivieren Sie Webhooks?

Sie aktivieren Webhooks, indem Sie `notify_url` zu den Assembly Instructions in IhremTemplate auf derselben JSON-Ebene wie `steps` hinzufügen:

```jsonc
{
  "steps": {
    // …
  },
  "notify_url": "https://example.com/transloadit_pingback"
}

```

Wenn Sie anschließend Ihr Template ausführen, informiert Transloadit Ihr Backend nach Abschluss der gesamten Verarbeitung. Dazu sendet es eine `POST`-Anfrage mit dem vollständigen Assembly Status JSON an die angegebene URL.

Wenn Ihre Nutzerinnen und Nutzer oder Ihr Programm nicht auf das Encoding warten sollen, müssen Sie häufig zusätzlich ein Flag setzen. Setzen Sie bei [Uppy](/de/docs/sdks/uppy.md) den Parameter`waitForEncoding` auf `false`. In vielen Backend-SDKs wird beim Warten auf das Encoding derAssembly Status ausdrücklich per Polling abgefragt. Verzichten Sie einfach darauf.

Ihr Backend muss mit einem `200`-Header antworten. Andernfalls geht Transloadit davon aus, dass die Notification fehlgeschlagen ist, und versucht die Zustellung einige Male mit exponentiellem Backoff erneut.

## Nutzlast der Notification anpassen

Standardmäßig enthalten Webhook-Anfragen das vollständigeAssembly Status JSON. Wenn Ihre Assemblies viele Ergebnisse erzeugen, können Sie die Nutzlast mit `notification_payload` verkleinern.

Fügen Sie es neben `notify_url` hinzu:

```jsonc
{
  "steps": {
    // …
  },
  "notify_url": "https://example.com/transloadit_pingback",
  "notification_payload": ["without_results", "without_upload_meta_data"]
}

```

Unterstützte Werte:

* `without_results`: Lässt das `results`-Objekt der obersten Ebene in der Webhook-Nutzlast weg
* `without_result_meta_data`: Behält `results` bei, entfernt aber `meta` aus jeder Ergebnisdatei
* `without_uploads`: Lässt das `uploads`-Array der obersten Ebene in der Webhook-Nutzlast weg
* `without_upload_meta_data`: Behält `uploads` bei, entfernt aber `meta` aus jeder hochgeladenen Datei

Sie können mehrere Werte im selben Array kombinieren. Dies gilt auch, wenn Sie Notifications über die Assembly-Seite erneut abspielen.

## Wie sieht diese POST-Anfrage aus?

Diese mehrteilige POST-Anfrage enthält ein Feld namens `transloadit` mit dem vollständigenAssembly Status JSON. Ein Beispiel dafür finden Sie in unserer[Dokumentation zur API-Antwort](/de/docs/api/assemblies-assembly-id-get.md#response). Sie enthält außerdem ein Feld `signature`, mit dem Sie optional überprüfen können, ob die Anfrage tatsächlich von uns stammt und nicht manipuliert wurde. Das folgende Codebeispiel zeigt, wie Sie diese Signatur mit IhremAuth Secret berechnen, sodass sie mit der von uns gesendeten Signatur übereinstimmt.

## Codebeispiel

Nehmen wir an, Sie hätten tatsächlich `"notify_url": "https://example.com/transloadit_pingback"`angegeben und der Backend-Server, der dort eingehende POST-Anfragen entgegennimmt, wäre in Node.js geschrieben.

###### Hinweis

Dieses Beispiel zeigt, wie Sie eingehende Webhook-Signaturen von Transloadit **überprüfen**. Dies unterscheidet sich vom **Erzeugen** von Signaturen für API-Anfragen. Verwenden Sie stattdessen unsere[SDKs](/de/docs/sdks.md), um Assemblies mit automatischer Signaturerzeugung zu erstellen. Wenn Sie sehen möchten, wie die Signaturprüfung intern funktioniert, können Sie den[Quellcode des Node-SDKs](https://github.com/transloadit/node-sdk/blob/main/src/Transloadit.ts) aufrufen.

Der Prüfserver könnte so aussehen:

```js
import crypto from 'node:crypto'
import http from 'node:http'

import formidable from 'formidable'

const PORT = process.argv[2] || 3020

if (!/^[A-Za-z0-9]{40}$/.test(process.env.AUTH_SECRET)) {
  throw new Error(`Please pass the secret from https://transloadit.com/c/template-credentials
    via the AUTH_SECRET environment var. It must be the auth secret that belongs to the auth key you used for the original Assembly.`)
}

const checkSignature = (fields, authSecret) => {
  const receivedSignature = fields.signature
  const payload = fields.transloadit

  if (!receivedSignature || !payload) {
    return false
  }

  // If the signature contains a colon, we expect it to be of format `algo:actual_signature`.
  // If there are no colons, we assume it's a legacy signature using SHA-1.
  const algoSeparatorIndex = receivedSignature.indexOf(':')
  const algo = algoSeparatorIndex === -1 ? 'sha1' : receivedSignature.slice(0, algoSeparatorIndex)

  try {
    const calculatedSignature = crypto
      .createHmac(algo, authSecret)
      .update(Buffer.from(payload, 'utf-8'))
      .digest('hex')

    // If we are in legacy signature mode, algoSeparatorIndex is -1 and we are
    // comparing the whole string. Otherwise we slice out the prefixed algo.
    return calculatedSignature === receivedSignature.slice(algoSeparatorIndex + 1)
  } catch {
    // We can assume the signature string was ill-formed.
    return false
  }
}

const respond = (res, code, messages) => {
  if (code !== 200) {
    console.error({ messages, code })
  }

  res.writeHead(code, {
    'Content-Type': 'application/json',
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': 'OPTIONS, POST, GET',
  })

  if (messages) {
    res.write(JSON.stringify({ messages }))
  }

  res.end()
}

http
  .createServer((req, res) => {
    if (req.method === 'OPTIONS') {
      return respond(res, 204)
    }
    if (req.url === '/transloadit_pingback' && req.method === 'POST') {
      const form = new formidable.IncomingForm()
      form.parse(req, (err, fields) => {
        if (err) {
          return respond(res, 500, [`Error while parsing multipart form`, err])
        }

        if (!checkSignature(fields, process.env.AUTH_SECRET)) {
          return respond(res, 403, [
            `Error while checking signatures`,
            `No match so payload was tampered with, or an invalid Auth Secret was used`,
          ])
        }

        let assembly = {}
        try {
          assembly = JSON.parse(fields.transloadit)
        } catch (err) {
          return respond(res, 500, [`Error while parsing transloadit field`, err])
        }

        console.log(`--> ${assembly.ok || assembly.error} ${assembly.assembly_ssl_url}`)

        for (const upload of assembly.uploads) {
          // save upload.ssl_url and metadata to your db here
          console.log(`    ^- uploaded '${upload.name}' ready at ${upload.ssl_url}`)
        }

        for (const stepName in assembly.results) {
          for (const result of assembly.results[stepName]) {
            // save result.ssl_url and metadata to your db here
            console.log(`    ^- ${stepName} '${result.name}' ready at ${result.ssl_url}`)
          }
        }

        return respond(res, 200, [`Success!`])
      })
    } else {
      return respond(res, 500, [
        `Welcome! I only know how to handle POSTs to /transloadit_pingback`,
        `No handler for req.url=${req.url}, req.method=${req.method}`,
      ])
    }
  })
  .listen(PORT, () => {
    console.log(`Server started, listening on http://0.0.0.0:${PORT}`)
  })

```

Sie könnten dieses Skript wie folgt ausführen:

```console
$ env AUTH_SECRET=******** node notification-backend-node.js 3020
Server started, listening on http://0.0.0.0:3020

```

## Codebeispiel lokal testen

Verwenden Sie für lokale Tests hinter einem NAT [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/), [ngrok](https://ngrok.com/) oder das von uns bereitgestellte [@transloadit/notify-url-relay](https://www.npmjs.com/package/@transloadit/notify-url-relay) über `npx -y @transloadit/notify-url-relay`. Anders als Tunnel fragt das Relay den öffentlichen Assembly Status per Polling ab und leitet abschließende Notifications an Ihren lokalen `notify_url`-Handler weiter.

Empfohlen (von uns bereitgestelltes Relay), in einem neuen Tab:

```console
$ TRANSLOADIT_SECRET=******** npx -y @transloadit/notify-url-relay \
    --notifyUrl "http://127.0.0.1:3020/transloadit_pingback" \
    --log-level info
notify-url-relay [ NOTICE] Listening on http://localhost:8888, forwarding to https://api2.transloadit.com, notifying http://127.0.0.1:3020/transloadit_pingback

```

Wenn Sie das Relay verwenden, richten Sie den Transloadit-Endpunkt Ihrer App oder Ihres SDKs auf `http://127.0.0.1:8888`.

Sie können jetzt [ein Template erstellen](/de/docs/topics/templates.md#how-to-create-a-template) und die folgenden Instructions einfügen:

```json
{
  "notify_url": "http://127.0.0.1:3020/transloadit_pingback",
  "steps": {
    ":original": {
      "robot": "/upload/handle"
    },
    "faces_detected": {
      "use": ":original",
      "robot": "/image/facedetect",
      "crop": true,
      "faces": "max-confidence",
      "crop_padding": "10%",
      "format": "preserve"
    }
  }
}

```

Wenn Sie stattdessen einen Tunnel bevorzugen, verwenden Sie [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/) oder [ngrok](https://ngrok.com/).

###### Hinweis

Zum Zeitpunkt der Erstellung[scheint ngrok Probleme mit AWS-Bereichen zu haben](https://github.com/inconshreveable/ngrok/issues/408#issuecomment-791161309). Sollte dies bei Ihnen der Fall sein, können Sie alternativ[Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/)oder das von uns bereitgestellte [@transloadit/notify-url-relay](https://www.npmjs.com/package/@transloadit/notify-url-relay) ausprobieren.

Jetzt können Sie den Test direkt im Browser durchführen. Die von uns verwendeten Instructions erkennen ein Gesicht. Laden Sie daher für optimale Ergebnisse über den Testbereich des Template-Editors ein Foto einer Person hoch. Wenn Sie kein Bild zur Hand haben, können Sie Uppys Webcam-Funktion verwenden.

Ihr Node.js-Skript sollte melden, dass es die Assembly Notification erfolgreich empfangen hat, wenn die Assembly abgeschlossen ist:

```plaintext
-- > ASSEMBLY_COMPLETED https://api2.transloadit.com/assemblies/b2b580bdc969427091a48f1f0d3d9d40
^- uploaded 'avatar.jpg' ready at https://s3.amazonaws.com/tmp.transloadit.com/ff89be82...
^- faces_detected 'avatar.jpg' ready at https://s3.amazonaws.com/tmp.transloadit.com/fd2f61b9...

```

Zusätzlich sehen Sie auf der Assembly-Seite einen Eintrag zur Notification. Dort können Sie die Zustellung für weitere Tests auch manuell erneut versuchen.

Das obige Beispiel zeigt, wie Sie Webhook-Signaturen in Node.js überprüfen. Informationen zum Erstellen von Assemblies mit automatischer Signaturerzeugung statt zur Überprüfung eingehender Webhooks finden Sie in den SDK-Beispielen der[Dokumentation zu Signature Authentication](/de/docs/api/authentication.md).
