Leads API
The Leads API is representative of every entity endpoint in BottleCRM — same shape, same conventions. Once you know this one, Contacts / Accounts / Opportunities / Tasks all behave identically.
Base URL
https://<your-host>/api/leads/
All requests require a valid bearer token (see Authentication).
List leads
GET /api/leads/
Authorization: Bearer <token>
Response:
{
"count": 142,
"next": "https://…/api/leads/?page=2",
"previous": null,
"results": [
{
"id": "ab12-cd34-…",
"title": "Acme renewal interest",
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@acme.com",
"status": "qualified",
"source": "website_form",
"assigned_to": { "id": "…", "email": "rep@yours.com" },
"custom_fields": { "industry": "saas" },
"created_at": "2026-04-01T10:14:33Z"
}
]
}
Filtering
GET /api/leads/?status=qualified&source=website_form
GET /api/leads/?search=acme
GET /api/leads/?assigned_to=<profile_id>
GET /api/leads/?cf_industry=saas # custom field filter
GET /api/leads/?ordering=-created_at
Filterable fields: status, source, assigned_to, tags, created_at__gte, created_at__lte, plus any custom field marked filterable.
Create a lead
POST /api/leads/
Content-Type: application/json
Authorization: Bearer <token>
{
"title": "Acme renewal interest",
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@acme.com",
"phone": "+1 555 1234",
"status": "new",
"source": "website_form",
"custom_fields": { "industry": "saas", "bant_score": 72 }
}
201 Created returns the full record. Server-derived fields — org, created_by, id, created_at — must not be in the request body; they are populated by the view.
Retrieve / update / delete
GET /api/leads/<id>/
PATCH /api/leads/<id>/ # partial update — only fields in the body change
PUT /api/leads/<id>/ # full update
DELETE /api/leads/<id>/ # soft-deletes
The detail GET response also includes custom_field_definitions so the frontend can render the right form fields without a second round-trip.
Convert a lead
POST /api/leads/<id>/convert/
{
"account_name": "Acme Corp",
"create_opportunity": true,
"opportunity_name": "Acme — Annual 2026",
"amount": 24000
}
The convert endpoint creates an Account, a Contact, and (optionally) an Opportunity in a single transaction, links them, and marks the lead as converted. If any step fails, the whole operation rolls back.
Bulk CSV upload
POST /api/leads/upload/
Content-Type: multipart/form-data
file=<leads.csv>
Response contains created, updated, and errors arrays — one entry per CSV row. The endpoint de-duplicates on email within the org.
Errors
| Status | Meaning |
|---|---|
400 |
Validation failed. Body: { "errors": { "field": "reason" } } |
401 |
Missing or invalid token |
403 |
Authenticated but not authorized for this record/role |
404 |
Record does not exist or belongs to another org |
429 |
Rate limit exceeded |
See Errors for the full structure.