Hadar API Documentation
Integrate your systems with Hadar using our REST API. Manage leads, properties, and appointments programmatically.
Authentication
All API endpoints require an API key passed in the X-API-Key header. Create and manage API keys from Settings → API Keys.
Example request with API key
curl -X GET "https://crm.hadar-ai.com/api/v1/leads" \
-H "X-API-Key: ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"Missing or invalid key returns 401
{
"error": {
"code": "UNAUTHORIZED",
"message": "Missing or invalid API key"
}
}Base URL
https://crm.hadar-ai.com/api/v1All endpoints below are relative to this base URL. Responses are JSON-encoded with Content-Type: application/json.
Response Format
All responses follow a consistent envelope. Successful responses contain a data field. List endpoints include a meta field for pagination.
Successful list response
{
"data": [ ... ],
"meta": {
"total": 1250,
"page": 1,
"limit": 20,
"pages": 63,
"has_next": true,
"has_prev": false
}
}Successful single-resource response
{
"data": {
"id": "lead-abc123",
"full_name": "Ahmed Al Mansouri",
"email": "ahmed@example.com",
"status": "new",
"created_at": "2026-04-03T10:30:00Z"
}
}Error Responses
Errors return a structured error object with a machine-readable code and a human-readable message.
Validation error (400)
{
"error": {
"code": "VALIDATION_FAILED",
"message": "Validation failed"
},
"details": {
"fieldErrors": {
"email": ["Invalid email"]
}
}
}| Status | Code | Description |
|---|---|---|
| 400 | VALIDATION_FAILED | Invalid request body or query parameters |
| 401 | UNAUTHORIZED | Missing or invalid X-API-Key header |
| 403 | FORBIDDEN | API key lacks permission for this action |
| 404 | NOT_FOUND | Resource does not exist |
| 429 | RATE_LIMITED | Too many requests. Check Retry-After header. |
| 500 | INTERNAL_ERROR | Server-side error. Retry or contact support. |
Resources
Leads
Create and retrieve leads. Filter by status, score, and channel. Supports pagination.
/api/v1/leadsProperties
List and create property listings. Filter by type, status, price range, bedrooms, and community.
/api/v1/propertiesAppointments
List and schedule appointments including viewings, calls, meetings, and follow-ups.
/api/v1/appointmentsWebhooks
Manage webhook subscriptions for real-time event notifications.
/api/v1/webhooksPractical Examples
List leads with filters
Request
curl -X GET "https://crm.hadar-ai.com/api/v1/leads?page=1&limit=10&status=qualified" \
-H "X-API-Key: ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"Response (200 OK)
{
"data": [
{
"id": "lead-abc123",
"full_name": "Ahmed Al Mansouri",
"email": "ahmed@example.com",
"phone": "+971501234567",
"nationality": "AE",
"status": "qualified",
"score": 85,
"budget_min_aed": 500000,
"budget_max_aed": 2000000,
"purpose": "Investment",
"timeline": "3 months",
"channel": "website",
"assigned_to": "agent-456",
"created_at": "2026-02-22T10:30:00Z"
}
],
"meta": {
"total": 342,
"page": 1,
"limit": 10,
"pages": 35,
"has_next": true,
"has_prev": false
}
}Filter parameters: status, channel, search. Pagination via page + limit (max 100).
Create a new lead
Request
curl -X POST "https://crm.hadar-ai.com/api/v1/leads" \
-H "X-API-Key: ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"full_name": "Ahmed Al Mansouri",
"email": "ahmed@example.com",
"phone": "+971501234567",
"nationality": "AE",
"budget_min_aed": 500000,
"budget_max_aed": 2000000,
"purpose": "Investment",
"timeline": "3 months",
"notes": "Interested in Downtown villas",
"channel": "api"
}'Response (201 Created)
{
"data": {
"id": "lead-xyz789",
"full_name": "Ahmed Al Mansouri",
"email": "ahmed@example.com",
"phone": "+971501234567",
"nationality": "AE",
"status": "new",
"score": 0,
"budget_min_aed": 500000,
"budget_max_aed": 2000000,
"purpose": "Investment",
"timeline": "3 months",
"channel": "api",
"created_at": "2026-04-03T14:22:00Z"
}
}At least one identifier (email or phone) is required. The lead is automatically assigned a new status and a score of 0.
JavaScript / Node.js
Fetch wrapper for API calls
const HADAR_API_KEY = process.env.HADAR_API_KEY;
const BASE_URL = "https://crm.hadar-ai.com/api/v1";
async function hadarApi(path, options = {}) {
const response = await fetch(`${BASE_URL}${path}`, {
...options,
headers: {
"X-API-Key": HADAR_API_KEY,
"Content-Type": "application/json",
...options.headers,
},
});
const json = await response.json();
if (!response.ok) {
throw new Error(json.error?.message || `API error: ${response.status}`);
}
return json;
}
// Usage
const leads = await hadarApi("/leads?status=qualified&limit=50");
console.log(leads.data); // Lead[]
console.log(leads.meta.total); // Total count
const newLead = await hadarApi("/leads", {
method: "POST",
body: JSON.stringify({
full_name: "Ahmed Al Mansouri",
email: "ahmed@example.com",
phone: "+971501234567",
channel: "api",
}),
});
console.log(newLead.data.id); // "lead-xyz789"Python
Using requests library
import os
import requests
API_KEY = os.environ["HADAR_API_KEY"]
BASE_URL = "https://crm.hadar-ai.com/api/v1"
HEADERS = {"X-API-Key": API_KEY}
# List qualified leads
response = requests.get(
f"{BASE_URL}/leads",
headers=HEADERS,
params={"status": "qualified", "limit": 50},
)
response.raise_for_status()
leads = response.json()
print(f"Found {leads['meta']['total']} qualified leads")
# Create a lead
new_lead = requests.post(
f"{BASE_URL}/leads",
headers={**HEADERS, "Content-Type": "application/json"},
json={
"full_name": "Ahmed Al Mansouri",
"email": "ahmed@example.com",
"phone": "+971501234567",
"channel": "api",
},
)
new_lead.raise_for_status()
print(f"Created lead: {new_lead.json()['data']['id']}")Webhooks
Configure webhooks to receive real-time notifications when events occur in your CRM. Set up webhook endpoints in Dashboard → Developers → Webhooks.
Available Events
Webhook payload (POST to your endpoint)
{
"event": "lead.created",
"timestamp": "2026-04-03T14:22:00Z",
"data": {
"id": "lead-xyz789",
"full_name": "Ahmed Al Mansouri",
"email": "ahmed@example.com",
"phone": "+971501234567",
"status": "new",
"channel": "website",
"created_at": "2026-04-03T14:22:00Z"
}
}Verifying webhook signatures (Node.js)
import crypto from "crypto";
function verifyWebhookSignature(payload, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(payload, "utf-8")
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your webhook handler
app.post("/webhooks/hadar", (req, res) => {
const signature = req.headers["x-webhook-signature"];
const isValid = verifyWebhookSignature(
JSON.stringify(req.body),
signature,
process.env.HADAR_WEBHOOK_SECRET
);
if (!isValid) {
return res.status(401).json({ error: "Invalid signature" });
}
const { event, data } = req.body;
switch (event) {
case "lead.created":
console.log("New lead:", data.full_name);
break;
case "appointment.scheduled":
console.log("New appointment:", data.title);
break;
}
res.status(200).json({ received: true });
});Rate Limiting
| Tier | Requests / min | Description |
|---|---|---|
| Standard | 60 | Default tier for all API keys |
| Professional | 300 | For high-volume integrations |
| Enterprise | 1000 | Custom limits available |
Rate limit headers are included in every response: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset. When rate-limited, wait until the Retry-After time before retrying.
Need Help?
For integration support or questions about the API:
- Email: support@hadar-ai.com
- OpenAPI Spec: Download JSON
- Developer Portal: Full interactive documentation (requires login)