Warpflow
For Developers

Webhooks

Send events from any platform into the Warpflow Signals pipeline using custom webhooks with sync or async processing.

Overview

Custom webhooks are the primary way to send data into Warpflow from any system that can make HTTP requests — form builders, CRMs, scheduling tools, monitoring systems, or your own backend.

Every webhook event flows through the same pipeline as GHL, Twilio, and email messages: normalize → classify → score → route → execute actions.

Endpoint

POST https://api.warpflow.ai/api/v1/webhook/custom/{tenant_id}/{source_id}
Authorization: Bearer <api_key>
Content-Type: application/json
ParameterDescription
tenant_idYour tenant identifier
source_idA logical name for the event source (e.g., typeform, calendly, hubspot, internal-crm). Used for filtering and analytics

Payload format

{
  "contact": {
    "name": "Jane Doe",
    "phone": "+15551234567",
    "email": "jane@example.com",
    "external_id": "crm-12345"
  },
  "content": {
    "body": "I need a quote for a kitchen remodel. Budget is $40k.",
    "subject": "Quote Request"
  },
  "channel": "webhook",
  "source": "typeform",
  "metadata": {
    "form_id": "abc123",
    "submitted_at": "2026-02-22T14:30:00Z"
  }
}

Field reference

FieldRequiredDescription
contact.nameNoContact's full name
contact.phoneNo*Phone number (E.164 format preferred)
contact.emailNo*Email address
contact.external_idNoYour system's ID for this contact (used for identity resolution)
content.bodyYesThe message or event content. This is what gets classified and scored
content.subjectNoSubject line (useful for form submissions and email-like events)
channelNoDefaults to "webhook". Override if you want routing rules to match on channel
sourceNoDefaults to the source_id from the URL path
metadataNoArbitrary key-value pairs. Stored with the lead but not processed by the pipeline

*At least one of phone, email, or external_id is needed for contact resolution. If none are provided, a new anonymous contact is created.

Processing modes

Async mode (default)

The event is queued for full pipeline processing. The endpoint returns immediately with a confirmation.

curl -X POST \
  https://api.warpflow.ai/api/v1/webhook/custom/acme/typeform \
  -H "Authorization: Bearer wf_acme_abc123" \
  -H "Content-Type: application/json" \
  -d '{
    "contact": { "name": "Jane Doe", "email": "jane@example.com" },
    "content": { "body": "I need a quote for a kitchen remodel" }
  }'

Response (202):

{
  "success": true,
  "message": "Event queued for processing",
  "lead_id": "lead_abc123"
}

The full pipeline runs in the background: classify → score → route → reply → CRM updates. Results are visible in the dashboard and via the conversations API.

Sync mode

Add ?mode=sync to get an immediate response with classification, score, matched rules, and generated reply. The pipeline runs synchronously and returns everything in one call.

curl -X POST \
  "https://api.warpflow.ai/api/v1/webhook/custom/acme/typeform?mode=sync" \
  -H "Authorization: Bearer wf_acme_abc123" \
  -H "Content-Type: application/json" \
  -d '{
    "contact": { "name": "Jane Doe", "email": "jane@example.com" },
    "content": { "body": "I need a quote for a kitchen remodel. Budget is $40k." }
  }'

Response (200):

{
  "success": true,
  "lead_id": "lead_abc123",
  "classification": {
    "intent": "quote_request",
    "urgency": "medium",
    "sentiment": "positive",
    "summary": "Kitchen remodel quote request with $40k budget",
    "confidence": 0.92
  },
  "score": {
    "value": 78,
    "tier": "hot",
    "buying_signals": ["stated_budget", "specific_project"],
    "recommended_actions": ["immediate_follow_up", "schedule_consultation"]
  },
  "matched_rules": [
    { "name": "Hot leads - immediate response", "actions": ["send_template", "escalate"] }
  ],
  "reply": {
    "body": "Hi Jane! Thanks for reaching out about your kitchen remodel...",
    "guardrail_passed": true
  }
}

When to use sync mode:

  • Form submission acknowledgments (show the user a relevant response)
  • Real-time lead qualification (score before routing in your own system)
  • Testing and development (see exactly what the pipeline produces)

When to use async mode:

  • High-volume event streams
  • Events where you don't need an immediate response
  • Production webhook integrations (Zapier, n8n, etc.)

Example payloads for common sources

Form submission (Typeform, JotForm, Gravity Forms)

{
  "contact": {
    "name": "John Smith",
    "email": "john@example.com",
    "phone": "+15559876543"
  },
  "content": {
    "body": "Service: Dental implants. Insurance: Delta Dental PPO. Preferred time: mornings.",
    "subject": "New Patient Form"
  },
  "source": "typeform"
}

Scheduling event (Calendly, Acuity)

{
  "contact": {
    "name": "Sarah Johnson",
    "email": "sarah@example.com"
  },
  "content": {
    "body": "Booked a free consultation for estate planning on March 5 at 2pm",
    "subject": "New Appointment"
  },
  "source": "calendly",
  "metadata": {
    "event_type": "free_consultation",
    "scheduled_time": "2026-03-05T14:00:00Z"
  }
}

CRM contact update (HubSpot, Salesforce)

{
  "contact": {
    "email": "existing@client.com",
    "external_id": "hubspot-contact-789"
  },
  "content": {
    "body": "Deal stage changed to 'Proposal Sent'. Deal value: $12,000."
  },
  "source": "hubspot"
}

Chat widget (Intercom, Drift, custom)

{
  "contact": {
    "name": "Anonymous Visitor",
    "external_id": "chat-session-456"
  },
  "content": {
    "body": "How much does a roof replacement cost for a 2,000 sq ft house?"
  },
  "source": "live-chat"
}

Rate limits

Custom webhook requests are rate-limited per tenant. If you exceed the limit, you'll receive a 429 response with a Retry-After header.

For high-volume integrations, use async mode — it's more efficient and handles backpressure gracefully.

Error responses

StatusMeaning
200Sync mode — processed successfully
202Async mode — queued for processing
400Invalid payload (missing content.body, malformed JSON)
401Missing or invalid API key
404Unknown tenant_id
429Rate limited — retry after the specified delay
500Server error — contact support if persistent

Next steps

On this page

We use cookies to understand how you use our site and improve your experience. Privacy Policy