# Webhooks

Statt darauf zu warten, dass eine Assembly abgeschlossen wird und die API-Anfrage beantwortet, können Sie auch einen Webhook konfigurieren, der auch als Assembly Notification bezeichnet wird. Sobald eine Assembly endet, 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 können. Ohne Datei-Uploads könnten sie das Browserfenster sogar sofort schließen.

## Wie aktivieren Sie Webhooks?

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

![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_6ZskK4ZaqZ1GYVxrmc4Qz2KncJJA)

```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 wird eine `POST`-Anfrage mit dem vollständigen Assembly Status JSON an die definierte URL gesendet.

Wenn Ihre Nutzer oder Ihr Programm nicht auf das Encoding warten sollen, müssen Sie häufig außerdem ein Flag setzen. Legen Sie bei [Uppy](/docs/sdks/uppy.md) den Parameter `waitForEncoding` auf `false`fest. In vielen Backend-SDKs wird beim Warten auf das Encoding explizit der Assembly Statusabgefragt. Verzichten Sie einfach darauf.

Ihr Backend muss mit einem `200`-Header antworten. Andernfalls geht Transloadit davon aus, dass die Notification fehlgeschlagen ist, und wiederholt sie mehrmals mit exponentiellem Backoff.

## 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:

![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_6ZskK4ZaqZ1GYVxrmc4Qz2KncJJA)

```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 übergeordnete Objekt `results` in der Webhook-Nutzlast aus
* `without_result_meta_data`: behält `results` bei, entfernt jedoch `meta` aus jeder Ergebnisdatei
* `without_uploads`: lässt das übergeordnete Array `uploads` in der Webhook-Nutzlast aus
* `without_upload_meta_data`: behält `uploads` bei, entfernt jedoch `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 wiedergeben.

## 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 zu API-Antworten](/docs/api/assemblies-assembly-id-get.md#response). Sie enthält außerdem ein Feld `signature`, mit dem Sie optional prü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 und mit der von uns gesendeten Signatur abgleichen.

## Codebeispiel

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

###### Hinweis

Dieses Beispiel zeigt, wie Sie eingehende Webhook-Signaturen von Transloadit **prüfen**. Dies unterscheidet sich vom **Generieren** von Signaturen für API-Anfragen. Verwenden Sie stattdessen unsere [SDKs](/docs/sdks.md), um Assemblies mit automatischer Signaturgenerierung zu erstellen. Wenn Sie nachvollziehen 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) einsehen.

Der Prüfserver könnte wie folgt aussehen:

![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_6ZskK4ZaqZ1GYVxrmc4Qz2KncJJA)

```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önnen dieses Skript wie folgt ausführen:

![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_6ZskK4ZaqZ1GYVxrmc4Qz2KncJJA)

```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 beim lokalen Testen hinter einem NAT [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/), [ngrok](https://ngrok.com/) oder das von uns angebotene [@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 ab und leitet abschließende Notifications an Ihren lokalen `notify_url`-Handler weiter.

Empfohlen (unser Relay), in einem neuen Tab:

![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_6ZskK4ZaqZ1GYVxrmc4Qz2KncJJA)

```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, legen Sie `http://127.0.0.1:8888` als Transloadit-Endpunkt Ihrer App oder Ihres SDKs fest.

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

![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_6ZskK4ZaqZ1GYVxrmc4Qz2KncJJA)

```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 angebotene [@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 verwendeten Instructionserkennen ein Gesicht. Laden Sie daher für optimale Ergebnisse im Testbereich des Template Editor ein Foto einer Person hoch. Wenn Ihnen kein Bild zur Verfügung steht, können Sie die Webcam-Funktion von Uppy verwenden.

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

![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_6ZskK4ZaqZ1GYVxrmc4Qz2KncJJA)

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

```

Außerdem sehen Sie auf der Assembly-Seite einen Eintrag zur Notification. Dort können Sie die Notification für weitere Tests auch manuell erneut senden.

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