API Reference
Everything you need to integrate voice AI lead qualification.
/api/v1/calls/triggerAI calls your leadResults via webhook + APIQualify
AI asks your custom criteria questions and evaluates answers server-side
Book
If qualified, the agent proposes viewing slots and books the visit
Report
Score, transcript, and criteria results delivered via webhook or API
Call Flow
1. POST /api/v1/calls/trigger
2. AI agent calls the lead via SIP
3. Agent introduces itself with your agency name
4. Agent confirms lead identity
5. Agent asks your qualification criteria
6. Answers evaluated server-side (deterministic)
7. If qualified → agent proposes viewing slots
8. If accepted → books viewing + SMS/email confirmation
9. Call ends → score calculated
10. POST results to your callback_webhook
11. GET /api/v1/calls/{callId} for results anytime
Authentication
All requests require an API key via the x-api-key header.
curl "https://immo.next-lab.tech/api/v1/usage" \
-H "x-api-key: ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"Header: x-api-key: ak_live_xxx
Alternative: Authorization: Bearer ak_live_xxx
Rate limit: 10 requests/minute per API key
Get a key: Contact abdullah@next-lab.tech
Quick Start
Trigger your first AI call in under a minute.
# Complete example with all available fields
curl -X POST "https://immo.next-lab.tech/api/v1/calls/trigger" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"lead": {
"firstName": "John",
"lastName": "Smith",
"phone": "+41791234567",
"email": "john.smith@example.com",
"language": "en",
"budget": "3500",
"remarks": "Prefers ground floor, needs parking"
},
"property": {
"title": "Modern 4.5 Room Apartment in Geneva",
"address": "Rue du Mont-Blanc 12, 1201 Geneva",
"price": 3100,
"type": "rent",
"description": "Beautiful renovated 95m2 apartment, bright, lake view.",
"features": ["Lake view", "Parking", "Cellar", "Balcony", "Equipped kitchen"],
"area": 95,
"rooms": 4.5,
"bathrooms": 2,
"terrace": 12
},
"agent_config": {
"agent_name": "Sarah",
"agency_name": "Avendo Real Estate",
"transfer_number": "+41791234000",
"notification_email": "notifications@avendo.ch",
"hangup_instruction": "Thank you for your interest. We will follow up by email within 24 hours. Have a great day!",
"qualification_criteria": [
{
"question": "What is your maximum monthly budget?",
"type": "number",
"expectedValue": "3100",
"eliminatory": true,
"rejectionMessage": "Unfortunately, the budget requirement for this property is 3100 CHF minimum."
},
{
"question": "Do you have a stable job?",
"type": "yes_no",
"expectedValue": "yes",
"eliminatory": true,
"rejectionMessage": "A stable employment is required for this property."
},
{
"question": "How many people will occupy the apartment?",
"type": "number",
"expectedValue": "1-4",
"eliminatory": false
}
],
"transfer_conditions": [
"Lead wants to negotiate the price",
"Lead asks about financing options",
"Lead mentions having a pet"
]
},
"viewing_slots": [
{
"date": "2026-03-20",
"startTime": "14:00",
"endTime": "15:00",
"maxBookings": 3
},
{
"date": "2026-03-21",
"startTime": "10:00",
"endTime": "11:00",
"maxBookings": 2
},
{
"date": "2026-03-22",
"startTime": "16:00",
"endTime": "17:00",
"maxBookings": 1
}
],
"callback_webhook": "https://your-api.com/webhooks/immo-calls",
"external_ref": "LEAD-12345-GENEVA"
}'Response:
{
"callId": "a91b4f0f-00bc-429f-bac8-2ec0969aa214",
"status": "initiated",
"externalRef": "LEAD-001",
"roomName": "immolab-call-..."
}Poll for Results
#!/bin/bash
CALL_ID="your-call-id"
API_KEY="your-api-key"
while true; do
RESULT=$(curl -s "https://immo.next-lab.tech/api/v1/calls/$CALL_ID" \
-H "x-api-key: $API_KEY")
STATUS=$(echo $RESULT | jq -r '.status')
echo "Status: $STATUS"
if [ "$STATUS" = "completed" ] || [ "$STATUS" = "failed" ]; then
echo $RESULT | jq .
break
fi
sleep 10
doneTrigger Call
/api/v1/calls/triggerTrigger an outbound AI voice call to qualify a lead.
Lead
| Parameter | Type | Required | Description |
|---|---|---|---|
| lead.firstName | string | required | First name (alias: prenom) |
| lead.lastName | string | required | Last name (alias: nom) |
| lead.phone | string | required | Phone number, intl format e.g. +41791234567 (alias: telephone) |
| lead.email | string | required | Email address |
| lead.language | string | required | "de", "fr", "en", or "nl" |
| lead.budget | string | optional | Lead's stated budget |
| lead.remarks | string | optional | Additional notes for the agent |
First name (alias: prenom)
Last name (alias: nom)
Phone number, intl format e.g. +41791234567 (alias: telephone)
Email address
"de", "fr", "en", or "nl"
Lead's stated budget
Additional notes for the agent
Property
| Parameter | Type | Required | Description |
|---|---|---|---|
| property.title | string | required | Property title |
| property.address | string | required | Full address |
| property.price | number | required | Price (rent per month or sale price) |
| property.type | string | optional | "sale" or "rent" (default: "sale") |
| property.description | string | required | Description the agent uses to answer property questions |
| property.features | string[] | optional | Array of features/amenities |
| property.area | number | optional | Living area in m² |
| property.rooms | number | optional | Number of rooms (e.g. 4.5) |
| property.bathrooms | number | optional | Number of bathrooms |
| property.terrace | number | optional | Terrace/balcony area in m² |
Property title
Full address
Price (rent per month or sale price)
"sale" or "rent" (default: "sale")
Description the agent uses to answer property questions
Array of features/amenities
Living area in m²
Number of rooms (e.g. 4.5)
Number of bathrooms
Terrace/balcony area in m²
Agent Configuration
| Parameter | Type | Required | Description |
|---|---|---|---|
| agent_config.agent_name | string | optional | AI persona name (e.g. "Sarah"). Used in greeting: "Hello, this is Sarah from..." |
| agent_config.agency_name | string | optional | Company/agency name (default: "Next Lab"). Used in greeting: "...from Your Agency" |
| agent_config.qualification_criteria | array | optional | Questions to qualify the lead (see Scoring) |
| agent_config.transfer_number | string | optional | Phone to transfer on complex questions |
| agent_config.transfer_conditions | array<string> | optional | Custom conditions that trigger transfer (in addition to defaults) |
| agent_config.notification_email | string | optional | Email to receive booking/call notifications |
| agent_config.hangup_instruction | string | optional | Custom farewell message when ending the call (max 1000 chars) |
AI persona name (e.g. "Sarah"). Used in greeting: "Hello, this is Sarah from..."
Company/agency name (default: "Next Lab"). Used in greeting: "...from Your Agency"
Questions to qualify the lead (see Scoring)
Phone to transfer on complex questions
Custom conditions that trigger transfer (in addition to defaults)
Email to receive booking/call notifications
Custom farewell message when ending the call (max 1000 chars)
Qualification Criteria
| Parameter | Type | Required | Description |
|---|---|---|---|
| question | string | required | The question the agent asks |
| type | string | optional | "yes_no", "number", "text", or "multiple_choice" (default: "yes_no") |
| expectedValue | string | required | Expected answer (see Scoring for evaluation rules) |
| eliminatory | boolean | optional | If true, wrong answer = disqualification (default: false) |
| rejectionMessage | string | optional | Custom message shown when criterion fails (deprecated - agent now continues all questions) |
| options | string[] | optional | Available choices for "multiple_choice" type criteria |
| order | number | optional | Display order (0-indexed, default: array order) |
The question the agent asks
"yes_no", "number", "text", or "multiple_choice" (default: "yes_no")
Expected answer (see Scoring for evaluation rules)
If true, wrong answer = disqualification (default: false)
Custom message shown when criterion fails (deprecated - agent now continues all questions)
Available choices for "multiple_choice" type criteria
Display order (0-indexed, default: array order)
Viewing Slots
| Parameter | Type | Required | Description |
|---|---|---|---|
| viewing_slots[].date | string | required | Date in YYYY-MM-DD format |
| viewing_slots[].startTime | string | required | Start time in HH:MM format |
| viewing_slots[].endTime | string | required | End time in HH:MM format |
| viewing_slots[].maxBookings | number | optional | Max bookings per slot (default: 1) |
Date in YYYY-MM-DD format
Start time in HH:MM format
End time in HH:MM format
Max bookings per slot (default: 1)
Advanced Options
| Parameter | Type | Required | Description |
|---|---|---|---|
| callback_webhook | string | optional | URL to POST results when call completes |
| external_ref | string | optional | Your unique reference ID (duplicates rejected with 409) |
URL to POST results when call completes
Your unique reference ID (duplicates rejected with 409)
Get Call Result
/api/v1/calls/{callId}Retrieve full results of a call including score, criteria, and transcript.
curl "https://immo.next-lab.tech/api/v1/calls/CALL_ID" \
-H "x-api-key: YOUR_API_KEY"Response:
{
"id": "a91b4f0f-00bc-429f-bac8-2ec0969aa214",
"status": "completed",
"externalRef": "LEAD-001",
"leadName": "Jean Dupont",
"leadPhone": "+41791234567",
"leadEmail": "jean@example.com",
"startedAt": "2026-03-07T21:55:47.227Z",
"endedAt": "2026-03-07T21:58:04.040Z",
"duration": "133s",
"outcome": null,
"score": 85,
"qualified": true,
"criteriaResults": [
{
"question": "Quel est votre budget mensuel?",
"answer": "environ 3200",
"passed": true,
"eliminatory": true
},
{
"question": "Avez-vous un emploi stable?",
"answer": "Oui, absolument",
"passed": true,
"eliminatory": true
}
],
"transcript": [
{ "role": "agent", "text": "Bonjour, ici Your Agency...", "timestamp": "..." },
{ "role": "user", "text": "Oui, bonjour", "timestamp": "..." }
],
"toolCallsLog": [
{ "timestamp": "...", "tool": "save_criterion_result" },
{ "timestamp": "...", "tool": "book_property_viewing" }
]
}List Calls
/api/v1/callsList all calls for your API key with optional filters.
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | number | optional | Max results, default 50, max 100 |
| offset | number | optional | Pagination offset, default 0 |
| status | string | optional | Filter by status: pending, ringing, in_progress, completed, failed |
Max results, default 50, max 100
Pagination offset, default 0
Filter by status: pending, ringing, in_progress, completed, failed
curl "https://immo.next-lab.tech/api/v1/calls?limit=20&status=completed" \
-H "x-api-key: YOUR_API_KEY"Response:
{
"calls": [
{
"id": "a91b4f0f-...",
"status": "completed",
"externalRef": "LEAD-001",
"leadName": "Jean Dupont",
"leadPhone": "+41791234567",
"score": 85,
"qualified": true,
"startedAt": "2026-03-07T21:55:47.227Z",
"duration": "133s",
"criteriaResults": [...]
}
],
"total": 14,
"limit": 50,
"offset": 0
}Usage Stats
/api/v1/usageGet your API key usage statistics, remaining calls, and qualification rates.
curl "https://immo.next-lab.tech/api/v1/usage" \
-H "x-api-key: YOUR_API_KEY"Response:
{
"totalCalls": 14,
"maxCalls": 31,
"remaining": 17,
"byStatus": {
"completed": 12,
"failed": 2
},
"activeCalls": [],
"averageScore": 78,
"qualifiedCount": 9
}Webhooks
When a call completes, if you provided callback_webhook, we POST the full results to your endpoint.
Method: POST
Content-Type: application/json
Timeout: 10 seconds
Fallback: If your endpoint is unreachable, results are always available via GET /api/v1/calls/{callId}
Webhook payload:
{
"event": "call.completed",
"callId": "a91b4f0f-00bc-429f-bac8-2ec0969aa214",
"externalRef": "LEAD-001",
"status": "completed",
"duration": "133s",
"leadName": "Jean Dupont",
"leadPhone": "+41791234567",
"leadEmail": "jean@example.com",
"score": 85,
"qualified": true,
"criteriaResults": [
{
"criterionId": "...",
"question": "Quel est votre budget mensuel?",
"answer": "environ 3200",
"passed": true,
"eliminatory": true
}
],
"transcript": [
{
"role": "agent",
"text": "Bonjour, ici Your Agency...",
"timestamp": "2026-03-07T21:55:50.000Z"
}
],
"completedAt": "2026-03-07T21:58:04.040Z"
}Tip: Use webhook.site to test webhook delivery during development.
Scoring System
Criteria answers are evaluated server-side (deterministic, not by AI). The score is calculated automatically after the call.
Evaluation Rules
| Type | expectedValue | Rule | Example |
|---|---|---|---|
| number | Single value, e.g. "3100" | Answer >= expected = PASS | Lead says "3200", expected "3100" → PASS |
| number | Range, e.g. "1-4" | Answer within range = PASS | Lead says "3" → PASS, "6" → FAIL |
| yes_no | "oui" or "non" | Detects positive/negative words (FR/EN/DE) | "Oui, absolument" → positive → matches "oui" |
| text | Free text | AI evaluates subjectively | -- |
Points & Qualification
Eliminatory criterion
Non-eliminatory criterion
score = (earned_points / max_possible_points) * 100
qualified = all eliminatory passed AND score >= 60%
Example: 2 eliminatory + 1 non-eliminatory = max 70 pts
The score adapts proportionally to the number of criteria. 2 criteria or 10 criteria — the percentage always reflects the match quality.
Number Formats
The server handles international number formats automatically:
3 200
FR (space)
3'200
CH (apostrophe)
3.200
DE (period)
3,200
EN (comma)
All formats are normalized to 3200 before comparison.
Supported Languages
FR
Francais
"fr"
DE
Deutsch
"de"
EN
English
"en"
NL
Nederlands
"nl"
Set via lead.language. The agent speaks, asks questions, and sends SMS/email confirmations in the selected language. Yes/no evaluation also detects positive/negative words in all supported languages.
Error Codes
| Status | Meaning | Example |
|---|---|---|
| 400 | Validation error | Missing or invalid fields in request body |
| 401 | Unauthorized | Missing or invalid API key |
| 403 | Forbidden | Usage limit exceeded or API key call limit reached |
| 404 | Not found | Call ID does not exist or belongs to another key |
| 409 | Conflict | Duplicate external_ref (already used in another call) |
| 429 | Rate limited | More than 10 requests/minute |
| 500 | Internal error | Server error — contact support |
Error response format:
{
"error": "Validation error",
"details": [
{
"path": ["lead", "telephone"],
"message": "String must contain at least 7 character(s)"
}
]
}Need help?
abdullah@next-lab.techImmoLab API by Next Lab
next-lab.tech