Leads API
The Leads API provides endpoints for capturing contact form submissions, scheduling property tours, and managing leads. The system automatically routes leads to the correct brokerage based on the request domain.
Contact Form Submission
Section titled “Contact Form Submission”Submit a contact form inquiry. The brokerage is automatically determined from the request domain.
POST /api/contactRequest Body
Section titled “Request Body”{ "firstName": "John", "lastName": "Smith", "email": "john.smith@example.com", "phone": "208-555-0123", "subject": "Interested in property at 123 Main St", "message": "I'd like to learn more about this listing...", "source_page": "/property/202412345", "utm_source": "google", "utm_medium": "organic", "utm_campaign": "spring-2024"}Fields
Section titled “Fields”| Field | Type | Required | Description |
|---|---|---|---|
firstName | string | Yes | Contact’s first name |
lastName | string | Yes | Contact’s last name |
email | string | Yes | Valid email address |
phone | string | No | Phone number |
subject | string | Yes | Message subject |
message | string | Yes | Full message content |
source_page | string | No | Page URL where form was submitted |
utm_source | string | No | UTM tracking: source |
utm_medium | string | No | UTM tracking: medium |
utm_campaign | string | No | UTM tracking: campaign |
Response
Section titled “Response”{ "success": true, "message": "Thank you for your message! We'll get back to you within 24 hours.", "lead_id": 456}Get Leads Count
Section titled “Get Leads Count”Get a summary of lead counts by status for the current domain’s brokerage.
GET /api/leads/countResponse
Section titled “Response”{ "new": 12, "contacted": 8, "qualified": 5, "converted": 3, "total": 28}Create Lead (Admin)
Section titled “Create Lead (Admin)”Create a lead manually from the admin UI. Distinct from POST /api/contact:
no Host-header brokerage lookup, no semantic-routing agent assignment.
Staff supply their own context — including which agent to assign.
Used for walk-ins, phone calls, and any other inbound interest that didn’t come through a website form.
POST /api/v1/admin/leadsScoping
Section titled “Scoping”- Non-admin users (brokers, agents, brokerage_owner) always
create leads under their own
user.brokerage_id. Anybrokerage_idin the request body is ignored. - Platform admins may target any brokerage by passing
brokerage_idin the body, or omit it to default to their own. TheLeadAddDialogin/admin/leadsrenders a brokerage picker specifically for this case. - An unbrokeraged non-admin user gets a
400 Bad Requestrather than silently leaking into a default tenant.
Request Body
Section titled “Request Body”{ "first_name": "Jane", "last_name": "Doe", "email": "jane@example.com", "phone": "208-555-0123", "subject": "Walk-in: interested in downtown condos", "message": "Came into the office. Pre-approved up to $450k. Wants something within walking distance of the river.", "assigned_agent_id": 42, "status": "contacted", "notes": "Followed up by phone same day.", "brokerage_id": 2}Fields
Section titled “Fields”| Field | Type | Required | Description |
|---|---|---|---|
first_name | string | Yes | Lead’s first name |
last_name | string | Yes | Lead’s last name |
email | string | Yes | Valid email address |
phone | string | No | Phone number |
subject | string | Yes | Short summary |
message | string | Yes | Full notes from intake |
assigned_agent_id | integer | No | Agent to assign — must belong to the resolved brokerage |
status | string | No | Defaults to "new". Set to e.g. "contacted" for already-underway intakes |
notes | string | No | Internal staff notes |
brokerage_id | integer | No | Admin only. Target brokerage. Ignored for non-admin users |
Response
Section titled “Response”201 Created returns the full lead detail object (same shape as GET /api/v1/admin/leads/{lead_id}).
Manual-admin leads are tagged with source_domain: "admin-manual"
and source_page: "/admin/leads" so analytics dashboards can split
organic conversions from staff-entered ones.
Error Responses
Section titled “Error Responses”| Status | Detail | When |
|---|---|---|
400 | No brokerage context — cannot create lead | Admin omitted brokerage_id and has no own brokerage; or non-admin user has no brokerage_id set |
400 | Agent {id} does not belong to brokerage {id} | assigned_agent_id belongs to a different tenant |
404 | Brokerage not found or disabled | brokerage_id doesn’t exist or has been soft-deleted |
Tour Requests
Section titled “Tour Requests”Schedule property viewing tours.
Request a Tour
Section titled “Request a Tour”POST /api/toursRequest Body
Section titled “Request Body”{ "listing_id": "202412345", "name": "Jane Doe", "email": "jane@example.com", "phone": "208-555-0456", "preferred_date": "2024-12-30", "preferred_time": "10:00 AM", "message": "I'm interested in seeing this property. Is it available Saturday morning?"}Fields
Section titled “Fields”| Field | Type | Required | Description |
|---|---|---|---|
listing_id | string | Yes | MLS listing ID to tour |
name | string | Yes | Requester’s full name |
email | string | Yes | Contact email |
phone | string | No | Contact phone |
preferred_date | string | No | Preferred tour date (YYYY-MM-DD) |
preferred_time | string | No | Preferred time slot |
message | string | No | Additional notes |
Response
Section titled “Response”{ "id": 789, "listing_id": "202412345", "name": "Jane Doe", "email": "jane@example.com", "status": "pending", "created_at": "2024-12-28T15:30:00Z"}List Tour Requests
Section titled “List Tour Requests”Get tour requests for the authenticated user’s brokerage.
GET /api/toursQuery Parameters
Section titled “Query Parameters”| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status (pending, confirmed, completed, cancelled) |
listing_id | string | Filter by property |
Response
Section titled “Response”[ { "id": 789, "listing_id": "202412345", "name": "Jane Doe", "email": "jane@example.com", "phone": "208-555-0456", "preferred_date": "2024-12-30", "preferred_time": "10:00 AM", "status": "pending", "created_at": "2024-12-28T15:30:00Z" }]Get Tour Request
Section titled “Get Tour Request”GET /api/tours/{request_id}Newsletter Subscription
Section titled “Newsletter Subscription”Subscribe to a brokerage’s email newsletter.
Subscribe
Section titled “Subscribe”POST /api/newsletter/subscribeRequest Body
Section titled “Request Body”{ "email": "subscriber@example.com", "first_name": "John", "interests": ["new-listings", "market-updates"]}Response
Section titled “Response”{ "success": true, "message": "Successfully subscribed to newsletter"}Lead Status Values
Section titled “Lead Status Values”Leads progress through a standard pipeline:
| Status | Description |
|---|---|
new | Just received, not yet reviewed |
contacted | Initial contact made |
qualified | Lead is qualified/interested |
showing | Scheduled for property showings |
offer | Making or negotiating offers |
converted | Successfully closed |
lost | Lead was lost or not interested |
Error Responses
Section titled “Error Responses”Missing Host Header
Section titled “Missing Host Header”{ "detail": "Missing Host header"}Status: 400 Bad Request
Brokerage Not Found
Section titled “Brokerage Not Found”{ "detail": "Brokerage not found"}Status: 404 Not Found
Validation Error
Section titled “Validation Error”{ "detail": [ { "loc": ["body", "email"], "msg": "value is not a valid email address", "type": "value_error.email" } ]}Status: 422 Unprocessable Entity
Related
Section titled “Related”- Integrating Contact Forms — Frontend implementation guide
- Multi-Tenant Routing — How domain-based routing works
- API Overview — General API information