Ingress Webhooks
Ingress webhooks are the simplest inbound integration: create a webhook in the RocketLead console, get a unique URL + token, and POST JSON to it from any external system (Zapier, Make, n8n, custom backends, form vendors). Each POST creates a lead entry in the bound Lead Pool.
Use ingress webhooks when:
- You don’t want to manage a Bearer token (e.g., a no-code platform that doesn’t support custom auth headers).
- The sending system already has its own retry/idempotency story.
- You only need to push leads in — you don’t need to read or update existing data.
For richer integrations (create bookings, update lead state), use the Connect API.
Endpoint
Section titled “Endpoint”| Method | Path | Auth |
|---|---|---|
POST | /public/ingress_webhooks/:id | x-rocketlead-ingress-token header |
The :id is the UUID of the ingress webhook configured in the console.
Authentication
Section titled “Authentication”Send the token as a custom header:
POST /public/ingress_webhooks/<uuid>x-rocketlead-ingress-token: <64-hex-token>Content-Type: application/jsonThe token is a 64-character hex string. Anything missing or wrong returns 401 Invalid webhook token.
Issuing a webhook
Section titled “Issuing a webhook”In the RocketLead console:
- Go to Einstellungen → Lead-Pools and open the destination Lead Pool.
- On its Webhooks tab, click Neuen Webhook hinzufügen.
- Copy the URL and the token — the token is shown once.
Tokens can be rotated (which invalidates the old one) but can’t be retrieved after creation.
{ contacts?: EntryContact[]; data?: Record<string, string | string[] | null>; customFieldData?: Record<string, unknown>;}| Field | Notes |
|---|---|
contacts | Who is on the lead — names, phone numbers, emails. See Stating who is on a lead. Without it the lead has nobody on it. |
data | Optional, free-form. Becomes the lead entry’s data column. Schema is auto-inferred — first POST locks the inferred types per key. A payload that states only contacts is complete. |
customFieldData | Optional. Validated against the org’s custom fields. Unknown custom-field IDs return 400. |
Example
Section titled “Example”curl -X POST https://api.rocketlead.io/public/ingress_webhooks/<uuid> \ -H "Content-Type: application/json" \ -H "x-rocketlead-ingress-token: <token>" \ -d '{ "contacts": [ { "firstName": "Maria", "lastName": "Schmidt", "channels": [ {"kind": "email", "value": "maria@example.com"}, {"kind": "phone", "value": "+49 30 1234567"} ] } ], "data": { "interest": "Anfängerkurs" }, "customFieldData": { "<custom-field-uuid>": "instagram_ad" } }'Response
Section titled “Response”The created lead entry (the same shape the Connect API returns for a lead),
plus identity: the people now on the request, with the IDs they were given.
{ "id": "<uuid>", "tableId": "<uuid>", "organizationId": "<uuid>", "data": {"course": "Kids BJJ"}, "leadStateId": "<uuid>", "createdAt": "2026-05-10T12:00:00Z", "identity": { "primary": { "id": "<contact-uuid>", "firstName": "Mika", "lastName": "Toivonen", "channels": [ {"id": "<channel-uuid>", "kind": "phone", "value": "+4917612345678", "isPrimary": true} ], "isPrimary": true, "isSubject": false, "position": 0 }, "participants": [{"id": "<contact-uuid>", "firstName": "Aino", "...": "..."}], "all": [{"...": "..."}], "source": "contacts" }}Why identity is worth reading
Section titled “Why identity is worth reading”A contact in your contacts array is created or attached depending on whether the organisation
already holds that person — so the IDs are not something you can work out from what you sent. This
is where you learn them, and it is the only place: the Connect API does not return people on its
lead endpoints and has no endpoint that reads them.
Use them to reference the person afterwards — attaching a note, reconciling against your own CRM, or sending a follow-up request about the same person.
identity is optional. The entry is committed before it is read back, so on the rare occasion
that read fails you get the entry without it rather than an error — retrying would create a second
lead. Treat its absence as “ask again later”, never as “no contacts were created”.
Side effects
Section titled “Side effects”- A
tableEntriesrow is inserted into the bound Lead Pool. - The default lead state for that pool is applied automatically (must exist; otherwise the request is rejected with
400). table.entry.addedautomation event fires on commit.
See Automations → Triggers for the event catalog.
Schema auto-inference
Section titled “Schema auto-inference”The bound table’s schema is inferred from the first POST per key:
// First POST locks typesdata: { email: "x@x", age: 25, optedIn: true }// → tableSchema: { email: "string", age: "number", optedIn: "boolean" }
// Subsequent POSTs must matchdata: { email: 123 }// → 400: type mismatch — email expected string, got number| Source value | Inferred type |
|---|---|
| string | string |
| number | number |
| boolean | boolean |
| array of strings | string[] |
null | (skipped — doesn’t infer a type) |
If a value can’t be coerced to the existing type, the request fails with 400 and a typeErrors block describing the conflict.
Idempotency
Section titled “Idempotency”Every POST creates a new lead entry. There’s no built-in deduplication. If your sender retries on a transient failure, you’ll get duplicate leads.
Two practical options:
- Dedupe upstream — let the sender (Zapier, Make, etc.) ensure exactly-once delivery using their built-in retry semantics.
- Let the studio resolve it — a repeated POST is a second lead, but a person whose name and channels are already on file is attached rather than created again, so both leads land on the same contact and are grouped together in the lead pool. See Contacts → Matching.
Stating who is on a lead
Section titled “Stating who is on a lead”A lead’s identity — names, phone numbers, emails — is stated in a contacts array. What you state is
what is stored; nothing is derived from data. The full contract — roles, rules, how known people
are matched — is on Connect API → Contacts; ingress
webhooks accept exactly the same document.
{ "data": {"course": "Kids BJJ"}, "contacts": [ { "firstName": "Mika", "lastName": "Toivonen", "isPrimary": true, "isSubject": false, "channels": [{"kind": "phone", "value": "+4917612345678"}] }, {"firstName": "Aino", "lastName": "Toivonen", "isSubject": true} ]}The parent is the primary contact and not a subject; the child is the subject. isSubject
defaults to true, so send false explicitly for someone enquiring on another person’s behalf.
The people are created or attached in the same transaction as the entry: they land with the lead or
the request is a 400 and neither exists.
Identity in data is ignored
Section titled “Identity in data is ignored”Name, phone and email fields in data are still accepted and stored, and read by nothing.
They do not name the lead, they do not reach automations, and they are not what the console shows.
Sending them costs you nothing, so a webhook sender that has not been adapted keeps working. But a
lead sent with identity only in data and no contacts array is a lead with nobody on it — and
an ingress webhook writes no activity log, so nothing else will tell you.
They are also a second copy of somebody’s name and contact details, sitting outside the records that the console edits, merges and deletes. Correcting a contact does not correct them. Treat them as something to stop sending rather than something to keep in step.
Errors
Section titled “Errors”| Status | Cause |
|---|---|
400 Validation failed | Body shape is wrong, custom field ID unknown, or schema type mismatch. |
400 No default lead state | The bound pool has no default leadStateId — set one in the console. |
401 Invalid webhook token | Missing or wrong x-rocketlead-ingress-token. |
404 Webhook not found | The :id doesn’t match any webhook in your org. |
500 | Internal — retry with backoff. |
Comparison: Ingress vs Connect API
Section titled “Comparison: Ingress vs Connect API”| Ingress | Connect API | |
|---|---|---|
| Auth | Custom header + URL secret | Authorization: Bearer rocketlead_* |
| Direction | Inbound only (push leads in) | Bidirectional (read/write) |
| Scopes | None — token = full pool access | read / write / admin |
| Setup | One webhook per pool, in-console | One token per integration |
| Best for | No-code platforms, simple lead push | CRMs, custom backends, complex sync |
| Schema | Auto-inferred per pool | Validated against shared schemas |
Related
Section titled “Related”- Connect API → Contacts — the
contactsdocument in full. - Connect API → Leads — the richer alternative for backends.
- Automations → Triggers —
table.entry.addedfires here too.