Save service readiness answers
Create or update the go-live readiness answers for one or more steps of one agent type at one rooftop. Spyne stores them as a questionnaire. Nothing else is triggered: no backend reads these answers, and they do not gate go-live.
Service agents: send
entity: service_inboundwith the stepsafterHoursandofficeHoursOverflow, orentity: service_outboundwithrunFirstCampaignonly.
Auth: Bearer token of a signed-in user · Rate limit: not documented · Processing: synchronous, one read and one write of the config, plus a change-log entry
Before you call it
- You need
enterpriseId,teamIdand the agent type (entity).entitymust be one of the seven values listed on Get readiness answers. - Send at least one step. Each entry needs a non-empty
question. - The body is deep-merged into the rooftop's config for that agent type. Each step you send replaces that step's whole array. Steps you leave out are kept. Sending
nullfor a step clears it. Agent records stored in the same config are not touched. - Because a step is replaced as a whole, send every entry you want to keep for that step, not only the changed one.
- If the rooftop has no config for this agent type yet, one is created. Every write is recorded in the config change log.
- The token's user ID is recorded. The route does not check that the token belongs to the
enterpriseIdorteamIdin the body.
Step keys the onboarding console writes, by agent type:
entity | Step key | Questions (label saved in question) |
|---|---|---|
sales_inbound | afterHours | "Calls during after hours", "Speed to Lead on after hour leads", "Followup on after hour leads", "Appointment Reminder" |
sales_inbound | officeHoursOverflow | "Overflow Calls", "Speed to lead", "Followup", "Appointment Reminder" |
sales_inbound | officeHoursAllLeads | "Handle all office-hours leads" |
sales_outbound | runFirstCampaign | "First campaign started" |
service_inbound | afterHours | "Calls during after hours" only |
service_inbound | officeHoursOverflow | "Overflow Calls" only |
service_outbound | runFirstCampaign | "First campaign started" |
acquisition_outbound | runFirstCampaign | "First campaign started" |
| any | viniCallTranscriptsRecordings | Written after a call transcript or recording upload. question is transcript or recordings; answer is an array of file URLs. |
The backend accepts any step key. Only afterHours is marked as agreed with the backend in the console code. The other key names are to be confirmed.
Request body
- Headers: set
Authorization: Bearer YOUR_TOKENandContent-Type: application/json. - Identify the rooftop and agent type: send
enterpriseId,teamIdandentity. - Add the answers: build
onboardingGoLiveReadinesswith one key per step you are saving. - Send it and check for
200.
| Parameter | Type | Required | Description |
|---|---|---|---|
enterpriseId | string | Required | Dealer group ID. Must not be empty. |
teamId | string | Required | Rooftop ID. Must not be empty. |
entity | string | Required | Agent type. One of the seven allowed values. |
enterpriseName | string | Optional | Stored on the config if sent. |
teamName | string | Optional | Stored on the config if sent. |
onboardingGoLiveReadiness | object | Required | Map of step name to an array of entries, or to null to clear the step. Must contain at least one key. |
<step> | array or null | Required | One key per step, for example afterHours. |
question | string | Required | Question text. Must not be empty. The console stores the human label here, not a key. |
answer | any | Required | Any JSON value. The key must be present. Toggle steps use a boolean. |
comment | string | Optional | Extra fields are kept as sent. The console sends comment: the reason when a toggle is off, or an empty string when on. |
Sample request
curl -X POST "https://api.spyne.ai/central-config/v1/vini-config/go-live-readiness" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"enterpriseId": "<enterpriseId>",
"teamId": "<teamId>",
"entity": "sales_inbound",
"onboardingGoLiveReadiness": {
"afterHours": [
{ "question": "Calls during after hours", "answer": true, "comment": "" },
{ "question": "Speed to Lead on after hour leads", "answer": true, "comment": "" },
{ "question": "Followup on after hour leads", "answer": false, "comment": "BDC covers this" },
{ "question": "Appointment Reminder", "answer": true, "comment": "" }
]
}
}'import requests
res = requests.post(
"https://api.spyne.ai/central-config/v1/vini-config/go-live-readiness",
headers={"Authorization": "Bearer YOUR_TOKEN"},
json={
"enterpriseId": "<enterpriseId>",
"teamId": "<teamId>",
"entity": "sales_outbound",
"onboardingGoLiveReadiness": {
"runFirstCampaign": [
{"question": "First campaign started", "answer": True, "comment": ""}
]
},
},
)
print(res.status_code, res.json())const res = await fetch(
'https://api.spyne.ai/central-config/v1/vini-config/go-live-readiness',
{
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({
enterpriseId: '<enterpriseId>',
teamId: '<teamId>',
entity: 'service_inbound',
onboardingGoLiveReadiness: {
viniCallTranscriptsRecordings: [
{ question: 'transcript', answer: ['<fileUrl>'] },
],
},
}),
}
);
console.log(res.status, await res.json());Sample response
The response is the whole saved config for this rooftop and agent type. That includes any agent records stored under their agent ID, not only the readiness map.
{
"_id": "<configId>",
"enterpriseId": "<enterpriseId>",
"teamId": "<teamId>",
"enterpriseName": "<enterpriseName>",
"teamName": "<teamName>",
"userId": "<userId>",
"domain": "product",
"type": "single",
"entity": "sales_inbound",
"entityconfig": {
"<agentId>": { "agentId": "<agentId>", "agentName": "<agentName>", "isOnboarded": false },
"onboardingGoLiveReadiness": {
"afterHours": [
{ "question": "Calls during after hours", "answer": true, "comment": "" }
]
}
},
"createdAt": "2026-10-01T15:04:05.000Z",
"updatedAt": "2026-10-12T15:04:05.000Z",
"__v": 0
}{
"success": false,
"message": "Validation error",
"errors": [
{
"code": "custom",
"message": "onboardingGoLiveReadiness must include at least one step",
"path": ["onboardingGoLiveReadiness"]
}
]
}{
"status": "401",
"message": "Invalid Auth key"
}{
"success": false,
"message": "Failed to save Go-Live readiness",
"error": "<error message>"
}Response parameters (200)
| Parameter | Type | Description |
|---|---|---|
_id | string | Config ID. |
enterpriseId | string | Dealer group ID. |
teamId | string | Rooftop ID. |
enterpriseName | string | Stored dealer group name, if any. |
teamName | string | Stored rooftop name, if any. |
userId | string | User who made the last write, taken from the token. |
domain | string | Always product. |
type | string | Always single. |
entity | string | Agent type. |
entityconfig | object | The full stored config: agent records keyed by agent ID, plus onboardingGoLiveReadiness. |
onboardingGoLiveReadiness | object | Readiness map after the merge. |
createdAt | string | ISO timestamp. |
updatedAt | string | ISO timestamp. |
__v | number | Document version. |
Errors
| Status | Code | When it happens |
|---|---|---|
400 | Validation error | A required field is missing or empty, entity is not allowed, onboardingGoLiveReadiness is empty, a step is not an array or null, or an entry has an empty question. |
401 | Invalid Auth key | No Authorization: Bearer header, or the user service rejects the token. |
401 | User ID not found in token | The token validated but carried no user ID. |
500 | Failed to save Go-Live readiness | Unexpected error during the write. |
What happens next
Nothing else runs. The answers sit in the config as a record of what the dealer said.
In the onboarding console, each save is paired with a call that marks the matching checklist task done or in progress, for example After Hours. That call is Mark a task done and runs in parallel with this one. If one call fails, the other is not rolled back. Saving every toggle as off with a comment still counts as a completed task.
To switch the agent live, call Put an agent live. Readiness is not checked there.
Updated about 1 hour ago
