Set CRM lead sync rules
Set which Vini conversations are written to the dealer's CRM for this rooftop.
This updates the rooftop's CRM lead sync gate: whether it is enforced, and which call types, outcomes and interest levels cause a conversation to sync to the CRM. Each list you send replaces the stored list. Appointment and Callback outcomes always sync and cannot be changed. Without agent, the settings are written for both the inbound and the outbound agent. The record is created if it does not exist.
Before you call it
- You need the bearer token of a signed-in console user with the
MODIFY_CRM_CONFIG_ALL(EDIT) permission on the dealer group and rooftop. - Save the rooftop's CRM first with Save an integration and
entity: "CRM". This endpoint does not check that. - Send at least
enforcedor one list. An empty update returns400. - One database write; the updated settings are returned. Rate limit: not documented.
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
| Header | — | — | — |
Authorization | string | Required | Bearer YOUR_TOKEN |
| Body | — | — | — |
enterprise_id | string | Required | <enterpriseId> of the dealer group. Snake case. |
team_id | string | Required | <teamId> of the rooftop. Snake case. |
agent | string | Optional | inbound or outbound. Leave it out to apply to both. The UI leaves it out. |
enforced | boolean | Optional | Whether the gate holds conversations. The UI always sends true. |
selectedCallTypes | string[] | Optional | Call types that sync. Any non-empty string is accepted except no_signal. Known sales values: sales_shopping, sales_routing, service_routing, finance_routing, parts_routing, human_request, operational, other. Unknown values are accepted with a warning. |
selectedOutcomes | string[] | Optional | Outcomes that sync, as Title Case labels. Allowed: Appointment, Transfer, Callback, Query Resolved, Qualified Lead, None. |
selectedInterests | string[] | Optional | Interest levels that sync. Allowed: not_interested, soft_considering, interested, strong_buying, no_vehicle_discussed. |
- Authenticate: set
Authorization: Bearer YOUR_TOKEN. - Identify the rooftop: send
enterprise_idandteam_idin snake case. - Outcomes: send them as the Title Case labels above.
- Leave out locked outcomes: do not send
lockedOutcomes.
Sample request
curl -X PUT "https://api.spyne.ai/leads/dealer/v1/crm-config/leads-sync" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"enterprise_id": "<enterpriseId>",
"team_id": "<teamId>",
"enforced": true,
"selectedCallTypes": ["sales_shopping", "human_request"],
"selectedOutcomes": ["Appointment", "Callback", "Transfer"],
"selectedInterests": ["interested", "strong_buying"]
}'import requests
res = requests.put(
"https://api.spyne.ai/leads/dealer/v1/crm-config/leads-sync",
headers={"Authorization": "Bearer YOUR_TOKEN"},
json={
"enterprise_id": "<enterpriseId>",
"team_id": "<teamId>",
"enforced": True,
"selectedCallTypes": ["sales_shopping"],
"selectedOutcomes": ["Appointment", "Callback"],
"selectedInterests": [],
},
)
print(res.json())const res = await fetch(
"https://api.spyne.ai/leads/dealer/v1/crm-config/leads-sync",
{
method: "PUT",
headers: {
Authorization: "Bearer YOUR_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({
enterprise_id: "<enterpriseId>",
team_id: "<teamId>",
enforced: true,
selectedCallTypes: ["sales_shopping"],
selectedOutcomes: ["Appointment", "Callback"],
selectedInterests: ["interested"],
}),
}
);
const body = await res.json();Sample response
{
"message": "CRM leads sync configuration updated successfully",
"crmLeadsSync": {
"enforced": true,
"selectedCallTypes": ["sales_shopping", "human_request"],
"selectedOutcomes": ["Appointment", "Callback", "Transfer"],
"selectedInterests": ["interested", "strong_buying"],
"lockedOutcomes": ["Appointment", "Callback"]
},
"changes": {
"selectedOutcomes": { "from": ["Appointment", "Callback"], "to": ["Appointment", "Callback", "Transfer"] }
}
}{
"message": "CRM leads sync configuration updated successfully",
"crmLeadsSync": { "enforced": true, "selectedCallTypes": ["sales_shoping"] },
"changes": {},
"warnings": [
"selectedCallTypes: \"sales_shoping\" is not a known sales call type. Accepted (service call types are configured per agent and cannot be validated here), but if this is a typo it will never match and those conversations will be held."
]
}{
"message": "Invalid CRM leads sync configuration",
"errors": ["selectedOutcomes contains unsupported outcome: \"Booked\". Supported: Appointment, Transfer, Callback, Query Resolved, Qualified Lead, None"],
"error": true
}{ "message": "Invalid Token", "error": true, "code": "ERROR_AUTH_401", "details": "<details>", "data": null }{ "message": "You don't have permission to update the CRM leads sync configuration", "error": true }{ "message": "Error updating CRM leads sync configuration", "error": "<error message>" }Response parameters
| Parameter | Type | Description |
|---|---|---|
message | string | Result message. |
crmLeadsSync | object or null | The stored settings after the update (for the inbound agent when agent is left out). |
crmLeadsSync.enforced | boolean | Whether the gate holds conversations. |
crmLeadsSync.selectedCallTypes, selectedOutcomes, selectedInterests | string[] | Stored selections. |
crmLeadsSync.lockedOutcomes | string[] | Outcomes that always sync. |
changes | object | from and to for each field that changed. |
warnings | string[] | Present only when unknown call types were accepted. |
Errors
| Status | Code | When it happens |
|---|---|---|
400 | Invalid CRM leads sync configuration | A value is not allowed, a list is not an array, enforced is not boolean, lockedOutcomes was sent, or the database rejected the value. |
400 | Nothing to update: provide 'enforced' and/or a selection list | No enforced and no list in the body. |
401 | Token error | Missing or rejected token. |
403 | Permission | The user lacks MODIFY_CRM_CONFIG_ALL (EDIT) on the dealer group and rooftop. |
408 | Timeout | User management could not be reached. |
500 | Error updating CRM leads sync configuration | Unexpected error. |
A typo in a call type is accepted with 200 and a warning. Those conversations will not match.
What happens next
When this call and the CRM save both succeed, the UI calls Mark a checklist task done in the Checklist section with taskName: "CRM Integration". If this call fails after the CRM save succeeded, the CRM link is already saved and the task is not marked done. Retrying the full save is safe.
Updated about 1 hour ago
