Inbound sources
Every call needs Authorization: Bearer <key>. 7 calls:
- List inbound sources:
GET /v1/inbound-sources - Create an inbound source:
POST /v1/inbound-sources - Get an inbound source:
GET /v1/inbound-sources/:id - Update an inbound source:
PATCH /v1/inbound-sources/:id - Delete an inbound source:
DELETE /v1/inbound-sources/:id - Rotate an inbound source secret:
POST /v1/inbound-sources/:id/rotate-secret - Send a lead to an inbound source:
POST /v1/inbound/leads/:sourceId
List inbound sources
GET /v1/inbound-sources
Headers
| Name | Type | Required | Description |
|---|---|---|---|
x-workspace-id | string | No | Workspace for session (JWT) callers. API keys carry their own workspace. |
Response 200
| Field | Type | Description |
|---|---|---|
data | object[] | |
data[].allowed_origins | string[] | |
data[].auth_mode | enum | One of: hmac, public_token, google_key, provider. |
data[].consent_attested_at | string (date-time) or null | When the consent attestation was confirmed. Null if made before it was asked. |
data[].consent_attested_by | string or null | Who confirmed it: user:<id> for a member, api_key:<id> for an API key. |
data[].consent_field | string or null | |
data[].consent_mode | enum | One of: crm_field, web_form, existing_relationship, not_sure. |
data[].consent_text | string or null | |
data[].created_at | string (date-time) | ISO 8601 timestamp. |
data[].default_labels | string[] | |
data[].default_owner_user_id | string (uuid) or null | |
data[].field_aliases | object | |
data[].has_secret | boolean | The HMAC secret or Google key is set. It is never returned. |
data[].id | string | Inbound source id (src_...). |
data[].intake_url | string | Where leads are posted. |
data[].kind | enum | One of: webhook, form, google_ads_lead_form, meta_lead_ads, calendly, typeform, nexhealth, healthie, mindbody, boulevard, vagaro, zenoti, jane_app, housecall_pro, shopify, stripe_customer. |
data[].last_lead_at | string (date-time) or null | ISO 8601 timestamp. |
data[].name | string | |
data[].outbound_tool | boolean | Every lead from this source is a cold contact. |
data[].public_token | string or null | For public_token sources. Not a secret: it goes in the page with the form. |
data[].status | enum | One of: active, paused. |
data[].updated_at | string (date-time) | ISO 8601 timestamp. |
data[].workflow_id | string (uuid) or null |
Errors: 400, 401, 403, 404, 409, 413, 415, 429, 500. See errors for the shape and codes.
Create an inbound source
POST /v1/inbound-sources
A place leads are posted to (intake_url); the dashboard calls these lead sources, and the webhook kind a Lead URL. hmac and google_key sources get a secret, returned once in secret; public_token sources get a token for the form snippet and accept posts only from allowed_origins. consent_attested: true is required (400 without it): the source records who confirmed that everyone the business sends has agreed to receive texts (the user, or the API key), and when, and the confirmation is audited.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
x-workspace-id | string | No | Workspace for session (JWT) callers. API keys carry their own workspace. |
idempotency-key | string | No | Makes the write safe to retry for 24 hours. 1 to 255 printable characters. Keys starting with outreach:, workflow: or agent: are reserved for messages bubbl3 sends itself and are refused with 400. Up to 255 characters. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
allowed_origins | string[] | No | Sites a public_token source accepts posts from. Up to 20 items. |
auth_mode | enum | No | webhook: hmac (default) or public_token. form: public_token. google_ads_lead_form: google_key. One of: hmac, public_token, google_key. |
consent_attested | enum | Yes | Required: confirms "Everyone we send to bubbl3 has agreed to receive texts from us." bubbl3 records who confirmed it and when. One of: true. |
consent_field | string or null | No | For consent_mode crm_field: the payload field that records consent. Up to 200 characters. |
consent_mode | enum | No | A mode other than not_sure also needs the compliance area at full access. One of: crm_field, web_form, existing_relationship, not_sure. Default "not_sure". |
consent_text | string or null | No | The consent wording shown next to the form, kept as evidence with each lead. Up to 2000 characters. |
default_labels | string[] | No | Label names added to every lead. Up to 20 items. |
default_owner_user_id | string (uuid) or null | No | A member who owns new leads. |
field_aliases | object | No | Payload field name to contact field, e.g. { "Mobile Number": "phone" }. Default {}. |
kind | enum | Yes | A Meta Lead Ads source is made by connecting a Meta Page, not here. One of: webhook, form, google_ads_lead_form. |
name | string | Yes | Up to 100 characters. |
outbound_tool | boolean | No | An outbound tool (Clay, Apollo, Instantly, Smartlead): every lead it sends is a cold contact. Not for public_token sources. Default false. |
workflow_id | string (uuid) or null | No | A workflow every lead is enrolled in. |
Response 201
| Field | Type | Description |
|---|---|---|
allowed_origins | string[] | |
auth_mode | enum | One of: hmac, public_token, google_key, provider. |
consent_attested_at | string (date-time) or null | When the consent attestation was confirmed. Null if made before it was asked. |
consent_attested_by | string or null | Who confirmed it: user:<id> for a member, api_key:<id> for an API key. |
consent_field | string or null | |
consent_mode | enum | One of: crm_field, web_form, existing_relationship, not_sure. |
consent_text | string or null | |
created_at | string (date-time) | ISO 8601 timestamp. |
default_labels | string[] | |
default_owner_user_id | string (uuid) or null | |
field_aliases | object | |
has_secret | boolean | The HMAC secret or Google key is set. It is never returned. |
id | string | Inbound source id (src_...). |
intake_url | string | Where leads are posted. |
kind | enum | One of: webhook, form, google_ads_lead_form, meta_lead_ads, calendly, typeform, nexhealth, healthie, mindbody, boulevard, vagaro, zenoti, jane_app, housecall_pro, shopify, stripe_customer. |
last_lead_at | string (date-time) or null | ISO 8601 timestamp. |
name | string | |
outbound_tool | boolean | Every lead from this source is a cold contact. |
public_token | string or null | For public_token sources. Not a secret: it goes in the page with the form. |
secret | string or null | The HMAC secret or Google key. Shown only in this response: store it now. |
status | enum | One of: active, paused. |
updated_at | string (date-time) | ISO 8601 timestamp. |
workflow_id | string (uuid) or null |
Errors: 400, 401, 403, 404, 409, 413, 415, 429, 500. See errors for the shape and codes.
Get an inbound source
GET /v1/inbound-sources/:id
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Inbound source id (src_...). |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
x-workspace-id | string | No | Workspace for session (JWT) callers. API keys carry their own workspace. |
Response 200
| Field | Type | Description |
|---|---|---|
allowed_origins | string[] | |
auth_mode | enum | One of: hmac, public_token, google_key, provider. |
consent_attested_at | string (date-time) or null | When the consent attestation was confirmed. Null if made before it was asked. |
consent_attested_by | string or null | Who confirmed it: user:<id> for a member, api_key:<id> for an API key. |
consent_field | string or null | |
consent_mode | enum | One of: crm_field, web_form, existing_relationship, not_sure. |
consent_text | string or null | |
created_at | string (date-time) | ISO 8601 timestamp. |
default_labels | string[] | |
default_owner_user_id | string (uuid) or null | |
field_aliases | object | |
has_secret | boolean | The HMAC secret or Google key is set. It is never returned. |
id | string | Inbound source id (src_...). |
intake_url | string | Where leads are posted. |
kind | enum | One of: webhook, form, google_ads_lead_form, meta_lead_ads, calendly, typeform, nexhealth, healthie, mindbody, boulevard, vagaro, zenoti, jane_app, housecall_pro, shopify, stripe_customer. |
last_lead_at | string (date-time) or null | ISO 8601 timestamp. |
name | string | |
outbound_tool | boolean | Every lead from this source is a cold contact. |
public_token | string or null | For public_token sources. Not a secret: it goes in the page with the form. |
status | enum | One of: active, paused. |
updated_at | string (date-time) | ISO 8601 timestamp. |
workflow_id | string (uuid) or null |
Errors: 400, 401, 403, 404, 409, 413, 415, 429, 500. See errors for the shape and codes.
Update an inbound source
PATCH /v1/inbound-sources/:id
Kind and auth mode are fixed: make a new source to change them. A public_token source keeps at least one allowed origin. A consent mode other than not_sure, or a consent field, needs the compliance area at full access.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Inbound source id (src_...). |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
x-workspace-id | string | No | Workspace for session (JWT) callers. API keys carry their own workspace. |
idempotency-key | string | No | Makes the write safe to retry for 24 hours. 1 to 255 printable characters. Keys starting with outreach:, workflow: or agent: are reserved for messages bubbl3 sends itself and are refused with 400. Up to 255 characters. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
allowed_origins | string[] | No | Up to 20 items. |
consent_field | string or null | No | Up to 200 characters. |
consent_mode | enum | No | A mode other than not_sure also needs the compliance area at full access. One of: crm_field, web_form, existing_relationship, not_sure. |
consent_text | string or null | No | Up to 2000 characters. |
default_labels | string[] | No | Up to 20 items. |
default_owner_user_id | string (uuid) or null | No | |
field_aliases | object | No | Payload field name to contact field, e.g. { "Mobile Number": "phone" }. |
name | string | No | Up to 100 characters. |
outbound_tool | boolean | No | An outbound tool (Clay, Apollo, Instantly, Smartlead): every lead it sends is a cold contact. Not for public_token sources. |
status | enum | No | One of: active, paused. |
workflow_id | string (uuid) or null | No |
Response 200
| Field | Type | Description |
|---|---|---|
allowed_origins | string[] | |
auth_mode | enum | One of: hmac, public_token, google_key, provider. |
consent_attested_at | string (date-time) or null | When the consent attestation was confirmed. Null if made before it was asked. |
consent_attested_by | string or null | Who confirmed it: user:<id> for a member, api_key:<id> for an API key. |
consent_field | string or null | |
consent_mode | enum | One of: crm_field, web_form, existing_relationship, not_sure. |
consent_text | string or null | |
created_at | string (date-time) | ISO 8601 timestamp. |
default_labels | string[] | |
default_owner_user_id | string (uuid) or null | |
field_aliases | object | |
has_secret | boolean | The HMAC secret or Google key is set. It is never returned. |
id | string | Inbound source id (src_...). |
intake_url | string | Where leads are posted. |
kind | enum | One of: webhook, form, google_ads_lead_form, meta_lead_ads, calendly, typeform, nexhealth, healthie, mindbody, boulevard, vagaro, zenoti, jane_app, housecall_pro, shopify, stripe_customer. |
last_lead_at | string (date-time) or null | ISO 8601 timestamp. |
name | string | |
outbound_tool | boolean | Every lead from this source is a cold contact. |
public_token | string or null | For public_token sources. Not a secret: it goes in the page with the form. |
status | enum | One of: active, paused. |
updated_at | string (date-time) | ISO 8601 timestamp. |
workflow_id | string (uuid) or null |
Errors: 400, 401, 403, 404, 409, 413, 415, 429, 500. See errors for the shape and codes.
Delete an inbound source
DELETE /v1/inbound-sources/:id
Leads posted to it are refused from now on. Contacts it created stay, with their consent evidence. A Meta Lead Ads source goes with its Page connection (409): disconnect the Page instead, or pause the source.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Inbound source id (src_...). |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
x-workspace-id | string | No | Workspace for session (JWT) callers. API keys carry their own workspace. |
idempotency-key | string | No | Makes the write safe to retry for 24 hours. 1 to 255 printable characters. Keys starting with outreach:, workflow: or agent: are reserved for messages bubbl3 sends itself and are refused with 400. Up to 255 characters. |
Response 200
| Field | Type | Description |
|---|---|---|
deleted | enum | One of: true. |
id | string | Inbound source id (src_...). |
Errors: 400, 401, 403, 404, 409, 413, 415, 429, 500. See errors for the shape and codes.
Rotate an inbound source secret
POST /v1/inbound-sources/:id/rotate-secret
Replaces the HMAC secret or Google key (returned once, in secret), or the public token. The old one stops working at once. A Meta Lead Ads source has no secret (409).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Inbound source id (src_...). |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
x-workspace-id | string | No | Workspace for session (JWT) callers. API keys carry their own workspace. |
idempotency-key | string | No | Makes the write safe to retry for 24 hours. 1 to 255 printable characters. Keys starting with outreach:, workflow: or agent: are reserved for messages bubbl3 sends itself and are refused with 400. Up to 255 characters. |
Response 200
| Field | Type | Description |
|---|---|---|
allowed_origins | string[] | |
auth_mode | enum | One of: hmac, public_token, google_key, provider. |
consent_attested_at | string (date-time) or null | When the consent attestation was confirmed. Null if made before it was asked. |
consent_attested_by | string or null | Who confirmed it: user:<id> for a member, api_key:<id> for an API key. |
consent_field | string or null | |
consent_mode | enum | One of: crm_field, web_form, existing_relationship, not_sure. |
consent_text | string or null | |
created_at | string (date-time) | ISO 8601 timestamp. |
default_labels | string[] | |
default_owner_user_id | string (uuid) or null | |
field_aliases | object | |
has_secret | boolean | The HMAC secret or Google key is set. It is never returned. |
id | string | Inbound source id (src_...). |
intake_url | string | Where leads are posted. |
kind | enum | One of: webhook, form, google_ads_lead_form, meta_lead_ads, calendly, typeform, nexhealth, healthie, mindbody, boulevard, vagaro, zenoti, jane_app, housecall_pro, shopify, stripe_customer. |
last_lead_at | string (date-time) or null | ISO 8601 timestamp. |
name | string | |
outbound_tool | boolean | Every lead from this source is a cold contact. |
public_token | string or null | For public_token sources. Not a secret: it goes in the page with the form. |
secret | string or null | The HMAC secret or Google key. Shown only in this response: store it now. |
status | enum | One of: active, paused. |
updated_at | string (date-time) | ISO 8601 timestamp. |
workflow_id | string (uuid) or null |
Errors: 400, 401, 403, 404, 409, 413, 415, 429, 500. See errors for the shape and codes.
Send a lead to an inbound source
POST /v1/inbound/leads/:sourceId
No API key: authenticate with the source's signing secret (bubbl3-signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">), its public token and an allowed Origin, or its Google Ads key. JSON or form-encoded. Send Idempotency-Key or event_id to make retries safe. Every refusal is the same 403.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
sourceId | string | Yes | Up to 64 characters. |
Response 200
| Field | Type | Description |
|---|---|---|
received | enum | One of: true. |
Errors: 400, 403, 413, 429. See errors for the shape and codes.