# Lead Post Webhook API > Reference documentation for authenticating and posting JSON leads to the TECOBI Lead Post Webhook API. Canonical documentation: https://www.tecobi.com/resources/api-docs/lead-post/ Version: v4 Status: current Method: POST Endpoint: /api/v4/webhooks/lead-post/ Authentication: x-tecobi-api-key header Last updated: 2026-08-12 ## Overview The Lead Post Webhook API accepts one JSON lead per request. TECOBI uses the supplied contact data to create a lead or link the request to an existing lead, then applies supported metadata, assignment, appointment, and comments fields. > **Before you begin:** Request an active client API key from your TECOBI representative. Keep the key server-side and never expose it in browser JavaScript or a public repository. Bulk arrays are not supported. Send each lead as a separate request. ## Endpoint ```http POST /api/v4/webhooks/lead-post/ Content-Type: application/json x-tecobi-api-key: ``` ## Authentication Include the active client's API key in the `x-tecobi-api-key` request header. | Header | Required | Accepted value | Notes | | --- | --- | --- | --- | | `x-tecobi-api-key` | Yes | Active TECOBI client API key | Missing or invalid keys return `400 Bad Request`. | ## Payload requirements The request body must be a JSON object. At least one of these contact values must be present for TECOBI to create or link a CRM lead: - `phone_number` - `phone` - `customData.phone_number` - `email` - `customData.email` If no phone or email is supplied, the endpoint can still return `201 Created`, but no CRM lead is created. Unknown fields are retained in the raw webhook payload, but they do not affect lead creation unless they are listed in this reference. ## Request examples ### Minimal request ```json { "first_name": "Jane", "last_name": "Customer", "phone_number": "15551234567", "email": "jane@example.com", "classification": "Sales" } ``` ### Complete request ```json { "lead_id": "abc-123", "service": 5579, "language_code": "en", "classification": "Sales", "first_name": "Jane", "last_name": "Customer", "phone_number": "15551234567", "email": "jane@example.com", "company": "Acme Co", "address_1": "123 Main St", "address_2": "Suite 4", "city": "Denver", "state": "Colorado", "zip_code": "80202", "assigned_to": "salesperson@exampledealer.com", "sales_manager_email": "manager@exampledealer.com", "appointment_time": "2026-08-12T15:00:00-06:00", "appointment_notes": "Wants to test drive", "appointment_location": "Showroom", "comments": { "Vehicle": "F-150", "Budget": "$45k" } } ``` ### cURL request ```bash curl --request POST \ --url 'https:///api/v4/webhooks/lead-post/' \ --header 'Content-Type: application/json' \ --header 'x-tecobi-api-key: ' \ --data '{ "first_name": "Jane", "last_name": "Customer", "phone_number": "15551234567", "email": "jane@example.com", "classification": "Sales" }' ``` Use the API origin supplied with your TECOBI client credentials. ## Field reference ### Lead identity | Field | Type | Required | Accepted values | Behavior | | --- | --- | --- | --- | --- | | `lead_id` | String or number | No | Any external identifier | Stored as the external lead ID. | | `contact_id` | String or number | No | Any external identifier | Fallback external lead ID when `lead_id` is absent. | | `first_name` | String | No | Any non-empty string | Saved as the first name. Defaults to `Unknown`. | | `last_name` | String | No | Any non-empty string | Saved as the last name. Defaults to `Unknown`. | | `customData.first_name` | String | No | Any non-empty string | Fallback when top-level `first_name` is absent or `Unknown`. | | `customData.last_name` | String | No | Any non-empty string | Fallback when top-level `last_name` is absent or `Unknown`. | ### Contact information | Field | Type | Required | Accepted values | Behavior | | --- | --- | --- | --- | --- | | `phone_number` | String | Conditionally | US number with or without `+1`, spaces, dashes, dots, or parentheses | Preferred phone field. Normalized toward `+1XXXXXXXXXX` and matched within the client. | | `phone` | String | Conditionally | Same as `phone_number` | Fallback phone field. | | `customData.phone_number` | String | Conditionally | Same as `phone_number` | Nested fallback phone field. | | `email` | String | Conditionally | Email address | Preferred email field. Used for matching when no phone is available. | | `customData.email` | String | Conditionally | Email address | Nested fallback email field. | At least one phone or email field is required for a lead to be created or linked. ### Lead metadata | Field | Type | Required | Accepted values | Behavior | | --- | --- | --- | --- | --- | | `classification` | String | No | `Sales`, `Reseller`, `Service`, `Parts`, `Employment`, `Collections`, `Service Canceled` | Defaults to `Sales`. Send a valid CRM classification. The database field is limited to 16 characters. | | `service` | Integer or integer string | No | Existing `LeadProviderService.id` | Defaults to `5579`. Resolves provider, master provider, and service when the ID exists. | | `customData.Service` | Integer or integer string | No | Existing `LeadProviderService.id` | Fallback service ID. The capital `S` is required. | | `language_code` | String | No | Existing `Language.code` | Preferred language code. | | `language` | String | No | Existing `Language.code` | Fallback when `language_code` is absent. | ### Assignment and roles Each role accepts the email of an active user on the client. Send it at the top level or inside `customData` with the same key. An unmatched email does not fail the request; TECOBI creates an internal warning note. | Field | Type | Required | Maps to | | --- | --- | --- | --- | | `assigned_to` | String | No | Lead assignee | | `secondary_rep_email` | String | No | Secondary representative | | `salesperson_2` | String | No | Secondary representative | | `sales_manager_email` | String | No | Sales manager | | `bdc_rep_email` | String | No | BDC representative | | `finance_manager_email` | String | No | Finance manager | | `service_rep_email` | String | No | Service representative | | `parts_rep_email` | String | No | Parts representative | ### Address and company | Field | Type | Required | Accepted values | Behavior | | --- | --- | --- | --- | --- | | `company_name` | String | No | Any string | Saved as the lead company name. | | `company` | String | No | Any string | Fallback company name. | | `address` | String | No | Any string | Primary street address. Can also provide a recognizable state fallback. | | `address_1`, `address1`, `street` | String | No | Any string | Primary street address fallbacks. | | `address_2`, `address2` | String | No | Any string | Appended to the primary address. | | `city` | String | No | Any string | Saved as the lead city. | | `state` | String | No | Two-letter abbreviation or full US state name | Recognized full names are converted to abbreviations. | | `postal_code`, `postalcode`, `zip`, `zip_code`, `zipcode` | String | No | Any string | Saved as the lead postal code. | ### Appointments Appointment fields can be sent at the top level or inside `customData` with the same key. | Field | Type | Required | Accepted values | Behavior | | --- | --- | --- | --- | --- | | `appointment_time` | String | No | Date/time string parseable by the API | Creates an appointment when valid. Naive times use the client's timezone and are converted to UTC. Invalid values produce an internal warning, not a failed request. Duplicate active appointments at the same time are ignored. | | `appointment_notes` | String | No | Any string | Saved as appointment notes. | | `appointment_location` | String | No | Any string | Saved as the appointment location. | ### Notes and comments | Field | Type | Required | Accepted values | Behavior | | --- | --- | --- | --- | --- | | `question_responses_lead_id` | String | No | Pipe-delimited string | Split on `|` and included in the generated inbound message. | | `comments` | Any JSON value | No | String, object, array, or other JSON value | Creates an internal comments message and is included in the generated inbound message. | | `message.body` | String | No | Any string | Fallback comments source when `comments` is absent. | | `customData.inquiry_details` | String | No | Any string | Final comments fallback. | Comments are normalized as follows: - Object values become `key: value` lines. - Array values become one line per non-empty item. - String values are retained as text. - Pipe-and-colon strings such as `Color: Red|Trim: XLT` become separate `key: value` lines. ### Nested custom data `customData` must be a JSON object. It provides fallback values for selected fields. Unknown nested keys remain in the raw payload but are ignored during lead creation. Supported keys are `phone_number`, `email`, `first_name`, `last_name`, `Service`, all assignment and role keys, all appointment keys, and `inquiry_details`. ## Lead processing behavior ### Phone leads TECOBI removes common punctuation and normalizes US phone values toward `+1` format. Leads are matched by client and normalized number. Existing matching leads are reactivated to an active, fresh status when applicable. ### Email-only leads When no phone is supplied, TECOBI attempts to match an existing client lead by normalized email and compatible first and last name. If no match is found, it creates a new web lead. ### New-lead defaults | Lead field | Default behavior | | --- | --- | | `status_id` | `1` | | `lead_type` | `SMS` with a phone number; otherwise `Web` | | `lead_status` | `New` | | `twilio_number` | Client default number | | `script` | Script assigned to the client default number | | `classification` | Request value, defaulting to `Sales` | | `provider`, `master_provider`, `service` | Derived from the resolved `LeadProviderService` | ## Responses ### Success ```http HTTP/1.1 201 Created Content-Type: application/json {} ``` The success body is always an empty JSON object and does not include the created or matched lead ID. ### Missing API key ```http HTTP/1.1 400 Bad Request Content-Type: application/json { "message": "x-tecobi-api-key header is required." } ``` ### Invalid API key ```http HTTP/1.1 400 Bad Request Content-Type: application/json { "message": "x-tecobi-api-key is not valid." } ``` ## Implementation notes - A valid request can return `201 Created` without creating a CRM lead when no phone or email is supplied. - Unknown fields are stored in the raw webhook payload but ignored by lead creation. - Invalid role emails and invalid appointment times create internal warnings instead of failing the request. - Lead creation is attempted synchronously during the request. - Send exactly one lead per request.