Documentation Index
Fetch the complete documentation index at: https://developer.lofty.com/llms.txt
Use this file to discover all available pages before exploring further.
Webhooks deliver real-time HTTP POST notifications to your endpoint when data changes in a Lofty account. Instead of polling the API, register an HTTPS callback URL and Lofty pushes a JSON payload on each subscribed event.
Subscribe
POST https://api.lofty.com/v1.0/webhook
{
"listId": 2,
"callbackUrl": "https://your-app.example.com/webhooks/lofty",
"limit": 100
}
| Field | Type | Required | Description |
|---|
listId | integer | Yes | Event type (1–12, see table below) |
callbackUrl | string | Yes | HTTPS endpoint to receive payloads |
limit | integer | No | Max events per batch (default 100, max 5000) |
The callbackUrl must use HTTPS. HTTP endpoints are rejected.
Manage subscriptions
| Method | Endpoint | Description |
|---|
POST | /v1.0/webhook | Create a subscription |
GET | /v1.0/webhooks | List active subscriptions |
DELETE | /v1.0/webhook/{subscribeId} | Delete a subscription |
Delivery timing
Events are typically delivered within 1 minute. During high traffic, delivery may be delayed but will always occur within 5 minutes.
Notification recipients
- Lofty checks which users in the lead’s team have subscribed to the event type.
- Hidden leads — only the assigned agent receives the notification.
- Other leads — the assigned agent and Company Owner/Admin receive the notification. All other subscribers are excluded.
Common fields
All payloads include:
| Field | Type | Description |
|---|
teamId | integer | Team ID where the event occurred |
listId | integer | Event type ID (1–12) |
Event types
| listId | Event | Triggers |
|---|
| 1 | Agent Info | Agent created or updated |
| 2 | Lead Info | Lead created, updated, or deleted |
| 3 | Lead Activity | Lead site activity (browse, favorite, search) |
| 4 | Listing Alert | Listing alert changed |
| 5 | Transaction | Transaction created, updated, or deleted |
| 6 | Call | Call event (MANUAL and LOGGED only, excludes AUTO) |
| 7 | Email | Email event (MANUAL and LOGGED only, excludes AUTO) |
| 8 | Text | Text message event (MANUAL and LOGGED only, excludes AUTO) |
| 9 | Note | Note created, updated, or deleted |
| 10 | Task | Task created, updated, finished, or deleted |
| 11 | Appointment | Appointment created, updated, finished, or deleted |
| 12 | Pipeline Change | Lead pipeline stage changed |
Call, Email, and Text webhooks do not fire for AUTO communications. Only agent-initiated (MANUAL) and after-the-fact logged (LOGGED) events trigger webhooks. Automated messages sent by Lofty (drip campaigns, auto-responders, Smart Plans, etc.) are excluded. If your integration depends on tracking all communications, you must also poll the Communication API for AUTO type events.
Event payloads
1. Agent Info
Triggered when an agent is created or updated.
{
"teamId": 123456,
"listId": 1,
"updatedAgent": [
{
"agentUserId": 789,
"updateTime": 1736935800000
}
]
}
| Field | Type | Description |
|---|
updatedAgent | array | List of updated agent records |
updatedAgent[].agentUserId | integer | User ID of the updated agent |
updatedAgent[].updateTime | integer | Epoch milliseconds of the update |
2. Lead Info
Triggered when a lead is created, updated, or deleted. Only the relevant action fields are present.
{
"teamId": 123456,
"listId": 2,
"createdLead": [
{ "leadId": 456, "updateTime": 1736935800000 }
],
"updatedLead": [
{ "leadId": 457, "updateTime": 1736935860000 }
],
"deletedLead": [
{ "leadId": 458, "updateTime": 1736935920000 }
]
}
| Field | Type | Description |
|---|
createdLead | array | Created leads (present on create) |
updatedLead | array | Updated leads (present on update) |
deletedLead | array | Deleted leads (present on delete) |
[].leadId | integer | Lead ID |
[].updateTime | integer | Epoch milliseconds of the change |
3. Lead Activity
Triggered when a lead performs site activity (SiteBrowse, SiteFavorite, SiteSearch, etc.).
{
"teamId": 123456,
"listId": 3,
"updatedLead": [
{ "leadId": 456, "updateTime": 1736935800000 }
]
}
| Field | Type | Description |
|---|
updatedLead | array | Leads with new activity |
updatedLead[].leadId | integer | Lead ID |
updatedLead[].updateTime | integer | Epoch milliseconds of the activity |
4. Listing Alert
Triggered when a listing alert changes.
{
"teamId": 123456,
"listId": 4,
"alertId": 789
}
| Field | Type | Description |
|---|
alertId | integer | ID of the changed alert |
5. Transaction
Triggered when a transaction is created, updated, or deleted. Only the relevant action fields are present.
{
"teamId": 123456,
"listId": 5,
"createdTransaction": [
{
"leadId": 456,
"transactionId": 789,
"updateTime": 1736935800000
}
],
"updatedTransaction": [
{
"leadId": 457,
"transactionId": 790,
"updateTime": 1736935860000,
"updatedField": { "fieldName": "newValue" },
"updatedFields": [{ "fieldName": "newValue" }]
}
],
"deletedTransaction": [
{
"leadId": 458,
"transactionId": 791,
"updateTime": 1736935920000
}
]
}
| Field | Type | Description |
|---|
createdTransaction | array | Created transactions (present on create) |
updatedTransaction | array | Updated transactions (present on update) |
deletedTransaction | array | Deleted transactions (present on delete) |
[].leadId | integer | Associated lead ID |
[].transactionId | integer | Transaction ID |
[].updateTime | integer | Epoch milliseconds of the change |
[].updatedField | object | Changed field key-value pair (nullable) |
[].updatedFields | array | List of changed field key-value pairs (nullable) |
6. Call
Triggered on a call event.
{
"teamId": 123456,
"listId": 6,
"createdCall": [
{
"callId": 101,
"leadId": 456,
"updateTime": 1736935800000,
"agentId": 789,
"communicationType": "MANUAL"
}
]
}
| Field | Type | Description |
|---|
createdCall | array | Call event records |
[].callId | integer | Event ID (MANUAL) or timeline ID (LOGGED) |
[].leadId | integer | Associated lead ID |
[].updateTime | integer | Epoch milliseconds of the call |
[].agentId | integer | Agent user ID |
[].communicationType | string | MANUAL or LOGGED |
7. Email
Triggered on an email event.
{
"teamId": 123456,
"listId": 7,
"createdEmail": [
{
"emailId": 101,
"leadId": 456,
"updateTime": 1736935800000,
"agentId": 789,
"communicationType": "MANUAL"
}
]
}
| Field | Type | Description |
|---|
createdEmail | array | Email event records |
[].emailId | integer | Event ID (MANUAL) or timeline ID (LOGGED) |
[].leadId | integer | Associated lead ID |
[].updateTime | integer | Epoch milliseconds of the event |
[].agentId | integer | Agent user ID |
[].communicationType | string | MANUAL or LOGGED |
8. Text
Triggered on a text/SMS event.
{
"teamId": 123456,
"listId": 8,
"createdText": [
{
"textId": 101,
"leadId": 456,
"updateTime": 1736935800000,
"agentId": 789,
"communicationType": "MANUAL"
}
]
}
| Field | Type | Description |
|---|
createdText | array | Text message event records |
[].textId | integer | Event ID (MANUAL) or timeline ID (LOGGED) |
[].leadId | integer | Associated lead ID |
[].updateTime | integer | Epoch milliseconds of the event |
[].agentId | integer | Agent user ID |
[].communicationType | string | MANUAL or LOGGED |
communicationType values — MANUAL: agent-initiated communication. LOGGED: communication recorded after the fact. AUTO events are excluded from webhooks.
9. Note
Triggered when a note is created, updated, or deleted. Only the relevant action fields are present.
{
"teamId": 123456,
"listId": 9,
"createdNote": [
{
"noteId": 101,
"leadId": 456,
"updateTime": 1736935800000,
"noteType": "Manual",
"agentId": 789
}
],
"updatedNote": {
"noteId": 102,
"leadId": 457,
"updateTime": 1736935860000,
"noteType": "Manual",
"agentId": 789
},
"deletedNote": {
"noteId": 103,
"leadId": 458,
"updateTime": 1736935920000,
"noteType": "Manual",
"agentId": 789
}
}
| Field | Type | Description |
|---|
createdNote | array | Created notes (present on create) |
updatedNote | object | Updated note (present on update) |
deletedNote | object | Deleted note (present on delete) |
[].noteId | integer | Note ID |
[].leadId | integer | Associated lead ID |
[].updateTime | integer | Epoch milliseconds of the change |
[].noteType | string | Manual, System, or AI |
[].agentId | integer | Agent user ID |
10. Task
Triggered when a task is created, updated, finished, or deleted. Multiple action fields may appear in a single payload.
{
"teamId": 123456,
"listId": 10,
"createdTask": [
{ "taskId": 101, "leadId": 456, "updateTime": 1736935800000 }
],
"updatedTask": [
{ "taskId": 102, "leadId": 457, "updateTime": 1736935860000 }
],
"finishedTask": [
{ "taskId": 103, "leadId": 458, "updateTime": 1736935920000 }
],
"deletedTask": [
{ "taskId": 104, "leadId": 459, "updateTime": 1736935980000 }
]
}
| Field | Type | Description |
|---|
createdTask | array | Created tasks (present on create) |
updatedTask | array | Updated tasks (present on update) |
finishedTask | array | Finished tasks (present on finish) |
deletedTask | array | Deleted tasks (present on delete) |
[].taskId | integer | Task ID |
[].leadId | integer | Associated lead ID |
[].updateTime | integer | Epoch milliseconds of the change |
11. Appointment
Triggered when an appointment is created, updated, finished, or deleted. Multiple action fields may appear in a single payload.
{
"teamId": 123456,
"listId": 11,
"createdAppt": [
{ "apptId": 101, "leadId": 456, "updateTime": 1736935800000 }
],
"updatedAppt": [
{ "apptId": 102, "leadId": 457, "updateTime": 1736935860000 }
],
"finishedAppt": [
{ "apptId": 103, "leadId": 458, "updateTime": 1736935920000 }
],
"deletedAppt": [
{ "apptId": 104, "leadId": 459, "updateTime": 1736935980000 }
]
}
| Field | Type | Description |
|---|
createdAppt | array | Created appointments (present on create) |
updatedAppt | array | Updated appointments (present on update) |
finishedAppt | array | Finished appointments (present on finish) |
deletedAppt | array | Deleted appointments (present on delete) |
[].apptId | integer | Appointment ID |
[].leadId | integer | Associated lead ID |
[].updateTime | integer | Epoch milliseconds of the change |
12. Pipeline Change
Triggered when a lead’s pipeline stage changes.
{
"teamId": 123456,
"listId": 12,
"updatedLeadstage": [
{
"leadId": 456,
"updateTime": 1736935800000,
"changeInfo": {
"oldstage": "New Lead",
"newstage": "Contacted"
}
}
]
}
| Field | Type | Description |
|---|
updatedLeadstage | array | Pipeline stage change records |
[].leadId | integer | Lead ID |
[].updateTime | integer | Epoch milliseconds of the stage change |
[].changeInfo.oldstage | string | Previous pipeline stage name |
[].changeInfo.newstage | string | New pipeline stage name |
Best practices
Respond immediately. Return 200 OK before processing. Move logic to a background job. Lofty may retry if no 200 is received.
Handle duplicates. The same event may be delivered more than once. Use event IDs and timestamps for idempotency.
Validate payloads. Check that listId matches your expected event type before processing.
Use HTTPS. Plain HTTP endpoints are rejected.
Use ngrok during development to tunnel webhooks to your local machine.
Example receiver
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhooks/lofty', (req, res) => {
res.sendStatus(200);
const { listId, events } = req.body;
processEvents(listId, events).catch(console.error);
});
async function processEvents(listId, events) {
for (const event of events) {
console.log(`Event type ${listId}:`, event);
}
}
app.listen(3000);
All IDs in webhook payloads are 64-bit integers. JavaScript clients must handle them carefully — see JS / TS Integration.