Lead API
Send leads straight into your SolarDialer account, and into a list, from your website form, your CRM or your lead seller. One HTTPS request per lead.
- Your API key
- Send a lead
- Fields
- Answers
- Examples
- Good to know
- Events and hooks
- Lists, campaigns, account
Your API key
On your Account page, under
API, press Create API key. The key starts with sdk_ and is
shown once: copy it then. Keep it secret, like a password: anyone with it can add leads to your account.
Replace key gives a new one and stops the old one at once; Turn off stops it.
Each account has its own key, and a key only ever adds leads to its own account.
Send a lead
POST https://solardialer.net/api/v1/leads
Authorization: Bearer sdk_your_key
Content-Type: application/json
{
"first_name": "Dana",
"last_name": "Rivera",
"phone": "215-555-0147",
"state": "PA",
"zip": "19103",
"list": "Website leads"
}
The key can also be sent as X-API-Key: sdk_your_key, and the fields as an ordinary form
(application/x-www-form-urlencoded) instead of JSON.
Fields
The same columns as the CSV upload's template, so a lead sent here is checked exactly like an uploaded one.
Required: first_name, phone, and for a US number state.
| Field | What it is |
|---|---|
first_name required | The lead's first name: the agent asks for them by it. |
phone required | Their phone number. US numbers in any common form (215-555-0147, (215) 555-0147); other countries with the + code (+44 20 7946 0958). |
lead_id | Your own id for the lead. Send the same id again to update the lead. Without it, the phone number identifies the lead. |
middle_name, last_name | The rest of their name. |
state required for US numbers | Their US state, two letters (PA): it decides the calling hours and rules that apply. |
zip, city, address | Where they live. |
country | Two letters (US, GB…), when the number alone doesn't say. |
second_phone, third_phone | Other numbers for them. Calls ring phone first; when nobody answers, the next call rings the second number, then the third. Once one reaches them, that one is called from then on. A number given twice is kept once. |
utility, source, notes | Their electric company, where the lead came from, anything else. |
best_time_to_call | In words (evenings); or best_call_start and best_call_end as times (17:00, 20:00), in their own time. |
say_name | How to say their name, if it's unusual (Shivon for Siobhan). |
consent_captured_at, consent_source_url, consent_ip, consent_user_agent, consent_disclosure_text, consent_vendor, consent_proof_ref | The record of their consent to be called: when, on which page, from which address, what they agreed to, and your proof (e.g. a TrustedForm or Jornaya id). Send them when you have them. |
list | The name of one of your lists to add the lead to. A new name makes the list. |
Other fields are ignored.
Answers
| Code | Meaning |
|---|---|
201 | A new lead was added. If part of it was left out, warnings says what (e.g. a consent_ip that isn't an IP address: the lead is added without its consent record). |
200 | The lead was already in your account (same lead_id, or the same phone without one): it was updated. |
400 | The lead was refused; errors says why (e.g. a missing first name or state, a number that can't be a phone). |
401 | No API key, or not a valid one. |
403 | The account is suspended. |
429 | More than 1000 leads in an hour: try again later. |
{"ok": true, "id": "u3-W1047", "lead_id": "W1047", "created": true, "list": "Website leads"}
{"ok": false, "errors": ["missing required field: first_name"]}
Examples
curl
curl https://solardialer.net/api/v1/leads \
-H "Authorization: Bearer sdk_your_key" \
-H "Content-Type: application/json" \
-d '{"first_name": "Dana", "phone": "215-555-0147", "state": "PA", "list": "Website leads"}'
JavaScript (your server)
const answer = await fetch("https://solardialer.net/api/v1/leads", {
method: "POST",
headers: {"Authorization": "Bearer " + process.env.SOLARDIALER_KEY,
"Content-Type": "application/json"},
body: JSON.stringify({first_name: "Dana", phone: "215-555-0147", state: "PA"}),
});
console.log(answer.status, await answer.json());
Python
import os, requests
answer = requests.post(
"https://solardialer.net/api/v1/leads",
headers={"Authorization": "Bearer " + os.environ["SOLARDIALER_KEY"]},
json={"first_name": "Dana", "phone": "215-555-0147", "state": "PA"},
)
print(answer.status_code, answer.json())
PHP
<?php
$lead = ["first_name" => "Dana", "phone" => "215-555-0147", "state" => "PA"];
$curl = curl_init("https://solardialer.net/api/v1/leads");
curl_setopt_array($curl, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("SOLARDIALER_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode($lead),
CURLOPT_RETURNTRANSFER => true,
]);
$body = curl_exec($curl);
echo curl_getinfo($curl, CURLINFO_RESPONSE_CODE), " ", $body, "\n";
curl_close($curl);
Java (11 or later)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class SendLead {
public static void main(String[] args) throws Exception {
String lead = "{\"first_name\": \"Dana\", \"phone\": \"215-555-0147\", \"state\": \"PA\"}";
HttpRequest request = HttpRequest.newBuilder(URI.create("https://solardialer.net/api/v1/leads"))
.header("Authorization", "Bearer " + System.getenv("SOLARDIALER_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(lead))
.build();
HttpResponse<String> answer = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(answer.statusCode() + " " + answer.body());
}
}
C# (.NET 6 or later)
using System.Net.Http.Headers;
using System.Net.Http.Json;
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer", Environment.GetEnvironmentVariable("SOLARDIALER_KEY"));
var lead = new { first_name = "Dana", phone = "215-555-0147", state = "PA" };
var answer = await client.PostAsJsonAsync("https://solardialer.net/api/v1/leads", lead);
Console.WriteLine($"{(int)answer.StatusCode} {await answer.Content.ReadAsStringAsync()}");
Each example reads the key from the SOLARDIALER_KEY environment variable, so it
never sits in your code.
Send leads from your server, never from code in a web page: a key in a page's code can be read by anyone who opens it.
Good to know
- One lead per request. A phone number is one lead per account: sending it again updates that lead rather than adding a second.
- Leads are added, not called: call them with the Call button, a campaign, or Call a lead now. Every call still gets the usual checks: the do-not-call list, each lead's calling hours, and the limits on attempts.
- A lead on the do-not-call list is still accepted, and is never called.
- Up to 1000 leads an hour per account.
Call a lead now
Send a new lead (from your web form, Facebook lead ads or a lead seller) and the agent calls it right away, as the Call button would:
POST https://solardialer.net/api/v1/calls
Authorization: Bearer YOUR_KEY
Content-Type: application/json
{"first_name": "Dana", "phone": "215-555-0147", "state": "PA", "list": "Web leads"}
The same fields as Send a lead: the lead is added (or updated), then called. The
answer is the lead's, plus called (true or false), call_id and, when it was not
called, reason: outside its calling hours, called too recently, on the do-not-call list...
The lead stays on its list, so a running campaign on that list calls it when it can. A problem your
account must fix first (the agent's script not checked yet, no calls left) answers 409
with what to do. Up to 300 calls an hour.
A call's recording
GET https://solardialer.net/api/v1/calls/{call_id}/recording
Authorization: Bearer YOUR_KEY
The audio file itself (MP3 or WAV), for any of your calls: the call_id and
recording_url in every call event point here. A call that isn't yours, or has no recording,
answers 404. Up to 600 an hour. In Zapier, the call triggers give it as a file a step can
save to Drive or attach to a CRM record.
Find a lead
GET https://solardialer.net/api/v1/leads?phone=2155550147 GET https://solardialer.net/api/v1/leads?lead_id=W1047 Authorization: Bearer YOUR_KEY
A list with the lead (its lead fields below, plus status and url),
or [] when you have none with that number (any of its three) or id.
Events and hooks
What happens on your calls, for Zapier, your CRM or your own server. Each event is JSON with its
id, event, created_at and the fields below.
| Event | When | Fields |
|---|---|---|
call.ended | Every call, when it ends | call_id,
outcome (talked, voicemail, transferred, transfer_failed, no_answer...), ended_reason,
started_at, seconds, campaign_id, campaign,
disposition, call_url (the call's page), recording_url (the audio,
with your key), transcript, summary (a line about the call, when
there is one), answers
(what the lead answered: homeowner, monthly_bill, roof...),
summary (an AI summary of the call in 2-3 sentences), objections
(the concerns they raised, short tags such as "too expensive"), interest (1 to 5;
these three only when the call reached a person), lead |
call.transferred | A transfer to your closer connected | As call.ended |
lead.interested | The lead said yes to your closer: the transfer connected, did not connect, or your closer's callback was booked instead | As call.ended |
callback.booked | The agent booked a callback | at,
when_text (their words), note, set_by, caller
(closer: your advisor calls them; agent: the agent calls them again),
lead |
lead.status_changed | You gave a lead a status | old,
new, lead |
lead.removed | A number went on the do-not-call list | call_id,
lead |
lead.created | A lead was added to your account (uploaded, typed in, sent by the API or given on a call); an update of a lead you have sends none | lead,
list (its list's name, or null), source (upload, form, dial, api, call,
import) |
campaign.finished | A campaign is done with every lead, or was stopped | campaign_id, campaign, how (done or stopped), leads,
calls, talked (conversations, transfers included), transferred,
appointments, finished_at |
lead is {"lead_id", "first_name", "last_name", "phone", "phones", "email", "state",
"source_id"}; source_id is the id the lead came in with.
Read them
GET https://solardialer.net/api/v1/events?event=call.ended&limit=25 Authorization: Bearer YOUR_KEY
Newest first, up to 100. Leave out event for every kind.
Have them sent to you (hooks)
POST https://solardialer.net/api/v1/hooks
Authorization: Bearer YOUR_KEY
Content-Type: application/json
{"event": "call.ended", "target_url": "https://example.com/solardialer"}
Answers 201 with the hook's id and secret. From then on each event of
that kind is POSTed to your URL (https only), in order, within a minute or so, with the header
X-SolarDialer-Signature: sha256=<HMAC-SHA256 of the body with the secret, in hex>.
Answer with any 2xx. Anything else is tried again after 1, 5 and 30 minutes, 2 and 12 hours, then the
hook stops (your Integrations page shows it); a 410 removes it.
DELETE https://solardialer.net/api/v1/hooks/{id}
Authorization: Bearer YOUR_KEY
Lists, campaigns, account
GET /api/v1/lists: your lists,[{"id", "name", "leads"}]. Send a lead with"list": "name"to put it on one; it joins that list's campaigns.GET /api/v1/campaigns:[{"id", "name", "status", "list_ids"}]. Send a lead (or call one) with"campaign_id": 5to put it in that campaign: it goes on the campaign's list (a campaign with none gets a list of its own name), over anylistsent. Another account's campaign answers400"That campaign wasn't found."GET /api/v1/me: the key's account,{"id", "name", "email", "company"}.
Every request needs your key. A missing or wrong key gets 401.