How it works
Visitor → Your website form → Your backend → Kushwaha Leads API → Database
Base URL: https://leads.kushwahatechsolutions.com/api/v1
A successful response means the inquiry has been stored. Email notifications are currently disabled. Your website should validate its form and apply its own spam controls and visitor rate limits.
Submit an inquiry
POST /api/v1/forms/YOUR_FORM_PUBLIC_ID/submissions Authorization: Bearer YOUR_WEBSITE_API_KEY Content-Type: application/json Accept: application/json Idempotency-Key: unique-id-for-this-inquiry
{
"name": "John Smith",
"email": "john@example.com",
"phone": "+14155550123",
"subject": "Website development",
"message": "I need a Laravel website.",
"fields": {"service": "Laravel", "budget": "5000–10000"}
}
Required: a valid email or phone, plus a nonblank message (maximum 5,000 characters). Name and subject allow 255 characters; phone allows 50. Requests must be JSON objects and no larger than 32 KB.
fields is optional: an object of at most 20 keys. Names start with a letter and contain up to 64 letters, numbers, underscores or hyphens. Values are scalars (strings up to 1,000 characters) or arrays of up to 10 scalars. Nested objects, unknown top-level fields, and uploads are rejected.
201 · Saved
{"success":true,"message":"Your inquiry has been submitted successfully.","data":{"submission_id":1024}}Safe retries
Use an optional Idempotency-Key of 1–100 printable ASCII characters, without spaces. Keep the same key and content when retrying one inquiry. A replay returns the original ID and 201; different content with that key returns 409. Keys are scoped to the website and form and retained until the lead is deleted. Use a new key for each new inquiry.
Without an idempotency key, retries can create duplicates. A timeout does not prove that storage failed. Preserve the inquiry and its key locally if reliable retries are needed.
Server-side integration examples
cURL
curl 'https://leads.kushwahatechsolutions.com/api/v1/forms/YOUR_FORM_PUBLIC_ID/submissions' \
-H 'Authorization: Bearer YOUR_WEBSITE_API_KEY' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Idempotency-Key: inquiry-unique-123' \
--data '{"email":"john@example.com","message":"Please contact me."}'
Laravel website
Put the URL, key and form ID in private server configuration. Map your environment values to config/services.php as services.kushwaha_leads. The inquiry ID below must come from your locally persisted inquiry or validated server-issued form token and remain stable across retries.
try {
$response = Http::withToken(config('services.kushwaha_leads.api_key'))
->acceptJson()
->withHeaders(['Idempotency-Key' => $inquiryId])
->connectTimeout(3)->timeout(10)
->post(
rtrim(config('services.kushwaha_leads.url'), '/')
. '/api/v1/forms/' . config('services.kushwaha_leads.form_id')
. '/submissions',
$validated
);
} catch (\Illuminate\Http\Client\ConnectionException $e) {
return back()->with('error', 'Unable to confirm storage. Please try again.');
}
if ($response->status() === 201) {
return back()->with('success', 'Your inquiry has been saved.');
}
return back()->with('error', 'Unable to save your inquiry. Please try again.');
Plain PHP backend
Your form posts to your own backend. Validate input, check CSRF and spam controls, and keep the credential in server environment variables. Never forward the API credential to the browser.
$curl = curl_init(getenv('KUSHWAHA_LEADS_URL')
. '/api/v1/forms/' . getenv('KUSHWAHA_LEADS_FORM_ID') . '/submissions');
curl_setopt_array($curl, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 3,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('KUSHWAHA_LEADS_API_KEY'),
'Content-Type: application/json',
'Accept: application/json',
'Idempotency-Key: ' . $inquiryId,
],
CURLOPT_POSTFIELDS => json_encode($validated, JSON_THROW_ON_ERROR),
]);
$result = curl_exec($curl);
$stored = $result !== false && curl_getinfo($curl, CURLINFO_RESPONSE_CODE) === 201;
curl_close($curl);
// Only show success when $stored is true. Retain inquiry ID for retries.Errors and troubleshooting
{"success":false,"message":"Request rejected.","data":null}| Status | Meaning / next step |
|---|---|
| 401 | Missing, invalid, revoked or expired key. Check your server configuration. |
| 403 | Disabled website or insufficient owner authorization. |
| 404 | Form missing, disabled, or belongs to another website; or owner record missing. |
| 409 | Idempotency key already used for different content. |
| 413 | Request exceeds 32 KB. |
| 415 | Use Content-Type: application/json. |
| 422 | Validation failed. Check errors and fix the request. |
| 429 | Rate limited. Respect Retry-After and retry with the same inquiry key. |
| 500 | Unexpected service error. Retain the inquiry for a safe retry. |
Default submission limits: 100/minute per website and per key, with a 200/minute source-IP ingress limit. The source IP seen here is usually your server’s IP. Your origin form should also apply a visitor limit (for example 10/minute).
Private owner API
Owner routes require a separate Sanctum token with the owner ability and an authorized owner account. Website submission keys cannot read or manage leads.
Authorization: Bearer OWNER_TOKEN
| Method | Path under /api/v1 |
|---|---|
| GET | /websites |
| GET | /websites/{id} |
| GET | /websites/{id}/forms |
| GET | /websites/{id}/submissions |
| GET | /websites/{id}/stats |
| GET | /forms/{id}/submissions |
| GET | /submissions |
| GET | /submissions/{id} |
| PATCH | /submissions/{id}/status |
| DELETE | /submissions/{id} |
| GET | /stats |
Owner routes use numeric IDs. Lead filters: website_id, form_id, status, search, from, to, page, and per_page (default 20, maximum 100). Dates use YYYY-MM-DD, inclusive in Asia/Kolkata by default. Results sort newest first by timestamp and ID.
Status body: {"status":"contacted"}. Allowed statuses: new, contacted, follow_up, converted, closed. Deletion permanently removes a lead and returns 204 with no body. Stats use the configured reporting timezone; stored timestamps use UTC.