Leads
Sales leads CRUD plus activities (followups), dedicated stage/assignee routes, and statuses. Lead payloads include assigned_to (email) plus assignee: { email, name }.
Activities = followups
Activities are followups rows with lead_id (no separate lead_activities table).
Still UI-only: calendar ICS/RSVP, work-queue side effects on call log, lead convert wizard, email templates/import, company/contact followups CRUD as a public module, reschedule-request/cancel meeting email flows.
Quickstart
Base URL (canonical)
https://api.strackerapp.com/v1/
Same gateway also answers under /api/v1 on strackerapp.com. Prefer the api. subdomain. Do not use api.stracker.io (not wired; returns 403).
Environments (keep separate)
| Env | Base | Keys |
|---|---|---|
| Production | https://api.strackerapp.com/v1 | sk_prod_… only |
| Local smoke | http://127.0.0.1:8888/api/v1 | sk_dev_… only |
Never mix prod keys with local, or local keys with prod.
Auth (all endpoints)
Org-scoped Bearer API key. Agent keys act as the agent with reports_to ACL inheritance.
Authorization: Bearer sk_REPLACE
Content-Type: application/json
# Optional alternate header:
# X-Stracker-Api-Key: sk_REPLACE
Response envelope
{
"success": true,
"data": { /* endpoint-specific payload */ }
}
Errors
Failed responses always have success: false and an error object with a stable code and a human-readable message:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "company_name is required"
}
}
| HTTP status | When | What to do |
|---|---|---|
400 | Validation failed (missing required fields, bad enum, migration not applied for company/contact notes, assignee not in org, slot unavailable). | Read error.message; fix the payload. |
401 | Missing or invalid Authorization / X-Stracker-Api-Key. | Check the API key; confirm the header is present. |
403 | Key is valid but lacks permission (e.g. non-admin calling agent admin endpoints), or org scope mismatch. | Use an org-admin key, or confirm the resource belongs to the key's org. |
404 | Resource not found or not visible to this org. | Verify the id and org scoping. |
500 | Unexpected server error. | Retry with backoff; report if persistent. |
Leads
Endpoints at a glance
| Action | Method + Path | Summary |
|---|---|---|
| List leads | GET /leads | List sales leads in the org. |
| Create a lead | POST /leads | Create a sales lead. Provide company_name and/or first_name/last_name. Optional note_id for UI-style auto-save of the actor’s own Text fo… |
| Get a lead | GET /leads/{id} | Fetch a single sales lead including assignee join. |
| Update a lead | PATCH/PUT /leads/{id} | Update lead fields. deal_stage_id syncs denormalized status to the stage name (same as UI). assigned_to must be an active org member emai… |
| List lead statuses / stages | GET /leads/statuses | List deal stages available for leads. |
| Set lead stage | POST/PATCH /leads/{id}/stage | Move a lead to a deal stage. Body: {"deal_stage_id": N}. Equivalent to PATCH lead with deal_stage_id. |
| Set lead assignee | POST/PATCH /leads/{id}/assignee | Assign a lead to an org member. Accepts assigned_to (email), user_id, or org_user_id. Pass assigned_to: null to unassign. Rejects emails … |
| List lead activities | GET /leads/{id}/activities | Activity timeline (followups). Filter: all|pending|completed|upcoming|past. |
| Create lead activity | POST /leads/{id}/activities | Create Call/Text/Meeting/Email/…. Optional deal_stage_id moves the stage. |
| Get lead activity | GET /leads/{id}/activities/{activityId} | Fetch a single activity. |
| Update lead activity | PATCH/PUT /leads/{id}/activities/{activityId} | Update notes/date/outcome, or set action=complete|uncomplete|reschedule|delete. Reschedule bumps event_sequence and does not send ICS. |
| Delete lead activity | DELETE /leads/{id}/activities/{activityId} | Same as action=delete. Does not send a calendar cancel email. |
GET /leads
List sales leads in the org.
Query parameters
| Field | Type | Required | Description |
|---|---|---|---|
q |
string |
no | Free-text search |
status |
string |
no | Status / stage name filter |
deal_stage_id |
integer |
no | Filter by deal stage id |
assigned_to |
string |
no | Assignee email |
limit |
integer |
no | Page size Default: 50. |
offset |
integer |
no | Offset |
Request example
curl -sS "https://api.strackerapp.com/v1/leads?limit=5" \
-H "Authorization: Bearer sk_REPLACE"
Response 200 OK
{
"success": true,
"data": {
"leads": [
{
"id": 1,
"company_name": "Acme",
"email": "[email protected]",
"deal_stage_id": 2,
"assigned_to": "[email protected]",
"assignee": { "email": "[email protected]", "name": "Alex Rep" }
}
],
"pagination": { "limit": 5, "offset": 0 }
}
}
POST /leads
Create a sales lead. Provide company_name and/or first_name/last_name. Optional note_id for UI-style auto-save of the actor’s own Text followup.
Body parameters
| Field | Type | Required | Description |
|---|---|---|---|
company_name |
string |
no | Company name |
first_name |
string |
no | Contact first name |
last_name |
string |
no | Contact last name |
email |
string |
no | |
phone |
string |
no | Phone |
website |
string |
no | Website |
channel_id |
integer |
no | Lead channel id |
deal_stage_id |
integer |
no | Initial stage |
assigned_to |
string |
no | Org-member email |
notes |
string |
no | Initial notes |
company_id |
integer |
no | Link to CRM company |
note_id |
integer |
no | Optional Text followup auto-save id |
Request example
curl -sS https://api.strackerapp.com/v1/leads \
-H "Authorization: Bearer sk_REPLACE" \
-H "Content-Type: application/json" \
-d '{"company_name":"Acme","email":"[email protected]","deal_stage_id":1}'
Response 201 Created
{
"success": true,
"data": {
"lead": { "id": 2, "company_name": "Acme", "email": "[email protected]", "deal_stage_id": 1 }
}
}
GET /leads/{id}
Fetch a single sales lead including assignee join.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id |
integer |
yes | Lead id |
Request example
curl -sS https://api.strackerapp.com/v1/leads/1 \
-H "Authorization: Bearer sk_REPLACE"
Response 200 OK
{
"success": true,
"data": {
"lead": {
"id": 1,
"company_name": "Acme",
"assigned_to": "[email protected]",
"assignee": { "email": "[email protected]", "name": "Alex Rep" },
"deal_stage_id": 2
}
}
}
PATCH PUT /leads/{id}
Update lead fields. deal_stage_id syncs denormalized status to the stage name (same as UI). assigned_to must be an active org member email (or null to unassign).
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id |
integer |
yes | Lead id |
Body parameters
| Field | Type | Required | Description |
|---|---|---|---|
company_name |
string |
no | Company name |
first_name |
string |
no | First name |
last_name |
string |
no | Last name |
email |
string |
no | |
phone |
string |
no | Phone |
deal_stage_id |
integer |
no | Move stage |
assigned_to |
string |
no | Assignee email or null |
notes |
string |
no | Notes |
status |
string |
no | Status string (prefer deal_stage_id) |
Request example
curl -sS -X PATCH https://api.strackerapp.com/v1/leads/1 \
-H "Authorization: Bearer sk_REPLACE" \
-H "Content-Type: application/json" \
-d '{"deal_stage_id":2,"assigned_to":"[email protected]"}'
Response 200 OK
{
"success": true,
"data": {
"lead": { "id": 1, "deal_stage_id": 2, "assigned_to": "[email protected]" }
}
}
GET /leads/statuses
List deal stages available for leads.
Request example
curl -sS https://api.strackerapp.com/v1/leads/statuses \
-H "Authorization: Bearer sk_REPLACE"
Response 200 OK
{
"success": true,
"data": {
"statuses": [
{ "id": 1, "name": "New" },
{ "id": 2, "name": "Qualified" }
]
}
}
POST PATCH /leads/{id}/stage
Move a lead to a deal stage. Body: {"deal_stage_id": N}. Equivalent to PATCH lead with deal_stage_id.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id |
integer |
yes | Lead id |
Body parameters
| Field | Type | Required | Description |
|---|---|---|---|
deal_stage_id |
integer |
yes | Target stage id |
Request example
curl -sS -X POST https://api.strackerapp.com/v1/leads/1/stage \
-H "Authorization: Bearer sk_REPLACE" \
-H "Content-Type: application/json" \
-d '{"deal_stage_id":1}'
Response 200 OK
{
"success": true,
"data": {
"lead": { "id": 1, "deal_stage_id": 1, "status": "New" }
}
}
POST PATCH /leads/{id}/assignee
Assign a lead to an org member. Accepts assigned_to (email), user_id, or org_user_id. Pass assigned_to: null to unassign. Rejects emails that are not active org_users.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id |
integer |
yes | Lead id |
Body parameters
| Field | Type | Required | Description |
|---|---|---|---|
assigned_to |
string |
no | rep email; null to unassign |
user_id |
integer |
no | users.id |
org_user_id |
integer |
no | org_users.id |
Request example
curl -sS -X POST https://api.strackerapp.com/v1/leads/1/assignee \
-H "Authorization: Bearer sk_REPLACE" \
-H "Content-Type: application/json" \
-d '{"assigned_to":"[email protected]"}'
Response 200 OK
{
"success": true,
"data": {
"lead": { "id": 1, "assigned_to": "[email protected]", "assignee": { "email": "[email protected]", "name": "Alex Rep" } }
}
}
GET /leads/{id}/activities
Activity timeline (followups). Filter: all|pending|completed|upcoming|past.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id |
integer |
yes | Lead id |
Query parameters
| Field | Type | Required | Description |
|---|---|---|---|
filter |
string |
no | all|pending|completed|upcoming|past |
followup_type |
string |
no | Text|Call|Meeting|Email|… |
limit |
integer |
no | Page size Default: 50. |
offset |
integer |
no | Offset |
Request example
curl -sS "https://api.strackerapp.com/v1/leads/1/activities?limit=10" \
-H "Authorization: Bearer sk_REPLACE"
Response 200 OK
{
"success": true,
"data": {
"activities": [
{ "id": 55, "followup_type": "Text", "notes": "pinged", "completed": 0 }
]
}
}
POST /leads/{id}/activities
Create Call/Text/Meeting/Email/…. Optional deal_stage_id moves the stage.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id |
integer |
yes | Lead id |
Body parameters
| Field | Type | Required | Description |
|---|---|---|---|
followup_type |
string |
yes | Text|Call|Meeting|Email|… |
notes |
string |
no | Notes |
followup_date |
string |
no | YYYY-MM-DD HH:MM:SS |
call_outcome |
string |
no | e.g. connected |
call_duration_minutes |
integer |
no | Call length |
meeting_type |
string |
no | discovery_call|demo|follow_up_call|in_person |
duration_minutes |
integer |
no | Meeting duration |
deal_stage_id |
integer |
no | Optional stage move |
Request example
curl -sS https://api.strackerapp.com/v1/leads/1/activities \
-H "Authorization: Bearer sk_REPLACE" \
-H "Content-Type: application/json" \
-d '{"followup_type":"Text","notes":"smoke activity note"}'
Response 201 Created
{
"success": true,
"data": {
"activity": { "id": 56, "followup_type": "Text", "notes": "smoke activity note" }
}
}
GET /leads/{id}/activities/{activityId}
Fetch a single activity.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id |
integer |
yes | Lead id |
activityId |
integer |
yes | Activity (followup) id |
Request example
curl -sS https://api.strackerapp.com/v1/leads/1/activities/55 \
-H "Authorization: Bearer sk_REPLACE"
Response 200 OK
{
"success": true,
"data": {
"activity": { "id": 55, "followup_type": "Call", "call_outcome": "connected", "notes": "spoke 5m" }
}
}
PATCH PUT /leads/{id}/activities/{activityId}
Update notes/date/outcome, or set action=complete|uncomplete|reschedule|delete. Reschedule bumps event_sequence and does not send ICS.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id |
integer |
yes | Lead id |
activityId |
integer |
yes | Activity id |
Body parameters
| Field | Type | Required | Description |
|---|---|---|---|
notes |
string |
no | Notes |
followup_date |
string |
no | YYYY-MM-DD HH:MM:SS |
call_outcome |
string |
no | Call outcome |
action |
string |
no | complete|uncomplete|reschedule|delete |
Request example
curl -sS -X PATCH https://api.strackerapp.com/v1/leads/1/activities/55 \
-H "Authorization: Bearer sk_REPLACE" \
-H "Content-Type: application/json" \
-d '{"action":"reschedule","followup_date":"2026-09-25 15:00:00"}'
Response 200 OK
{
"success": true,
"data": {
"activity": { "id": 55, "followup_date": "2026-09-25 15:00:00", "event_sequence": 2 }
}
}
DELETE /leads/{id}/activities/{activityId}
Same as action=delete. Does not send a calendar cancel email.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id |
integer |
yes | Lead id |
activityId |
integer |
yes | Activity id |
Request example
curl -sS -X DELETE https://api.strackerapp.com/v1/leads/1/activities/55 \
-H "Authorization: Bearer sk_REPLACE"
Response 200 OK
{
"success": true,
"data": { "deleted": true }
}