Webhooks

Webhooks

Gemlead schickt eine HTTPS-Anfrage an deinen Server, sobald ein Lead gefunden wird, heiß wird, antwortet oder auf deine Nachricht reagiert, oder sobald Sam AI eine Antwort bereithält, eine sendet oder dir eine Unterhaltung übergibt. Sie ist signiert, damit du weißt, dass sie von uns kommt.

Endpoint einrichten

Füge in der App unter API einen Endpoint hinzu oder nutze Webhook-Endpoint erstellen. Gib ihm eine öffentliche HTTPS-URL und die gewünschten Ereignistypen, und bewahre das zurückgegebene secret auf, um Signaturen zu prüfen.

  • Nur HTTPS, keine Zugangsdaten in der URL und keine private, Loopback- oder Link-Local-Adresse.
  • Bis zu 10 Endpoints pro Konto.
  • filters.persona_ids beschränkt die Ereignisse eines Endpoints auf die Leads bestimmter Personas, egal welchen Typs.
  • Sende ein Testereignis, um deinen Server zu prüfen, bevor echte Ereignisse ankommen.

Webhooks sind in den Tarifen Business und Enterprise enthalten. Wechselt das Konto zu Free oder Starter, bleiben seine Endpoints erhalten, senden aber nichts, bis es wieder Business oder Enterprise nutzt.

POST/v1/webhook_endpoints
curl -X POST https://gemlead.ai/api/v1/webhook_endpoints \
  -H "Authorization: Bearer $GEMLEAD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks/gemlead",
    "events": ["lead.hot", "reply.received"],
    "description": "CRM sync",
    "filters": {
      "persona_ids": ["lvWBPnytpS"]
    }
  }'

Ereignistypen

Jedes Ereignis hat denselben Aufbau. Sein data hängt von seinem type ab.

TypGesendet, wenn
lead.createdGemlead findet und bewertet einen neuen Lead, egal mit welchem Score. Jeder Lead löst es einmal aus, mit seinem ersten Score, und nur innerhalb von 3 Tagen, nachdem er gefunden wurde. Eine neue Bewertung eines älteren Leads löst es also nie aus. Ein Lead, der gleich 60 oder mehr erreicht, löst auch lead.hot aus.
lead.hotDer Score eines Leads erreicht zum ersten Mal 60 oder mehr. Jeder Lead löst es einmal aus, eine neue Bewertung nie wieder.
reply.receivedEin Lead schickt dir eine neue Nachricht, ob in einer Kampagne oder nicht. Jede Nachricht löst es einmal aus, wenn sie jünger als 6 Stunden ist. Kommen viele Nachrichten des Leads auf einmal an, lösen es nur die 5 neuesten aus.
reaction.receivedEin Lead reagiert mit einem Emoji auf eine deiner Nachrichten. Jedes Emoji des Leads auf einer Nachricht löst es einmal aus.
sam.suggestion.readySam AI hält in einer Unterhaltung im Modus draft eine neue Antwort bereit, die du senden kannst. Jeder Vorschlag löst es einmal aus, wenn er jünger als 6 Stunden ist.
sam.reply.sentEine Antwort von Sam AI geht von deinem X-Konto raus, selbstständig im Modus auto oder sobald du sie in der App freigibst. Jede Antwort löst es einmal aus, wenn sie jünger als 6 Stunden ist. Nachrichten, die du selbst schreibst, lösen es nie aus, du findest sie über Unterhaltung abrufen.
sam.handoffSam AI übergibt dir eine Unterhaltung, wenn er ohne dich keine gute Antwort sieht, der Lead nur ein Foto oder eine Sprachnachricht geschickt hat oder seine Antwort nicht rausgehen konnte. Im Modus auto überlässt er dir danach die nächsten Nachrichten des Leads. Jede Übergabe löst es einmal aus, wenn sie jünger als 6 Stunden ist.

Das Ereignisobjekt

  • idstring
    Eindeutige Kennung des Ereignisses, evt_ und 24 Zeichen. Steht auch im Header Gemlead-Event-Id.
  • objectstring
    Immer event.
  • typestring
    Der Ereignistyp.
    lead.createdlead.hotreply.receivedreaction.receivedsam.suggestion.readysam.reply.sentsam.handoff
  • created_atstring
    Wann das Ereignis eingetreten ist, ISO 8601.
  • account_idstring
    Das Gemlead-Konto, in dem es eingetreten ist.
  • dataobject
    Der Inhalt des Ereignisses, abhängig von seinem Typ.

lead.created

Gemlead findet und bewertet einen neuen Lead, egal mit welchem Score. Jeder Lead löst es einmal aus, mit seinem ersten Score, und nur innerhalb von 3 Tagen, nachdem er gefunden wurde. Eine neue Bewertung eines älteren Leads löst es also nie aus. Ein Lead, der gleich 60 oder mehr erreicht, löst auch lead.hot aus.

Daten

lead.created
{
  "id": "evt_8kQ2vXr4LmT9wPz1Hc6YbN3d",
  "object": "event",
  "type": "lead.created",
  "created_at": "2026-11-05T14:03:40.117Z",
  "account_id": "a9PzK2vRtM",
  "data": {
    "object": {
      "id": "k3VnQ8wZpR",
      "object": "lead",
      "profile": {
        "x_user_id": "1592047713",
        "username": "mark_picks",
        "name": "Mark Ellis",
        "bio": "Free NBA picks every day. Sports fan first.",
        "followers_count": 2150,
        "following_count": 940,
        "location": "Denver, CO",
        "website": "https://markpicks.net",
        "profile_image_url": "https://pbs.twimg.com/profile_images/1592047713/mark_400x400.jpg",
        "verified": false,
        "url": "https://x.com/mark_picks"
      },
      "score": 38,
      "score_reason": "Shares free picks for fun and sells nothing yet, no sign of a paying community.",
      "hot": false,
      "status": "scored",
      "persona": {
        "id": "lvWBPnytpS",
        "name": "Tipsters"
      },
      "campaign": null,
      "conversation_id": null,
      "locked_to": null,
      "found_at": "2026-11-05T14:02:11.482Z",
      "scored_at": "2026-11-05T14:03:40.117Z",
      "updated_at": "2026-11-05T14:03:40.117Z",
      "metadata": {}
    }
  }
}

lead.hot

Der Score eines Leads erreicht zum ersten Mal 60 oder mehr. Jeder Lead löst es einmal aus, eine neue Bewertung nie wieder.

Daten

lead.hot
{
  "id": "evt_8kQ2vXr4LmT9wPz1Hc6YbN3d",
  "object": "event",
  "type": "lead.hot",
  "created_at": "2026-11-05T14:03:40.117Z",
  "account_id": "a9PzK2vRtM",
  "data": {
    "object": {
      "id": "msgOtJ8dvT",
      "object": "lead",
      "profile": {
        "x_user_id": "1460283925",
        "username": "jane_builds",
        "name": "Jane Doe",
        "bio": "Runs a paid trading community. Daily setups, no hype.",
        "followers_count": 12400,
        "following_count": 380,
        "location": "Austin, TX",
        "website": "https://janedoe.com",
        "profile_image_url": "https://pbs.twimg.com/profile_images/1460283925/jane_400x400.jpg",
        "verified": true,
        "url": "https://x.com/jane_builds"
      },
      "score": 86,
      "score_reason": "Runs a paid trading community and asks how to collect payments for it.",
      "hot": true,
      "status": "scored",
      "persona": {
        "id": "lvWBPnytpS",
        "name": "Tipsters"
      },
      "campaign": null,
      "conversation_id": null,
      "locked_to": null,
      "found_at": "2026-11-05T14:02:11.482Z",
      "scored_at": "2026-11-05T14:03:40.117Z",
      "updated_at": "2026-11-05T14:03:40.117Z",
      "metadata": {}
    }
  }
}

reply.received

Ein Lead schickt dir eine neue Nachricht, ob in einer Kampagne oder nicht. Jede Nachricht löst es einmal aus, wenn sie jünger als 6 Stunden ist. Kommen viele Nachrichten des Leads auf einmal an, lösen es nur die 5 neuesten aus.

Daten

  • leadobject
    Der Lead, der geschrieben hat.
  • messageobject
    Die neue Nachricht, mit dem Wert inbound in direction.
  • campaignobject oder null
    Die Kampagne des Leads, mit id, name und status.
  • conversation_idstring
    Damit holst du über Unterhaltung abrufen den ganzen Verlauf.
reply.received
{
  "id": "evt_8kQ2vXr4LmT9wPz1Hc6YbN3d",
  "object": "event",
  "type": "reply.received",
  "created_at": "2026-11-05T14:03:40.117Z",
  "account_id": "a9PzK2vRtM",
  "data": {
    "lead": {
      "id": "msgOtJ8dvT",
      "object": "lead",
      "profile": {
        "x_user_id": "1460283925",
        "username": "jane_builds",
        "name": "Jane Doe",
        "bio": "Runs a paid trading community. Daily setups, no hype.",
        "followers_count": 12400,
        "following_count": 380,
        "location": "Austin, TX",
        "website": "https://janedoe.com",
        "profile_image_url": "https://pbs.twimg.com/profile_images/1460283925/jane_400x400.jpg",
        "verified": true,
        "url": "https://x.com/jane_builds"
      },
      "score": 86,
      "score_reason": "Runs a paid trading community and asks how to collect payments for it.",
      "hot": true,
      "status": "in_conversation",
      "persona": {
        "id": "lvWBPnytpS",
        "name": "Tipsters"
      },
      "campaign": {
        "id": "c7RkQ2wLxe",
        "name": "Tipsters warm intro",
        "status": "in_progress"
      },
      "conversation_id": "msgOtJ8dvT",
      "locked_to": null,
      "found_at": "2026-11-05T14:02:11.482Z",
      "scored_at": "2026-11-05T14:03:40.117Z",
      "updated_at": "2026-11-05T14:03:40.117Z",
      "metadata": {}
    },
    "message": {
      "id": "1985321409876543210",
      "object": "message",
      "direction": "inbound",
      "text": "Yes, I want to charge for my Discord. How does it work?",
      "created_at": "2026-11-06T08:12:44.000Z",
      "media": [],
      "reactions": [],
      "encrypted": false,
      "reply_to_message_id": null
    },
    "campaign": {
      "id": "c7RkQ2wLxe",
      "name": "Tipsters warm intro",
      "status": "in_progress"
    },
    "conversation_id": "msgOtJ8dvT"
  }
}

reaction.received

Ein Lead reagiert mit einem Emoji auf eine deiner Nachrichten. Jedes Emoji des Leads auf einer Nachricht löst es einmal aus.

Daten

  • leadobject
    Der Lead, der reagiert hat.
  • messageobject
    Deine Nachricht, auf die der Lead reagiert hat, mit dem Wert outbound in direction.
  • reactionobject
    Die neue Reaktion, mit dem Wert inbound in direction.
reaction.received
{
  "id": "evt_8kQ2vXr4LmT9wPz1Hc6YbN3d",
  "object": "event",
  "type": "reaction.received",
  "created_at": "2026-11-05T14:03:40.117Z",
  "account_id": "a9PzK2vRtM",
  "data": {
    "lead": {
      "id": "msgOtJ8dvT",
      "object": "lead",
      "profile": {
        "x_user_id": "1460283925",
        "username": "jane_builds",
        "name": "Jane Doe",
        "bio": "Runs a paid trading community. Daily setups, no hype.",
        "followers_count": 12400,
        "following_count": 380,
        "location": "Austin, TX",
        "website": "https://janedoe.com",
        "profile_image_url": "https://pbs.twimg.com/profile_images/1460283925/jane_400x400.jpg",
        "verified": true,
        "url": "https://x.com/jane_builds"
      },
      "score": 86,
      "score_reason": "Runs a paid trading community and asks how to collect payments for it.",
      "hot": true,
      "status": "in_conversation",
      "persona": {
        "id": "lvWBPnytpS",
        "name": "Tipsters"
      },
      "campaign": {
        "id": "c7RkQ2wLxe",
        "name": "Tipsters warm intro",
        "status": "in_progress"
      },
      "conversation_id": "msgOtJ8dvT",
      "locked_to": null,
      "found_at": "2026-11-05T14:02:11.482Z",
      "scored_at": "2026-11-05T14:03:40.117Z",
      "updated_at": "2026-11-05T14:03:40.117Z",
      "metadata": {}
    },
    "message": {
      "id": "1985329981234567890",
      "object": "message",
      "direction": "outbound",
      "text": "Happy to show you. Do you sell picks monthly or per event?",
      "created_at": "2026-11-06T08:20:03.000Z",
      "media": [],
      "reactions": [
        {
          "emoji": "👍",
          "direction": "inbound",
          "created_at": "2026-11-06T08:24:51.000Z"
        }
      ],
      "encrypted": false,
      "reply_to_message_id": "1985321409876543210"
    },
    "reaction": {
      "emoji": "👍",
      "direction": "inbound",
      "created_at": "2026-11-06T08:24:51.000Z"
    }
  }
}

sam.suggestion.ready

Sam AI hält in einer Unterhaltung im Modus draft eine neue Antwort bereit, die du senden kannst. Jeder Vorschlag löst es einmal aus, wenn er jünger als 6 Stunden ist.

Daten

  • leadobject
    Der Lead, dem Sam AI antwortet.
  • conversation_idstring
    Damit liest du über Sam AI einer Unterhaltung abrufen den Vorschlag erneut oder änderst den Modus.
  • campaignobject oder null
    Die Kampagne des Leads, mit id, name und status.
  • suggestionobject
    Die Antwort, die Sam AI bereithält.
sam.suggestion.ready
{
  "id": "evt_8kQ2vXr4LmT9wPz1Hc6YbN3d",
  "object": "event",
  "type": "sam.suggestion.ready",
  "created_at": "2026-11-05T14:03:40.117Z",
  "account_id": "a9PzK2vRtM",
  "data": {
    "lead": {
      "id": "msgOtJ8dvT",
      "object": "lead",
      "profile": {
        "x_user_id": "1460283925",
        "username": "jane_builds",
        "name": "Jane Doe",
        "bio": "Runs a paid trading community. Daily setups, no hype.",
        "followers_count": 12400,
        "following_count": 380,
        "location": "Austin, TX",
        "website": "https://janedoe.com",
        "profile_image_url": "https://pbs.twimg.com/profile_images/1460283925/jane_400x400.jpg",
        "verified": true,
        "url": "https://x.com/jane_builds"
      },
      "score": 86,
      "score_reason": "Runs a paid trading community and asks how to collect payments for it.",
      "hot": true,
      "status": "in_conversation",
      "persona": {
        "id": "lvWBPnytpS",
        "name": "Tipsters"
      },
      "campaign": {
        "id": "c7RkQ2wLxe",
        "name": "Tipsters warm intro",
        "status": "in_progress"
      },
      "conversation_id": "msgOtJ8dvT",
      "locked_to": null,
      "found_at": "2026-11-05T14:02:11.482Z",
      "scored_at": "2026-11-05T14:03:40.117Z",
      "updated_at": "2026-11-05T14:03:40.117Z",
      "metadata": {}
    },
    "conversation_id": "msgOtJ8dvT",
    "campaign": {
      "id": "c7RkQ2wLxe",
      "name": "Tipsters warm intro",
      "status": "in_progress"
    },
    "suggestion": {
      "messages": [
        "It takes two minutes. You set a price, members pay, and they get their Discord role right away.",
        "Want the link to set it up?"
      ],
      "stage": "answer_question",
      "why": "She asked how it works, so the reply explains it in one line and offers the link.",
      "replying_to_message_id": "1985321409876543210",
      "created_at": "2026-11-06T08:13:20.000Z"
    }
  }
}

sam.reply.sent

Eine Antwort von Sam AI geht von deinem X-Konto raus, selbstständig im Modus auto oder sobald du sie in der App freigibst. Jede Antwort löst es einmal aus, wenn sie jünger als 6 Stunden ist. Nachrichten, die du selbst schreibst, lösen es nie aus, du findest sie über Unterhaltung abrufen.

Daten

  • leadobject
    Der Lead, dem Sam AI geantwortet hat.
  • conversation_idstring
    Damit holst du über Unterhaltung abrufen den ganzen Verlauf.
  • campaignobject oder null
    Die Kampagne des Leads, mit id, name und status.
  • messagesArray von strings
    Die DMs, die Sam AI gesendet hat, in ihrer Reihenfolge.
  • sent_bystring
    auto, wenn Sam AI sie selbstständig gesendet hat, approved, wenn du sie in der App freigegeben hast.
    autoapproved
  • replying_to_message_idstring oder null
    Die Nachricht des Leads, auf die sie geantwortet hat.
  • sent_atstring
    Wann sie rausging, ISO 8601.
sam.reply.sent
{
  "id": "evt_8kQ2vXr4LmT9wPz1Hc6YbN3d",
  "object": "event",
  "type": "sam.reply.sent",
  "created_at": "2026-11-05T14:03:40.117Z",
  "account_id": "a9PzK2vRtM",
  "data": {
    "lead": {
      "id": "msgOtJ8dvT",
      "object": "lead",
      "profile": {
        "x_user_id": "1460283925",
        "username": "jane_builds",
        "name": "Jane Doe",
        "bio": "Runs a paid trading community. Daily setups, no hype.",
        "followers_count": 12400,
        "following_count": 380,
        "location": "Austin, TX",
        "website": "https://janedoe.com",
        "profile_image_url": "https://pbs.twimg.com/profile_images/1460283925/jane_400x400.jpg",
        "verified": true,
        "url": "https://x.com/jane_builds"
      },
      "score": 86,
      "score_reason": "Runs a paid trading community and asks how to collect payments for it.",
      "hot": true,
      "status": "in_conversation",
      "persona": {
        "id": "lvWBPnytpS",
        "name": "Tipsters"
      },
      "campaign": {
        "id": "c7RkQ2wLxe",
        "name": "Tipsters warm intro",
        "status": "in_progress"
      },
      "conversation_id": "msgOtJ8dvT",
      "locked_to": null,
      "found_at": "2026-11-05T14:02:11.482Z",
      "scored_at": "2026-11-05T14:03:40.117Z",
      "updated_at": "2026-11-05T14:03:40.117Z",
      "metadata": {}
    },
    "conversation_id": "msgOtJ8dvT",
    "campaign": {
      "id": "c7RkQ2wLxe",
      "name": "Tipsters warm intro",
      "status": "in_progress"
    },
    "messages": [
      "It takes two minutes. You set a price, members pay, and they get their Discord role right away.",
      "Want the link to set it up?"
    ],
    "sent_by": "auto",
    "replying_to_message_id": "1985321409876543210",
    "sent_at": "2026-11-06T08:14:05.000Z"
  }
}

sam.handoff

Sam AI übergibt dir eine Unterhaltung, wenn er ohne dich keine gute Antwort sieht, der Lead nur ein Foto oder eine Sprachnachricht geschickt hat oder seine Antwort nicht rausgehen konnte. Im Modus auto überlässt er dir danach die nächsten Nachrichten des Leads. Jede Übergabe löst es einmal aus, wenn sie jünger als 6 Stunden ist.

Daten

  • leadobject
    Der Lead, dem du antworten sollst.
  • conversation_idstring
    Damit holst du über Unterhaltung abrufen den ganzen Verlauf.
  • campaignobject oder null
    Die Kampagne des Leads, mit id, name und status.
  • reasonstring oder null
    Warum Sam AI sie dir übergibt, so wie er es formuliert hat.
sam.handoff
{
  "id": "evt_8kQ2vXr4LmT9wPz1Hc6YbN3d",
  "object": "event",
  "type": "sam.handoff",
  "created_at": "2026-11-05T14:03:40.117Z",
  "account_id": "a9PzK2vRtM",
  "data": {
    "lead": {
      "id": "msgOtJ8dvT",
      "object": "lead",
      "profile": {
        "x_user_id": "1460283925",
        "username": "jane_builds",
        "name": "Jane Doe",
        "bio": "Runs a paid trading community. Daily setups, no hype.",
        "followers_count": 12400,
        "following_count": 380,
        "location": "Austin, TX",
        "website": "https://janedoe.com",
        "profile_image_url": "https://pbs.twimg.com/profile_images/1460283925/jane_400x400.jpg",
        "verified": true,
        "url": "https://x.com/jane_builds"
      },
      "score": 86,
      "score_reason": "Runs a paid trading community and asks how to collect payments for it.",
      "hot": true,
      "status": "in_conversation",
      "persona": {
        "id": "lvWBPnytpS",
        "name": "Tipsters"
      },
      "campaign": {
        "id": "c7RkQ2wLxe",
        "name": "Tipsters warm intro",
        "status": "in_progress"
      },
      "conversation_id": "msgOtJ8dvT",
      "locked_to": null,
      "found_at": "2026-11-05T14:02:11.482Z",
      "scored_at": "2026-11-05T14:03:40.117Z",
      "updated_at": "2026-11-05T14:03:40.117Z",
      "metadata": {}
    },
    "conversation_id": "msgOtJ8dvT",
    "campaign": {
      "id": "c7RkQ2wLxe",
      "name": "Tipsters warm intro",
      "status": "in_progress"
    },
    "reason": "She wants a call before she signs up."
  }
}

Zustellung

Gemlead sendet jedes Ereignis als JSON-POST und wartet 10 Sekunden auf deine Antwort. Jeder 2xx gilt als zugestellt. Eine Weiterleitung, jeder andere Status, ein Timeout oder ein Verbindungsfehler gilt als Fehlschlag, und das Ereignis wird erneut gesendet.

HeaderWert
Gemlead-SignatureDer Zeitstempel und die Signatur, t=1762444800,v1=5f2b...
Gemlead-Event-IdDie id des Ereignisses, um bereits verarbeitete Ereignisse zu überspringen.
Gemlead-Event-TypeDer type des Ereignisses.
User-AgentGemlead-Webhooks/1.0
Anfrage
POST /webhooks/gemlead HTTP/1.1
Content-Type: application/json; charset=utf-8
User-Agent: Gemlead-Webhooks/1.0
Gemlead-Event-Id: evt_8kQ2vXr4LmT9wPz1Hc6YbN3d
Gemlead-Event-Type: lead.hot
Gemlead-Signature: t=1762444800,v1=58ca5e964b92d617c979d3f067cdc2035a1a1a9d3f848d7d927c2db09b8e9baa

Signaturen prüfen

Jeder kann eine Anfrage an deine URL schicken. Prüfe bei jeder, ob sie von Gemlead kommt, bevor du ihr vertraust.

  1. Lies den rohen Body als Bytes, vor jedem JSON-Parsing. Parsen und erneutes Schreiben verändert die Bytes.
  2. Teile den Header Gemlead-Signature an den Kommas. t ist eine Unix-Zeit in Sekunden, v1 die Signatur.
  3. Berechne den HMAC SHA256 aus t, einem Punkt und dem rohen Body, mit dem ganzen Secret des Endpoints als Schlüssel, whsec_ eingeschlossen, und kodiere ihn als Hex.
  4. Vergleiche ihn in konstanter Zeit mit jedem v1.
  5. Lehne ein t ab, das mehr als 5 Minuten von deiner Uhr abweicht, damit sich eine alte Anfrage nicht wieder einspielen lässt. Jeder neue Versuch wird mit einem frischen t signiert.

Antworte mit 400, wenn die Prüfung fehlschlägt. Bewahre das Secret in einer Umgebungsvariable wie GEMLEAD_WEBHOOK_SECRET auf.

const crypto = require("crypto");
const express = require("express");

// rawBody: the request body exactly as received, before any JSON parsing.
function verifyGemleadSignature(rawBody, header, secret, toleranceSeconds = 300) {
  if (typeof header !== "string") return false;
  let timestamp = null;
  const signatures = [];
  for (const part of header.split(",")) {
    const [key, value] = part.split("=", 2).map((s) => (s || "").trim());
    if (key === "t") timestamp = value;
    if (key === "v1" && value) signatures.push(value);
  }
  if (!/^\d+$/.test(timestamp || "") || signatures.length === 0) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > toleranceSeconds) return false;
  const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.`).update(rawBody).digest();
  return signatures.some((signature) => {
    const given = Buffer.from(signature, "hex");
    return given.length === expected.length && crypto.timingSafeEqual(given, expected);
  });
}

const app = express();

// The body must stay raw on this route.
app.post("/webhooks/gemlead", express.raw({ type: "application/json" }), (req, res) => {
  const ok = verifyGemleadSignature(
    req.body,
    req.get("Gemlead-Signature"),
    process.env.GEMLEAD_WEBHOOK_SECRET,
  );
  if (!ok) return res.status(400).send("Invalid signature");

  const event = JSON.parse(req.body.toString("utf8"));
  // Already handled? Skip it, retries send the same event.id again.
  if (event.type === "lead.created") {
    // event.data.object is the new lead, hot or not
  } else if (event.type === "lead.hot") {
    // event.data.object is the lead
  } else if (event.type === "reply.received") {
    // event.data.lead, event.data.message, event.data.campaign, event.data.conversation_id
  }
  res.sendStatus(200);
});

app.listen(3000);

Wiederholungen

Schlägt eine Zustellung fehl, versucht Gemlead es noch 7-mal, mit jeweils längerer Pause. Nach dem 8. Versuch, etwa 45 Stunden nach dem ersten, wird die Zustellung als fehlgeschlagen markiert.

VersuchGesendet
1Sofort
21 Minute nach dem vorherigen Fehlschlag
35 Minuten nach dem vorherigen Fehlschlag
430 Minuten nach dem vorherigen Fehlschlag
52 Stunden nach dem vorherigen Fehlschlag
66 Stunden nach dem vorherigen Fehlschlag
712 Stunden nach dem vorherigen Fehlschlag
824 Stunden nach dem vorherigen Fehlschlag

Ein Endpoint, der 3 Tage in Folge fehlschlägt, mit mindestens 8 fehlgeschlagenen Versuchen, wird deaktiviert. Sein status wechselt auf disabled, sein disabled_reason auf failing, und die noch wartenden Zustellungen enden als fehlgeschlagen. Repariere deinen Server und schalte den Endpoint dann in der App oder mit Webhook-Endpoint ändern wieder ein, damit beginnt die Zählung von vorn. Testereignisse werden einmal gesendet und zählen nie mit.

Zustellungen auflisten zeigt Statuscode und Fehler jedes Versuchs, damit du siehst, was dein Server geantwortet hat.

Best Practices

  • Antworte innerhalb von 10 Sekunden mit 2xx und erledige die Arbeit danach in einer Queue.
  • Jedes Ereignis wird mindestens einmal zugestellt. Nutze seine id, die auch in Gemlead-Event-Id steht, um ein bereits verarbeitetes Ereignis zu überspringen.
  • Ereignisse können in falscher Reihenfolge ankommen. Vergleiche created_at oder rufe den Lead erneut ab, um seinen aktuellen Stand zu bekommen.
  • Prüfe jede Signatur und lehne alte Zeitstempel ab.
  • Wenn du das Secret erneuerst, wird es sofort ersetzt. Deploye das neue direkt danach. Zustellungen, die in der Zwischenzeit fehlschlagen, werden wiederholt und mit dem neuen signiert.
  • Ereignisse und Zustellungen werden 30 Tage aufbewahrt.