Make.com
Capture leads from Facebook Lead Ads, Typeform, custom Webhook via Make's HTTP module, step by step.
Setup
Create a scenario with your source as trigger and the HTTP > Make a request module as action.
Create a new scenario
In Make.com, click Create a new scenario. Pick a trigger (Facebook Lead Ads, custom Webhook, Typeform — depending on your source).
Add the HTTP module
After the trigger, add the HTTP > Make a request module. Not the generic "Webhook response" module — that one is for receiving, not sending.
Configure URL, method and headers
URL: https://api.capturia.io/v1/leads/capture
Method: POST
Headers: add Authorization, value Bearer cap_live_xxxxxxxx
Body content type: application/JSON
Body input method: JSON string
Build the JSON body
In the body field that appears, write the literal JSON with Make variables between {{ }}:
{ "phone": "{{1.phone_number}}", "sms_consent": true, "first_name": "{{1.first_name}}", "email": "{{1.email}}", "source_label": "make_scenario", "utm": { "source": "{{1.utm_source}}", "campaign": "{{1.utm_campaign}}" } }
{{1.fieldname}} refers to module 1 (the trigger). Adapt to the module number in your scenario.
Capture SMS consent
If your source has a consent field, map it: "sms_consent": "{{1.consent_field}}" (Capturia accepts true, "yes", "oui", etc.).
If no source field, hardcode "sms_consent": true but keep external proof (landing page checkbox, archived copy). See the LCAP section below.
Test and activate
Click Run once to execute the scenario on the last trigger lead. Check the HTTP module: output 200 with body { "status": "created", ... } confirms the connection. Activate the scenario via the ON toggle at the bottom left.
Field mapping
The mapping below is for a Facebook Lead Ads trigger. For other triggers (Typeform, custom Webhook), adapt source field names — the Capturia target stays the same.
| Capturia field | Required | Source field | Notes |
|---|---|---|---|
| phone | Yes | {{1. | E.164 (+15145551234), NANP 10 or 11 digits, or formatted (514) 555-1234 / 514-555-1234. 10-digit numbers auto-prefixed with +1. |
| sms_consent | Yes | {{1. | Boolean true OR string 'true' / 'yes' / 'oui' / 'vrai' / '1' / 'on' / 'checked' (case-insensitive). Accepted at the body root or inside the customData object (GoHighLevel webhooks). |
| first_name | No | {{1. | Lead's first name |
| last_name | No | {{1. | Last name |
| No | {{1. | RFC 5322 valid email | |
| source_label | No | "make_scenario" | Free-form tag (e.g. 'webflow_form', 'fb_lead_ads') |
| utm. | No | {{1. | Nested utm object, or the flat key utm.source |
| utm. | No | {{1. | Nested utm object, or the flat key utm.medium |
| utm. | No | {{1. | Nested utm object, or the flat key utm.campaign |
| tags | No | [] | Array of strings, or a comma-separated string |
| pipeline. | No | "identifier copied from Capturia" | Text identifier of the target pipeline (not its UUID). Copy it in Capturia: Administration, API Keys, "Your pipeline and stage identifiers" section. Without it, the default pipeline is targeted. Nested pipeline object, or flat key. |
| pipeline. | No | "identifier copied from Capturia" | Text identifier of the target stage, copied from the same place. Without it, the stage marked "Default stage" next to the identifiers. Sent without pipeline_slug, it is looked up in your default pipeline. Nested pipeline object, or flat key. |
SMS Consent — CASL / Quebec Law 25
The sms_consent field is required. Canada's Anti-Spam Law (CASL) and Quebec's Law 25 require explicit consent before any commercial SMS. Capturia stores a timestamped record of this attestation for your legal protection.
Recommended pattern — consent captured at source
Add a custom consent question directly in the source form (FB Lead Form custom question, Typeform field, GHL form checkbox). Map this field to sms_consent. Consent is explicit, dated, and tied to the lead who gave it.
Alternative pattern — checkbox on the landing page
If the source form doesn't allow custom questions, add a consent checkbox on the landing page before the ad. Keep a copy of the page (Wayback Machine, dated screenshot) as evidence.
Legal grey area
Hardcoding sms_consent: true without real lead attestation = legal risk. In case of complaint, Capturia may be required to provide proof of consent. Without proof, liability falls on the business that sends the lead.
Step-by-step test
Click Run once to execute the scenario on the last trigger lead. Check the HTTP module: output 200 with body { "status": "created", ... } confirms the connection. Activate the scenario via the ON toggle at the bottom left.
Error catalog
| Code | Error | Cause | Fix |
|---|---|---|---|
| 400 | invalid_payload | Empty body or malformed JSON (extra comma, missing quote) | Validate the JSON you send, then send again |
| 401 | missing_api_key | Authorization header absent or malformed | Add the header Authorization: Bearer cap_live_... |
| 401 | invalid_api_key | Key revoked, expired, or mistyped | Check the key in Capturia, generate a new one if needed |
| 403 | terms_acceptance_required | API terms of use not accepted (or a new version to accept) | The account owner accepts the terms in Capturia, on the API Keys page |
| 403 | insufficient_scope | Key lacks the leads:capture scope | Create a new key with the "Lead capture" preset |
| 415 | invalid_content_type | Body sent as a form (Form, x-www-form-urlencoded) instead of JSON | Send as JSON (Payload Type = JSON, Encoding = JSON, Body content type = application/JSON) |
| 422 | invalid_phone | Phone missing or format not recognized | Use E.164 (+15145551234) or NANP 10 digits (5145551234) |
| 422 | invalid_email | Malformed email | Send a valid address, or leave the field out |
| 422 | missing_consent | sms_consent absent, false, or unrecognized value — the field is looked up at the body root AND inside the customData object (GHL) | Map the consent field to sms_consent (true / yes / oui). From GHL: fixed value true behind a condition on the consent checkbox |
| 422 | invalid_payload | Another field refused (text too long, malformed date, negative value, etc.) — details names the field | Fix the field named in details, then send again |
| 422 | pipeline_not_found | pipeline.pipeline_slug matches no pipeline of the account | Copy the pipeline's text identifier again from the API Keys page (it is not its UUID) |
| 422 | stage_not_found | pipeline.stage_slug matches no stage of the targeted pipeline | Copy the stage's text identifier again from the API Keys page, under the right pipeline |
| 429 | rate_limit_exceeded | The key's request quota is exceeded | Space out sends at the source (the Retry-After header gives the delay) |
| 429 | rate_limited_ip | More than 100 requests per minute from the same IP address | Reduce the rate or contact support |
FAQ — common pitfalls
| Symptom | Cause |
|---|---|
| Platform test green (200 OK) but lead doesn't show up in pipeline | The same contact was already sent in the last 5 minutes — response status: idempotent_replay, no duplicate (tests often reuse the same number). If the contact already existed, it keeps its stage: see the "not in the right pipeline" entry below. |
| 415 "Unsupported Content-Type" or 400 "Body is not valid JSON" | 415: the tool sends a form (Payload Type set to Form, form-encoded Encoding) instead of JSON. 400: the JSON is malformed, often an extra comma or a missing quote around a variable. |
| 200 OK response but no SMS sent to lead | sms_consent: true but AI agent not configured, or SMS AI agent disabled, or no Twilio number provisioned for the client. |
| 200 OK response but the lead is not in the right pipeline or stage | Read ignored_fields in the response: it names every received field that was not applied. unknown_field: the field carries a name the API does not know (pipeline.id instead of pipeline.pipeline_slug, for instance). existing_contact_kept: the contact already existed and keeps its pipeline and stage. duplicate_field: the same field arrived twice (root and Custom Data, for instance) and the other value was kept. |
| Lead created without UTM tracking | Field sent under a name the API does not know (utm_source). Send the nested utm object or the flat key utm.source; ignored_fields names the field that was not applied. |
| 403 terms_acceptance_required after an update that used to work | A new version of the API terms of use must be accepted: the account owner accepts it in Capturia, on the API Keys page, and the key works again. |