Create / Upload Lead Endpoint
curl --request POST \
--url https://app.tuco.ai/api/leads \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"leads": [
{
"phone": "<string>",
"firstName": "<string>",
"lastName": "<string>",
"email": "<string>",
"companyName": "<string>",
"jobTitle": "<string>",
"notes": "<string>",
"linkedinUrl": "<string>",
"altPhone1": "<string>",
"altPhone2": "<string>",
"altPhone3": "<string>",
"altEmail1": "<string>",
"altEmail2": "<string>",
"altEmail3": "<string>",
"customFields": {},
"contactOwnerEmail": "<string>"
}
],
"listId": "<string>",
"defaultCountryCode": "<string>",
"source": "<string>",
"ghlLocationId": "<string>",
"contactOwnerEmail": "<string>"
}
'import requests
url = "https://app.tuco.ai/api/leads"
payload = {
"leads": [
{
"phone": "<string>",
"firstName": "<string>",
"lastName": "<string>",
"email": "<string>",
"companyName": "<string>",
"jobTitle": "<string>",
"notes": "<string>",
"linkedinUrl": "<string>",
"altPhone1": "<string>",
"altPhone2": "<string>",
"altPhone3": "<string>",
"altEmail1": "<string>",
"altEmail2": "<string>",
"altEmail3": "<string>",
"customFields": {},
"contactOwnerEmail": "<string>"
}
],
"listId": "<string>",
"defaultCountryCode": "<string>",
"source": "<string>",
"ghlLocationId": "<string>",
"contactOwnerEmail": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
leads: [
{
phone: '<string>',
firstName: '<string>',
lastName: '<string>',
email: '<string>',
companyName: '<string>',
jobTitle: '<string>',
notes: '<string>',
linkedinUrl: '<string>',
altPhone1: '<string>',
altPhone2: '<string>',
altPhone3: '<string>',
altEmail1: '<string>',
altEmail2: '<string>',
altEmail3: '<string>',
customFields: {},
contactOwnerEmail: '<string>'
}
],
listId: '<string>',
defaultCountryCode: '<string>',
source: '<string>',
ghlLocationId: '<string>',
contactOwnerEmail: '<string>'
})
};
fetch('https://app.tuco.ai/api/leads', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.tuco.ai/api/leads",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'leads' => [
[
'phone' => '<string>',
'firstName' => '<string>',
'lastName' => '<string>',
'email' => '<string>',
'companyName' => '<string>',
'jobTitle' => '<string>',
'notes' => '<string>',
'linkedinUrl' => '<string>',
'altPhone1' => '<string>',
'altPhone2' => '<string>',
'altPhone3' => '<string>',
'altEmail1' => '<string>',
'altEmail2' => '<string>',
'altEmail3' => '<string>',
'customFields' => [
],
'contactOwnerEmail' => '<string>'
]
],
'listId' => '<string>',
'defaultCountryCode' => '<string>',
'source' => '<string>',
'ghlLocationId' => '<string>',
'contactOwnerEmail' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.tuco.ai/api/leads"
payload := strings.NewReader("{\n \"leads\": [\n {\n \"phone\": \"<string>\",\n \"firstName\": \"<string>\",\n \"lastName\": \"<string>\",\n \"email\": \"<string>\",\n \"companyName\": \"<string>\",\n \"jobTitle\": \"<string>\",\n \"notes\": \"<string>\",\n \"linkedinUrl\": \"<string>\",\n \"altPhone1\": \"<string>\",\n \"altPhone2\": \"<string>\",\n \"altPhone3\": \"<string>\",\n \"altEmail1\": \"<string>\",\n \"altEmail2\": \"<string>\",\n \"altEmail3\": \"<string>\",\n \"customFields\": {},\n \"contactOwnerEmail\": \"<string>\"\n }\n ],\n \"listId\": \"<string>\",\n \"defaultCountryCode\": \"<string>\",\n \"source\": \"<string>\",\n \"ghlLocationId\": \"<string>\",\n \"contactOwnerEmail\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.tuco.ai/api/leads")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"leads\": [\n {\n \"phone\": \"<string>\",\n \"firstName\": \"<string>\",\n \"lastName\": \"<string>\",\n \"email\": \"<string>\",\n \"companyName\": \"<string>\",\n \"jobTitle\": \"<string>\",\n \"notes\": \"<string>\",\n \"linkedinUrl\": \"<string>\",\n \"altPhone1\": \"<string>\",\n \"altPhone2\": \"<string>\",\n \"altPhone3\": \"<string>\",\n \"altEmail1\": \"<string>\",\n \"altEmail2\": \"<string>\",\n \"altEmail3\": \"<string>\",\n \"customFields\": {},\n \"contactOwnerEmail\": \"<string>\"\n }\n ],\n \"listId\": \"<string>\",\n \"defaultCountryCode\": \"<string>\",\n \"source\": \"<string>\",\n \"ghlLocationId\": \"<string>\",\n \"contactOwnerEmail\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.tuco.ai/api/leads")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"leads\": [\n {\n \"phone\": \"<string>\",\n \"firstName\": \"<string>\",\n \"lastName\": \"<string>\",\n \"email\": \"<string>\",\n \"companyName\": \"<string>\",\n \"jobTitle\": \"<string>\",\n \"notes\": \"<string>\",\n \"linkedinUrl\": \"<string>\",\n \"altPhone1\": \"<string>\",\n \"altPhone2\": \"<string>\",\n \"altPhone3\": \"<string>\",\n \"altEmail1\": \"<string>\",\n \"altEmail2\": \"<string>\",\n \"altEmail3\": \"<string>\",\n \"customFields\": {},\n \"contactOwnerEmail\": \"<string>\"\n }\n ],\n \"listId\": \"<string>\",\n \"defaultCountryCode\": \"<string>\",\n \"source\": \"<string>\",\n \"ghlLocationId\": \"<string>\",\n \"contactOwnerEmail\": \"<string>\"\n}"
response = http.request(request)
puts response.read_bodyLines & Leads
Create / Upload Lead Endpoint
Create a single lead or upload multiple leads into a Tuco workspace.
POST
/
api
/
leads
Create / Upload Lead Endpoint
curl --request POST \
--url https://app.tuco.ai/api/leads \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"leads": [
{
"phone": "<string>",
"firstName": "<string>",
"lastName": "<string>",
"email": "<string>",
"companyName": "<string>",
"jobTitle": "<string>",
"notes": "<string>",
"linkedinUrl": "<string>",
"altPhone1": "<string>",
"altPhone2": "<string>",
"altPhone3": "<string>",
"altEmail1": "<string>",
"altEmail2": "<string>",
"altEmail3": "<string>",
"customFields": {},
"contactOwnerEmail": "<string>"
}
],
"listId": "<string>",
"defaultCountryCode": "<string>",
"source": "<string>",
"ghlLocationId": "<string>",
"contactOwnerEmail": "<string>"
}
'import requests
url = "https://app.tuco.ai/api/leads"
payload = {
"leads": [
{
"phone": "<string>",
"firstName": "<string>",
"lastName": "<string>",
"email": "<string>",
"companyName": "<string>",
"jobTitle": "<string>",
"notes": "<string>",
"linkedinUrl": "<string>",
"altPhone1": "<string>",
"altPhone2": "<string>",
"altPhone3": "<string>",
"altEmail1": "<string>",
"altEmail2": "<string>",
"altEmail3": "<string>",
"customFields": {},
"contactOwnerEmail": "<string>"
}
],
"listId": "<string>",
"defaultCountryCode": "<string>",
"source": "<string>",
"ghlLocationId": "<string>",
"contactOwnerEmail": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
leads: [
{
phone: '<string>',
firstName: '<string>',
lastName: '<string>',
email: '<string>',
companyName: '<string>',
jobTitle: '<string>',
notes: '<string>',
linkedinUrl: '<string>',
altPhone1: '<string>',
altPhone2: '<string>',
altPhone3: '<string>',
altEmail1: '<string>',
altEmail2: '<string>',
altEmail3: '<string>',
customFields: {},
contactOwnerEmail: '<string>'
}
],
listId: '<string>',
defaultCountryCode: '<string>',
source: '<string>',
ghlLocationId: '<string>',
contactOwnerEmail: '<string>'
})
};
fetch('https://app.tuco.ai/api/leads', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.tuco.ai/api/leads",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'leads' => [
[
'phone' => '<string>',
'firstName' => '<string>',
'lastName' => '<string>',
'email' => '<string>',
'companyName' => '<string>',
'jobTitle' => '<string>',
'notes' => '<string>',
'linkedinUrl' => '<string>',
'altPhone1' => '<string>',
'altPhone2' => '<string>',
'altPhone3' => '<string>',
'altEmail1' => '<string>',
'altEmail2' => '<string>',
'altEmail3' => '<string>',
'customFields' => [
],
'contactOwnerEmail' => '<string>'
]
],
'listId' => '<string>',
'defaultCountryCode' => '<string>',
'source' => '<string>',
'ghlLocationId' => '<string>',
'contactOwnerEmail' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.tuco.ai/api/leads"
payload := strings.NewReader("{\n \"leads\": [\n {\n \"phone\": \"<string>\",\n \"firstName\": \"<string>\",\n \"lastName\": \"<string>\",\n \"email\": \"<string>\",\n \"companyName\": \"<string>\",\n \"jobTitle\": \"<string>\",\n \"notes\": \"<string>\",\n \"linkedinUrl\": \"<string>\",\n \"altPhone1\": \"<string>\",\n \"altPhone2\": \"<string>\",\n \"altPhone3\": \"<string>\",\n \"altEmail1\": \"<string>\",\n \"altEmail2\": \"<string>\",\n \"altEmail3\": \"<string>\",\n \"customFields\": {},\n \"contactOwnerEmail\": \"<string>\"\n }\n ],\n \"listId\": \"<string>\",\n \"defaultCountryCode\": \"<string>\",\n \"source\": \"<string>\",\n \"ghlLocationId\": \"<string>\",\n \"contactOwnerEmail\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.tuco.ai/api/leads")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"leads\": [\n {\n \"phone\": \"<string>\",\n \"firstName\": \"<string>\",\n \"lastName\": \"<string>\",\n \"email\": \"<string>\",\n \"companyName\": \"<string>\",\n \"jobTitle\": \"<string>\",\n \"notes\": \"<string>\",\n \"linkedinUrl\": \"<string>\",\n \"altPhone1\": \"<string>\",\n \"altPhone2\": \"<string>\",\n \"altPhone3\": \"<string>\",\n \"altEmail1\": \"<string>\",\n \"altEmail2\": \"<string>\",\n \"altEmail3\": \"<string>\",\n \"customFields\": {},\n \"contactOwnerEmail\": \"<string>\"\n }\n ],\n \"listId\": \"<string>\",\n \"defaultCountryCode\": \"<string>\",\n \"source\": \"<string>\",\n \"ghlLocationId\": \"<string>\",\n \"contactOwnerEmail\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.tuco.ai/api/leads")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"leads\": [\n {\n \"phone\": \"<string>\",\n \"firstName\": \"<string>\",\n \"lastName\": \"<string>\",\n \"email\": \"<string>\",\n \"companyName\": \"<string>\",\n \"jobTitle\": \"<string>\",\n \"notes\": \"<string>\",\n \"linkedinUrl\": \"<string>\",\n \"altPhone1\": \"<string>\",\n \"altPhone2\": \"<string>\",\n \"altPhone3\": \"<string>\",\n \"altEmail1\": \"<string>\",\n \"altEmail2\": \"<string>\",\n \"altEmail3\": \"<string>\",\n \"customFields\": {},\n \"contactOwnerEmail\": \"<string>\"\n }\n ],\n \"listId\": \"<string>\",\n \"defaultCountryCode\": \"<string>\",\n \"source\": \"<string>\",\n \"ghlLocationId\": \"<string>\",\n \"contactOwnerEmail\": \"<string>\"\n}"
response = http.request(request)
puts response.read_bodyEndpoint
- Method:
POST - Path:
/api/leads
Request body
object[]
required
One or more contacts. A single contact is a one-element array.
body is accepted as an alias
for this field.Show lead fields
Show lead fields
string
The phone number, in E.164 (e.g.
"+12025551234"). This is the identifier that matters —
it is how the lead is deduplicated, and how you text them later. Numbers without a country
code are normalized using defaultCountryCode.string
First name. Used for personalization in campaigns.
string
Last name.
string
Email address. Lowercased before dedup.
string
Company name.
string
Job title.
string
Free-text notes.
string
LinkedIn profile URL.
string
Alternate phone 1. Tried when the primary number is not reachable, and counted for dedup.
string
Alternate phone 2.
string
Alternate phone 3.
string
Alternate email 1.
string
Alternate email 2.
string
Alternate email 3.
object
Free-form key/value data. See custom field keys for the ones already in use.
string
Email of the workspace user who owns this contact. Falls back to the first user in the workspace when omitted or unrecognised.
string
The list these leads belong to, from
GET /api/lists.
Omit to use the Quick Sends list. An id that does not exist returns 404.string
default:"+1"
Used to normalize phone numbers that arrive without a country code, e.g.
"+44".string
Origin label stored on each lead, e.g.
"api" or "gohighlevel".string
GoHighLevel location id, stored on the created leads when syncing from GHL.
string
Applies an owner to every lead in the request. A per-lead
contactOwnerEmail wins over this.Example body
{
"leads": [
{
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"phone": "+12025551234",
"companyName": "Acme Corp",
"jobTitle": "CEO",
"notes": "Warm intro from Sarah",
"contactOwnerEmail": "rep@yourcompany.com",
"customFields": {
"industry": "Technology",
"segment": "Mid-market"
}
}
],
"listId": "507f1f77bcf86cd799439011",
"source": "api",
"defaultCountryCode": "+1"
}
leads– one or more contacts; a single contact is just a one‑element array.listId– Optional. Tuco list that should own these leads. Omit to use the Quick Sends list.source– optional origin label (for example,"api"or"gohighlevel").defaultCountryCode– used to normalize phone numbers when no country code is present.ghlLocationId– optional; used when syncing with GoHighLevel.contactOwnerEmail– Email of the workspace user who owns this contact. If omitted or invalid, defaults to the first user in the workspace.- You may send
bodyas an alias forleads(same array shape).
Response
Success – status201 Created when at least one lead was created, or 200 OK when the request succeeded but no new leads were inserted. Example:
{
"message": "Leads saved successfully",
"savedCount": 1,
"duplicateCount": 0,
"totalProcessed": 1,
"listId": "507f1f77bcf86cd799439011",
"leadId": "674a1b2c3d4e5f678901234a",
"leadIds": ["674a1b2c3d4e5f678901234a"],
"leadListIds": ["507f1f77bcf86cd799439011"],
"ghlContactId": null,
"ghlLocationId": null,
"hsPortalId": null,
"hsContactId": null
}
savedCount– number of new leads actually inserted.duplicateCount– leads skipped as duplicates within the same list (email/phone‑based).totalProcessed– total rows you attempted to upload.listId– list the leads were added to.leadIds– array of Tuco lead IDs for newly created leads only, in the same order as inserted. Use these to link created leads back to your system or to fetch/update them later.leadListIds– array of list IDs the new leads were added to (one list per request; same aslistIdwhen adding to a single list).duplicates– present only when some (but not all) leads were duplicates; each entry describes the submitted input and the existing lead in the list.
200 OK (not 409). No new leads are created; the body includes duplicateOnly: true, duplicateCount, totalProcessed, leadId (first existing), existingLeadIds, leadIds, duplicates (each with input and existingLead including _id), and integration IDs (ghlContactId, ghlLocationId, etc.) so you can reconcile with existing records and continue your workflow (e.g. n8n).
Other errors: Validation failures return 400 with a readable error. If the requested list does not exist, you get 404. If the workspace is temporarily read‑only (e.g. payment past due), you get 402 with code: "READ_ONLY". See /api-reference/errors for more.