{{-- Paces theme --}} {{-- PrismJS syntax highlighting --}} {{-- ══ Topbar ══════════════════════════════════════════════════════════════ --}}
{{ url('/api') }}
{{-- ══ Sidebar ══════════════════════════════════════════════════════════════ --}} {{-- ══ Main content ══════════════════════════════════════════════════════════ --}}
{{-- ── Overview ── --}}

Overview

The Lead Intake API lets external sources — landing pages, ad networks, affiliate platforms — push leads directly into the CRM and later query their first-time deposit (FTD) status.

Each API key is issued per integration. Contact the integration team to receive yours.

Submit Leads

POST /v1/leads
Validate, deduplicate & store

Check FTD Status

GET /v1/leads/{token}
Query deposit history

Unique Token

40-char SHA1 reference
returned on creation

{{-- ── Authentication ── --}}

Authentication

All /api/v1/* requests must carry a valid X-API-Key header. Your API key will be provided to you by the integration team.

POST /api/v1/leads HTTP/1.1
Host: {{ request()->getHost() }}
X-API-Key: YOUR_API_KEY
Content-Type: application/json
Accept: application/json
Tip: You may also pass the key as a query parameter ?api_key=YOUR_KEY, but the header is preferred for security.
{{-- ── Rate Limiting ── --}}

Rate Limiting

Endpoints are limited to 60 requests per minute per IP address. When exceeded the API returns HTTP 429.

HeaderDescription
X-RateLimit-LimitMaximum requests per minute
X-RateLimit-RemainingRequests remaining in the current window
Retry-AfterSeconds to wait before retrying (only on 429)
{{-- ── Error Format ── --}}

Error Format

All error responses share a consistent JSON structure:

FieldTypeWhen presentDescription
successbooleanAlwaysAlways false on errors
messagestringAlwaysHuman-readable error description
errorsobject422 onlyField-level validation details, key = field name
existing_bystring409 only"email" or "phone" — which field duplicated
lead_tokenstring409 onlyThe 40-char token of the existing lead
{{-- ════════════════════════════════════════════════════════════ --}} {{-- ── Endpoint 1: POST /v1/leads ── --}} {{-- ════════════════════════════════════════════════════════════ --}}

Endpoints

POST /api/v1/leads Submit a new lead

Creates a new lead in the CRM assigned to the office bound to the API key. Duplicates are detected by email and phone number. On success the response includes a 40-character SHA1 lead_token you can use later to query FTD status.

Request Body (JSON)

FieldTypeRequiredValidationDescription
fname string Required max 100 Lead's first name
lname string Required max 100 Lead's last name
email string Required valid email, max 150 Email address — used for duplicate detection
phone string Required /^\+?[1-9]\d{6,14}$/, max 30 International phone number (E.164), e.g. +14155550123. Used for duplicate detection.
country string Optional max 100 Lead's country name, e.g. Germany
aff_source_id string Optional max 100 Your affiliate / traffic-source identifier
funnel_name string Optional max 150 Landing page or funnel name the lead came from

Responses

{{-- 201 --}}
201 CreatedLead accepted and stored.
{
    "success": true,
    "message": "Lead accepted.",
    "lead_token": "a3f7c2d19e4b0865f1234abcd5678901a2b3c4d5"
}
{{-- 409 --}}
409 ConflictEmail or phone already exists. The existing lead's token is returned.
{
    "success": false,
    "message": "Lead already exists in the system.",
    "existing_by": "email",
    "lead_token": "a3f7c2d19e4b0865f1234abcd5678901a2b3c4d5"
}
{{-- 422 --}}
422 UnprocessableValidation failed — missing or invalid fields.
{
    "success": false,
    "message": "Validation failed.",
    "errors": {
        "phone": ["Phone number is not valid. Use international format, e.g. +12125551234."],
        "email": ["The email field is required."]
    }
}
{{-- 401 --}}
401 UnauthorizedAPI key missing or invalid.
{ "success": false, "message": "Invalid API key." }
{{-- 429 --}}
429 Too Many RequestsRate limit exceeded.
{ "message": "Too Many Attempts." }
{{-- ════════════════════════════════════════════════════════ --}} {{-- ── Endpoint 2: GET /v1/leads?from=&to= ── --}} {{-- ════════════════════════════════════════════════════════ --}}
GET /api/v1/leads?from=YYYY-MM-DD&to=YYYY-MM-DD List leads and FTD status by period

Returns leads created within the inclusive date range for the office assigned to your API key. Each record reports whether the lead is a client and whether an approved deposit exists. Results are paginated and ordered by creation date.

Query Parameters

ParameterRequiredDescription
fromYesStart date in YYYY-MM-DD format.
toYesEnd date in YYYY-MM-DD format; included in full.
pageNoPage number, default 1.
per_pageNoRecords per page, default 100, maximum 500.
ftdNoUse true to return only leads with an approved deposit, or false to return only leads without one.

Example

curl -s "{{ url('/api/v1/leads') }}?from=2026-08-01&to=2026-08-31&ftd=true&per_page=100" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Accept: application/json"
200 OKMatching leads and conversion summary.
{
    "success": true,
    "period": { "from": "2026-08-01", "to": "2026-08-31", "inclusive": true, "ftd": true },
    "summary": { "total": 1, "ftd": 1, "without_ftd": 0, "clients": 1 },
    "data": [
        {
            "id": 123,
            "lead_token": "a3f7c2d19e4b0865f1234abcd5678901a2b3c4d5",
            "fname": "Jane",
            "lname": "Example",
            "email": "jane@example.com",
            "phone": "+61400000000",
            "country": "Australia",
            "status": "Client",
            "is_client": true,
            "ftd": true,
            "ftd_amount": 500.00,
            "aff_source_id": "campaign-42",
            "funnel_name": "Sydney landing page",
            "created_at": "2026-08-15 10:30:00"
        }
    ],
    "pagination": {
        "current_page": 1,
        "per_page": 100,
        "last_page": 1,
        "total": 2,
        "next_page_url": null,
        "previous_page_url": null
    }
}
{{-- ════════════════════════════════════════════════════════ --}} {{-- ── Endpoint 3: GET /v1/leads/{token} ── --}} {{-- ════════════════════════════════════════════════════════ --}}
GET /api/v1/leads/{token} Check FTD status

Returns the first-time deposit (FTD) status for a lead identified by its 40-character lead_token. An FTD is considered present when at least one deposit with status approved exists for the lead.

Path Parameter

ParameterTypeDescription
token string (40 chars) The SHA1 token returned by POST /v1/leads on creation

Responses

{{-- 200 no FTD --}}
200 OKLead found — no FTD yet.
{
    "success": true,
    "lead_token": "a3f7c2d19e4b0865f1234abcd5678901a2b3c4d5",
    "ftd": false,
    "ftd_amount": null
}
{{-- 200 with FTD --}}
200 OKLead found — FTD confirmed.
{
    "success": true,
    "lead_token": "a3f7c2d19e4b0865f1234abcd5678901a2b3c4d5",
    "ftd": true,
    "ftd_amount": 500.00
}
{{-- 404 --}}
404 Not FoundNo lead matches the supplied token.
{ "success": false, "message": "Lead not found." }
{{-- 422 --}}
422 UnprocessableToken format is invalid (not 40 hex chars).
{ "success": false, "message": "Invalid lead token format." }
{{-- ── Code Examples ── --}}

Code Examples

{{-- cURL --}}

cURL — Submit lead

curl -s -X POST "{{ url('/api/v1/leads') }}" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "fname":         "John",
    "lname":         "Doe",
    "email":         "john.doe@example.com",
    "phone":         "+14155550123",
    "country":       "United States",
    "aff_source_id": "fb_campaign_001",
    "funnel_name":   "crypto-lp-v3"
  }'

cURL — Check FTD status

curl -s "{{ url('/api/v1/leads') }}/a3f7c2d19e4b0865f1234abcd5678901a2b3c4d5" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Accept: application/json"
{{-- PHP --}}

PHP

<?php

function callApi(string $method, string $url, ?array $body, string $key): array {
    $ch = curl_init($url);
    $headers = ['X-API-Key: ' . $key, 'Accept: application/json'];

    if ($body !== null) {
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
        $headers[] = 'Content-Type: application/json';
        if ($method === 'POST') curl_setopt($ch, CURLOPT_POST, true);
    }
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => $headers,
    ]);
    $response = curl_exec($ch);
    $status   = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    return ['status' => $status, 'body' => json_decode($response, true)];
}

// ── Submit a lead ─────────────────────────────────────────────
$r = callApi('POST', '{{ url('/api/v1/leads') }}', [
    'fname'         => 'John',
    'lname'         => 'Doe',
    'email'         => 'john.doe@example.com',
    'phone'         => '+14155550123',
    'country'       => 'United States',
    'aff_source_id' => 'fb_campaign_001',
    'funnel_name'   => 'crypto-lp-v3',
], 'YOUR_API_KEY');

if ($r['status'] === 201) {
    $token = $r['body']['lead_token'];
    echo "Lead accepted. Token: $token\n";
} elseif ($r['status'] === 409) {
    $token = $r['body']['lead_token'];
    echo "Duplicate ({$r['body']['existing_by']}). Existing token: $token\n";
} else {
    echo "Error {$r['status']}: {$r['body']['message']}\n";
}

// ── Check FTD status ──────────────────────────────────────────
$r2 = callApi('GET', '{{ url('/api/v1/leads') }}/' . $token, null, 'YOUR_API_KEY');
if ($r2['body']['ftd']) {
    echo "FTD confirmed! Amount: " . $r2['body']['ftd_amount'] . "\n";
} else {
    echo "No FTD yet.\n";
}
{{-- JavaScript --}}

JavaScript (fetch)

const API_KEY  = 'YOUR_API_KEY';
const BASE_URL = '{{ url('/api/v1') }}';

// ── Submit a lead ─────────────────────────────────────────────
async function submitLead(lead) {
    const res = await fetch(`${BASE_URL}/leads`, {
        method: 'POST',
        headers: {
            'X-API-Key':    API_KEY,
            'Content-Type': 'application/json',
            'Accept':       'application/json',
        },
        body: JSON.stringify(lead),
    });

    const data = await res.json();

    if (res.status === 201) {
        console.log('Accepted. Token:', data.lead_token);
        return data.lead_token;
    }
    if (res.status === 409) {
        console.warn('Duplicate by', data.existing_by, '— token:', data.lead_token);
        return data.lead_token;
    }
    throw new Error(`Error ${res.status}: ${data.message}`);
}

// ── Check FTD status ──────────────────────────────────────────
async function checkFtd(token) {
    const res  = await fetch(`${BASE_URL}/leads/${token}`, {
        headers: { 'X-API-Key': API_KEY, 'Accept': 'application/json' },
    });
    const data = await res.json();
    return data; // { ftd: true/false, ftd_amount: N | null }
}

// ── Usage ─────────────────────────────────────────────────────
const token = await submitLead({
    fname:         'John',
    lname:         'Doe',
    email:         'john.doe@example.com',
    phone:         '+14155550123',
    country:       'United States',
    aff_source_id: 'fb_campaign_001',
    funnel_name:   'crypto-lp-v3',
});

const ftdStatus = await checkFtd(token);
console.log(ftdStatus.ftd ? `FTD: $${ftdStatus.ftd_amount}` : 'No FTD yet');
{{-- Python --}}

Python (requests)

import requests

API_KEY  = 'YOUR_API_KEY'
BASE_URL = '{{ url('/api/v1') }}'
HEADERS  = {'X-API-Key': API_KEY, 'Accept': 'application/json'}

# ── Submit a lead ─────────────────────────────────────────────
r = requests.post(
    f'{BASE_URL}/leads',
    headers=HEADERS,
    json={
        'fname':         'John',
        'lname':         'Doe',
        'email':         'john.doe@example.com',
        'phone':         '+14155550123',
        'country':       'United States',
        'aff_source_id': 'fb_campaign_001',
        'funnel_name':   'crypto-lp-v3',
    }
)

data  = r.json()
token = None

if r.status_code == 201:
    token = data['lead_token']
    print(f'Accepted. Token: {token}')
elif r.status_code == 409:
    token = data['lead_token']
    print(f"Duplicate by {data['existing_by']}. Existing token: {token}")
else:
    print(f"Error {r.status_code}: {data['message']}")
    exit(1)

# ── Check FTD status ──────────────────────────────────────────
r2   = requests.get(f'{BASE_URL}/leads/{token}', headers=HEADERS)
data2 = r2.json()

if data2['ftd']:
    print(f"FTD confirmed! Amount: {data2['ftd_amount']}")
else:
    print('No FTD yet.')

Lead Intake API — {{ config('app.name') }} — v1 — Contact Admin

{{-- Scripts --}}