API Reference Overview
This page summarizes the Public API v1 endpoints for leads. Every request uses the Authorization: Bearer <api_key> header and the following base URL:
https://konektor.id
The examples on this page show the fields most integrations use. For machine-readable references and importable artifacts, use the OpenAPI and Postman links at the end of this page.
Authentication and API Key Scopes
Official supported scopes:
| Scope | Access |
|---|---|
leads.read | GET /api/v1/leads, GET /api/v1/leads/{identifier} |
leads.write | POST /api/v1/leads, PATCH /api/v1/leads/{identifier}, POST /api/v1/leads/upsert |
leads.delete | DELETE /api/v1/leads/{identifier} |
leads.bulk | POST /api/v1/leads/bulk-upsert and compatible access for upsert |
The API rejects keys that are invalid, revoked, expired, missing the required scope, or sent from an IP outside the key allowlist.
Delete Scope Deprecation
The delete endpoint accepts the legacy leads.write scope only until September 30, 2026. After that date, delete requests must use leads.delete.
When the legacy fallback scope is used during the transition window, the backend sends x-konektor-deprecation-warning and Warning headers.
Public API v1 Lead Endpoints
| Method | Path | Scope | Description |
|---|---|---|---|
GET | /api/v1/leads | leads.read | List leads with advanced filters and pagination |
POST | /api/v1/leads | leads.write | Create a lead |
GET | /api/v1/leads/{identifier} | leads.read | Get lead detail |
PATCH | /api/v1/leads/{identifier} | leads.write | Update a lead |
DELETE | /api/v1/leads/{identifier} | leads.delete | Soft delete a lead |
POST | /api/v1/leads/upsert | leads.write or leads.bulk | Upsert a lead by matcher |
POST | /api/v1/leads/bulk-upsert | leads.bulk | Bulk upsert up to 100 items |
{identifier} Format
{identifier} can be any of the following:
iduniqueCodeexternalRefphone
Examples:
/api/v1/leads/550e8400-e29b-41d4-a716-446655440000/api/v1/leads/API_7F3A1B2C/api/v1/leads/CRM-1024/api/v1/leads/6281234567890
When you use a phone number, send a stable canonical format if possible. The backend still normalizes common phone formatting variations.
List Leads GET /api/v1/leads
This endpoint supports filtering, sorting, and cursor pagination. Cursor pagination stays stable with id as the tie-breaker.
Available query parameters:
| Query | Description |
|---|---|
status | Single status or CSV, for example new,qualified |
priority | low, medium, high, urgent |
source | website, whatsapp, phone, email, referral, social, ads, event, other |
adPlatform | meta, google, tiktok, linkedin, posthog, other |
assignedTo | Assigned user UUID |
createdFrom, createdTo | createdAt bounds in ISO 8601 format |
updatedFrom, updatedTo | updatedAt bounds in ISO 8601 format |
sortBy | createdAt or updatedAt |
sortOrder | asc or desc |
cursor | Keyset pagination token |
limit | Maximum 100, default 50 |
page | Legacy mode. Ignored when cursor is present |
search | Search by name, email, phone, uniqueCode, or externalRef |
Example request:
curl -X GET "https://konektor.id/api/v1/leads?status=new,qualified&limit=2&sortBy=createdAt&sortOrder=desc" \
-H "Authorization: Bearer <api_key>"
Example response:
{
"success": true,
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"uniqueCode": "API_7F3A1B2C",
"externalRef": "CRM-1024",
"firstName": "Budi",
"lastName": "Santoso",
"email": "budi@example.com",
"phone": "6281234567890",
"status": "new",
"priority": "high",
"source": "website",
"adPlatform": "meta",
"assignedTo": null,
"estimatedValue": 3500000,
"actualValue": null,
"notes": "Product demo requested",
"createdAt": "2026-03-06T02:15:30.000Z",
"updatedAt": "2026-03-06T02:15:30.000Z",
"deletedAt": null
}
],
"pagination": {
"page": 1,
"limit": 2,
"total": 24,
"totalPages": 12,
"cursor": null,
"nextCursor": "<opaque-cursor-token>"
}
}
When you use cursor mode, page and totalPages are null. Treat nextCursor as an opaque token and send it back unchanged in the next request.
Create Lead POST /api/v1/leads
The only required field is firstName. When these fields are omitted, the backend applies the following defaults: status = new, priority = medium, source = other, and uniqueCode is generated automatically.
Example request:
curl -X POST "https://konektor.id/api/v1/leads" \
-H "Authorization: Bearer <api_key>" \
-H "Content-Type: application/json" \
-d '{
"firstName": "Budi",
"lastName": "Santoso",
"email": "budi@example.com",
"phone": "6281234567890",
"priority": "high",
"source": "website",
"adPlatform": "meta",
"estimatedValue": 3500000,
"externalRef": "CRM-1024",
"notes": "Product demo requested"
}'
Example response:
{
"success": true,
"message": "Lead created successfully",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"uniqueCode": "API_7F3A1B2C",
"externalRef": "CRM-1024",
"firstName": "Budi",
"lastName": "Santoso",
"email": "budi@example.com",
"phone": "6281234567890",
"status": "new",
"priority": "high",
"source": "website",
"adPlatform": "meta",
"estimatedValue": 3500000,
"actualValue": null,
"notes": "Product demo requested",
"createdAt": "2026-03-06T02:15:30.000Z",
"updatedAt": "2026-03-06T02:15:30.000Z",
"deletedAt": null
},
"lead": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"uniqueCode": "API_7F3A1B2C",
"externalRef": "CRM-1024",
"firstName": "Budi",
"lastName": "Santoso",
"email": "budi@example.com",
"phone": "6281234567890",
"status": "new",
"priority": "high",
"source": "website",
"adPlatform": "meta",
"estimatedValue": 3500000,
"actualValue": null,
"notes": "Product demo requested",
"createdAt": "2026-03-06T02:15:30.000Z",
"updatedAt": "2026-03-06T02:15:30.000Z",
"deletedAt": null
}
}
Get Lead Detail GET /api/v1/leads/{identifier}
This endpoint returns one lead by id, uniqueCode, externalRef, or phone.
If the lead has custom fields from Lead Form or k_* query params, the detail response includes customFields and customFieldValues. Use customFields when you need the label and field type. Use customFieldValues when you only need a quick lookup by key.
For k_* query params, Konektor keeps the original key, such as k_budget. In customFieldValues, Konektor also adds an alias without the k_ prefix, such as budget, as long as that alias does not conflict with another custom field.
Current custom field limits:
- Up to 10 custom fields per lead.
- Keys are up to 40 characters, lowercase
snake_case, and must start with a letter. - Text values from
k_*query params are capped at 255 characters. - Values can be strings or booleans, depending on the field type.
Example request:
curl -X GET "https://konektor.id/api/v1/leads/CRM-1024" \
-H "Authorization: Bearer <api_key>"
Example response:
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"uniqueCode": "API_7F3A1B2C",
"externalRef": "CRM-1024",
"firstName": "Budi",
"lastName": "Santoso",
"email": "budi@example.com",
"phone": "6281234567890",
"status": "new",
"priority": "high",
"source": "website",
"customFields": [
{
"key": "k_budget",
"label": "Budget",
"type": "text",
"value": "50jt"
}
],
"customFieldValues": {
"k_budget": "50jt",
"budget": "50jt"
},
"createdAt": "2026-03-06T02:15:30.000Z",
"updatedAt": "2026-03-06T02:15:30.000Z",
"deletedAt": null
}
}
Update Lead PATCH /api/v1/leads/{identifier}
Send only the fields you want to change. For nullable fields such as lastName, phone, externalRef, notes, estimatedValue, and actualValue, send null to clear the stored value.
This endpoint also accepts value as an alias of actualValue. The update response includes previousStatus and the value alias inside the returned lead object.
Example request:
curl -X PATCH "https://konektor.id/api/v1/leads/CRM-1024" \
-H "Authorization: Bearer <api_key>" \
-H "Content-Type: application/json" \
-d '{
"status": "qualified",
"actualValue": 1500000,
"notes": "Demo has been scheduled"
}'
Example response:
{
"success": true,
"message": "Lead updated successfully",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"uniqueCode": "API_7F3A1B2C",
"externalRef": "CRM-1024",
"firstName": "Budi",
"status": "qualified",
"priority": "high",
"source": "website",
"actualValue": 1500000,
"notes": "Demo has been scheduled",
"previousStatus": "new",
"value": 1500000,
"createdAt": "2026-03-06T02:15:30.000Z",
"updatedAt": "2026-03-06T03:10:00.000Z",
"deletedAt": null
},
"lead": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"uniqueCode": "API_7F3A1B2C",
"externalRef": "CRM-1024",
"firstName": "Budi",
"status": "qualified",
"priority": "high",
"source": "website",
"actualValue": 1500000,
"notes": "Demo has been scheduled",
"previousStatus": "new",
"value": 1500000,
"createdAt": "2026-03-06T02:15:30.000Z",
"updatedAt": "2026-03-06T03:10:00.000Z",
"deletedAt": null
}
}
If the payload is valid but does not change any stored value, the backend returns success: true, message: "No changes detected", and the latest lead object.
Upsert Lead POST /api/v1/leads/upsert
The upsert body must include match and lead. match.by only accepts id, uniqueCode, phone, or externalRef.
When a matching lead exists, the backend returns action: "updated". When no lead is found and createIfMissing is not set to false, the backend creates a new lead and returns action: "created".
When match.by is phone, uniqueCode, or externalRef, the backend uses match.value for creation if the same field is omitted from lead.
Example request:
curl -X POST "https://konektor.id/api/v1/leads/upsert" \
-H "Authorization: Bearer <api_key>" \
-H "Content-Type: application/json" \
-d '{
"match": {
"by": "phone",
"value": "6281234567890"
},
"lead": {
"firstName": "Budi",
"status": "qualified",
"priority": "high",
"source": "whatsapp"
},
"createIfMissing": true
}'
Example response:
{
"success": true,
"action": "updated",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"uniqueCode": "API_7F3A1B2C",
"externalRef": "CRM-1024",
"firstName": "Budi",
"phone": "6281234567890",
"status": "qualified",
"priority": "high",
"source": "whatsapp",
"createdAt": "2026-03-06T02:15:30.000Z",
"updatedAt": "2026-03-06T03:25:00.000Z",
"deletedAt": null
},
"lead": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"uniqueCode": "API_7F3A1B2C",
"externalRef": "CRM-1024",
"firstName": "Budi",
"phone": "6281234567890",
"status": "qualified",
"priority": "high",
"source": "whatsapp",
"createdAt": "2026-03-06T02:15:30.000Z",
"updatedAt": "2026-03-06T03:25:00.000Z",
"deletedAt": null
}
}
Bulk Upsert POST /api/v1/leads/bulk-upsert
This endpoint accepts items[] with a maximum of 100 items. options.continueOnError defaults to true, so later items still run when an earlier item fails.
Example request:
curl -X POST "https://konektor.id/api/v1/leads/bulk-upsert" \
-H "Authorization: Bearer <api_key>" \
-H "Content-Type: application/json" \
-d '{
"items": [
{
"match": {
"by": "externalRef",
"value": "CRM-1024"
},
"lead": {
"firstName": "Budi",
"status": "qualified"
}
},
{
"match": {
"by": "externalRef",
"value": "CRM-2048"
},
"lead": {},
"createIfMissing": false
}
],
"options": {
"continueOnError": true
}
}'
Example response:
{
"success": false,
"processed": 2,
"created": 0,
"updated": 1,
"failed": 1,
"results": [
{
"index": 0,
"match": {
"by": "externalRef",
"value": "CRM-1024"
},
"success": true,
"action": "updated",
"lead": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"uniqueCode": "API_7F3A1B2C",
"externalRef": "CRM-1024",
"firstName": "Budi",
"status": "qualified",
"createdAt": "2026-03-06T02:15:30.000Z",
"updatedAt": "2026-03-06T03:25:00.000Z",
"deletedAt": null
}
},
{
"index": 1,
"match": {
"by": "externalRef",
"value": "CRM-2048"
},
"success": false,
"error": {
"statusCode": 404,
"statusMessage": "Lead not found and createIfMissing is false"
}
}
]
}
Delete Lead DELETE /api/v1/leads/{identifier}
This endpoint performs a soft delete. A successful response only returns the status and message.
Example request:
curl -X DELETE "https://konektor.id/api/v1/leads/CRM-1024" \
-H "Authorization: Bearer <api_key>"
Example response:
{
"success": true,
"message": "Lead deleted successfully"
}
If the request still uses the legacy leads.write scope during the transition window, the successful response also includes the x-konektor-deprecation-warning header.
Response and Error Format
Backend response wrappers:
- List endpoints return
success,data, andpagination. - Create, update, and upsert return
success, the lead object indata, and theleadalias. - Detail returns
successanddata. - Delete returns
successandmessage.
Common error format:
{
"statusCode": 404,
"statusMessage": "Lead not found"
}
Other common errors:
{
"statusCode": 400,
"statusMessage": "Invalid cursor format"
}
{
"statusCode": 409,
"statusMessage": "Lead with this externalRef already exists",
"data": {
"field": "externalRef"
}
}
{
"statusCode": 429,
"statusMessage": "Daily lead limit reached"
}
Validation errors can also include a data field with per-field validator details.
Data Security
Primary lead PII fields, such as name, email, and phone, are encrypted at rest. Phone lookup also considers normalized phone variants so common formatting differences can still match the same lead.
Current Limits and Quotas
- Create and upsert-create still enforce the workspace daily lead quota.
bulk-upsertaccepts at most100items per request.- Transport or runtime platform limits can still apply outside the business quotas above.
Contract Artifacts
Public contract artifacts are available at:
Need More Help?
Our team is ready to help you maximize ad tracking and business attribution.
© 2026 Konektor. All rights reserved.
