TextConvo Docs

    Everything you need to integrate TextConvo — API reference, CRM guides, and webhook documentation.

    Last updated: July 2026

    Introduction

    TextConvo provides an API to ingest leads and webhooks to receive conversation events. Ingested leads are engaged by AI across your configured journeys.

    Base URL

    https://api.textconvo.ai

    Scope: The public API currently covers lead ingestion and event webhooks. Voice, RCS, WhatsApp, and email are orchestrated by the platform internally — API access to those channels is on our roadmap.

    Quick Start

    Try your first API call right away. Replace YOUR_API_KEY with your actual key.

    bash
    curl -X POST https://api.textconvo.ai/functions/v1/ingest-lead \
      -H "Content-Type: application/json" \
      -H "X-API-Key: YOUR_API_KEY" \
      -H "X-Source-Key: YOUR_SOURCE_KEY" \
      -H "X-Request-Id: $(uuidgen)" \
      -d '{
        "phone": "+15035551234",
        "first_name": "John",
        "last_name": "Doe",
        "email": "john@example.com"
      }'

    Authentication

    Every lead-ingestion request must include all three of the following headers. Missing any one returns 401 or 400.

    HeaderRequiredDescription
    X-API-KeyYesYour API key
    X-Source-KeyYesIdentifies the lead source
    X-Request-IdYesUnique per request (idempotency key)
    http
    POST /functions/v1/ingest-lead
    Content-Type: application/json
    X-API-Key: YOUR_API_KEY
    X-Source-Key: YOUR_SOURCE_KEY
    X-Request-Id: 3f1a9c8e-2b4d-4a1e-9c6f-0d2e8b7a1c33

    HMAC Authentication

    HMAC request signing is optional and enabled per source. When enabled, send two additional headers alongside the required auth headers so TextConvo can verify the request was not tampered with in transit.

    Additional Headers (when HMAC is enabled)

    HeaderDescription
    X-TC-TimestampUnix timestamp — must be within ±300 seconds of server time
    X-TC-SignatureHex HMAC-SHA256 signature (case-insensitive compare)

    Signature Formula

    Sign the string `${timestamp}.${rawBody}` — the timestamp, a literal dot, then the exact raw request body — keyed with your HMAC secret, and hex-encode the result.

    text
    X-TC-Signature = HEX(
      HMAC_SHA256(
        key   = HMAC_SECRET,
        data  = timestamp + "." + rawBody
      )
    )

    Example Request

    bash
    curl -X POST https://api.textconvo.ai/functions/v1/ingest-lead \
      -H "Content-Type: application/json" \
      -H "X-API-Key: YOUR_API_KEY" \
      -H "X-Source-Key: YOUR_SOURCE_KEY" \
      -H "X-Request-Id: 3f1a9c8e-2b4d-4a1e-9c6f-0d2e8b7a1c33" \
      -H "X-TC-Timestamp: 1710000000" \
      -H "X-TC-Signature: 9f86d081884c7d659a2feaa0c55ad015..." \
      -d '{ "phone": "+15035551234" }'

    Note: The signature is compared case-insensitively against the hex you send. Timestamps outside the ±300-second window are rejected.

    Lead Ingestion API

    POST/functions/v1/ingest-lead

    Create a new lead inside TextConvo. The lead will be matched to an existing contact by phone number or created as a new contact. A journey will be queued automatically based on your account configuration.

    Request Headers

    HeaderRequiredDescription
    X-API-KeyYesYour API key
    X-Source-KeyYesIdentifies the lead source
    X-Request-IdYesUnique per request (idempotency key)
    Content-TypeYesapplication/json
    X-TC-TimestampIf HMAC enabledUnix timestamp (±300s skew)
    X-TC-SignatureIf HMAC enabledHex HMAC-SHA256 signature

    Request Body

    json
    {
      "phone": "string — REQUIRED, E.164 format e.g. +15035551234",
      "email": "string (optional)",
      "external_id": "string (optional) — your CRM's ID for this lead",
      "lead_external_id": "string (optional) — alias of external_id",
      "first_name": "string (optional)",
      "last_name": "string (optional)",
      "address": "string (optional)",
      "city": "string (optional)",
      "state": "string (optional) — auto-normalized to 2-letter code",
      "zip": "string (optional)",
      "affiliate_id": "string (optional)",
      "ip_address": "string (optional)",
      "user_agent": "string (optional)",
      "landing_page": "string (optional)",
      "metadata": { "any_key": "any_value" },
      "custom_fields": {
        "any_non_standard_field": "must be nested here, not top-level"
      }
    }

    Required field: phone is the only required field. Every other field is optional but validated against a whitelist — any non-standard field must be nested inside custom_fields, never sent top-level. metadata and custom_fields are each capped at 2KB, and state is auto-normalized to a 2-letter code.

    Success Response (202 Accepted)

    json
    {
      "success": true,
      "ingestion_request_id": "<uuid>"
    }

    Idempotency

    The X-Request-Id header is an idempotency key. If you retry a request with the same X-Request-Id, TextConvo does not ingest the lead again — it returns the original result with "duplicate": true and the same ingestion_request_id. This makes safe retries possible after a network timeout.

    json
    {
      "success": true,
      "duplicate": true,
      "ingestion_request_id": "<uuid>"
    }

    Error Response

    json
    {
      "success": false,
      "error": "phone is required",
      "code": "MISSING_REQUIRED_FIELD"
    }

    Webhooks

    TextConvo sends webhook events as conversations progress — lead acceptance, message delivery and replies, opt-outs, CRM sync, and scheduled-call activity. Each event body includes an event field naming the event.

    Emitted Events

    • lead.accepted— Lead accepted and matched or created as a contact
    • lead.rejected— Lead rejected (validation or dedupe)
    • lead.delivered— Message delivered to the carrier
    • lead.failed— Message failed to deliver
    • lead.reply— Contact replied to a message
    • lead.click— Contact clicked a tracked link
    • lead.opt_out— Contact opted out of messaging
    • lead.sent— Email message sent
    • lead.crm_updated— Inbound CRM change reflected back into TextConvo
    • journey.crm_pushed— Journey outcome pushed to your connected CRM
    • scheduled_call.confirmed— A scheduled call was confirmed
    • scheduled_call.cancelled— A scheduled call was cancelled
    • support.call_me_now— Contact requested an immediate callback
    • support.call_back_later— Contact requested a callback later

    Payload Structures

    lead.accepted

    json
    {
      "event": "lead.accepted",
      "lead_id": "lead_abc123",
      "contact_id": "cnt_xyz789",
      "phone": "+15035551234",
      "lead_external_id": "crm_00042"
    }

    lead.delivered

    json
    {
      "event": "lead.delivered",
      "message_id": "msg_abc123",
      "contact_id": "cnt_xyz789",
      "phone": "+15035551234",
      "delivered_at": "2026-07-20T10:12:00Z"
    }

    lead.failed

    json
    {
      "event": "lead.failed",
      "message_id": "msg_abc123",
      "contact_id": "cnt_xyz789",
      "phone": "+15035551234",
      "failed_at": "2026-07-20T10:12:00Z",
      "error_code": "carrier_rejected"
    }

    lead.reply

    json
    {
      "event": "lead.reply",
      "message_id": "msg_reply123",
      "contact_id": "cnt_xyz789",
      "phone": "+15035551234",
      "message_text": "Yes, send the quote",
      "received_at": "2026-07-20T10:18:00Z"
    }

    lead.opt_out

    json
    {
      "event": "lead.opt_out",
      "contact_id": "cnt_xyz789",
      "phone": "+15035551234",
      "channel": "sms",
      "opted_out_at": "2026-07-20T10:25:00Z"
    }

    journey.crm_pushed

    json
    {
      "event": "journey.crm_pushed",
      "contact_id": "cnt_xyz789",
      "crm_key": "hubspot",
      "outcome": "completed",
      "pushed_at": "2026-07-20T11:30:00Z",
      "lead_id": "lead_abc123",
      "crm_contact_id": "473031564003",
      "fields_synced": ["textconvo_journey_status", "textconvo_outcome"]
    }

    Webhook Security

    Every webhook TextConvo sends includes the following headers. Verify the signature before trusting the payload.

    HeaderDescription
    X-TextConvo-SignatureSignature in the form sha256=<hex>
    X-TextConvo-TimestampUnix timestamp used in the signed string
    X-TextConvo-Source-KeyIdentifies which source the webhook is for

    Recompute HMAC-SHA256 over `${X-TextConvo-Timestamp}.${rawBody}` with your webhook secret, hex-encode it, and compare it against the signature header after stripping the sha256= prefix.

    javascript
    const crypto = require('crypto');
    
    const header = req.headers['x-textconvo-signature'] || '';
    const timestamp = req.headers['x-textconvo-timestamp'];
    // rawBody is the exact, unparsed request body string.
    const expected = crypto
      .createHmac('sha256', process.env.WEBHOOK_SECRET)
      .update(`${timestamp}.${rawBody}`)
      .digest('hex');
    
    const received = header.replace(/^sha256=/, '');
    const isValid = crypto.timingSafeEqual(
      Buffer.from(expected, 'hex'),
      Buffer.from(received, 'hex')
    );

    Rate Limits

    Rate limits are configured per API key. Defaults are 60 requests/minute and 10 requests/second. Exceeding limits returns HTTP 429 — contact us to adjust limits or enable overflow queueing for burst traffic.

    Limit Exceeded

    When a limit is exceeded, TextConvo returns HTTP 429.

    json
    {
      "error": "Rate limit exceeded",
      "retry_after": 30
    }

    Error Codes

    CodeDescription
    400Bad Request — Invalid parameters
    401Unauthorized — Invalid API key
    403Forbidden — Insufficient permissions
    429Rate Limit — Too many requests
    500Server Error — Internal error

    CRM Integrations

    Overview

    TextConvo supports bi-directional CRM integration. Your CRM sends leads via webhook, TextConvo runs the journey, and pushes results back automatically.

    CRM → Webhook → TextConvo → Journey → Push results back → CRM

    HubSpot Integration Guide

    Prerequisites

    • HubSpot account (any paid plan)
    • TextConvo account with API access
    • Super Admin access in HubSpot

    Step 1 — Create a Legacy App in HubSpot

    1. Go to app-na2.hubspot.com
    2. Click Settings (gear icon) → Integrations → Legacy Apps
    3. Click "Create legacy app" → Select "Private"
    4. Name it "TextConvo"
    5. Go to Scopes tab and add crm.objects.contacts.read and crm.objects.contacts.write
    6. Click "Create app"
    7. Go to Auth tab → click "Show token"
    8. Copy your Access Token (starts with pat-)

    Step 2 — Create custom contact properties

    In HubSpot → Settings → Data Management → Properties → Contact properties → Create property:

    Property labelInternal nameField type
    TextConvo Journey Statustextconvo_journey_statusSingle-line text
    TextConvo Journey Datetextconvo_journey_dateDate picker
    TextConvo Outcometextconvo_outcomeSingle-line text
    TextConvo Notestextconvo_notesMulti-line text

    Step 3 — Connect in TextConvo

    1. Go to Integrations → CRM Connectors → HubSpot
    2. Click "Connect"
    3. Paste your Access Token (pat-na2-...)
    4. Paste your Webhook Client Secret (found in HubSpot → Settings → Integrations → Private Apps → your app → Auth tab → Client Secret)
    5. Review field mappings (defaults are recommended)
    6. Select outbound events to sync back
    7. Click "Save & Activate"

    Step 4 — Configure HubSpot webhook

    1. In your HubSpot app → Webhooks tab
    2. Set Target URL to your TextConvo webhook URL:
      1. In TextConvo → Integrations → CRM Connectors → HubSpot → Manage
      2. Copy the Webhook URL shown at the top
      3. Paste it into HubSpot as the Target URL
    1. Subscribe to Contact → Created and Contact → Property changed events
    2. Include properties: firstname, lastname, phone, email, company
    3. Click Subscribe

    Step 5 — Test the integration

    1. Create a test contact in HubSpot with a phone number
    2. Check TextConvo → Integrations → HubSpot → Webhook logs
    3. Verify contact appears in TextConvo contacts
    4. Check HubSpot contact for TextConvo properties after journey completes

    Field Mapping Reference

    HubSpot fieldTextConvo field
    firstnamefirst_name
    lastnamelast_name
    phonephone (required)
    emailemail
    companycompany
    hs_lead_sourcelead_source

    Troubleshooting

    IssueSolution
    Webhook logs show “Invalid signature”Check Client Secret is correct in TextConvo
    Contact created but no journeyPhone number missing on HubSpot contact
    Push fails with 400Custom HubSpot properties not created (Step 2)
    Push fails with 401Access Token expired — reconnect in TextConvo

    HubSpot Integration FAQ

    GoHighLevel Integration Guide

    Prerequisites

    • GoHighLevel account (any Agency plan)
    • TextConvo account with API access
    • Super Admin access in TextConvo

    Step 1 — Create a Private Integration Token in GHL

    1. Log into GHL and switch to your Sub-Account
    2. Go to Settings → Private Integrations → Create New Integration
    3. Name it "TextConvo"
    4. Select these scopes:
      • contacts.readonly
      • contacts.write
      • locations.readonly
    5. Click Create and copy the token immediately (starts with pit-)

    You cannot view this token again after leaving the page.

    Step 2 — Find your Location ID

    1. In GHL, switch to your Sub-Account
    2. Look at the URL in your browser:
    text
    app.gohighlevel.com/location/{YOUR_LOCATION_ID}/dashboard

    Copy the Location ID from the URL.

    Step 3 — Create custom contact fields in GHL

    In GHL → Settings → Custom Fields → Add Field (type: Text) for each:

    Field KeyPurpose
    textconvo_journey_statusJourney completion status
    textconvo_journey_outcomeJourney outcome (qualified, not interested etc)
    textconvo_reentry_tokenTriggers re-entry into a new journey

    Step 4 — Connect in TextConvo

    1. Go to Integrations → CRM Connectors → GoHighLevel
    2. Click "Connect"
    3. Select your Account
    4. Paste your Private Integration Token (pit-xxx)
    5. Paste your Sub-Account Location ID
    6. Review field mappings (defaults recommended)
    7. Select outbound events to sync back
    8. Click "Save & Activate"

    Step 5 — Set up TWO GHL Workflows

    GHL uses Workflows to send webhooks. You need two separate workflows.

    Get your Webhook URL first: TextConvo → Integrations → CRM Connectors → GoHighLevel → Manage → copy the Webhook URL.

    Workflow 1 — New Contacts

    1. GHL → Automation → Workflows → Create Workflow → Start from Scratch
    2. Add Trigger: Contact Created (no filters needed)
    3. Add Action: Webhook
      • Method: POST
      • URL: paste your TextConvo Webhook URL copied above
    4. Save and click Publish

    Workflow 2 — Contact Updates

    1. GHL → Automation → Workflows → Create Workflow → Start from Scratch
    2. Add Trigger: Contact Changed (no filters needed)
    3. Add Action: Webhook
      • Method: POST
      • URL: paste the same TextConvo Webhook URL
    4. Save and click Publish

    Step 6 — Test the integration

    1. Create a test contact in GHL with a phone number
    2. Check TextConvo → Inquiries — contact should appear within seconds with SOURCE = GoHighLevel
    3. Update a field on the GHL contact
    4. Check the Activity Timeline in TextConvo — should show "GoHighLevel contact updated" with the changed field

    Field Mapping Reference

    GoHighLevel fieldTextConvo field
    firstNamefirst_name
    lastNamelast_name
    phonephone (required)
    emailemail
    sourcelead_source
    tagstags

    Troubleshooting

    IssueSolution
    No contacts appearing after creationConfirm Workflow 1 (Contact Created) is Published
    No timeline events on field changesConfirm Workflow 2 (Contact Changed) is Published
    Test connection fails with 401Token invalid — recreate Private Integration in GHL
    Test connection fails with 404Location ID incorrect — check URL in GHL sub-account
    SOURCE shows as “gohighlevel” not “GoHighLevel”Reconnect the integration — display name fix is live

    GoHighLevel Integration FAQ

    Zoho CRM Integration Guide

    Prerequisites

    • Zoho CRM account (any paid plan)
    • TextConvo account with API access
    • Super Admin access in TextConvo

    Step 1 — Generate an OAuth Access Token

    For testing, use the Self Client method:

    1. Go to api-console.zoho.com
    2. Click "Self Client"
    3. Click "Generate Code"
    4. In the Scope field paste:
      text
      ZohoCRM.modules.contacts.ALL,ZohoCRM.org.READ,ZohoCRM.modules.notes.CREATE
    5. Set duration to 10 minutes
    6. Click Create — copy the token immediately (starts with 1000.)

    For production use, create a Server-based OAuth app to get a refresh token that never expires. Self Client tokens expire in 10 minutes.

    Step 2 — Connect in TextConvo

    1. Go to Integrations → CRM Connectors → Zoho CRM
    2. Click Connect
    3. Select your Account
    4. Paste your OAuth Access Token (starts with 1000.)
    5. Enter any value for Webhook Secret (e.g. test123) — optional for testing
    6. Review field mappings (defaults recommended)
    7. Select outbound events to sync back
    8. Click Save & Activate

    Get your Webhook URL from: TextConvo → Integrations → CRM Connectors → Zoho CRM → Manage → Webhook URL.

    Step 3 — Create TWO Workflow Rules in Zoho CRM

    Zoho sends webhooks via Workflow Rules. You need two separate rules.

    Rule 1 — New Contacts

    1. Zoho CRM → Setup → Automation → Workflow Rules → Create Rule
    2. Module: Contacts
    3. Rule Name: TextConvo — Contact Created
    4. Execute On: Record Action → Create
    5. Condition: All Contacts → Done
    6. Instant Action: Webhook
      • URL: paste your TextConvo Webhook URL
      • Method: POST
      • Body Type: Raw → Format: JSON
      • Body:
    json
    {
      "contact_id": "${!Contacts.id}",
      "First_Name": "${!Contacts.First_Name}",
      "Last_Name": "${!Contacts.Last_Name}",
      "Phone": "${!Contacts.Phone}",
      "Mobile": "${!Contacts.Mobile}",
      "Email": "${!Contacts.Email}",
      "operation": "insert"
    }
    1. Save and Associate → Activate the rule

    Rule 2 — Contact Updates

    1. Create another Workflow Rule
    2. Module: Contacts
    3. Rule Name: TextConvo — Contact Updated
    4. Execute On: Record Action → Edit
    5. Condition: All Contacts → Done
    6. Instant Action: Webhook
      • URL: same TextConvo Webhook URL
      • Method: POST
      • Body Type: Raw → Format: JSON
      • Body:
    json
    {
      "contact_id": "${!Contacts.id}",
      "First_Name": "${!Contacts.First_Name}",
      "Last_Name": "${!Contacts.Last_Name}",
      "Phone": "${!Contacts.Phone}",
      "Mobile": "${!Contacts.Mobile}",
      "Email": "${!Contacts.Email}",
      "operation": "update"
    }
    1. Save and Associate → Activate the rule

    Important: The merge field syntax requires ${!Contacts.field_name} with the exclamation mark and underscores. Spaces in field names are not supported.

    Step 4 — Create custom fields in Zoho for outbound sync

    In Zoho CRM → Setup → Customization → Modules → Contacts → Fields → Add Custom Field (type: Single Line Text):

    • Field name: TextConvo_Journey_Status
    • Field name: TextConvo_Journey_Outcome

    Step 5 — Test the integration

    1. Create a new contact in Zoho CRM with a phone number (include country code e.g. +1)
    2. Check TextConvo → Inquiries — contact should appear within seconds with SOURCE = Zoho
    3. Edit a field on that same contact (e.g. change first name)
    4. Check the Activity Timeline in TextConvo — should show "Zoho contact updated" with the changed field

    Field Mapping Reference

    Zoho CRM fieldTextConvo field
    First_Namefirst_name
    Last_Namelast_name
    Phonephone (required)
    Emailemail
    Lead_Sourcelead_source

    Important Notes

    • OAuth tokens from Self Client expire in 10 minutes (testing only)
    • For production, use a Server-based OAuth app to get a non-expiring refresh token
    • Two workflow rules are required — one for Create, one for Edit
    • Merge fields must use ${!Contacts.field_name} syntax with ! and underscores
    • Zoho limits webhooks to 10 fields maximum per notification

    Troubleshooting

    IssueSolution
    No contacts appearingConfirm Rule 1 (Create) is Active in Zoho
    No timeline events on editsConfirm Rule 2 (Edit) is Active and body has operation=update
    Connection invalid after 10 minOAuth token expired — regenerate from api-console.zoho.com
    Duplicate inquiries on editCheck Rule 2 body — operation must be "update" not "insert"
    Merge field error on saveUse ${!Contacts.First_Name} syntax — exclamation mark required
    Test connection fails 401Token expired — generate a new one from Self Client

    Zoho CRM Integration FAQ

    Pipedrive

    Coming soon

    Documentation coming soon. Contact us to join the early access list.

    Contact us

    Zendesk

    Coming soon

    Documentation coming soon. Contact us to join the early access list.

    Contact us

    Salesforce

    Coming soon

    Documentation coming soon. Contact us to join the early access list.

    Contact us

    Push Journey Results

    Pushing journey results back to your CRM is not a separate endpoint you call. When a journey outcome is ready, TextConvo automatically writes it back to any connected CRM and emits the journey.crm_pushed webhook event (documented under Webhooks). Listen for that event to know when a result has synced.

    CRM → Webhook → TextConvo → Journey → Auto write-back + journey.crm_pushed → CRM

    journey.crm_pushed payload

    json
    {
      "event": "journey.crm_pushed",
      "contact_id": "cnt_xyz789",
      "crm_key": "hubspot",
      "outcome": "completed",
      "pushed_at": "2026-07-20T11:30:00Z",
      "lead_id": "lead_abc123",
      "crm_contact_id": "473031564003",
      "fields_synced": ["textconvo_journey_status", "textconvo_outcome"]
    }