Ir al contenido

Leads

Esta página aún no está disponible en tu idioma.

Leads are inbound prospects captured before they become customers, from the BenchKey lead widget, live chat, manual entry, or an import. A lead carries the contact information the prospect supplied, a workflow status (e.g. new, won, lost), a source, optional UTM attribution, and, once worked, links to the converted ticket and/or a matched customer record.

Archived leads are hidden from list responses by default. Pass include_archived=true to include them.

{
"object": "lead",
"id": "4821",
"status": "new",
"source": "lead_widget",
"channel": "widget",
"type": null,
"priority": null,
"urgency_tag": null,
"lead_category": null,
"lead_path": null,
"category_id": null,
"customer_name": "Alex Rivera",
"customer_email": "alex.rivera@example.com",
"customer_phone": "512-555-0142",
"contact_name": null,
"contact_email": null,
"contact_phone": null,
"assigned_to": null,
"ai_summary": "Customer has a cracked screen on an iPhone 15 Pro.",
"quoted_price": {
"amount_cents": 14900,
"amount": "149.00",
"currency": "usd"
},
"source_page_url": "https://yourshop.com/repair",
"source_referrer": null,
"utm_source": "google",
"utm_medium": "cpc",
"utm_campaign": "iphone-repair",
"utm_term": null,
"utm_content": null,
"first_response_at": null,
"linked_ticket_id": null,
"merged_into_lead_id": null,
"duplicate_of_lead_id": null,
"matched_customer_id": null,
"matched_ticket_customer_id": null,
"customer": null,
"converted_ticket_id": null,
"converted_at": null,
"last_activity_at": "2025-12-01T18:44:00.000Z",
"archived_at": null,
"created_at": "2025-12-01T18:44:00.000Z",
"updated_at": "2025-12-01T18:44:00.000Z"
}
FieldTypeDescription
idstringLead id
statusstring|nullWorkflow status: new, contacted, quoted, won, lost, etc. Discover the tenant’s live status registry with GET /statuses/leads
sourcestring|nullCapture source (e.g. lead_widget, manual)
channelstring|nullInbound channel: email, sms, phone, voicemail, widget, or manual
typestring|nullLead type (read-only, set by the app)
prioritystring|nullPriority level (read-only)
urgency_tagstring|nullUrgency label set by staff
lead_categorystring|nullLead category label
lead_pathstring|nullLead path/flow identifier, local or mail_in
category_idstring|nullRepair category id associated with the lead, represented as a string
customer_namestring|nullProspect’s name
customer_emailstring|nullProspect’s email
customer_phonestring|nullProspect’s phone
contact_namestring|nullSecondary contact name
contact_emailstring|nullSecondary contact email
contact_phonestring|nullSecondary contact phone
assigned_tostring|nullStaff member assigned to this lead
ai_summarystring|nullAI-generated summary of the lead
quoted_priceobject|nullQuoted price, { amount_cents, amount, currency }
source_page_urlstring|nullURL the lead was captured on
source_referrerstring|nullHTTP Referer at capture time
utm_sourcestring|nullUTM source
utm_mediumstring|nullUTM medium
utm_campaignstring|nullUTM campaign
utm_termstring|nullUTM term
utm_contentstring|nullUTM content
first_response_atstring|nullISO-8601 timestamp of the shop’s first response, the response-time SLA signal for reporting tools
linked_ticket_idstring|nullActive ticket this lead’s inbox is locked to (distinct from converted_ticket_id)
merged_into_lead_idstring|nullThe surviving lead this one was merged into. A merged lead reads status dead, use this pointer to reconcile instead of double-counting
duplicate_of_lead_idstring|nullThe original lead this one was marked a duplicate of
matched_customer_idstring|nullPublic customer email id this lead is linked to (dereferenceable via GET /customers/:id)
matched_ticket_customer_idstring|nullBenchKey’s internal all-tickets order_id customer-link value
customerobject|nullSummary of the linked customer, { object, id, name, email, phone }, or null if unlinked
converted_ticket_idstring|nullTicket id created when the lead was converted
converted_atstring|nullISO-8601 timestamp of conversion
last_activity_atstring|nullISO-8601 timestamp of last activity
archived_atstring|nullISO-8601 timestamp of archival, or null if active
created_atstringISO-8601 creation timestamp
updated_atstringISO-8601 last-updated timestamp

GET /api/v1/leads

Returns a cursor-paginated list of leads, newest first. Archived leads are excluded unless include_archived=true.

Scope required: leads.read

ParameterTypeDescription
statusstringFilter by workflow status (e.g. new, won, lost)
sourcestringFilter by capture source
channelstringFilter by inbound channel
customer_idstringFilter to leads matched to a specific customer. Accepts the customer’s email address (the public customer id) or a positive integer order-id. Blank or whitespace-only values return 400 invalid_field.
convertedbooleantrue = only converted leads; false = only unconverted. Any other value returns 400 invalid_filter.
qstringSearch by customer/contact name, email, or phone
updated_sincestringISO-8601 or Unix epoch, only leads updated at or after this time. Arrays or objects return 400 invalid_query; non-parseable strings return 400 invalid_filter
include_archivedbooleanInclude archived leads (default: false). Accepted values: true/false/1/0/yes/no. Any other value returns 400 invalid_filter
limitintegerPage size, 1–100 (default: 25)
cursorstringOpaque cursor from a previous response’s next_cursor
Terminal window
curl "https://app.benchkey.com/api/v1/leads?status=new&limit=10" \
-H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa"
const res = await fetch(
"https://app.benchkey.com/api/v1/leads?status=new&limit=10",
{ headers: { Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" } }
);
const { data, has_more, next_cursor } = await res.json();
{
"object": "list",
"data": [
{
"object": "lead",
"id": "4821",
"status": "new",
"source": "lead_widget",
"channel": "widget",
"customer_name": "Alex Rivera",
"customer_email": "alex.rivera@example.com",
"customer_phone": "512-555-0142",
"quoted_price": { "amount_cents": 14900, "amount": "149.00", "currency": "usd" },
"matched_customer_id": null,
"customer": null,
"converted_ticket_id": null,
"converted_at": null,
"archived_at": null,
"created_at": "2025-12-01T18:44:00.000Z",
"updated_at": "2025-12-01T18:44:00.000Z"
}
],
"has_more": false,
"next_cursor": null
}

See Pagination for how to page through results.


GET /api/v1/leads/:id

Fetches a single lead by id.

Scope required: leads.read

Terminal window
curl "https://app.benchkey.com/api/v1/leads/4821" \
-H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa"
const res = await fetch("https://app.benchkey.com/api/v1/leads/4821", {
headers: { Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" },
});
const lead = await res.json();

Returns the lead object. Returns 404 if no lead with that id exists.

Archived leads return 404 by default. GET /:id applies the same visibility filter as the list: archived leads are hidden unless you pass ?include_archived=true. This keeps the single-record path consistent with what the list exposes.


POST /api/v1/leads

Creates an inbound lead. At least one of customer_name, customer_email, or customer_phone is required. Fires the same follow-up-sequence enrollment, auto-assignment, and customer auto-match the app performs for a staff-created lead.

Scope required: leads.write

FieldRequiredDescription
customer_nameone of three*Prospect’s name
customer_emailone of three*Prospect’s email address
customer_phoneone of three*Prospect’s phone number
channelnoemail, sms, phone, voicemail, widget, or manual (default: manual)
category_idnoRepair category id (positive integer; non-integer or non-positive values return 400 invalid_field)
assigned_tonoStaff user id to assign the lead to
lead_categorynoLead category label
lead_pathnoLead path/flow identifier
notesnoInternal note recorded on the lead

* At least one of customer_name, customer_email, or customer_phone must be provided.

Terminal window
curl -X POST https://app.benchkey.com/api/v1/leads \
-H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" \
-H "Content-Type: application/json" \
-d '{
"customer_name": "Alex Rivera",
"customer_email": "alex.rivera@example.com",
"customer_phone": "512-555-0142",
"channel": "widget",
"notes": "Cracked screen, iPhone 15 Pro"
}'
const res = await fetch("https://app.benchkey.com/api/v1/leads", {
method: "POST",
headers: {
Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa",
"Content-Type": "application/json",
},
body: JSON.stringify({
customer_name: "Alex Rivera",
customer_email: "alex.rivera@example.com",
customer_phone: "512-555-0142",
channel: "widget",
notes: "Cracked screen, iPhone 15 Pro",
}),
});
const lead = await res.json(); // HTTP 201

Returns the created lead object with HTTP 201.


PATCH /api/v1/leads/:id

Updates one or more mutable fields on a lead. A status change drives the follow-up-sequence lifecycle (enrollment or cancellation) just as a staff edit would. Only fields included in the request body are changed.

Scope required: leads.write

At least one field must be provided:

FieldDescription
statusNew workflow status (e.g. contacted, quoted, won, lost)
assigned_toStaff user id, or null to unassign
category_idRepair category id
customer_nameProspect’s name
customer_emailProspect’s email
customer_phoneProspect’s phone
contact_nameSecondary contact name
contact_emailSecondary contact email
contact_phoneSecondary contact phone
on_behalf_ofName of person on whose behalf the contact is calling
urgency_tagUrgency label
quoted_priceQuoted price in dollars (e.g. 149.00), or null to clear
lead_categoryLead category label
lead_pathLead path/flow identifier
Terminal window
curl -X PATCH "https://app.benchkey.com/api/v1/leads/4821" \
-H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" \
-H "Content-Type: application/json" \
-d '{ "status": "quoted", "quoted_price": 149.00 }'
const res = await fetch("https://app.benchkey.com/api/v1/leads/4821", {
method: "PATCH",
headers: {
Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa",
"Content-Type": "application/json",
},
body: JSON.stringify({ status: "quoted", quoted_price: 149.00 }),
});
const lead = await res.json();

Returns the updated lead object.

Archived leads return 404 on PATCH. PATCH /:id applies the same visibility rule as the list and GET /:id: an archived lead is treated as non-existent and returns 404 not_found. To update an archived lead, first restore it via POST /:id/unarchive.


POST /api/v1/leads/:id/archive

Moves a lead to the archived bucket. Archived leads are excluded from list responses unless include_archived=true.

Scope required: leads.write

Terminal window
curl -X POST "https://app.benchkey.com/api/v1/leads/4821/archive" \
-H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa"
const res = await fetch("https://app.benchkey.com/api/v1/leads/4821/archive", {
method: "POST",
headers: { Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" },
});
const lead = await res.json();

Returns the updated lead object with archived_at set.


POST /api/v1/leads/:id/unarchive

Restores an archived lead to active status.

Scope required: leads.write

Terminal window
curl -X POST "https://app.benchkey.com/api/v1/leads/4821/unarchive" \
-H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa"
const res = await fetch("https://app.benchkey.com/api/v1/leads/4821/unarchive", {
method: "POST",
headers: { Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" },
});
const lead = await res.json();

Returns the updated lead object with archived_at set to null.


POST /api/v1/leads/:id/convert-to-ticket

Converts an inbound lead into a work ticket. Fires the full conversion workflow, order numbering, audit log, queue routing, and any kiosk/mail-in side effects. The lead is atomically claimed so a repeated call cannot create a second ticket.

Returns the updated lead, whose converted_ticket_id is the newly created ticket’s id.

Scope required: leads.write

FieldRequiredDescription
service_nameyesDescription of the work/issue
customer_namenoOverrides the lead’s name on the ticket (defaults to lead’s name)
customer_emailnoCustomer email on the ticket
customer_phonenoCustomer phone on the ticket
device_namenoDevice description (e.g. “iPhone 15 Pro”)
category_idnoRepair category id
task_typeno1 = mail-in, 2 = walk-in (default)
assigned_tonoStaff user id
queue_namenoQueue to route the ticket to
pricenoStarting price on the ticket
notesnoInternal notes to copy to the ticket
address1noStreet address (for mail-in)
address2noAddress line 2
citynoCity
statenoState
zipnoPostal code
trigger_kiosk_signaturenotrue to trigger the kiosk signature flow after conversion (boolean)
carry_over_attachmentsnotrue to copy the lead’s file attachments to the new ticket (boolean)
send_mail_in_linknotrue to send a mail-in shipping label link to the customer (boolean)
Terminal window
curl -X POST "https://app.benchkey.com/api/v1/leads/4821/convert-to-ticket" \
-H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" \
-H "Content-Type: application/json" \
-d '{
"service_name": "Cracked screen replacement",
"device_name": "iPhone 15 Pro",
"price": "149.00"
}'
const res = await fetch("https://app.benchkey.com/api/v1/leads/4821/convert-to-ticket", {
method: "POST",
headers: {
Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa",
"Content-Type": "application/json",
},
body: JSON.stringify({
service_name: "Cracked screen replacement",
device_name: "iPhone 15 Pro",
price: "149.00",
}),
});
const lead = await res.json();
// lead.converted_ticket_id is the new ticket id

Returns the updated lead object. converted_ticket_id is set to the new ticket’s id.


POST /api/v1/leads/:id/link-customer

Attaches the lead to an existing customer record. The internal matcher resolves customer_id by numeric id, email address, or phone number. Returns the updated lead with customer populated.

Scope required: leads.write

FieldRequiredDescription
customer_idyesCustomer id, email, or phone to link
Terminal window
curl -X POST "https://app.benchkey.com/api/v1/leads/4821/link-customer" \
-H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" \
-H "Content-Type: application/json" \
-d '{ "customer_id": "alex.rivera@example.com" }'
const res = await fetch("https://app.benchkey.com/api/v1/leads/4821/link-customer", {
method: "POST",
headers: {
Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa",
"Content-Type": "application/json",
},
body: JSON.stringify({ customer_id: "alex.rivera@example.com" }),
});
const lead = await res.json();
// lead.customer is now populated

Returns the updated lead object with customer populated.


DELETE /api/v1/leads/:id/link-customer

Clears the lead’s customer match, useful to undo a wrong link or treat the lead as brand-new. Returns the updated lead with customer and matched_customer_id cleared.

Scope required: leads.write

Terminal window
curl -X DELETE "https://app.benchkey.com/api/v1/leads/4821/link-customer" \
-H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa"
const res = await fetch("https://app.benchkey.com/api/v1/leads/4821/link-customer", {
method: "DELETE",
headers: { Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" },
});
const lead = await res.json();

Returns the updated lead object with customer set to null.


HTTP statusCodeMeaning
400invalid_idThe :id is not a valid integer
400invalid_queryA list filter parameter was supplied as an array or object instead of a single string value
400missing_contactPOST body has none of customer_name, customer_email, or customer_phone
400missing_fieldA required field is absent (e.g. service_name for convert)
400invalid_fieldA field value is invalid (e.g. malformed email, negative quoted_price, non-integer customer_id filter, or non-positive-integer category_id on create/update/convert)
400no_updatable_fieldsPATCH body contains no recognized fields
400invalid_filterA filter value is invalid: updated_since is not a valid timestamp, converted is not a recognized boolean, or include_archived is not a recognized boolean (true/false/1/0/yes/no)
404not_foundNo lead with that id exists
409conflictLead is already converted (cannot convert twice)
403insufficient_scopeAPI key lacks leads.read or leads.write

See Errors for the full error envelope format.

Estado del sistema