Connect Disqua Helpdesk to any CRM or database. When a customer emails your support address, Disqua automatically queries your endpoint to identify the sender — showing their name, plan, lifetime value, and a direct link into your CRM — right inside the ticket view.
On this page
Every time a new support ticket is created from an inbound email, Disqua sends a
POST request to your configured endpoint with the sender's email address.
Your server responds with customer data (or found: false), and Disqua
displays it in the ticket detail sidebar — no page refresh needed.
1. Email arrives → support@yourcompany.com
2. Disqua creates ticket from email
3. Disqua POSTs to your endpoint (async, non-blocking):
POST https://your-api.com/crm/lookup
X-API-Key: your-secret
{ "email": "jan.novak@company.cz", "ticket_id": "uuid", ... }
4. Your server queries its CRM/database
5. Your server responds with customer data
6. Disqua shows the result in the ticket sidebar:
✓ Jan Novák [Pro zákazník] Plan: Business MRR: €149
[Zobrazit v CRM →]
Lookup runs in the background. Ticket is created immediately — the CRM panel updates when your endpoint responds.
Results are cached (configurable TTL). Agents see instant results without re-querying your CRM on every view.
Configure a link template and agents get a one-click button to open the customer record in your CRM.
Build your lookup endpoint
Create an HTTP endpoint that accepts POST requests and returns customer data. See Request format and Code examples below.
Open Helpdesk Settings → YourCRM
In your Disqua workspace, go to Helpdesk → Settings → YourCRM and click Přidat connector.
Configure the connector
Název
Human-readable name, e.g. Billing CRM
Lookup URL
Your endpoint URL, e.g. https://api.yourapp.com/crm/lookup
Secret Header Name
HTTP header to send your secret, e.g. X-API-Key
Secret Header Value
Your API key or token (stored AES-256-GCM encrypted)
Link Template
Deep link into CRM, e.g. https://crm.yourapp.com/customers/{customer_id}
Cache TTL (h)
How long to cache lookup results (default: 24 hours)
Save and verify
Save the connector. Open an existing email ticket — the YourCRM panel appears in the right sidebar. Use the refresh button to trigger a live lookup and verify the response.
Disqua sends a POST request to your endpoint with
Content-Type: application/json.
The request times out after 3 seconds — keep your response fast.
POST /crm/lookup HTTP/1.1
Content-Type: application/json
User-Agent: Disqua-CRM-Connector/1.0
X-API-Key: your-secret-here ← your configured secret header
{
"email": "jan.novak@company.cz", // sender's email address
"ticket_id": "b8a1bf5d-8ce7-46ac-...", // Disqua ticket UUID
"workspace_id": "f8e7e88d-2108-11f1-...", // Disqua workspace UUID
"source": "disqua-helpdesk" // always this value
}
Only the email field is required for your lookup logic. The other fields are provided for logging or routing purposes.
Your endpoint must respond with HTTP 200 and a JSON body. Any non-2xx response is treated as an error. Response must arrive within 3 seconds.
{
"found": true,
"customer_id": "12345", // used in link_template substitution
"name": "Jan Novák", // displayed in ticket sidebar
"badge": "Business zákazník", // badge label (max 30 chars)
"badge_color": "green", // green | blue | yellow | red | gray
"fields": { // max 5 fields displayed
"Plan": "Business",
"MRR": "€149",
"Zákazník od": "2023-04-15",
"Tickets": "47",
"NPS": "9"
}
}
{
"found": false
}
When found: false, Disqua shows a "Neznámý" badge with a tooltip linking to this documentation.
| Field | Type | Required | Description |
|---|---|---|---|
found | boolean | Yes | Whether this email is a known customer |
customer_id | string | No | Your internal customer ID, used in {customer_id} link template |
name | string | No | Customer display name shown in the sidebar |
badge | string | No | Short badge label (max 30 chars), e.g. "Pro Customer", "Free Trial" |
badge_color | string | No | One of: green, blue, yellow, red, gray |
fields | object | No | Key-value pairs shown in the sidebar (max 5, string values only) |
Minimal server-side implementations to get you started.
// Install: npm install express
const express = require('express');
const app = express();
app.use(express.json());
app.post('/crm/lookup', async (req, res) => {
// Verify the secret
if (req.headers['x-api-key'] !== process.env.DISQUA_SECRET) {
return res.status(401).json({ error: 'Unauthorized' });
}
const { email } = req.body;
// Query your database
const customer = await db.query(
'SELECT * FROM customers WHERE email = ?', [email]
);
if (!customer) {
return res.json({ found: false });
}
res.json({
found: true,
customer_id: customer.id,
name: customer.full_name,
badge: customer.plan + ' Customer',
badge_color: customer.plan === 'business' ? 'green' : 'blue',
fields: {
'Plan': customer.plan,
'MRR': '€' + customer.mrr,
'Member since': customer.created_at.toISOString().slice(0, 10),
'Tickets': String(customer.ticket_count),
},
});
});
app.listen(3000);
<?php
// Verify the secret header
if ($_SERVER['HTTP_X_API_KEY'] !== getenv('DISQUA_SECRET')) {
http_response_code(401);
die(json_encode(['error' => 'Unauthorized']));
}
$body = json_decode(file_get_contents('php://input'), true);
$email = $body['email'] ?? '';
// Query your database
$stmt = $pdo->prepare('SELECT * FROM customers WHERE email = ?');
$stmt->execute([$email]);
$customer = $stmt->fetch();
header('Content-Type: application/json');
if (!$customer) {
echo json_encode(['found' => false]);
exit;
}
echo json_encode([
'found' => true,
'customer_id' => $customer['id'],
'name' => $customer['full_name'],
'badge' => ucfirst($customer['plan']) . ' Customer',
'badge_color' => 'green',
'fields' => [
'Plan' => $customer['plan'],
'MRR' => '€' . $customer['mrr'],
'Tickets' => (string)$customer['ticket_count'],
],
]);
# pip install flask
from flask import Flask, request, jsonify
import os
app = Flask(__name__)
@app.route('/crm/lookup', methods=['POST'])
def crm_lookup():
# Verify the secret
if request.headers.get('X-API-Key') != os.environ['DISQUA_SECRET']:
return jsonify({'error': 'Unauthorized'}), 401
email = request.json.get('email')
# Query your database
customer = db.session.query(Customer).filter_by(email=email).first()
if not customer:
return jsonify({'found': False})
return jsonify({
'found': True,
'customer_id': str(customer.id),
'name': customer.full_name,
'badge': customer.plan.title() + ' Customer',
'badge_color': 'green',
'fields': {
'Plan': customer.plan,
'MRR': f'€{customer.mrr}',
'Tickets': str(customer.ticket_count),
}
})
Configure a Link Template in your connector settings to generate a direct link to the customer record in your CRM. Disqua substitutes placeholders with values from the lookup response.
| Placeholder | Replaced with |
|---|---|
{customer_id} | The customer_id from your lookup response |
{email} | The URL-encoded sender email address |
Template: https://app.hubspot.com/contacts/{customer_id}
Result: https://app.hubspot.com/contacts/12345
Template: https://app.intercom.com/a/users/{customer_id}
Result: https://app.intercom.com/a/users/usr_abc123
Template: https://admin.yourapp.com/users?email={email}
Result: https://admin.yourapp.com/users?email=jan.novak%40company.cz
Template: https://console.stripe.com/customers/{customer_id}
Result: https://console.stripe.com/customers/cus_Abc123Def456
Your endpoint controls the badge color. Use it to communicate customer tier or status at a glance.
Disqua caches lookup results to avoid re-querying your endpoint on every ticket view. Configure the Cache TTL to balance freshness vs. load on your CRM.
| TTL | Recommended for |
|---|---|
| 0 hours | Always re-query — for real-time data (high CRM load) |
| 1 hour | Fast-changing data (trial status, support credits) |
| 24 hours (default) | Plan, MRR, registration date — changes slowly |
| 168 hours | Static data (customer ID, company) — changes rarely |
Configure a Secret Header in Disqua and verify it on your endpoint. The secret is stored AES-256-GCM encrypted in Disqua's database and never logged.
// Node.js — verify secret header
const SECRET = process.env.DISQUA_CRM_SECRET;
app.post('/crm/lookup', (req, res) => {
const incoming = req.headers['x-api-key'];
// Use timing-safe comparison to prevent timing attacks
if (!incoming || !crypto.timingSafeEqual(
Buffer.from(incoming),
Buffer.from(SECRET)
)) {
return res.status(401).json({ error: 'Unauthorized' });
}
// proceed...
});
openssl rand -hex 32===)fields| Status | Disqua shows | When |
|---|---|---|
| found | Customer panel with data | 200 + found: true |
| not_found | "Neznámý" badge + ⓘ tooltip | 200 + found: false |
| error | "Chyba" badge + HTTP status | Non-2xx response or invalid JSON |
| timeout | "Timeout (3s)" badge | No response within 3 seconds |
In Helpdesk → Settings → YourCRM, select a connector and click Načíst log to see the last 10 lookup results — including errors, HTTP status codes, and timestamps. Use this to debug why lookups are failing.
curl -s -X POST https://your-api.com/crm/lookup \
-H "Content-Type: application/json" \
-H "X-API-Key: your-secret" \
-d '{
"email": "test@example.com",
"ticket_id": "test-ticket-id",
"workspace_id": "test-workspace-id",
"source": "disqua-helpdesk"
}'
Your workspace can have multiple CRM connectors. Each one queries a different endpoint and shows a separate panel in the ticket sidebar. Useful when customer data lives in multiple systems:
Example: SaaS company
Example: E-commerce