Webhook
Receive automatic notifications when events occur in your Discko organization.
Getting Started
Step 1: Create your endpoint
Create an HTTP endpoint that will receive POST requests from Discko:
// Example with Express.js
app.post('/webhook/discko', (req, res) => {
const { date, event, data } = req.body;
console.log('Event received:', event);
console.log('Date:', date);
console.log('Data:', data);
// Process your data here
// Send an email, save to database, etc.
res.status(200).send('OK');
});Step 2: Configure in Discko
- Log in to your Discko dashboard
- Go to Mise en place > CRM
- Expand “Webhook”
- Enter your endpoint URL
- Test the connection
Step 3: Handle events
Process different event types in your endpoint:
app.post('/webhook/discko', (req, res) => {
const { date, event, data } = req.body;
switch (event) {
case 'test':
console.log('Test webhook received');
break;
case 'lead.unlocked':
console.log('Lead unlocked:', data.id);
// Process lead (locked or unlocked)
handleLeadUnlocked(data);
break;
default:
console.log('Unknown event:', event);
}
res.status(200).send('OK');
});How it works
- Method: POST to your configured URL
- Payload: JSON with
{ date, event, data }structure - Retry: Automatic retry with exponential backoff
- Authentication: No signature verification (yet)
Expected Response Codes
200-299: Success (no retry)300-399: Redirect (followed automatically)
Retry Policy
If your endpoint responds with an error, Discko will automatically retry delivery:
- Retry attempts: 8 retries (9 total attempts including the initial request)
- Retry delay: Exponential backoff using formula
2^(attempt-1) * 1000ms- 1st retry: 1 second
- 2nd retry: 2 seconds
- 3rd retry: 4 seconds
- 4th retry: 8 seconds
- 5th retry: 16 seconds
- 6th retry: 32 seconds
- 7th retry: 64 seconds
- 8th retry: 128 seconds
- Retryable status codes:
408Request Timeout413Payload Too Large429Too Many Requests500Internal Server Error502Bad Gateway503Service Unavailable504Gateway Timeout
Events
All webhooks follow this structure:
{
"date": "2024-01-15T10:30:00Z",
"event": "event_name",
"data": { /* event-specific data */ }
}test
Test event with empty data
Schema
The test event is an empty object.
Example
{
"date": "2024-01-15T10:30:00Z",
"event": "test",
"data": {}
}lead.unlocked
Lead event fired when a lead is processed (locked or unlocked)
Schema
| Field | Type | Description |
|---|---|---|
id | String | Unique lead ID |
locked | Boolean | Whether the lead is locked |
url | String? | Lead URL |
unlockSource | String? | Reason if locked: "insufficient_balance" or "out_of_free_credits" |
date | Date | When the lead was completed |
status | String | Status of the lead: "NEW", "IN_PROGRESS", "WON", or "LOST" |
completion | Number | Completion percentage (0-100) |
category | String? | Category of the lead |
contact | Object | Contact information (person or company) |
contact.type | String | Contact type: "PERSON" or "COMPANY" |
contact.firstName | String? | First name |
contact.lastName | String? | Last name |
contact.email | String? | Email address |
contact.phone | String? | Phone number |
contact.discProfile | String? | DISC profile type |
contact.discProfileExplanation | String? | DISC profile explanation |
contact.address | String? | Address (person only) |
contact.linkedin | String? | LinkedIn profile (company only) |
contact.jobTitle | String? | Job title (company only) |
company | Object? | Company information (can be null) |
company.name | String? | Company name |
company.address | String? | Company address |
company.website | String? | Company website |
analysis | Object | Lead scoring and analysis |
analysis.fitScore | Number? | Fit score (0-2) |
analysis.fitScoreExplanation | String? | Explanation of fit score |
analysis.intentScore | Number? | Intent score (0-2) |
analysis.intentScoreExplanation | String? | Explanation of intent score |
analysis.budget | String? | Budget information |
analysis.budgetExplanation | String? | Budget explanation |
analysis.deadline | Date? | Project deadline |
analysis.deadlineExplanation | String? | Deadline explanation |
analysis.score | Number? | Overall lead score (0-8). Can be null when locked |
summary | Object | Lead summary |
summary.abstract | String? | Brief abstract of the lead needs |
summary.needs | String? | Raw needs expressed by the lead |
summary.comment | String? | Additional comments |
summary.categories | Array | Array of summary categories |
summary.categories[].name | String | Name of the category |
summary.categories[].description | String | Description of the category |
summary.categories[].summary | Array | Array of summary points |
Note: the
urlfield is not present when the lead is locked.
Examples
Individual
{
"date": "2024-01-15T10:30:00Z",
"event": "lead.unlocked",
"data": {
"id": "req_abc123",
"locked": false,
"url": "https://app.discko.io/acme/leads/req_abc123",
"date": "2024-01-15T10:30:00Z",
"status": "NEW",
"completion": 100,
"category": "Web Development",
"contact": {
"type": "PERSON",
"firstName": "Jane",
"lastName": "Doe",
"email": "jane.doe@email.com",
"phone": "+1234567890",
"address": "123 Main St, New York, NY",
"discProfile": "Dominant",
"discProfileExplanation": "Results-oriented and direct communicator"
},
"company": null,
"analysis": {
"fitScore": 2,
"fitScoreExplanation": "Strong match with our ideal customer profile",
"intentScore": 2,
"intentScoreExplanation": "High purchase intent, ready to move forward",
"budget": "10000-15000",
"budgetExplanation": "Budget aligns well with project scope",
"deadline": "2024-02-15T00:00:00.000Z",
"deadlineExplanation": "Needs delivery before product launch",
"score": 7
},
"summary": {
"abstract": "High-value lead seeking e-commerce solution with urgent timeline",
"needs": "Modern e-commerce website with payment integration",
"comment": "Looking for a quick turnaround",
"categories": [
{
"name": "E-commerce Features",
"description": "Required e-commerce functionality",
"summary": [
"Shopping cart integration",
"Payment gateway setup",
"Inventory management"
]
},
{
"name": "Timeline",
"description": "Project timeline requirements",
"summary": [
"Launch before product release",
"Quick turnaround needed",
"Delivery by mid-February"
]
}
]
}
}
}Company
{
"date": "2024-01-15T10:30:00Z",
"event": "lead.unlocked",
"data": {
"id": "req_xyz789",
"locked": false,
"url": "https://app.discko.io/acme/leads/req_xyz789",
"date": "2024-01-15T10:30:00Z",
"status": "NEW",
"completion": 100,
"category": "Digital Marketing",
"contact": {
"type": "COMPANY",
"firstName": "John",
"lastName": "Smith",
"email": "john.smith@acme.com",
"phone": "+1987654321",
"linkedin": "https://linkedin.com/in/johnsmith",
"jobTitle": "Marketing Director",
"discProfile": "Influencer",
"discProfileExplanation": "Enthusiastic and people-oriented"
},
"company": {
"name": "ACME Corp",
"address": "456 Business Ave, San Francisco, CA",
"website": "https://acme.com"
},
"analysis": {
"fitScore": 2,
"fitScoreExplanation": "Perfect match for our B2B services",
"intentScore": 2,
"intentScoreExplanation": "Active evaluation phase, comparing vendors",
"budget": "50000+",
"budgetExplanation": "Enterprise budget, multiple projects planned",
"deadline": "2024-03-01T00:00:00.000Z",
"deadlineExplanation": "Want to start campaign in Q1",
"score": 8
},
"summary": {
"abstract": "Enterprise lead with strong fit and budget, ready to switch providers",
"needs": "Comprehensive digital marketing strategy and execution",
"comment": "Currently working with another agency but not satisfied",
"categories": [
{
"name": "Marketing Strategy",
"description": "Strategic marketing requirements",
"summary": [
"Full digital marketing audit",
"Multi-channel campaign strategy",
"Performance metrics and KPIs"
]
},
{
"name": "Execution Needs",
"description": "Implementation and execution requirements",
"summary": [
"Content creation and management",
"Social media management",
"PPC and SEO campaigns",
"Regular reporting and optimization"
]
}
]
}
}
}Locked
{
"date": "2024-01-15T10:30:00Z",
"event": "lead.unlocked",
"data": {
"id": "req_locked123",
"locked": true,
"unlockSource": "insufficient_balance",
"analysis": {
"score": null
},
"date": "2024-01-15T10:30:00Z"
}
}Integration Examples
Slack Notification
const { WebClient } = require('@slack/web-api');
const slack = new WebClient(process.env.SLACK_TOKEN);
app.post('/webhook/discko', async (req, res) => {
const { event, data } = req.body;
if (event === 'lead.unlocked') {
const contactName = `${data.contact.firstName} ${data.contact.lastName}`;
const companyInfo = data.company ? ` (${data.company.name})` : '';
await slack.chat.postMessage({
channel: '#leads',
text: `🎯 New lead completed by ${contactName}${companyInfo} - Score: ${data.analysis.score}/8`
});
}
res.status(200).send('OK');
});CRM Integration
const mysql = require('mysql2/promise');
app.post('/webhook/discko', async (req, res) => {
const { event, data } = req.body;
if (event === 'lead.unlocked') {
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'password',
database: 'crm'
});
await connection.execute(
`INSERT INTO leads (
discko_id, category, email, phone, company_name,
first_name, last_name, job_title, linkedin,
fit_score, intent_score, lead_score, needs,
budget, abstract, deadline, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[
data.id,
data.category,
data.contact.email,
data.contact.phone,
data.company?.name,
data.contact.firstName,
data.contact.lastName,
data.contact.jobTitle,
data.contact.linkedin,
data.analysis.fitScore,
data.analysis.intentScore,
data.analysis.score,
data.summary.needs,
data.analysis.budget,
data.summary.abstract,
data.analysis.deadline,
data.date
]
);
await connection.end();
}
res.status(200).send('OK');
});Email Notification
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransporter({
service: 'gmail',
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS
}
});
app.post('/webhook/discko', async (req, res) => {
const { event, data } = req.body;
if (event === 'lead.unlocked') {
const name = `${data.contact.firstName} ${data.contact.lastName}`;
const companyInfo = data.company ? `<p><strong>Company:</strong> ${data.company.name}</p>` : '';
await transporter.sendMail({
from: 'noreply@yourcompany.com',
to: 'sales@yourcompany.com',
subject: `Lead unlocked: ${data.category} (Score: ${data.analysis.score}/8)`,
html: `
<h2>Lead Unlocked</h2>
<p><strong>Contact:</strong> ${name}</p>
<p><strong>Email:</strong> ${data.contact.email}</p>
<p><strong>Phone:</strong> ${data.contact.phone}</p>
${companyInfo}
<p><strong>Category:</strong> ${data.category}</p>
<p><strong>Lead Score:</strong> ${data.analysis.score}/8</p>
<p><strong>Fit Score:</strong> ${data.analysis.fitScore}/2 - ${data.analysis.fitScoreExplanation}</p>
<p><strong>Intent Score:</strong> ${data.analysis.intentScore}/2 - ${data.analysis.intentScoreExplanation}</p>
<p><strong>Budget:</strong> ${data.analysis.budget}</p>
<p><strong>Deadline:</strong> ${data.analysis.deadline}</p>
<p><strong>Needs:</strong> ${data.summary.needs}</p>
<p><strong>Abstract:</strong> ${data.summary.abstract}</p>
<p><strong>Completed:</strong> ${data.date}</p>
`
});
}
res.status(200).send('OK');
});Last updated on