REST API Documentation

Programmatically create leads, read quotes, send SMS, manage appointments, and query your pipeline — all authenticated with scoped API keys.

Base URL: https://api.quoteleadshq.com/v1

Authentication

All API requests must include a Bearer token in the Authorization header. Create API keys from the Integrations page in your dashboard. Tokens are hashed with SHA-256 — the raw key is only shown once at creation time.

Request curl https://api.quoteleadshq.com/v1/leads \ -H "Authorization: Bearer qlhq_your_api_token_here"

If the token is invalid, revoked, or expired the API returns 401 Unauthorized.

Rate Limiting

API requests are rate-limited to 100 requests per minute per company. If you exceed the limit, the API returns 429 Too Many Requests. Wait and retry after the window resets.

Pagination

All list endpoints support pagination via query parameters. The response includes a meta object with the total count.

ParameterTypeDefaultDescription
pageinteger1Page number (starts at 1)
per_pageinteger25Items per page (max 100)
Response Shape { "data": [ ... ], "meta": { "page": 1, "per_page": 25, "total": 142 } }

API Scopes

Each API token is created with specific scopes that control what resources it can access. If a token lacks the required scope, the API returns 403 Forbidden.

leads:read
List and get leads
leads:write
Create and update leads
quotes:read
List and get quotes
sms:send
Send SMS messages
appointments:read
List appointments
pipeline:read
Get pipeline summary

Leads

GET /leads List all leads

Returns a paginated list of leads for your company. Supports filtering by pipeline stage, AI status, and text search.

leads:read

Query Parameters

ParameterTypeDescription
pageoptionalintegerPage number (default 1)
per_pageoptionalintegerResults per page, max 100 (default 25)
stageoptionalstringFilter by pipeline stage: new_lead, follow_up, quote_in_progress, quoted, closed_won, closed_lost
ai_statusoptionalstringFilter by AI status: hot, warm, cold, new
searchoptionalstringSearch by name, phone, or email (case-insensitive)

Example Request

curl "https://api.quoteleadshq.com/v1/leads?stage=new_lead&per_page=10" \ -H "Authorization: Bearer qlhq_..."

Example Response

{ "data": [ { "id": "uuid-here", "name": "Sarah Denton", "email": "sarah@example.com", "phone": "+61400000000", "pipeline_stage": "new_lead", "ai_status": "warm", "ai_score": 62, "source": "website", "created_at": "2026-04-01T10:00:00Z" } ], "meta": { "page": 1, "per_page": 10, "total": 47 } }
POST /leads Create a new lead

Creates a new lead for your company. At least name or phone is required. Fires a lead.created webhook event.

leads:write

Request Body (JSON)

FieldTypeDescription
nameoptionalstringFull name of the lead
first_nameoptionalstringFirst name
last_nameoptionalstringLast name
emailoptionalstringEmail address
phoneoptionalstringPhone number (E.164 format)
pipeline_stageoptionalstringInitial pipeline stage (default: new_lead)
sourceoptionalstringLead source (e.g. "website", "zapier")
custom_dataoptionalobjectCustom key-value data
metadataoptionalobjectAdditional metadata

Example Request

curl -X POST "https://api.quoteleadshq.com/v1/leads" \ -H "Authorization: Bearer qlhq_..." \ -H "Content-Type: application/json" \ -d '{ "name": "Marcus Webb", "phone": "+61412345678", "email": "marcus@example.com", "source": "website_form" }'

Example Response (201)

{ "data": { "id": "c3f0e8a2-...", "name": "Marcus Webb", "phone": "+61412345678", "email": "marcus@example.com", "pipeline_stage": "new_lead", "source": "website_form", "created_at": "2026-04-03T10:30:00Z" } }
GET /leads/:id Get a single lead

Returns the full details of a single lead by ID. Returns 404 if the lead doesn't exist or belongs to a different company.

leads:read

Example Request

curl "https://api.quoteleadshq.com/v1/leads/c3f0e8a2-..." \ -H "Authorization: Bearer qlhq_..."

Example Response

{ "data": { "id": "c3f0e8a2-...", "name": "Marcus Webb", "phone": "+61412345678", "email": "marcus@example.com", "pipeline_stage": "new_lead", "ai_status": "warm", "ai_score": 62, "ai_summary": "Interested in roof restoration...", "source": "website_form", "created_at": "2026-04-03T10:30:00Z" } }
PATCH /leads/:id Update a lead

Updates an existing lead. Only include the fields you want to change. Fires a lead.updated webhook event.

leads:write

Request Body (JSON)

FieldTypeDescription
nameoptionalstringFull name
first_nameoptionalstringFirst name
last_nameoptionalstringLast name
emailoptionalstringEmail address
phoneoptionalstringPhone number
pipeline_stageoptionalstringPipeline stage
sourceoptionalstringLead source
ai_enabledoptionalbooleanEnable/disable AI for this lead
custom_dataoptionalobjectCustom data
metadataoptionalobjectMetadata

Example Request

curl -X PATCH "https://api.quoteleadshq.com/v1/leads/c3f0e8a2-..." \ -H "Authorization: Bearer qlhq_..." \ -H "Content-Type: application/json" \ -d '{ "pipeline_stage": "follow_up" }'

Quotes

GET /quotes List all quotes

Returns a paginated list of quotes. Optionally filter by status.

quotes:read

Query Parameters

ParameterTypeDescription
statusoptionalstringFilter by status: draft, sent, viewed, accepted, declined
pageoptionalintegerPage number
per_pageoptionalintegerResults per page

Example Response

{ "data": [ { "id": "uuid-here", "quote_number": "Q-00042", "status": "sent", "subtotal": 9400, "tax": 940, "total": 10340, "lead_id": "lead-uuid", "created_at": "2026-04-02T14:00:00Z", "sent_at": "2026-04-02T14:30:00Z", "viewed_at": "2026-04-02T15:10:00Z" } ], "meta": { "page": 1, "per_page": 25, "total": 12 } }
GET /quotes/:id Get a single quote

Returns full details of a quote including line items and metadata.

quotes:read

Example Response

{ "data": { "id": "uuid-here", "quote_number": "Q-00042", "status": "sent", "subtotal": 9400, "tax": 940, "total": 10340, "line_items": [ { "description": "Kitchen bench-top removal", "quantity": 1, "unit_price": 2400 }, { "description": "Stone bench-top install", "quantity": 1, "unit_price": 7000 } ], "metadata": { "abn": "12 345 678 901", "deposit_terms": "50% upfront" }, "lead_id": "lead-uuid", "created_at": "2026-04-02T14:00:00Z" } }

SMS

POST /sms Send an SMS

Send an SMS message to a lead via your company's configured Twilio number. The message is stored in the conversation thread. SMS credits are deducted automatically.

sms:send

Request Body (JSON)

FieldTypeDescription
lead_idrequiredstringUUID of the lead to message
messagerequiredstringSMS body text

Example Request

curl -X POST "https://api.quoteleadshq.com/v1/sms" \ -H "Authorization: Bearer qlhq_..." \ -H "Content-Type: application/json" \ -d '{ "lead_id": "c3f0e8a2-...", "message": "Hi Marcus, just following up on your enquiry!" }'

Example Response

{ "success": true, "message": "SMS sent" }

Error Responses

StatusDescription
400Missing lead_id or message, or lead has no phone
402Insufficient SMS credits
404Lead not found or belongs to different company

Appointments

GET /appointments List appointments

Returns a paginated list of appointments, including the associated lead details. Optionally filter by type.

appointments:read

Query Parameters

ParameterTypeDescription
typeoptionalstringFilter by appointment type: callback, onsite
pageoptionalintegerPage number
per_pageoptionalintegerResults per page

Example Response

{ "data": [ { "id": "uuid-here", "title": "Callback: Sarah Denton", "status": "scheduled", "appointment_type": "callback", "start_time": "2026-04-04T09:00:00Z", "end_time": "2026-04-04T09:30:00Z", "booked_by": "ai", "leads": { "name": "Sarah Denton", "phone": "+61400000000", "email": "sarah@example.com" } } ], "meta": { "page": 1, "per_page": 25, "total": 8 } }

Pipeline

GET /pipeline Pipeline summary

Returns the count of leads in each pipeline stage for your company.

pipeline:read

Example Response

{ "data": { "new_lead": 12, "follow_up": 8, "quote_in_progress": 3, "quoted": 5, "closed_won": 22, "closed_lost": 7 } }

Webhooks

Configure webhook endpoints in your dashboard under Integrations → Webhooks. When events occur, QuoteLeadsHQ sends a POST request to your URL with the event payload. Each delivery is signed with HMAC-SHA256 using your webhook secret.

Verify the signature by computing HMAC-SHA256(webhook_secret, request_body) and comparing it to the X-Webhook-Signature header. The event type is in the X-Webhook-Event header.

Available Events

lead.createdA new lead was created (via API, SMS, or dashboard)
lead.updatedA lead was updated (stage change, field edit, etc.)
quote.acceptedA quote was accepted by the customer
quote.declinedA quote was declined by the customer
appointment.bookedAn appointment was booked (callback or on-site visit)
sale.completedA deal was marked as closed/won

Webhook Payload

{ "event": "lead.created", "timestamp": "2026-04-03T10:30:00.000Z", "data": { "id": "c3f0e8a2-...", "name": "Marcus Webb", "phone": "+61412345678", // ... full resource object } }

Signature Verification (Node.js example)

const crypto = require('crypto'); function verifyWebhook(body, signature, secret) { const expected = crypto .createHmac('sha256', secret) .update(body) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected) ); }

Error Codes

The API uses standard HTTP status codes. All error responses include an error field with a human-readable message.

{ "error": "Lead not found" }
200Success
201Created
400Bad Request — missing or invalid params
401Unauthorized — invalid or expired token
402Payment Required — no SMS credits
403Forbidden — insufficient scope
404Not Found — resource doesn't exist
429Rate Limited — too many requests
500Server Error — internal failure