curl --request POST \
--url https://api.upliftai.org/v1/contact-sources/preview \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"kind": "google_sheets",
"ref": "1kQ7bP2xLd9xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx!Contacts!A:E"
}
'{
"summary": {
"willBeCalled": 3,
"excluded": {
"invalidPhone": 1,
"missingName": 0,
"duplicate": 1,
"dnc": 0,
"recentlyCalled": 0
},
"errors": [
{
"row": 4,
"issue": "duplicate",
"value": "03001234567"
},
{
"row": 5,
"issue": "invalidPhone",
"value": "0345"
}
]
},
"detectedMapping": {
"nameColumn": "Name",
"phoneColumn": "Mobile No.",
"attributeColumns": [
"City",
"Policy Amount",
"Due Date"
]
},
"sample": [
{
"name": "عائشہ صدیقی",
"phone": "+923001234567",
"attributes": {
"City": "لاہور",
"Policy Amount": "Rs 18,000",
"Due Date": "25 اگست"
}
},
{
"name": "بلال احمد",
"phone": "03211234567",
"attributes": {
"City": "کراچی",
"Policy Amount": "Rs 42,500",
"Due Date": "27 اگست"
}
},
{
"name": "حمزہ شیخ",
"phone": "+923339876543",
"attributes": {
"City": "ملتان",
"Policy Amount": "Rs 30,000",
"Due Date": "30 اگست"
}
}
],
"total": 5,
"columns": [
"Name",
"Mobile No.",
"City",
"Policy Amount",
"Due Date"
],
"mappingIssues": []
}Dry-run a contact import
See exactly who a campaign would dial before you commit. It takes the same inputs as /contact-lists: a Google Sheet ref, or inline data holding CSV text or pasted “Name, +phone” lines. Nothing is stored or registered, so run it as often as you like. The one input it doesn’t take is an uploaded file — re-preview the upload instead.
For sheets the read is live, and the sheet must be reachable: shared as anyone-with-the-link, or read through a Google connectionId. When it isn’t, the 500 doesn’t forward Google’s reason — a wrong spreadsheet id, a missing tab, and an unshared file all look identical.
Already registered the source? Re-check it with the stored mapping instead. Column naming rules: Special column names.
curl --request POST \
--url https://api.upliftai.org/v1/contact-sources/preview \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"kind": "google_sheets",
"ref": "1kQ7bP2xLd9xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx!Contacts!A:E"
}
'{
"summary": {
"willBeCalled": 3,
"excluded": {
"invalidPhone": 1,
"missingName": 0,
"duplicate": 1,
"dnc": 0,
"recentlyCalled": 0
},
"errors": [
{
"row": 4,
"issue": "duplicate",
"value": "03001234567"
},
{
"row": 5,
"issue": "invalidPhone",
"value": "0345"
}
]
},
"detectedMapping": {
"nameColumn": "Name",
"phoneColumn": "Mobile No.",
"attributeColumns": [
"City",
"Policy Amount",
"Due Date"
]
},
"sample": [
{
"name": "عائشہ صدیقی",
"phone": "+923001234567",
"attributes": {
"City": "لاہور",
"Policy Amount": "Rs 18,000",
"Due Date": "25 اگست"
}
},
{
"name": "بلال احمد",
"phone": "03211234567",
"attributes": {
"City": "کراچی",
"Policy Amount": "Rs 42,500",
"Due Date": "27 اگست"
}
},
{
"name": "حمزہ شیخ",
"phone": "+923339876543",
"attributes": {
"City": "ملتان",
"Policy Amount": "Rs 30,000",
"Due Date": "30 اگست"
}
}
],
"total": 5,
"columns": [
"Name",
"Mobile No.",
"City",
"Policy Amount",
"Due Date"
],
"mappingIssues": []
}Authorizations
Project API key (sk_api_…).
Body
- Google Sheet
- Inline CSV or pasted rows
google_sheets The spreadsheet id, optionally followed by ! and a tab or A1 range ({id}!Contacts!A:E). A bare id reads the first tab, columns A–ZZ.
Reads through this Google connection instead of the public-link path.
Overrides column auto-detection.
Show child attributes
Show child attributes
Response
The sheet as it stands right now. willBeCalled plus every count in excluded reconciles to total.
What the import kept and dropped.
Show child attributes
Show child attributes
The columns actually used, after applying mapping. When a name was joined from two columns, nameColumn reads First Name + Last Name.
Show child attributes
Show child attributes
The first ten contacts that would be dialed.
10Show child attributes
Show child attributes
Rows read, before validation.
The header row as parsed — what a mapping override chooses from. Absent for free-form paste, which has no headers.
Non-empty means column detection failed. An unmapped phone column then shows up as every row invalidPhone with an empty value.
phoneColumnNotFound, nameColumnNotFound 