Skip to content

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.

MethodPathAuth
POST/public/ingress_webhooks/:idx-rocketlead-ingress-token header

The :id is the UUID of the ingress webhook configured in the console.

Send the token as a custom header:

POST /public/ingress_webhooks/<uuid>
x-rocketlead-ingress-token: <64-hex-token>
Content-Type: application/json

The token is a 64-character hex string. Anything missing or wrong returns 401 Invalid webhook token.

In the RocketLead console:

  1. Go to Einstellungen → Lead-Pools and open the destination Lead Pool.
  2. On its Webhooks tab, click Neuen Webhook hinzufügen.
  3. 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>;
}
FieldNotes
contactsWho is on the lead — names, phone numbers, emails. See Stating who is on a lead. Without it the lead has nobody on it.
dataOptional, 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.
customFieldDataOptional. Validated against the org’s custom fields. Unknown custom-field IDs return 400.
Terminal window
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"
}
}'

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"
}
}

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

  • A tableEntries row 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.added automation event fires on commit.

See Automations → Triggers for the event catalog.

The bound table’s schema is inferred from the first POST per key:

// First POST locks types
data: { email: "x@x", age: 25, optedIn: true }
// → tableSchema: { email: "string", age: "number", optedIn: "boolean" }
// Subsequent POSTs must match
data: { email: 123 }
// → 400: type mismatch — email expected string, got number
Source valueInferred type
stringstring
numbernumber
booleanboolean
array of stringsstring[]
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.

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:

  1. Dedupe upstream — let the sender (Zapier, Make, etc.) ensure exactly-once delivery using their built-in retry semantics.
  2. 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.

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.

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.

StatusCause
400 Validation failedBody shape is wrong, custom field ID unknown, or schema type mismatch.
400 No default lead stateThe bound pool has no default leadStateId — set one in the console.
401 Invalid webhook tokenMissing or wrong x-rocketlead-ingress-token.
404 Webhook not foundThe :id doesn’t match any webhook in your org.
500Internal — retry with backoff.
IngressConnect API
AuthCustom header + URL secretAuthorization: Bearer rocketlead_*
DirectionInbound only (push leads in)Bidirectional (read/write)
ScopesNone — token = full pool accessread / write / admin
SetupOne webhook per pool, in-consoleOne token per integration
Best forNo-code platforms, simple lead pushCRMs, custom backends, complex sync
SchemaAuto-inferred per poolValidated against shared schemas