Webhooks let you send data from Ansa to your server in real-time. When a form is submitted or a conversation event occurs, Ansa sends an HTTP POST request to your endpoint with the event payload.
Quick Start
Create an endpoint on your server to receive webhook payloads
Configure the webhook URL in your tool settings (dashboard or API)
Handle incoming requests and process the data
Example Endpoint (Node.js / Express)
app . post ( "/webhooks/ansa" , express . json (), ( req , res ) => {
const { submissionId , toolName , formData , conversationId } = req . body ;
console . log ( `Form submitted: ${ toolName } ` );
console . log ( "Data:" , formData );
// Process the submission (save to DB, send email, etc.)
// Respond with 200 to acknowledge receipt
res . status ( 200 ). json ({ received: true });
});
When a form is submitted, Ansa sends the data to your configured webhook URL.
Payload Structure
{
"submissionId" : "sub_abc123" ,
"toolId" : "tool_xyz789" ,
"toolName" : "contact_form" ,
"conversationId" : "conv_def456" ,
"formData" : {
"name" : "Jane Smith" ,
"email" : "jane@example.com" ,
"message" : "I have a question about pricing"
},
"submittedAt" : "2024-12-23T14:30:00.000Z"
}
Payload Fields
Field Type Description submissionIdstringUnique identifier for this submission toolIdstringID of the form tool toolNamestringName of the form tool conversationIdstringID of the conversation where the form was shown formDataobjectKey-value pairs of submitted form fields submittedAtstringISO 8601 timestamp
Set the webhook URL when creating or updating a form tool:
In the Dashboard:
Go to Agents → Tools
Edit your form tool
Under Post-Submission Actions , set the Webhook URL
Via API:
curl -X POST https://api.ansa.so/tools \
-H "Authorization: Bearer $ANSA_API_KEY " \
-H "Content-Type: application/json" \
-d '{
"name": "contact_form",
"type": "form",
"formWebhookUrl": "https://your-server.com/webhooks/ansa",
"formExecutionMode": "webhook",
"formFieldsSchema": [
{ "name": "email", "label": "Email", "type": "email" },
{ "name": "message", "label": "Message", "type": "textarea" }
]
}'
Execution Modes
Control how form submissions are processed:
Mode Behavior webhookData sent to webhook only (default) clientData handled by client-side JavaScript only bothData sent to both webhook and client handler
{
"formExecutionMode" : "webhook" ,
"formWebhookUrl" : "https://your-server.com/api/forms"
}
Handling Webhook Requests
Basic Handler
// Express.js
app . post ( "/webhooks/ansa" , express . json (), async ( req , res ) => {
try {
const { submissionId , toolName , formData } = req . body ;
// Process based on form type
switch ( toolName ) {
case "contact_form" :
await handleContactForm ( formData );
break ;
case "quote_request" :
await handleQuoteRequest ( formData );
break ;
default :
console . log ( `Unknown form: ${ toolName } ` );
}
res . status ( 200 ). json ({ success: true });
} catch ( error ) {
console . error ( "Webhook error:" , error );
res . status ( 500 ). json ({ error: "Processing failed" });
}
});
async function handleContactForm ( data ) {
// Send email notification
await sendEmail ({
to: "team@yourcompany.com" ,
subject: `New contact from ${ data . name } ` ,
body: data . message
});
// Save to database
await db . insert ( "contacts" , {
name: data . name ,
email: data . email ,
message: data . message ,
createdAt: new Date ()
});
}
Next.js API Route
// app/api/webhooks/ansa/route.ts
import { NextRequest , NextResponse } from "next/server" ;
interface AnsaWebhookPayload {
submissionId : string ;
toolId : string ;
toolName : string ;
conversationId : string ;
formData : Record < string , unknown >;
submittedAt : string ;
}
export async function POST ( request : NextRequest ) {
const payload : AnsaWebhookPayload = await request . json ();
console . log ( "Received Ansa webhook:" , payload . toolName );
// Process the form submission
if ( payload . toolName === "lead_form" ) {
await createLead ( payload . formData );
}
return NextResponse . json ({ received: true });
}
async function createLead ( data : Record < string , unknown >) {
// Add to your CRM, database, etc.
}
Python (Flask)
from flask import Flask, request, jsonify
app = Flask( __name__ )
@app.route ( "/webhooks/ansa" , methods = [ "POST" ])
def handle_ansa_webhook ():
payload = request.json
submission_id = payload.get( "submissionId" )
tool_name = payload.get( "toolName" )
form_data = payload.get( "formData" , {})
print ( f "Form submitted: { tool_name } " )
print ( f "Data: { form_data } " )
# Process the submission
if tool_name == "contact_form" :
handle_contact_form(form_data)
return jsonify({ "received" : True }), 200
def handle_contact_form ( data ):
# Your processing logic here
pass
PHP (Laravel)
// routes/api.php
Route :: post ( '/webhooks/ansa' , function ( Request $request ) {
$payload = $request -> all ();
Log :: info ( 'Ansa webhook received' , [
'toolName' => $payload [ 'toolName' ],
'formData' => $payload [ 'formData' ]
]);
// Process based on form type
match ( $payload [ 'toolName' ]) {
'contact_form' => ContactSubmission :: create ( $payload [ 'formData' ]),
'quote_request' => QuoteRequest :: create ( $payload [ 'formData' ]),
default => null
};
return response () -> json ([ 'received' => true ]);
});
Post-Submission Actions
Beyond the primary webhook, you can configure additional actions:
Slack Notifications
Send form submissions to a Slack channel:
{
"formPostActions" : [
{
"type" : "slack" ,
"webhookUrl" : "https://hooks.slack.com/services/T00/B00/xxx" ,
"message" : "New {{_toolName}} submission! \n Email: {{email}} \n Message: {{message}}"
}
]
}
Template variables:
{{fieldName}} — Value of the form field
{{_toolName}} — Name of the form tool
Multiple Webhooks
Send data to multiple endpoints:
{
"formWebhookUrl" : "https://your-server.com/primary" ,
"formPostActions" : [
{
"type" : "webhook" ,
"webhookUrl" : "https://zapier.com/hooks/catch/xxx" ,
"payload" : {
"email" : "{{email}}" ,
"source" : "ansa_chat"
}
},
{
"type" : "webhook" ,
"webhookUrl" : "https://your-crm.com/api/leads"
}
]
}
Retry Logic
Ansa automatically retries failed webhook deliveries:
Attempt Delay 1 Immediate 2 1 minute 3 5 minutes 4 30 minutes 5 2 hours
After 5 failed attempts, the submission is marked as webhook_failed. You can:
Fix your endpoint and manually retry from the dashboard
Query failed submissions via API and reprocess
Checking Submission Status
curl https://api.ansa.so/form-submissions?status=webhook_failed \
-H "Authorization: Bearer $ANSA_API_KEY "
Response Handling
Ansa records your webhook response for debugging:
Success Response
Return a 2xx status code:
res . status ( 200 ). json ({
success: true ,
leadId: "lead_123" // Your response is stored
});
Error Response
Non-2xx responses trigger a retry:
// This will trigger a retry
res . status ( 500 ). json ({
error: "Database unavailable"
});
Timeout
Webhooks have a 30-second timeout. For long-running operations:
Acknowledge immediately with 200
Process asynchronously (queue, background job)
app . post ( "/webhooks/ansa" , ( req , res ) => {
// Acknowledge immediately
res . status ( 200 ). json ({ received: true });
// Process in background
processSubmissionAsync ( req . body ). catch ( console . error );
});
async function processSubmissionAsync ( payload ) {
// Slow operations here
await syncToCRM ( payload );
await sendWelcomeEmail ( payload . formData . email );
}
Integration Examples
HubSpot CRM
app . post ( "/webhooks/ansa" , async ( req , res ) => {
const { formData } = req . body ;
await fetch ( "https://api.hubapi.com/crm/v3/objects/contacts" , {
method: "POST" ,
headers: {
"Authorization" : `Bearer ${ process . env . HUBSPOT_TOKEN } ` ,
"Content-Type" : "application/json"
},
body: JSON . stringify ({
properties: {
email: formData . email ,
firstname: formData . firstName ,
lastname: formData . lastName ,
company: formData . company
}
})
});
res . json ({ success: true });
});
Airtable
app . post ( "/webhooks/ansa" , async ( req , res ) => {
const { formData , submittedAt } = req . body ;
await fetch ( `https://api.airtable.com/v0/ ${ AIRTABLE_BASE } /Leads` , {
method: "POST" ,
headers: {
"Authorization" : `Bearer ${ process . env . AIRTABLE_TOKEN } ` ,
"Content-Type" : "application/json"
},
body: JSON . stringify ({
fields: {
"Email" : formData . email ,
"Name" : formData . name ,
"Message" : formData . message ,
"Submitted" : submittedAt
}
})
});
res . json ({ success: true });
});
Google Sheets (via Apps Script)
Deploy a Google Apps Script as a web app:
function doPost ( e ) {
const payload = JSON . parse ( e . postData . contents );
const sheet = SpreadsheetApp . openById ( SHEET_ID ). getActiveSheet ();
sheet . appendRow ([
new Date (),
payload . toolName ,
payload . formData . email ,
payload . formData . name ,
payload . formData . message
]);
return ContentService . createTextOutput (
JSON . stringify ({ success: true })
). setMimeType ( ContentService . MimeType . JSON );
}
Best Practices
Respond quickly, process later. Acknowledge webhooks immediately with a 200 response, then process data asynchronously to avoid timeouts.
Return 200 quickly — Acknowledge receipt before processing
Use idempotency — Handle duplicate deliveries gracefully using submissionId
Log everything — Store payloads for debugging
Handle errors — Return appropriate status codes so Ansa can retry
Secure your endpoint — Use HTTPS and consider IP allowlisting
Debugging
Check webhook delivery status in the dashboard:
Go to Leads or Form Submissions
Find the submission
View Webhook Response for success/error details
You can also view the raw response your server returned.
Next Steps
Form Tools Configure form tools with webhooks
Post Actions Set up Slack and email notifications