For developers and connected apps
BotDesk API and webhooks
Send every new lead from your chatbot to the tools you already use, or read and update your leads from your own code. Webhooks and the API are on the Growth plan and up. Every lead is still emailed to you as well.
Webhooks: a message to another app for every new lead
A webhook is a web address another app gives you. When your chatbot captures a lead, BotDesk sends the lead to that address within a few seconds.
- In your BotDesk dashboard, open Integrations.
- Paste the address into Send new leads to another app and click Save address. It must start with
https://, use the normal https port (or 8443), and be a public address (not an IP address,localhostor a private network name). If BotDesk can't use an address, it says why right under the box and keeps the address you had before. - Click Send test event. You see right away whether the other app received it.
A lead is sent when the chatbot collects someone's name and phone or email in the chat, when a visitor fills in the booking or call-back form, and when the bot hands a visitor with contact details to your team. If the same person sends the same request again within 2 minutes, it is sent once, so a visitor who repeats themselves does not show up twice. If a delivery fails, their next try goes out normally.
Delivery, retries and the delivery log
- Your app should answer with any
2xxstatus within 5 seconds. Do the slow work after you answer. - If the address cannot be reached, times out, or answers
5xxor429, BotDesk tries again after about 1 second and then about 3 seconds (3 tries at most). - Redirects are not followed, so paste the final address. (Google Apps Script web apps answer with a redirect after they run, which counts as delivered.)
- The last 25 deliveries (time, event, status, how long it took, which try) are under Integrations > Signing secret and recent deliveries.
Slack and Discord
Paste a Slack or Discord webhook address and BotDesk posts a short readable message instead of raw data, for example:
New lead from your website: Dana Ruiz · (617) 555-0111 · Water heater is leaking, can someone come today?
- Slack: add the Incoming Webhooks app to your workspace, pick a channel, and copy the address that starts with
https://hooks.slack.com/. - Discord: open Server Settings > Integrations > Webhooks, click New Webhook, pick a channel and click Copy Webhook URL.
Messages never ping @everyone or anyone else, whatever a visitor types.
Zapier and Make
Zapier
- Create a Zap. For the trigger choose Webhooks by Zapier and the event Catch Hook. (Zapier's webhook trigger may need a paid Zapier plan.)
- Copy the webhook URL Zapier shows you and paste it in BotDesk under Integrations, then click Save address.
- Click Send test event in BotDesk, then Test trigger in Zapier. Zapier now knows every field (name, phone, email, message and the rest).
- Add your action: a Google Sheets row, a CRM contact, a text message, anything Zapier connects to.
Make
- Create a scenario and add the Webhooks module with the trigger Custom webhook. Click Add, name it, and copy the address.
- Paste it in BotDesk under Integrations, click Save address, then Send test event.
- Make learns the data structure from the test. Add the modules that should run for each lead.
Older Zaps keep working: the top-level fields BotDesk has always sent (name, phone, message and the rest) are still there.
Events and payload
| Event | When |
|---|---|
lead.created | The chatbot captured a lead in chat (name plus phone or email), a sales call request, a hand-off with contact details, or a call-back form in lead mode. |
booking.requested | A visitor sent the booking form, with an optional day, time of day and their time zone. |
booking.created | Your bot booked a real time (Appointments set to Book real times). data.booking has id, start and end (ISO, UTC), timezone (your business's zone), status (confirmed, or pending when you confirm each one), service, provider and the visitor's answers. The visitor's name, phone, email and note are in data as for leads. |
booking.confirmed | You confirmed a pending booking. |
booking.cancelled | You or the visitor cancelled a booking. The time is free again. |
booking.rescheduled | Reserved for when a booking moves. Today a change is a cancel plus a new booking.created. |
test.ping | You clicked Send test event. It carries a sample lead named Test Lead. |
Headers
| Header | Value |
|---|---|
X-BotDesk-Event | The event name, for example lead.created. |
X-BotDesk-Delivery | The delivery id (evt_...). The same id on every retry, so you can skip duplicates. |
X-BotDesk-Signature | t=<unix seconds>,v1=<hex HMAC-SHA256>. See Check the signature. |
Body (JSON, version 1)
{
"id": "evt_6f1c0a9b2d4e8f7a1c3b5d7e",
"event": "booking.requested",
"created_at": "2026-10-15T14:03:22.114Z",
"version": 1,
"data": {
"id": "ld_8c1f4e2a9b7d3c60",
"event": "booking.requested",
"type": "booking",
"status": "new",
"note": "",
"name": "Dana Ruiz",
"phone": "(617) 555-0111",
"email": "dana@example.com",
"contact": "(617) 555-0111",
"message": "Water heater is leaking, can someone come today?",
"conversation_excerpt": "Do you fix water heaters? Yes, we repair and replace them...",
"message_en": "",
"preferred": "today after 3",
"preferred_date": "2026-10-15",
"preferred_window": "afternoon",
"visitor_timezone": "America/New_York",
"visitor_note": "Water heater is leaking, can someone come today?",
"language": "",
"location": "",
"traffic_source": "search",
"conversation_id": "c_9f2b7a",
"created_at": "2026-10-15T14:03:21.870Z",
"updated_at": "",
"business": "Northgate Heating",
"bot_name": "Northgate Assistant"
},
"source": "botdesk",
"business": "Northgate Heating",
"name": "Dana Ruiz",
"phone": "(617) 555-0111",
"preferred": "today after 3",
"message": "Do you fix water heaters? Yes, we repair and replace them...",
"conversationId": "c_9f2b7a",
"timestamp": "2026-10-15T14:03:21.870Z"
}
Use the fields inside data for anything new. type is chat, sales_call, transfer, booking, callback or test. preferred_window is morning, afternoon, evening or any. Empty fields are empty strings, never missing.
messageis the visitor's own request: what they asked for in the chat, or the note they typed on the booking form (empty if they typed none).conversation_excerptis, for booking and call-back forms, the chat just before the form, for context. It is empty for chat leads.businessis your business name;bot_nameis the name your chat assistant shows.- The top-level fields after
dataare kept from the first version so older Zaps keep working. There,messagestill carries a form's chat text.
Check the signature
Each bot has its own signing secret (whsec_...). Copy it from Integrations > Signing secret and recent deliveries. The signature is an HMAC-SHA256 of the text <t>.<raw body>, using the whole secret (including whsec_) as the key. Compare it to v1 and reject anything older than 5 minutes. Always use the raw body exactly as received, before any JSON parsing.
Slack and Discord ignore this header, so you only need it for your own endpoint.
Node.js (Express)
const crypto = require('crypto');
const express = require('express');
const app = express();
app.post('/botdesk', express.raw({ type: 'application/json' }), (req, res) => {
const parts = Object.fromEntries((req.get('X-BotDesk-Signature') || '')
.split(',').map(p => p.split('=')));
const expected = crypto.createHmac('sha256', process.env.BOTDESK_WEBHOOK_SECRET)
.update(parts.t + '.' + req.body)
.digest('hex');
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
const ok = fresh && typeof parts.v1 === 'string' && parts.v1.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
if (!ok) return res.status(400).send('bad signature');
const event = JSON.parse(req.body);
res.sendStatus(200); // answer first
if (event.event === 'lead.created') {
// save event.data.name, event.data.phone, ...
}
});
Python (Flask)
import hashlib, hmac, json, os, time
from flask import Flask, abort, request
app = Flask(__name__)
SECRET = os.environ["BOTDESK_WEBHOOK_SECRET"].encode()
@app.post("/botdesk")
def botdesk():
raw = request.get_data()
header = request.headers.get("X-BotDesk-Signature", "")
parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
t = parts.get("t", "0")
expected = hmac.new(SECRET, t.encode() + b"." + raw, hashlib.sha256).hexdigest()
if not t.isdigit() or abs(time.time() - int(t)) > 300:
abort(400)
if not hmac.compare_digest(expected, parts.get("v1", "")):
abort(400)
event = json.loads(raw)
# save event["data"]["name"], event["data"]["phone"], ...
return "", 200
Lost the secret or shared it by mistake? Click Regenerate. The old secret stops working at once, so update your app right after.
REST API
The API lets your own code read your leads, mark them as contacted or booked, add facts your chatbot should know, and read simple stats. It is on the Growth plan and up.
Keys
- Create a key in your dashboard under Integrations > API keys. It starts with
bd_live_and is shown once. Store it somewhere safe. You can have up to 5 keys and revoke any of them. - This secret key is different from your site key (the
bd_...value in your install code). The site key is public by design; it can never call the API. - Use the key only from a server or a tool like Zapier or Make. Never put it in a web page or app that visitors can open.
Authentication
Send the key in the Authorization header on every request:
Authorization: Bearer bd_live_your_key_here
The base address is https://botdesk.studio/api/v1. Requests and responses are JSON.
Errors and limits
Every error has the same shape and a short code you can test for:
{ "error": { "code": "invalid_api_key", "message": "This key was revoked or never existed." } }
| Status | Codes |
|---|---|
| 400 | nothing_to_update, invalid_id, invalid_limit, invalid_cursor, invalid_status, note_too_long, invalid_mode, invalid_text, invalid_json, invalid_client |
| 401 | missing_api_key, invalid_api_key |
| 403 | plan_required, account_inactive, account_suspended |
| 404 | not_found, lead_not_found, client_not_found |
| 413 | knowledge_too_long |
| 429 | rate_limited (60 requests a minute per key), daily_limit (10,000 a day per key) |
Endpoints
GET/api/v1/me
Your business name, plan and site key. Agency accounts also get their list of clients.
curl https://botdesk.studio/api/v1/me \
-H "Authorization: Bearer $BOTDESK_KEY"
{ "data": { "business_name": "Northgate Heating", "bot_name": "Northgate Assistant",
"plan": "growth", "site_key": "bd_...", "client": false } }
GET/api/v1/leads?limit=25&cursor=&status=
Your leads, newest first. limit is 1 to 100 (default 25). To get the next page, pass the next_cursor from the previous answer as cursor; paging stays correct even when new leads arrive in between. status is optional: new, contacted, booked or lost. Each lead has the same fields as a webhook's data, including business and bot_name. The newest 500 leads are kept.
curl "https://botdesk.studio/api/v1/leads?limit=50" \
-H "Authorization: Bearer $BOTDESK_KEY"
{ "data": [ { "id": "ld_8c1f4e2a9b7d3c60", "status": "new", "name": "Dana Ruiz", ... } ],
"has_more": true,
"next_cursor": "eyJpIjoibGRf..." }
GET/api/v1/leads/{id}
One lead by its id.
curl https://botdesk.studio/api/v1/leads/ld_8c1f4e2a9b7d3c60 \
-H "Authorization: Bearer $BOTDESK_KEY"
PATCH/api/v1/leads/{id}
Set a lead's status (new, contacted, booked, lost) and/or a private note (up to 500 characters). You see the same status and note in the dashboard's Leads tab.
curl -X PATCH https://botdesk.studio/api/v1/leads/ld_8c1f4e2a9b7d3c60 \
-H "Authorization: Bearer $BOTDESK_KEY" \
-H "Content-Type: application/json" \
-d '{"status":"booked","note":"Tuesday 9 AM, two-story house"}'
POST/api/v1/knowledge
Add facts your chatbot should know. This is the same Key facts your bot must get right text you can edit in the dashboard under Knowledge. mode is append (default: adds your text on a new paragraph) or replace (swaps the whole text; an empty text clears it). The text holds up to 20,000 characters; a longer update is refused with knowledge_too_long, never cut off. Your plan decides how much of it the bot reads in a chat: the answer says how many characters it reads now (chars_used_in_chat) and your plan's limit (plan_chat_limit). The bot uses this text unless Knowledge is set to Just scan my website.
curl -X POST https://botdesk.studio/api/v1/knowledge \
-H "Authorization: Bearer $BOTDESK_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"We are closed Thanksgiving Day.","mode":"append"}'
{ "data": { "mode": "append", "chars": 1840, "max_chars": 20000, "chars_used_in_chat": 1840, "plan_chat_limit": 10000 } }
GET/api/v1/stats
Message and booking totals, messages per day for the last 30 days, thumbs up and down on answers, and how many stored leads have each status.
curl https://botdesk.studio/api/v1/stats \
-H "Authorization: Bearer $BOTDESK_KEY"
GET/api/v1/bookings
Bookings your bot made, from any calendar (the BotDesk calendar, Cal.com or Calendly), oldest first. Optional: from and to (ISO dates; default yesterday to 90 days ahead, at most 400 days), status (pending, confirmed, cancelled, completed or no_show) and limit (1 to 500, default 100).
curl "https://botdesk.studio/api/v1/bookings?status=confirmed" \
-H "Authorization: Bearer $BOTDESK_KEY"
{ "data": [ { "id": "bk_3f9a0c1d2e4b5a6c7d8e", "status": "confirmed", "start": "2026-10-21T18:00:00.000Z", "end": "2026-10-21T19:00:00.000Z", "timezone": "America/New_York", "service": "Estimate visit", "minutes": 60, "arrival_window": false, "name": "Maria Lopez", "phone": "(617) 555-0177", "email": "maria@example.com", "note": "Side door", "answers": [], "calendar": "botdesk", "calendar_name": "BotDesk calendar", "source": "chat" } ] }
GET/api/v1/bookings/{id}
One booking by its id (bk_...). A booking that is not on this bot answers 404 booking_not_found.
Agencies: working on a client's bot
An agency key acts on the agency's own bot. Add ?client=<the client's site key> to any endpoint to act on one of your own clients instead. GET /api/v1/me lists your clients and their site keys. A site key that is not one of your clients answers 404 client_not_found.
curl "https://botdesk.studio/api/v1/leads?client=bd_CLIENT_SITE_KEY" \
-H "Authorization: Bearer $BOTDESK_KEY"
Connect your data
Your bot can use live information from your business: an up to date price list for everyone, and, for customers who are signed in to your website, their own account details like an order number or the next appointment. Find all of it in your dashboard under Integrations.
Live prices (Growth plan and up)
Under Integrations > Live prices, link a Google Sheet or a JSON address, or upload a CSV file. The list needs a name column and a price column. unit, notes, availability and link columns are optional. You see a preview and any rows that were skipped before you save.
- Google Sheet: in Google Sheets open File > Share > Publish to web, pick the tab with your prices, choose "Comma-separated values (.csv)" and click Publish. Paste that link.
- JSON: a list of objects, or an object with an
itemsorproductslist. Each object has anameand aprice. - A linked list is read again every 6 hours while people chat. If reading it fails 3 times in a row, you get an email, and the bot keeps the last good list.
- When the list is more than 2 days old, the bot says which date the prices are from.
- A price from your list wins over a price the website scan found for the same item.
Signed-in customers (Pro plan and up)
When a customer is signed in to your website, your server makes a short token that says who they are and what the bot may tell them. The page hands the token to the chat. BotDesk checks the signature with your bot's identity secret, and the bot answers only from what is inside that token. It never sees any other customer.
- In Integrations > For your developer, click Create secret. The secret is shown only once, right then: copy it and keep it on your server only (an environment variable). Never put it in a web page. BotDesk can't show it again later; if it gets lost, click Replace secret, which makes a new one and stops the old one at once.
- On your server, for a signed-in customer, make a token: a standard JWT signed with HS256 and that secret. Claims:
sub(your customer id, required),email,name,iat,exp(required, at most 24 hours afteriat), anddata: up to 20 fields, each up to 300 characters, that the bot may tell this customer. - Put it in the page before the BotDesk code:
window.botdeskVisitorToken = "...";. A single-page app can callBotDesk.identify(token)after sign-in and to refresh it. - On sign-out, call
BotDesk.identify(null). The chat then starts over: the conversation, the stored history and the messages on screen are cleared, so the next person on that browser never sees them. A page that loads with no token after a signed-in session does the same by itself. - Send every page that carries a token with
Cache-Control: private, no-store, so no cache or CDN ever serves one customer's token to someone else. - Paste a token into Test a token in the dashboard to see exactly which fields the bot will see.
How long a token works: a token is valid until its exp, up to 24 hours, and anyone who copies it can use it until then (it can't be taken back early, other than by replacing the secret). Keep tokens short, 15 to 60 minutes, and refresh them with BotDesk.identify(newToken). After you replace the secret, tokens made with the old one keep working for 10 more minutes, so your site has time to switch.
Privacy: by default, chat history, transcript emails and lead emails show only that a customer was signed in and their name. Their account details are replaced with [private]. You can choose to keep them in Integrations. The older window.botdeskVisitor name and email (not signed) is still only shown to you on the dashboard. It never unlocks account details.
Node.js
// Node.js: no extra packages. Keep the secret in an environment variable.
const crypto = require('crypto');
function botdeskToken(user) {
const b64 = (o) => Buffer.from(JSON.stringify(o)).toString('base64url');
const now = Math.floor(Date.now() / 1000);
const head = b64({ alg: 'HS256', typ: 'JWT' });
const body = b64({
sub: String(user.id), email: user.email, name: user.name,
iat: now, exp: now + 1800, // 15 to 60 minutes is best (24 hours at most)
data: { // up to 20 fields the bot may tell THIS customer
last_order: user.lastOrderNumber,
order_status: user.lastOrderStatus,
},
});
const sig = crypto.createHmac('sha256', process.env.BOTDESK_IDENTITY_SECRET)
.update(head + '.' + body).digest('base64url');
return head + '.' + body + '.' + sig;
}
// Pages that carry the token must never be cached for other people:
// res.set('Cache-Control', 'private, no-store');
// In your page, only for a signed-in customer:
// <script>window.botdeskVisitorToken = "<%= botdeskToken(currentUser) %>";</script>
// Single-page app: BotDesk.identify(newToken) to refresh it, and on sign-out:
// BotDesk.identify(null); // clears the chat so the next person never sees it
PHP
<?php
// PHP: keep the secret in an environment variable.
function botdesk_token(array $user): string {
$b64 = function ($s) { return rtrim(strtr(base64_encode($s), '+/', '-_'), '='); };
$now = time();
$head = $b64(json_encode(['alg' => 'HS256', 'typ' => 'JWT']));
$body = $b64(json_encode([
'sub' => (string) $user['id'], 'email' => $user['email'], 'name' => $user['name'],
'iat' => $now, 'exp' => $now + 1800, // 15 to 60 minutes is best (24 hours at most)
'data' => (object) ['last_order' => $user['last_order'], 'order_status' => $user['order_status']],
]));
$sig = $b64(hash_hmac('sha256', $head . '.' . $body, getenv('BOTDESK_IDENTITY_SECRET'), true));
return $head . '.' . $body . '.' . $sig;
}
// Pages that carry the token must never be cached for other people:
// header('Cache-Control: private, no-store'); // before any output
// In your page, only for a signed-in customer:
// <script>window.botdeskVisitorToken = <?= json_encode(botdesk_token($user)) ?>;</script>
// On sign-out (single-page apps): BotDesk.identify(null);
Python
# Python: standard library only. Keep the secret in an environment variable.
import base64, hashlib, hmac, json, os, time
def _b64(b: bytes) -> str:
return base64.urlsafe_b64encode(b).rstrip(b"=").decode()
def botdesk_token(user) -> str:
now = int(time.time())
head = _b64(json.dumps({"alg": "HS256", "typ": "JWT"}).encode())
body = _b64(json.dumps({
"sub": str(user.id), "email": user.email, "name": user.name,
"iat": now, "exp": now + 1800, # 15 to 60 minutes is best (24 hours at most)
"data": {"last_order": user.last_order, "order_status": user.order_status},
}).encode())
secret = os.environ["BOTDESK_IDENTITY_SECRET"].encode()
sig = _b64(hmac.new(secret, f"{head}.{body}".encode(), hashlib.sha256).digest())
return f"{head}.{body}.{sig}"
# Pages that carry the token must never be cached for other people:
# response.headers["Cache-Control"] = "private, no-store"
# In your template, only for a signed-in customer:
# <script>window.botdeskVisitorToken = {{ token|tojson }};</script>
# On sign-out (single-page apps): BotDesk.identify(null);
WordPress and WooCommerce
Adds the token for every signed-in WordPress user, plus their last WooCommerce order when WooCommerce is active.
<?php
// WordPress: add to your theme's functions.php or a small plugin.
// In wp-config.php: define('BOTDESK_IDENTITY_SECRET', 'bdid_...');
add_action('wp_head', function () {
if (!is_user_logged_in() || !defined('BOTDESK_IDENTITY_SECRET')) return;
$u = wp_get_current_user();
$data = [];
// WooCommerce: the customer's last order, if WooCommerce is active.
if (function_exists('wc_get_customer_last_order')) {
$o = wc_get_customer_last_order($u->ID);
if ($o) {
$data['last_order'] = $o->get_order_number();
$data['order_status'] = wc_get_order_status_name($o->get_status());
$data['order_date'] = $o->get_date_created() ? $o->get_date_created()->date('M j, Y') : '';
}
}
$b64 = function ($s) { return rtrim(strtr(base64_encode($s), '+/', '-_'), '='); };
$now = time();
$head = $b64(wp_json_encode(['alg' => 'HS256', 'typ' => 'JWT']));
$body = $b64(wp_json_encode([
'sub' => (string) $u->ID, 'email' => $u->user_email, 'name' => $u->display_name,
'iat' => $now, 'exp' => $now + 1800, 'data' => (object) $data,
]));
$sig = $b64(hash_hmac('sha256', $head . '.' . $body, BOTDESK_IDENTITY_SECRET, true));
echo '<script>window.botdeskVisitorToken = ' . wp_json_encode($head . '.' . $body . '.' . $sig) . ';</script>';
});
// Pages that carry the token must never be cached for other people.
add_action('send_headers', function () {
if (is_user_logged_in()) header('Cache-Control: private, no-store');
});
// After sign-out the next page has no token, so the chat starts fresh by itself.
// A page that signs out without reloading must call BotDesk.identify(null).
Live lookups (Pro plan and up)
For live data your token doesn't carry, add up to 5 lookups. Each one is a read-only GET to an https address on your server, with a name and a sentence telling the bot when to use it.
- Values in the address come only from the signed-in customer (
{customer.id},{customer.email},{customer.data.your_field}) or from inputs you declare with a pattern, for exampleorder_number: [A-Z0-9-]{3,20}. Values are URL-encoded and can never change the host. - Only
httpson port 443 or 8443, no IP addresses or private names, no redirects, a 5 second timeout and a 20 KB answer limit. - The bot only sees the fields you list (like
statusortracking.url), each cut to 300 characters. - Lookups that use customer details only run for a verified signed-in customer. That check is in BotDesk's code, not left to the AI.
- A lookup for signed-in customers must put a customer value (
{customer.id},{customer.email}or{customer.data.your_field}) in its address. Every call also carries the verified customer id, URL-encoded, in anX-BotDesk-Customerheader. Your API must check that the record belongs to this customer: an order number the visitor types is not proof that the order is theirs. - After 5 failures in a row a lookup pauses for 10 minutes. The dashboard keeps a log of the last 25 calls, without the answers.
- An optional secret header (for example
X-Api-Key) is stored encrypted. Every call also carriesX-BotDesk-Signature: t=<unix seconds>,v1=<hex>, an HMAC-SHA256 of<t>.GET <full address>, a line break, then theX-BotDesk-Customervalue (empty when there is none), made with your webhook signing secret.
// Node.js: check that a lookup really came from BotDesk.
const crypto = require('crypto');
function fromBotDesk(req) {
const parts = Object.fromEntries((req.get('X-BotDesk-Signature') || '')
.split(',').map(p => p.split('=')));
const url = 'https://' + req.get('host') + req.originalUrl; // the full address BotDesk called
const customer = req.get('X-BotDesk-Customer') || ''; // URL-encoded customer id
const expected = crypto.createHmac('sha256', process.env.BOTDESK_WEBHOOK_SECRET)
.update(parts.t + '.GET ' + url + '
' + customer).digest('hex');
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return fresh && typeof parts.v1 === 'string' && parts.v1.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
}
// Then, ALWAYS: only return the record if it belongs to decodeURIComponent(customer).
GoHighLevel
Connect a GoHighLevel sub-account and every lead your chatbot captures becomes a contact there (name, phone, email, source BotDesk). This connection does not use webhooks or API keys.
- In your dashboard, open Integrations and click Connect GoHighLevel.
- GoHighLevel opens. Pick the sub-account and approve. BotDesk only asks for permission to create contacts.
- You land back on a page that says GoHighLevel is connected. New leads now show up as contacts.
The connection is stored encrypted. Click Disconnect in Integrations to stop sending leads there.
Questions, or need an event we don't send yet? Email hello@botdesk.studio.