Docs

Webhooks

Zuletzt aktualisiert: 2026-10-036 Min. Lesezeit

Ein Webhook ist ein POST, das GREM an Ihre URL sendet, wenn etwas passiert: Ein Auftrag endet, ein Objekt oder ein Lead ändert sich. Die Verwaltung von Abonnements braucht den Bereich webhooks und ist kostenlos. Webhooks einer Agentur werden mit dem agenturweiten Schlüssel verwaltet; ein Agentenschlüssel erhält 403.

Abonnieren

curl -X POST https://developers.grem.capital/api/v1/webhooks \
  -H "Authorization: Bearer gsk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/grem-hook","events":["content.completed","content.failed","lead.created"]}'
{ "id": "...", "url": "https://example.com/grem-hook", "events": [...], "status": "active",
  "failureCount": 0, "lastDeliveryAt": null, "secret": "whsec_..." }
  • url - eine öffentliche https://-Adresse auf dem Standardport (443), ohne user:password darin. Andere Ports, Zugangsdaten in der Adresse sowie private, lokale und interne Adressen werden abgelehnt (400). Die Portregel gilt beim Setzen einer Adresse: Früher mit einem anderen Port gespeicherte Adressen erhalten weiterhin Zustellungen.
  • events - mindestens ein Ereignis aus der Liste unten; unbekannte Namen werden verworfen.
  • secret - das Signaturgeheimnis. Es wird nur einmal zurückgegeben - speichern Sie es.
  • Bis zu 10 Adressen pro Konto und getrennt davon bis zu 10 Sandbox-Adressen, die mit Testschlüsseln angelegt wurden; die 11. wird mit 422 business_rule abgelehnt.
  • Mit einem Testschlüssel angelegte Adressen sind Sandbox-Adressen: Sie erhalten nur Ereignisse von Sandbox-Aufträgen (Sandbox).

Abonnements lassen sich auch im Kabinett verwalten: API → Webhooks. Dort können Sie auch das Signaturgeheimnis ändern.

Verwaltung

AnfrageWas sie tut
GET /webhooksIhre Abonnements (ohne Geheimnisse)
PATCH /webhooks/{id}url, events oder status (active / disabled) ändern
DELETE /webhooks/{id}Abonnement löschen
POST /webhooks/{id}/testProbeereignis senden; Body {"eventType": "content.completed"} bestimmt die Form
GET /webhooks/{id}/deliveriesdie letzten 50 Zustellungen: Status, Antwortcode, Latenz, Antwortanfang
POST /webhooks/{id}/deliveries/{deliveryId}/resendZustellung erneut senden; deliveryId ist die _id einer Zeile aus dem Protokoll

PATCH ohne ein gültiges Ereignis in events oder mit einem status außer active / disabled wird mit 400 abgelehnt. DELETE eines unbekannten Abonnements antwortet mit 404. Im Zustellprotokoll ist payload das data des Ereignisses.

Ereignisse

Fertige Aufträge

EreignisWann
valuation.completed, valuation.failedvollständiger Bewertungsbericht
review.completed, review.failedBerichtsprüfung
content.completed, content.partial, content.failedContent-Paket (partial - einige Dateien fehlgeschlagen)
text.completed, text.failedTexte
video.completed, video.failedVideo
avatar.completed, avatar.failedAvatar-Video
photo.completed, photo.failedFotoverbesserung
floorplan.completed, floorplan.failedGrundriss
pdf.completed, pdf.failedPDF-Präsentation

data: { "jobId", "status", "state" }. status ist der Rohstatus des Werkzeugs in Großbuchstaben (zum Beispiel COMPLETED) - verzweigen Sie nach state. Ist ein Auftrag nach 30 Minuten nicht fertig - .failed mit "reason": "timeout". Ein Auftrag wird nur verfolgt, wenn beim Anlegen ein Abonnement für sein Ereignis bestand (Aufträge und Ergebnisse).

Objekte

EreignisWann
object.createdSie haben ein Objekt erstellt
object.updatedein Objekt wurde geändert, auch von einem Moderator
object.publishedein Objekt wurde öffentlich
object.deletedein Entwurf wurde gelöscht oder ein veröffentlichtes Objekt archiviert

data: { "objectId", "kind", "status", "occurredAt" }; bei object.deleted statt status - mode (deleted oder archived). Vollständige Daten - GET /objects/{kind}/{id}.

Leads

EreignisWann
lead.createdein neuer Lead an Sie
lead.updatedStatus oder Verantwortlicher geändert

data: { "leadId", "status", "type", "source", "objectId", "occurredAt" }. Keine personenbezogenen Daten des Kunden im Ereignis - den Lead lesen Sie mit GET /leads/{id}.

Swipe Match

EreignisWann
match.lead.offeredIhnen (oder einem Agenten Ihrer Agentur) wurde ein Lead angeboten
match.lead.offer_closeddas Angebot wurde ohne Sie geschlossen (Zeit abgelaufen)
match.lead.matchedder Lead wurde Ihnen zugewiesen
match.lead.updatedder Lead ist in eine neue Phase gewechselt
match.lead.unassignedder Lead wurde Ihnen entzogen

data: { "leadId", "eventId", "eventCode", "occurredAt", "source", "stage", "active", "leadKind", "clientType", "country", "brokerId", "agencyId" }. Name, Telefon und E-Mail des Kunden sind nie in Ereignissen (Swipe Match).

Zustellformat

POST https://example.com/grem-hook
Content-Type: application/json
x-grem-event: content.completed
x-grem-signature: t=1759400000,v1=3b78a03b78058d229023db2a0b4ee072...

{ "id": "...", "type": "content.completed", "createdAt": "2026-10-02T10:00:00.000Z", "data": { ... } }
  • id ist in allen Wiederholungen einer Zustellung gleich - nutzen Sie es, um ein Ereignis nicht doppelt zu verarbeiten.
  • t und createdAt sind der Zeitpunkt des konkreten Versuchs.
  • Eine Testzustellung (POST /webhooks/{id}/test) mit einem eventType aus der Liste trägt test: true in data; ohne ihn ist der Ereignistyp webhook.test. Ein eventType, der nicht in der Liste steht, ist 400. webhook.test selbst wird angenommen.

Signatur prüfen

Die Signatur ist ein HMAC-SHA256 über den String "<t>.<roher Body>" mit dem Abonnementgeheimnis, hexadezimal in v1. Prüfen Sie vor jeder Verarbeitung und verwerfen Sie veraltete t (Schutz vor Wiedereinspielung).

Berechnen Sie die Signatur über den rohen Body, genau wie empfangen, vor dem JSON-Parsen. Erneutes Serialisieren ändert die Bytes, und die Signatur stimmt nicht.

const crypto = require('crypto');

function verifyGremSignature(secret, header, rawBody, toleranceSec = 300) {
  const parts = Object.fromEntries(String(header || '').split(',').map((kv) => kv.split('=')));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1 || '');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

app.post('/grem-hook', express.raw({ type: 'application/json' }), (req, res) => {
  const raw = req.body.toString('utf8');
  if (!verifyGremSignature(process.env.GREM_WEBHOOK_SECRET, req.get('x-grem-signature'), raw)) {
    return res.status(400).send('invalid signature');
  }
  const event = JSON.parse(raw);
  // event.id, event.type, event.data
  res.sendStatus(200);
});

Im SDK erledigt das verifyWebhookSignature().

Wiederholungen und Deaktivierung

  • Eine Zustellung ist erfolgreich, wenn Ihr Server innerhalb von 10 Sekunden mit 2xx antwortet. Antworten Sie schnell, schwere Arbeit im Hintergrund.
  • Eine fehlgeschlagene Zustellung wird nach 5 Minuten, 30 Minuten, 2 Stunden und 6 Stunden wiederholt - insgesamt bis zu 5 Versuche.
  • Nach 5 vollständig fehlgeschlagenen Zustellungen in Folge wird das Abonnement deaktiviert (status: disabled). Aktivieren Sie es wieder mit PATCH /webhooks/{id} und {"status":"active"} oder im Kabinett.
  • Das Zustellprotokoll (GET /webhooks/{id}/deliveries) zeigt den Antwortcode Ihres Servers und den Anfang des Antwortbodys - daran sehen Sie, warum eine Zustellung fehlschlug.

Verwandte Artikel

War dieser Artikel hilfreich?