Welcome to Centermark’s internal developer portal. This hub explains how to integrate with the EFE Leads API
using clear technical references, working examples, and practical guidance for production use.
This documentation is intended for authenticated Centermark users only. It is not public and should not be shared
outside approved partner and client development teams.
Use this portal to:
The Enspire Leads API provides programmatic access to your lead feed data, allowing you to retrieve and integrate
lead information directly into your systems. This RESTful API delivers call, web form, and offer leads in real-time,
enabling automated workflows for lead distribution, tracking, and reporting.
Before integrating with the API, ensure you have:
https://api.yodle.comA typical integration follows these steps:
The API returns structured JSON data containing lead details, contact information, attribution data, and timestamps.
Lead types include CALL (phone inquiries), WEB (form submissions), and OFFER (promotional engagements).
The EFE Leads API uses HTTP Basic Authentication to secure all endpoint calls. Every request must include a
properly formatted Authorization header containing your credentials encoded in Base64.
Before making any API requests, you must authenticate using credentials provided by Centermark. Authentication
follows the standard HTTP Basic Authentication scheme:
username:passwordBasic (note the space after “Basic”)Authorization header of every API request| Header | Required | Format | Example |
|---|---|---|---|
Authorization |
Yes | Basic <base64_credentials> |
Basic dGVzdDp0ZXN0 |
Example credentials:
your_usernameyour_passwordStep-by-step construction:
1. Combine: "your_username:your_password"
2. Base64 encode: "eW91cl91c2VybmFtZTp5b3VyX3Bhc3N3b3Jk"
3. Add prefix: "Basic eW91cl91c2VybmFtZTp5b3VyX3Bhc3N3b3Jk"
4. Set header: Authorization: Basic eW91cl91c2VybmFtZTp5b3VyX3Bhc3N3b3Jk
const axios = require('axios');
const username = 'your_username';
const password = 'your_password';
const fmaId = '123456';
// Create Base64 encoded credentials
const credentials = Buffer.from(`${username}:${password}`).toString('base64');
const authHeader = `Basic ${credentials}`;
const config = {
headers: {
'Authorization': authHeader
}
};
axios.get(`https://api.yodle.com/api/v2/fma/${fmaId}/leads`, config)
.then(response => console.log(response.data))
.catch(error => console.error(error));
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
public class LeadsApiClient
{
private readonly HttpClient _httpClient;
private readonly string _username = "your_username";
private readonly string _password = "your_password";
public LeadsApiClient()
{
_httpClient = new HttpClient();
_httpClient.BaseAddress = new Uri("https://api.yodle.com");
// Create Base64 encoded credentials
var credentials = Convert.ToBase64String(
Encoding.ASCII.GetBytes($"{_username}:{_password}")
);
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic", credentials);
}
public async Task GetLeads(int fmaId, DateTime startDate, DateTime endDate)
{
var url = $"/api/v2/fma/{fmaId}/leads?startDate={startDate:yyyy-MM-ddTHH:mm:ss}&endDate={endDate:yyyy-MM-ddTHH:mm:ss}";
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
curl -X GET \
'https://api.yodle.com/api/v2/fma/123456/leads?startDate=2024-01-01T00:00:00&endDate=2024-12-31T23:59:59' \
-H 'Authorization: Basic eW91cl91c2VybmFtZTp5b3VyX3Bhc3N3b3Jk'
import requests
import base64
username = 'your_username'
password = 'your_password'
fma_id = '123456'
# Create Base64 encoded credentials
credentials = base64.b64encode(f'{username}:{password}'.encode()).decode()
headers = {
'Authorization': f'Basic {credentials}'
}
url = f'https://api.yodle.com/api/v2/fma/{fma_id}/leads'
params = {
'startDate': '2024-01-01T00:00:00',
'endDate': '2024-12-31T23:59:59'
}
response = requests.get(url, headers=headers, params=params)
print(response.json())
Important: Base64 encoding is NOT encryption. It’s a reversible encoding scheme, meaning anyone
with access to your Authorization header can decode your credentials. Treat the encoded value with the same
security as you would treat a plaintext password.
.gitignore)Transmission Security:
If you receive authentication errors (401 Unauthorized), verify:
Testing Your Authentication:
# Quick test to verify your credentials work
curl -X GET \
'https://api.yodle.com/api/v2/fma/YOUR_FMA_ID/leads?startDate=2024-01-01T00:00:00&endDate=2024-01-02T00:00:00&page=0' \
-H 'Authorization: Basic YOUR_BASE64_CREDENTIALS' \
-v
# A successful response returns HTTP 200 with lead data
# A failed authentication returns HTTP 401 Unauthorized
The Leads endpoint retrieves lead data for a specific FMA (franchise/location) within a specified date range. It returns call leads, web form submissions, and offer engagements with full contact details and attribution information.
Method: GET
URL: https://api.yodle.com/api/v2/fma/{fma_id}/leads
| Parameter | Required | Type | Description | Example |
|---|---|---|---|---|
fma_id |
Yes | Integer | Your unique franchise/location identifier in the Centermark system | 123456 |
| Parameter | Required | Type | Default | Description | Example |
|---|---|---|---|---|---|
startDate |
Yes | ISO 8601 DateTime | – | Start of date range to query | 2014-02-16T13:01:06 |
endDate |
Yes | ISO 8601 DateTime | – | End of date range to query | 2015-01-04T01:23:45 |
page |
No | Integer | 0 |
Which page to retrieve (0-indexed). Page size is 1,000 leads | 1 |
filtered |
No | Boolean | true |
Whether to return filtered leads (true) or unfiltered leads (false) | false |
The API returns a JSON object with the following top-level fields:
| Field | Type | Description |
|---|---|---|
fmaLeadResources |
Array | Array of lead objects (see Field and Payload Reference below) |
lastPage |
Boolean | Indicates whether this is the last page of results |
page=0)lastPage field in the response to determine if more pages are availablelastPage is false, increment the page parameter to retrieve the next pagelastPage returns trueBy default (filtered=true), the API returns only filtered/validated leads that meet quality criteria. Set filtered=false to retrieve all leads including those that may not meet standard quality thresholds.
GET https://api.yodle.com/api/v2/fma/123456/leads?startDate=2024-01-01T00:00:00&endDate=2024-01-31T23:59:59&page=0
Authorization: Basic dGVzdDp0ZXN0
The API returns a JSON response containing an array of lead objects. Each lead contains common fields plus type-specific data depending on whether it’s a CALL, WEB, or OFFER lead.
Each lead in the fmaLeadResources array contains the following fields:
| Field | Type | Description |
|---|---|---|
id |
Integer | Unique identifier for the lead in the system |
clientId |
Integer | Unique identifier for your location in the system |
leadType |
Enum | Type of lead: CALL, WEB, or OFFER |
referral |
String | Primary referral source |
subReferral |
String | Secondary referral classification |
franchiseNumber |
String | Franchise or location identifier |
createdDate |
ISO 8601 DateTime | Timestamp when the lead was created |
attribution |
Object | Attribution data (see Attribution table below) |
callLeadData |
Object or null | Only populated for CALL leads (see CallLeadData table below) |
webLeadData |
Object or null | Only populated for WEB leads (see WebLeadData table below) |
offerLeadData |
Object or null | Only populated for OFFER leads (see OfferLeadData table below) |
| Field | Type | Description |
|---|---|---|
primaryAttribution |
String or null | Primary attribution category |
secondaryAttribution |
String or null | Secondary attribution details |
| Field | Type | Description |
|---|---|---|
name |
String | Name of the lead |
sourceNumber |
String | Phone number the lead called from |
trackingNumber |
String | Tracking number the lead called |
destinationNumber |
String | Number that was ultimately contacted |
duration |
String | Call duration |
callStatus |
String | Indicates if the call was answered |
address |
Object | Address information for the lead (see Address table below) |
| Field | Type | Description |
|---|---|---|
street1 |
String | Primary street address |
street2 |
String | Secondary address line (apartment, suite, etc.) |
city |
String | City name |
state |
String | State or province code |
zipcode |
String | Postal/ZIP code |
country |
String | Country code |
| Field | Type | Description |
|---|---|---|
emailAddress |
String | Email address of the lead |
formData |
Object | Dynamic key-value pairs containing all form fields submitted by the lead (e.g., firstName, lastName, phone, comments) |
| Field | Type | Description |
|---|---|---|
offerId |
String | Unique identifier for the offer |
emailAddress |
String | Email address of the lead |
{
"fmaLeadResources": [
{
"id": 6343159740,
"clientId": 100001,
"leadType": "CALL",
"referral": "Yodle",
"subReferral": "Organic",
"franchiseNumber": "D1",
"createdDate": "2015-11-16T17:18:37.000",
"attribution": {
"primaryAttribution": "unpaid",
"secondaryAttribution": null
},
"callLeadData": {
"name": "Steveson Steve H",
"sourceNumber": "9045551234",
"trackingNumber": "8045551234",
"destinationNumber": "7045551234",
"duration": "00:00:27",
"callStatus": "Answered",
"address": {
"street1": "33 Fake St",
"street2": "",
"city": "New York",
"state": "NY",
"zipcode": "11111",
"country": "US"
}
},
"webLeadData": null,
"offerLeadData": null
},
{
"id": 6345686398,
"clientId": 100002,
"leadType": "WEB",
"referral": "Yodle",
"subReferral": "Organic",
"franchiseNumber": "DS199",
"createdDate": "2015-11-17T17:10:28.386",
"attribution": {
"primaryAttribution": null,
"secondaryAttribution": null
},
"callLeadData": null,
"webLeadData": {
"emailAddress": "test.lead@enspireforenterprise.com",
"formData": {
"firstName": "Matt",
"lastName": "Wong",
"phone": "19045551234",
"Comments": "Hi I have a question",
"email": "test.lead@enspireforenterprise.com",
"reasonInquiry": "General Inquiry"
}
},
"offerLeadData": null
}
],
"lastPage": true
}
Production-oriented integration examples demonstrating how to retrieve leads, handle pagination, and process data.
const axios = require('axios');
// Load credentials from environment variables (never hardcode)
const API_USERNAME = process.env.ENSPIRE_API_USERNAME;
const API_PASSWORD = process.env.ENSPIRE_API_PASSWORD;
const FMA_ID = process.env.FMA_ID;
class LeadsApiClient {
constructor(username, password, fmaId) {
this.baseUrl = 'https://api.yodle.com';
this.fmaId = fmaId;
this.authHeader = 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64');
}
async getLeads(startDate, endDate, filtered = true) {
const allLeads = [];
let page = 0;
let lastPage = false;
while (!lastPage) {
try {
const response = await axios.get(
`${this.baseUrl}/api/v2/fma/${this.fmaId}/leads`,
{
headers: { 'Authorization': this.authHeader },
params: { startDate, endDate, page, filtered }
}
);
const { fmaLeadResources, lastPage: isLastPage } = response.data;
allLeads.push(...fmaLeadResources);
lastPage = isLastPage;
page++;
console.log(`Retrieved page ${page}, leads count: ${fmaLeadResources.length}`);
} catch (error) {
if (error.response?.status === 401) {
throw new Error('Authentication failed. Check credentials.');
}
throw error;
}
}
return allLeads;
}
}
// Usage
(async () => {
const client = new LeadsApiClient(API_USERNAME, API_PASSWORD, FMA_ID);
const startDate = '2024-01-01T00:00:00';
const endDate = '2024-01-31T23:59:59';
try {
const leads = await client.getLeads(startDate, endDate);
console.log(`Total leads retrieved: ${leads.length}`);
// Process leads by type
leads.forEach(lead => {
switch(lead.leadType) {
case 'CALL':
console.log(`Call from ${lead.callLeadData.sourceNumber}`);
break;
case 'WEB':
console.log(`Web lead: ${lead.webLeadData.emailAddress}`);
break;
case 'OFFER':
console.log(`Offer lead: ${lead.offerLeadData.offerId}`);
break;
}
});
} catch (error) {
console.error('Failed to retrieve leads:', error.message);
process.exit(1);
}
})();
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
public class LeadsApiClient : IDisposable
{
private readonly HttpClient _httpClient;
private readonly string _fmaId;
public LeadsApiClient(string username, string password, string fmaId)
{
_httpClient = new HttpClient
{
BaseAddress = new Uri("https://api.yodle.com")
};
var credentials = Convert.ToBase64String(
Encoding.ASCII.GetBytes($"{username}:{password}")
);
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic", credentials);
_fmaId = fmaId;
}
public async Task> GetAllLeadsAsync(
DateTime startDate,
DateTime endDate,
bool filtered = true)
{
var allLeads = new List();
int page = 0;
bool lastPage = false;
while (!lastPage)
{
var startDateStr = startDate.ToString("yyyy-MM-ddTHH:mm:ss");
var endDateStr = endDate.ToString("yyyy-MM-ddTHH:mm:ss");
var url = $"/api/v2/fma/{_fmaId}/leads?" +
$"startDate={startDateStr}&" +
$"endDate={endDateStr}&" +
$"page={page}&" +
$"filtered={filtered.ToString().ToLower()}";
var response = await _httpClient.GetAsync(url);
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
throw new UnauthorizedAccessException("Authentication failed.");
}
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize(json);
allLeads.AddRange(result.FmaLeadResources);
lastPage = result.LastPage;
page++;
Console.WriteLine($"Retrieved page {page}, leads: {result.FmaLeadResources.Count}");
}
return allLeads;
}
public void Dispose() => _httpClient?.Dispose();
}
public class LeadsResponse
{
public List FmaLeadResources { get; set; }
public bool LastPage { get; set; }
}
public class Lead
{
public long Id { get; set; }
public int ClientId { get; set; }
public string LeadType { get; set; }
public string Referral { get; set; }
public string SubReferral { get; set; }
public string FranchiseNumber { get; set; }
public DateTime CreatedDate { get; set; }
public Attribution Attribution { get; set; }
public CallLeadData CallLeadData { get; set; }
public WebLeadData WebLeadData { get; set; }
public OfferLeadData OfferLeadData { get; set; }
}
// Usage
class Program
{
static async Task Main(string[] args)
{
// Load from configuration (appsettings.json, environment variables, etc.)
var username = Environment.GetEnvironmentVariable("ENSPIRE_API_USERNAME");
var password = Environment.GetEnvironmentVariable("ENSPIRE_API_PASSWORD");
var fmaId = Environment.GetEnvironmentVariable("FMA_ID");
using var client = new LeadsApiClient(username, password, fmaId);
try
{
var leads = await client.GetAllLeadsAsync(
new DateTime(2024, 1, 1),
new DateTime(2024, 1, 31, 23, 59, 59)
);
Console.WriteLine($"Total leads retrieved: {leads.Count}");
// Process by type
var callLeads = leads.Where(l => l.LeadType == "CALL").ToList();
var webLeads = leads.Where(l => l.LeadType == "WEB").ToList();
var offerLeads = leads.Where(l => l.LeadType == "OFFER").ToList();
Console.WriteLine($"Calls: {callLeads.Count}, Web: {webLeads.Count}, Offers: {offerLeads.Count}");
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error: {ex.Message}");
Environment.Exit(1);
}
}
}
<?php
class LeadsApiClient {
private $baseUrl = 'https://api.yodle.com';
private $fmaId;
private $authHeader;
public function __construct($username, $password, $fmaId) {
$this->fmaId = $fmaId;
// Create Base64 encoded credentials
$credentials = base64_encode("$username:$password");
$this->authHeader = "Authorization: Basic $credentials";
}
public function getAllLeads($startDate, $endDate, $filtered = true) {
$allLeads = [];
$page = 0;
$lastPage = false;
while (!$lastPage) {
$url = sprintf(
'%s/api/v2/fma/%s/leads?startDate=%s&endDate=%s&page=%d&filtered=%s',
$this->baseUrl,
$this->fmaId,
urlencode($startDate),
urlencode($endDate),
$page,
$filtered ? 'true' : 'false'
);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [$this->authHeader]);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
$error = curl_error($ch);
curl_close($ch);
throw new Exception("cURL error: $error");
}
curl_close($ch);
if ($httpCode === 401) {
throw new Exception('Authentication failed. Check credentials.');
}
if ($httpCode !== 200) {
throw new Exception("HTTP error $httpCode: $response");
}
$data = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception('Failed to parse JSON response');
}
$allLeads = array_merge($allLeads, $data['fmaLeadResources']);
$lastPage = $data['lastPage'];
$page++;
echo "Retrieved page $page, leads count: " . count($data['fmaLeadResources']) . "\n";
}
return $allLeads;
}
public function processLeads($leads) {
$stats = [
'CALL' => 0,
'WEB' => 0,
'OFFER' => 0
];
foreach ($leads as $lead) {
$stats[$lead['leadType']]++;
switch ($lead['leadType']) {
case 'CALL':
if (isset($lead['callLeadData'])) {
echo "Call from {$lead['callLeadData']['sourceNumber']}\n";
}
break;
case 'WEB':
if (isset($lead['webLeadData'])) {
echo "Web lead: {$lead['webLeadData']['emailAddress']}\n";
}
break;
case 'OFFER':
if (isset($lead['offerLeadData'])) {
echo "Offer lead: {$lead['offerLeadData']['offerId']}\n";
}
break;
}
}
return $stats;
}
}
// Usage
try {
// Load credentials from environment variables (never hardcode)
$username = getenv('ENSPIRE_API_USERNAME');
$password = getenv('ENSPIRE_API_PASSWORD');
$fmaId = getenv('FMA_ID');
if (!$username || !$password || !$fmaId) {
throw new Exception('Required environment variables not set');
}
$client = new LeadsApiClient($username, $password, $fmaId);
$startDate = '2024-01-01T00:00:00';
$endDate = '2024-01-31T23:59:59';
echo "Retrieving leads...\n";
$leads = $client->getAllLeads($startDate, $endDate);
echo "Total leads retrieved: " . count($leads) . "\n";
$stats = $client->processLeads($leads);
echo "\nBreakdown:\n";
echo "Calls: {$stats['CALL']}\n";
echo "Web: {$stats['WEB']}\n";
echo "Offers: {$stats['OFFER']}\n";
} catch (Exception $e) {
error_log("Failed to retrieve leads: " . $e->getMessage());
exit(1);
}
?>
The API returns structured JSON error responses for invalid requests. Understanding these error codes and their causes will help you quickly diagnose and resolve integration issues.
When an error occurs, the API returns an appropriate HTTP status code along with a JSON response body containing error details:
{
"timestamp": 1777652067436,
"status": 401,
"error": "Unauthorized",
"message": "Bad Credentials",
"path": "/api/v2/fma/123456/leads"
}
HTTP Status: 401 Unauthorized
Cause: The provided username/password credentials are incorrect or invalid.
Example Response:
{
"timestamp": 1777652067436,
"status": 401,
"error": "Unauthorized",
"message": "Bad Credentials",
"path": "/api/v2/fma/123456/leads"
}
Resolution:
username:password is accurateBasic <base64_credentials> with a space after “Basic”HTTP Status: 400 Bad Request
Cause: The FMA ID in the URL path contains non-numeric characters or is malformed.
Example Request: GET /api/v2/fma/1234S6/leads
Example Response:
{
"exception": "org.springframework.beans.TypeMismatchException",
"path": "/api/v2/fma/1234S6/leads",
"error": "Bad Request",
"message": "Failed to convert value of type 'java.lang.String' to required type 'long'; nested exception is java.lang.NumberFormatException: For input string: \"1234S6\"",
"timestamp": "2026-05-01T12:15:41.774-04:00",
"status": 400
}
Resolution:
123456, not 1234S6)HTTP Status: 403 Forbidden
Cause: Your credentials are valid, but you don’t have permission to access the specified FMA ID.
Example Response:
{
"exception": "org.springframework.security.access.AccessDeniedException",
"path": "/api/v2/fma/123456/leads",
"error": "Forbidden",
"message": "Not authorized",
"timestamp": "2026-05-01T12:16:13.402-04:00",
"status": 403
}
Resolution:
HTTP Status: 400 Bad Request
Cause: The request URL contains syntax errors, such as multiple query string delimiters.
Example Request: GET /api/v2/fma/123456/leads?startDate=2026-10-13T00:00:00&endDate=2026-01-14T23:00:00?test
Response: 400 Bad Request with no response body
Resolution:
? characters in the URL (only one ? should appear before the first parameter)& to separate query parameters, not ?YYYY-MM-DDTHH:mm:ss/api/v2/fma/123456/leads?startDate=2026-01-13T00:00:00&endDate=2026-01-14T23:00:00
If date parameters don’t match the expected ISO 8601 format, the API may return a 400 Bad Request.
Resolution:
YYYY-MM-DDTHH:mm:ss (e.g., 2024-01-15T00:00:00)T between date and time componentsOmitting required parameters (startDate or endDate) will result in a 400 Bad Request.
Resolution:
startDate and endDate query parameterspage, filtered) can be omitted to use defaultsImplement appropriate retry strategies:
When encountering errors, systematically verify:
If you encounter an error not documented here or need assistance:
Version 1.0 – May 2026
If you spot an issue (incorrect field name, outdated behavior, unclear example), send feedback to the
Centermark/AFE support channel or your EFE project contact. Include:
This portal contains implementation guidance that may include sensitive integration details. Keep credentials
private, follow least-privilege principles, and do not paste secrets into tickets or shared docs.