Endpoints
Quick reference of resources — customers, health, playbooks, tasks, users, analytics.
Quick reference for the main endpoints. The complete, authoritative spec is at api.lealup.com/v1/openapi.json.
Conventions
- All paths are prefixed with
/v1/. - All require
Authorization: Bearer {token}— see Authentication. - POST/PATCH expect
Content-Type: application/json. - Responses are JSON (
application/json) orapplication/problem+jsonon errors. - Pagination: cursor in query
cursor=..., returnsnext_cursorandhas_more.
Customers
GET /v1/customers
List customers.
Query params:
status—active|trial|churned|paused(multiple comma-separated).owner_id— filter by owner.segment_id— filter by segment.health_status—healthy|neutral|at_risk.min_arr,max_arr— ARR range.renewal_within_days—30,60,90.q— full-text search on name, email_domain.limit— default 50, max 200.cursor— pagination.
Response:
{
"data": [{"id": "...", "name": "...", ...}],
"next_cursor": "eyJ...",
"has_more": true
}POST /v1/customers
Create a customer.
Body:
{
"name": "Acme Corp",
"email_domain": "acme.com",
"arr": 145000,
"owner_email": "[email protected]",
"status": "active",
"renewal_date": "2026-06-15",
"custom_fields": {
"tier": "Platinum",
"industry": "Fintech"
}
}Recommended headers: Idempotency-Key.
Response: 201 Created with the full customer (includes id, tenant_id, created_at).
GET /v1/customers/{id}
Customer detail. Includes current health score, renewal info, pending tasks.
PATCH /v1/customers/{id}
Partial update. Only the fields sent are updated.
{
"arr": 180000,
"custom_fields": {
"tier": "Platinum"
}
}DELETE /v1/customers/{id}
Soft-delete. Reversible for 30 days via Admin → Customers → Trash.
For hard-delete (GDPR): DELETE /v1/customers/{id}?hard=true — requires scope admin:*.
POST /v1/customers/bulk-import
Bulk import (equivalent to CSV via UI).
Body:
{
"customers": [/* up to 1000 */],
"mode": "create_or_update",
"conflict_resolution": "skip"
}Returns job_id — poll at GET /v1/jobs/{job_id}.
Contacts
GET /v1/customers/{id}/contacts
List a customer's contacts.
POST /v1/customers/{id}/contacts
Create a contact.
{
"name": "Maria Perez",
"email": "[email protected]",
"title": "VP Operations",
"is_champion": true,
"whatsapp_number": "+56911112222"
}Health
GET /v1/customers/{id}/health
Current score + breakdown by dimension.
GET /v1/customers/{id}/health/history
Time series (default last 90 days).
Query: from, to (ISO 8601), granularity (daily default, weekly).
POST /v1/customers/{id}/health/recalculate
Force immediate recalculation (useful after batch-importing events).
GET /v1/health/model
Returns the health model configuration (weights, thresholds, active dimensions).
PATCH /v1/health/model
Update the model. Requires scope health:write + admin role.
Playbooks and tasks
GET /v1/playbooks
List playbooks.
POST /v1/playbooks
Create a playbook.
{
"name": "Enterprise At-Risk",
"trigger": {
"type": "condition",
"expression": "health_score < 60 AND segment == 'Enterprise'"
},
"actions": [
{"type": "create_task", "title": "Call champion", "assignee": "owner"},
{"type": "notify", "channel": "slack", "target": "#cs-alerts"}
],
"enabled": true
}GET /v1/playbooks/{id}/runs
Run history (which customers matched, which actions fired).
GET /v1/tasks
Tasks.
Query:
owner_id,assignee_idstatus—pending|in_progress|completed|skipped.due_before,due_after— ISO dates.customer_id,playbook_id.
PATCH /v1/tasks/{id}
Update status or reassignment.
POST /v1/tasks/{id}/complete
Complete with structured outcome.
{
"outcome": "resolved",
"notes": "Champion confirmed the usage drop was due to an internal deployment, not a churn signal"
}Users
GET /v1/users
List users (admin only).
GET /v1/users/me
Current actor's profile (user or api_key).
POST /v1/users
Invite a user (admin only, scope users:write).
{
"email": "[email protected]",
"role": "csm",
"locale": "es",
"timezone": "America/Santiago",
"initial_territory_ids": ["..."]
}PATCH /v1/users/{id}/role
Change role. Admin only.
Analytics
GET /v1/analytics/portfolio
Portfolio summary: total ARR, customers by status, health distribution, top risks.
GET /v1/analytics/renewals
Renewal pipeline by month.
Query: from, to, forecast_category.
GET /v1/analytics/cohorts
Retention cohort analysis.
Query: cohort_period — month | quarter. months_out — default 12.
Events (ingestion)
See Event ingestion for the full treatment.
POST /v1/events
Batch event ingestion.
{
"events": [/* up to 1000 */]
}GET /v1/events
Event query (debugging). Rate-limited.
Webhooks (outbound)
See Webhooks.
GET /v1/webhook-subscriptions
POST /v1/webhook-subscriptions
DELETE /v1/webhook-subscriptions/{id}
Integrations
GET /v1/integrations
Status of integrations (Gmail, Calendar, Slack, etc.).
POST /v1/integrations/{name}/connect
Start OAuth flow. Returns authorization URL.
DELETE /v1/integrations/{name}
Disconnect.
Rate limits
Per account, per endpoint class:
| Endpoint class | Rate limit (default) |
|---|---|
| Reads (GET) | 1000 req/min |
| Writes (POST, PATCH, DELETE) | 300 req/min |
Ingestion (POST /v1/events) | 60 batches/min (up to 1000 events each) |
Bulk (POST /v1/customers/bulk-import) | 10 req/hour |
Headers on each response:
X-RateLimit-LimitX-RateLimit-RemainingX-RateLimit-Reset(unix timestamp)
If you exceed: 429 Too Many Requests + Retry-After header (seconds).
Scale/Enterprise tiers have higher limits. Contact sales if you need more.
Errors — format (RFC 7807)
{
"type": "https://api.lealup.com/errors/validation_error",
"title": "Validation error",
"status": 400,
"detail": "Field 'arr' must be a positive number",
"instance": "/v1/customers",
"trace_id": "01HXYZ123ABC",
"errors": [
{"field": "arr", "code": "must_be_positive", "message": "..."}
]
}Common status codes
200OK201Created204No Content (DELETEs)400validation error401unauthenticated403forbidden (scope/permission)404not found (also when crossing accounts)409conflict (duplicate, invalid state)422unprocessable (semantic error, e.g., renewal_date < today)429rate limited500server error (report with trace_id)
Next
- Event ingestion — feed health.
- Webhooks — receive outbound signals.