Sparky AI Reports — Integration Guide
Use the LiveSwitch API to run, view, and manage Sparks from a third-party system. Sparks are AI reports under the Sparky brand. Developers can follow this guide manually or give it to an AI agent for implementation.
To use this guide with an AI agent:
- Add this whole file to the agent's context.
- Add your LiveSwitch API credentials (client ID and redirect URI).
- Specify the scenarios from Section 4 that the agent must implement.
Section 6 gives the agent a complete endpoint reference without the need to read the earlier sections again.
1. What This Feature Does
Sparks are AI reports and analyses under the "Sparky" brand. LiveSwitch can run them on these API entities:
- A Conversation: The product UI calls this a Session. It is one interaction, such as a live call, recording request, photo session, or upload.
- A Project: A container for multiple Conversations, such as a job, claim, or installation. A Project report can summarize all content in the Project.
| Term | Meaning |
|---|---|
| Conversation | One session or interaction. A Conversation has a lifecycle (status). It holds recordings, photos, and documents. |
| Project | A group of Conversations with contacts, tags, and attributes. A Project has its own status (NotStarted, InProgress, Completed). |
| Spark template | The definition of a report type. It specifies what the report analyzes. It specifies the inputs, if any. The API provides Spark templates. You cannot create Spark templates yourself. |
| Spark run | One execution of a Spark template on a Conversation or Project. Its lifecycle is queued → running → completed | failed | cancelled. |
| Task / Input | A Spark template can define one or more tasks. Each task has typed input fields for the start of a Spark run, such as free-text instructions or a required document. Some inputs use valid answers from a connected platform, such as a customer in SmartMoving. |
| Validation token | The result of POST /v1/spark-templates/{id}/validate-inputs. It holds the validated inputs. A Spark run uses it when the Spark template has user inputs. |
2. Prerequisites & Authentication
2.1 Authentication
Sparks use the same authentication as the rest of the API. Follow the Authentication page. It gives the base URL of the API. It also explains how to:
- Get a Developer App (
client_idandclient_secret). - Log in a user and get an access token.
- Refresh the access token.
Send the access token in the Authorization: Bearer {access_token} header on every request.
For Sparks, add the scopes in section 2.2 to the scope value of the login URL.
2.2 Required scopes
| Scope | Grants |
|---|---|
openid profile email | Required for every integration, regardless of the endpoints |
sparks | Read Spark runs (list or get) |
sparks.write | Create and delete Spark runs, and validate Spark inputs |
spark-templates | List Spark templates and get the options for their inputs |
conversations / conversations.write | Read, create, and update Conversations (sessions) |
projects / projects.write | Read, create, and update Projects |
webhooks / webhooks.write | View and manage webhook subscriptions for the "run when data arrives" scenario. Only administrators have access. Regular users get 403 on all webhook endpoints, which includes read requests. |
3. Core Concepts You Need Before Writing Code
3.1 Spark run lifecycle — there is no manual "start"
When you POST a Spark run, LiveSwitch immediately creates it with queued status.
The API returns 202 Accepted.
This occurs even if the recording or other media is not ready.
LiveSwitch automatically changes the status to running when the media becomes available.
The final status is completed, failed, or cancelled.
queued → running → completed | failed | cancelled
For the scenario in §4.4, you can request a Spark run at any time.
You do not need to schedule the Spark run.
LiveSwitch waits until the media is available before it starts the Spark run.
3.2 Conversation status — your "is there data yet?" signal
ConversationDto.status is a plain string.
This value is relevant to Spark runs:
ReadyForReviewmeans that at least one recording is available for this Conversation.
A Conversation has several other status values during its lifecycle:
| Status | Meaning |
|---|---|
NotStarted | The Conversation exists but has no activity yet |
Sent | LiveSwitch sent an invitation or notification to the contact |
Scheduled | A Scheduled-type Conversation awaits its startAt time |
InProgress | The session is active |
ReadyForReview | At least one recording is available. This status is the trigger for Spark runs. |
Reviewed | Someone reviewed the Conversation in the LiveSwitch app |
Use webhooks to monitor this status.
Do not poll for it.
If a Conversation gets multiple recordings, ReadyForReview can occur more than once.
Do not treat this status as a single final event.
3.3 No native "report ready" event (yet)
LiveSwitch has no "Spark completed" or "Conversation ended" webhook event.
It supports generic Create / Update / Delete events for a fixed set of entities:
- Conversations (
Conversations) - Contacts (
Contacts) - App installations (
AppInstallations) - Recordings (
Recordings) - Documents (
Documents) - Accounts (
Accounts)
Poll GET /v1/sparks/{id} or the list endpoints to find out when a report finishes.
LiveSwitch plans a dedicated "Spark completed" event for a future release.
This guide will change when that event becomes available.
4. Scenario Walkthroughs
4.1 Discover available report types (Spark templates)
Before you start a Spark run, get your organization's available Spark templates and their inputs:
GET /v1/spark-templates?installed=true&limit=50
Authorization: Bearer {token}
The response uses SparkTemplatesResponseDto:
{
"data": [
{
"id": "template-uuid",
"name": "Damage Assessment Summary",
"description": "Summarizes visible damage from session photos/recordings.",
"category": { "id": "cat-uuid", "name": "Field Service", "description": "..." },
"requiresUserInput": true,
"requiresPlatformLogin": false,
"tasks": [
{
"id": "task-1",
"codeName": "summary-task",
"name": "Summary",
"type": "user-input",
"inputs": [
{
"key": "focusArea",
"label": "Focus Area",
"type": "text",
"required": false,
"description": "...",
"multiple": false,
"options": null,
"lookup": null,
"validation": { "required": false, "minLength": null, "maxLength": 200, "min": null, "max": null, "pattern": null, "patternMessage": null },
"default": null
}
],
"isRequired": true
}
],
"createdAt": "...",
"updatedAt": "..."
}
],
"nextCursor": "opaque-cursor-string-or-null"
}To get the next page, send nextCursor as cursor.
Repeat until nextCursor is null.
Record the id for sparkTemplateId.
Record each task's codeName and input keys for §4.2/4.3.
Each input specifies how to get a valid answer:
| Field | Meaning |
|---|---|
type | The value type: text, textarea, number, email, phone, url, date, datetime, time, checkbox, select, radio, multiselect, autocomplete or multi_autocomplete. |
required | If true, the input requires a value. |
multiple | If true, the input accepts a list of answers. |
options | A fixed list of valid answers. The input accepts only the value of an option. |
lookup | The answers come from a connected platform. The options endpoint provides them (§4.2, step 1). |
validation | The limits for the answer. The minLength, maxLength and pattern limits apply to text. |
default | The value that the Spark run uses if you do not send this input. |
An input cannot define both options and lookup.
This SmartMoving example shows an input with lookup:
{
"key": "quote_number",
"label": "Quote Number",
"type": "select",
"required": true,
"multiple": false,
"options": null,
"lookup": {
"hasOptions": true,
"searchable": false,
"displayLabelRequired": false,
"platformName": "SmartMoving",
"dependsOn": ["customer_name"]
}
}lookup field | Meaning |
|---|---|
hasOptions | If true, the options endpoint provides the valid answers. |
searchable | If true, the options endpoint requires a search term, such as a name, phone number, or email. |
dependsOn | The keys of inputs in the same task that require answers first. In this example, a customer selection is necessary before you can get the quotes. |
displayLabelRequired | If true, the platform uses the option label to confirm the value. The input requires the label with the value. |
platformName | The platform that provides the answers. |
4.2 Start a Spark run on an existing Conversation (session)
If the Spark template has no user inputs (requiresUserInput: false), send only the Spark template ID:
POST /v1/conversations/{conversationId}/sparks
Authorization: Bearer {token}
Content-Type: application/json
{
"sparkTemplateId": "template-uuid",
"shareWith": ["anyone"]
}
If the Spark template has user inputs (requiresUserInput: true), complete these three steps:
- Get the valid options for each selection input.
- Validate all the inputs to get a validation token (
validationToken). - Start the Spark run with the validation token (
validationToken).
The examples use the SmartMoving Spark template "Create a Moving Inventory in SmartMoving".
Its task identify_opportunity_id has two inputs:
- Customer lookup (
customer_name): A searchable lookup - Quote lookup (
quote_number): A lookup that depends oncustomer_name
Step 1 — Get the options for each selection input
Repeat this step for each input with lookup or options.
For a free-text input, go to step 2.
GET /v1/spark-templates/{sparkTemplateId}/tasks/{taskCodeName}/inputs/{fieldKey}/options
Authorization: Bearer {token}
| Query parameter | When to send it |
|---|---|
search | If lookup.searchable is true, send search. The search term must have 3 or more characters. |
parent[key] | For each key in lookup.dependsOn, send this parameter. Use the value of the option selected for that input. |
Find the customer:
GET /v1/spark-templates/{sparkTemplateId}/tasks/identify_opportunity_id/inputs/customer_name/options?search=Peter{
"options": [
{ "value": "086d9a35-f81d-413d-8188-b3e000bf0775", "label": "Peter Arroyo", "search": "Peter" }
]
}Get the quotes for the selected customer:
GET /v1/spark-templates/{sparkTemplateId}/tasks/identify_opportunity_id/inputs/quote_number/options?parent[customer_name]=086d9a35-f81d-413d-8188-b3e000bf0775{
"options": [
{ "value": "a9491534-767e-46c1-a7d7-b4d400cfab1f", "label": "Quote Number 2259" },
{ "value": "b532ec0a-49eb-4119-b45e-b4d5004fca25", "label": "Quote Number 2263" }
]
}Select an option in your code or show these options to your user.
Keep the full option object for step 2.
Step 2 — Validate the inputs
Send the inputs for all tasks.
For a lookup input, send the option object exactly as step 1 returned it (value, label and search).
Do not send only the label.
The API cannot find "Peter Arroyo" as a value.
POST /v1/spark-templates/{sparkTemplateId}/validate-inputs
Authorization: Bearer {token}
Content-Type: application/json
{
"tasks": [
{
"codeName": "identify_opportunity_id",
"inputs": {
"customer_name": { "value": "086d9a35-f81d-413d-8188-b3e000bf0775", "label": "Peter Arroyo", "search": "Peter" },
"quote_number": { "value": "a9491534-767e-46c1-a7d7-b4d400cfab1f", "label": "Quote Number 2259" }
}
}
]
}
Use these value formats:
- Text, number, date: Send the value. Send
dateanddatetimein ISO 8601 format (2026-10-01,2026-10-01T14:30:00Z). - Checkbox (
checkbox): Sendtrueorfalse. - Fixed list (
options): Send only thevalueof the option. - Lookup (
lookup): Send the option object from step 1. - Multiple values (
multiple: true): Send a list of values or option objects.
For fixed-list inputs, the API adds the label.
For lookup inputs, the API also accepts displayLabel instead of label.
The API searches the connected platform again to confirm each lookup value.
If the inputs are valid, the API returns 200:
{
"valid": true,
"validationToken": "eyJ...",
"validatedAt": "2026-10-18T15:10:19Z",
"expiresAt": "2026-10-19T15:10:19Z"
}If an input is invalid, the API returns 422.
The response groups errors by task and input key:
{
"title": "ValidationFailed",
"status": 422,
"detail": "Spark validation failed. See errors for the affected tasks and required platform connections.",
"errors": {
"tasks": [
{
"codeName": "identify_opportunity_id",
"inputs": {
"customer_name": [
"No matching option found for 'Customer Name' with value 'Peter'",
"'Customer Name' is looked up in SmartMoving. Find it with GET /v1/spark-templates/.../options?search=..., then send back the option you picked exactly as returned, including its search: { \"value\": ..., \"label\": ..., \"search\": ... }."
]
}
}
]
}
}| Error | Cause |
|---|---|
Field '…' is required | A required input has no value. |
No matching option found for '…' with value '…' | The platform does not have this value. Or the value does not belong to the selected parent, such as a quote for a different customer. |
Unknown input field. | The task does not have this input key. |
Unknown task code names: … | The Spark template does not have this task codeName. |
Step 3 — Start the Spark run with the validation token
POST /v1/conversations/{conversationId}/sparks
Authorization: Bearer {token}
Content-Type: application/json
{
"sparkTemplateId": "template-uuid",
"validationToken": "eyJ...",
"shareWith": ["anyone"]
}
The validation token has these properties:
- The validation token holds the validated inputs
- The validation token is valid for 24 hours
- The validation token becomes invalid earlier if the Spark template changes
- A valid validation token can be part of more than one create call
- Each create call starts a new Spark run with the same Spark template and inputs
- The validation token is specific to one Spark template
The API returns these errors:
- With both
tasksand a validation token, the API returns400 - With an invalid validation token, the API returns
422The validation token is not valid. Validate the inputs again. - With a validation token for a different Spark template, the API returns
422 - Without a validation token for a Spark template with user inputs, the API returns
422Validate the inputs first …
Do not send tasks with the validation token.
Run without validation. A request with
tasks,"skipInputValidation": true, and no validation token bypasses input validation. The API does not examine the inputs. An incorrect input can cause a Spark run to use incorrect data. This option is for inputs that are already correct.
The shareWith: ["anyone"] field makes the completed report public through a shareUrl (see §4.5).
To keep the report private to your organization, omit this field.
The response is 202 Accepted:
{ "id": "spark-run-uuid", "status": "queued" }Poll GET /v1/sparks/{id} until status is completed/failed/cancelled.
4.3 Start a Spark run on an existing Project
The Project endpoint accepts the same request format as the Conversation endpoint.
A Project report summarizes all content in the Project.
POST /v1/projects/{projectId}/sparks
Authorization: Bearer {token}
Content-Type: application/json
{
"sparkTemplateId": "template-uuid",
"validationToken": "eyJ...",
"shareWith": ["anyone"]
}
If the Spark template has user inputs, complete steps 1 and 2 of §4.2 first.
A validation token works for both Conversations and Projects.
The response has the same { "id": ..., "status": "queued" } format.
Poll for the status as in §4.2.
4.4 Automatically run a report as soon as session data comes in
Select the procedure that matches the report trigger:
A. You know the Conversation ID and need the report as soon as its recording arrives.
Call the POST endpoint in §4.2 immediately.
For example, call it after you create the Conversation or when a user requests a report.
No additional automation is necessary.
Spark runs start with queued status.
They automatically change to running only when media exists.
You can send the request before the recording finishes.
B. You need reports for Conversations that you do not directly control.
Examples include each new session in a Project or each new recording for an existing Conversation.
Use webhooks for these cases.
Step 1 — Create a webhook subscription
Create a webhook subscription:
POST /v1/webhooks
Authorization: Bearer {token}
Content-Type: application/json
{
"eventType": "Update",
"entityName": "Conversations",
"name": "Auto-run Sparky report",
"url": "https://your-system.example.com/webhooks/liveswitch",
"enabled": true
}
To receive events for new sessions too, also subscribe to eventType: "Create" on Conversations.
Step 2 — Handle webhook deliveries
Handle webhook deliveries.
Each callback includes these headers:
| Header | Meaning |
|---|---|
x-liveswitch-transaction-id | Unique delivery ID |
x-liveswitch-entity-id | The Conversation ID |
x-liveswitch-webhook-id | The subscription that triggered the callback |
x-liveswitch-webhook-event-type | CREATE / UPDATE / DELETE |
x-liveswitch-webhook-entity-name | conversation |
Webhook payloads contain the entity DTO (ConversationDto for Conversation events).
Some events contain incomplete entity details.
If the body lacks status or other required fields, get the current state with GET /v1/conversations/{x-liveswitch-entity-id}.
Step 3 — Request the Spark run
Check for the "ready" status.
If the Conversation is ready, request the Spark run:
def on_webhook(headers, body):
conversation = body if "status" in body else get_conversation(headers["x-liveswitch-entity-id"])
if conversation["status"] == "ReadyForReview":
if not already_sparked(conversation["id"]): # status can repeat — dedupe yourself
create_spark_for_conversation(conversation["id"], template_id="...")Prevent duplicate Spark runs in your system.
For example, check whether a Spark run already exists for the same Conversation and recording.
The ReadyForReview event can occur again when the Conversation gets a second recording.
For Spark templates with user inputs, create_spark_for_conversation must validate the inputs (§4.2, step 2).
It must send the new validation token (validationToken).
When the webhook arrives, validate the inputs.
When you subscribe, do not validate the inputs.
A validation token is valid for only 24 hours.
Step 4 — Respond to the webhook
Respond quickly with 2xx.
After a 5xx response, LiveSwitch retries the delivery up to 3 times.
LiveSwitch does not retry after a 4xx response.
Webhook deliveries do not currently have signatures.
To validate a delivery, check that x-liveswitch-webhook-id matches a subscription that you created.
Consider a private or secret URL path for your endpoint.
4.5 Pull existing reports (view reports)
List all Spark runs on a Conversation:
GET /v1/conversations/{conversationId}/sparks
List all Spark runs on a Project:
GET /v1/projects/{projectId}/sparks
Both endpoints return this format:
{
"data": [
{ "id": "...", "sparkTemplateId": "...", "status": "completed", "createdAt": "...", "shareUrl": "https://..." }
],
"total": 3
}Get one Spark run to poll its status or get its share link:
GET /v1/sparks/{id}
This endpoint returns the same fields as one data[] entry above.
Neither list endpoint currently supports pagination.
Typical Conversations and Projects have too few Spark runs for this limit to cause a problem.
This limit can matter when a Conversation or Project has many Spark runs.
SparkDto exposes only metadata (id, sparkTemplateId, status, createdAt, shareUrl).
It does not expose report content directly.
Users can edit reports in the LiveSwitch app UI.
A report copy from the API can become outdated as soon as a user edits the report.
A report link always opens the current version.
The shareUrl field has a value if you sent shareWith: ["anyone"] at creation.
This URL is public.
It requires no login.
The LiveSwitch app UI also shows completed Spark runs in the Conversation or Project.
To view a completed report, select one method:
- Use the
shareUrl. - Open the Conversation or Project in the LiveSwitch app UI through
conversationUrl/projectUrl.
If your integration needs report content in its own UI, provide a link to shareUrl.
A link guarantees access to the latest edited version.
A copy of the content cannot give this guarantee.
4.6 "Updating" a report
The API has no PUT/PATCH endpoint for Spark runs.
Users edit report content in the LiveSwitch app UI.
The API supports only Create (POST) and Delete (DELETE) operations for Spark runs.
To change a report's inputs and generate it again:
- Use
DELETEto delete the old Spark run. - Validate the new inputs (§4.2, step 2).
- Use
POSTto create a new Spark run with the new validation token (validationToken).
4.7 Delete a report
DELETE /v1/sparks/{id}
When you delete a Spark run, LiveSwitch also cancels any schedule that awaits execution or is in the queue.
LiveSwitch also removes the analysis job.
You can safely delete a Spark run with queued or running status to cancel it.
Deletion also works for completed Spark runs.
5. End-to-End Example: Full Automation Flow
A typical integration automatically creates reports for new field-service sessions.
Your system or a LiveSwitch app user creates a Conversation.
Your system uses POST /v1/conversations for this step.
The Conversation can optionally link to a Project through projectId/projectExternalId.
After a recording arrives, status becomes ReadyForReview.
The webhook subscription from §4.4 receives an Update event.
Then complete these steps:
- Check for an existing Spark run to prevent duplicate Spark runs for the same Conversation.
- If the Spark template has user inputs, get the options (§4.2, step 1).
- If the Spark template has user inputs, validate the inputs (§4.2, step 2).
- Call
POST /v1/conversations/{id}/sparkswith the appropriatesparkTemplateId.
If the Spark template has user inputs, include the validation token (validationToken). - Poll
GET /v1/sparks/{id}untilstatusiscompleted.
For example, poll every 30–60 seconds or use a job queue. - If you enabled report sharing, get the
shareUrl.
Store or forward the URL.
Alternatively, direct users to the Conversation'sconversationUrlin the LiveSwitch app. - If you no longer need the report, delete it with
DELETE /v1/sparks/{id}.
6. API Quick Reference
| Action | Method & Path | Scope | Notes |
|---|---|---|---|
| List Spark templates | GET /v1/spark-templates | spark-templates | Query: installed, limit, cursor |
| Get the options for a Spark template input | GET /v1/spark-templates/{sparkTemplateId}/tasks/{taskCodeName}/inputs/{fieldKey}/options | spark-templates | Query: search (3 or more characters when lookup.searchable is true), parent[key] (for each key in lookup.dependsOn) |
| Validate Spark template inputs | POST /v1/spark-templates/{sparkTemplateId}/validate-inputs | sparks.write | Body: tasks[]. Returns a validation token in validationToken (valid for 24 hours) |
| List Spark runs on a Conversation | GET /v1/conversations/{conversationId}/sparks | sparks | Returns {data[], total} |
| Start a Spark run on a Conversation | POST /v1/conversations/{conversationId}/sparks | sparks.write | Body: sparkTemplateId, validationToken (for Spark templates with user inputs), shareWith[]. Alternative: tasks[] with skipInputValidation: true |
| List Spark runs on a Project | GET /v1/projects/{projectId}/sparks | sparks | Returns {data[], total} |
| Start a Spark run on a Project | POST /v1/projects/{projectId}/sparks | sparks.write | Same body format as the Conversation request |
| Get one Spark run | GET /v1/sparks/{id} | sparks | Poll for status |
| Delete a Spark run | DELETE /v1/sparks/{id} | sparks.write | Also cancels Spark runs with queued or running status |
| Get a Conversation | GET /v1/conversations/{id} | conversations | Check status |
| List Conversations | GET /v1/conversations | conversations | page, pageSize, where, order |
| Create a Conversation | POST /v1/conversations | conversations.write | The request requires type. The Conversations API reference gives the full request body. |
| Update a Conversation | PUT /v1/conversations/{id} | conversations.write | Only contactId, notes, tags, name, projectId, assignedUserId are editable |
| Get a Project | GET /v1/projects/{id} | projects | |
| List Projects | GET /v1/projects | projects | page, pageSize, where, order, status, externalId, contactId |
| Create a Project | POST /v1/projects | projects.write | The request requires name |
| Update a Project | PUT /v1/projects/{id} | projects.write | |
| Create webhook subscription | POST /v1/webhooks | webhooks.write | The request requires eventType + entityName |
| List webhooks | GET /v1/webhooks | webhooks | |
| Get a webhook | GET /v1/webhooks/{id} | webhooks | |
| Update a webhook | PUT /v1/webhooks/{id} | webhooks.write | |
| Delete a webhook | DELETE /v1/webhooks/{id} | webhooks.write | |
| Delete all webhooks for an app | DELETE /v1/webhooks/app/{appId} | webhooks.write |
Updated 3 days ago