English
English
Appearance
English
English
Appearance
This group covers Call Scripts and Disposition Codes — two core building blocks the agent uses on every call: the script tells them what to say; the disposition classifies the result when the call ends.
| Endpoint | Purpose |
|---|---|
GET /api/telesales/scripts | List the customer's scripts |
GET /api/telesales/scripts/{id} | Detail for one script (with detected placeholders) |
POST /api/telesales/scripts | Create a script |
PUT /api/telesales/scripts/{id} | Update a script |
DELETE /api/telesales/scripts/{id} | Delete a script |
POST /api/telesales/scripts/{id}/preview | Render a preview using a real lead + agent |
Scripts use Markdown syntax. variables are auto-detected from the content — no separate declaration needed. When the agent opens a lead, each variable is replaced with the live value from the lead's custom_fields plus the agent's profile. Industry-specific placeholders (contract number, account ID, order code, ...) are declared as custom_fields per customer.
GET /api/telesales/scripts — list | Field | Type | Required | Description | Valid values |
|---|---|---|---|---|
q | string | optional | Search by name (LIKE %keyword%) | |
campaign_id | integer | optional | Filter scripts in use by a given campaign | |
page | integer | optional | Page number | ≥ 1 (default 1) |
per_page | integer | optional | Records per page | 1-200 (default 50) |
data[]) | Field | Type | Description | Valid values |
|---|---|---|---|
id | integer | Script ID | |
name | string | Script name (unique per account) | max 150 chars |
content_preview | string | Markdown content — first 200 chars | |
placeholders_detected[] | array<string> | variables detected in the content | |
used_by_campaigns[] | array<integer> | Campaign IDs currently using this script | |
created_at | datetime | Creation timestamp (ISO 8601 UTC) | |
updated_at | datetime | Last update timestamp (ISO 8601 UTC) |
Response 200:
{
"data": [
{
"id": 12,
"name": "Default Outbound Script",
"content_preview": "## Opening\nHello {{customer_name}}...",
"placeholders_detected": ["customer_name", "agent_name", "brand_name"],
"used_by_campaigns": [36, 42],
"created_at": "2026-06-06T06:00:00Z",
"updated_at": "2026-06-06T06:00:00Z"
}
],
"meta": { "current_page": 1, "last_page": 1, "per_page": 50, "total": 3 }
}content_preview is truncated to the first 200 chars. Use GET /api/telesales/scripts/{id} to fetch the full content.
GET /api/telesales/scripts/{id} — detail | Field | Type | Description |
|---|---|---|
id | integer | Script ID |
| Field | Type | Description | Valid values |
|---|---|---|---|
id | integer | Script ID | |
name | string | Script name | max 150 chars |
content | string | Full Markdown content | max 32 KB |
placeholders_detected[] | array<string> | variables parsed from content | |
used_by_campaigns[] | array<object> | Array of {id, name} for campaigns using this script | |
created_at | datetime | Creation timestamp (ISO 8601 UTC) | |
updated_at | datetime | Last update timestamp (ISO 8601 UTC) |
Response 200:
{
"data": {
"id": 12,
"name": "Default Outbound Script",
"content": "## Opening\nHello {{customer_name}}, this is {{agent_name}} from {{brand_name}}...\n\n## Body\n... talk track ...\n\n## Closing\nThank you.",
"placeholders_detected": ["customer_name", "agent_name", "brand_name"],
"used_by_campaigns": [
{ "id": 36, "name": "Summer Promo" },
{ "id": 42, "name": "Retention Q3" }
],
"created_at": "2026-06-06T06:00:00Z",
"updated_at": "2026-06-06T06:00:00Z"
}
}Response 404: { "message": "Script not found." }
POST /api/telesales/scripts — create | Field | Type | Required | Description | Valid values |
|---|---|---|---|---|
name | string | ✅ | Script name — unique per account | max 150 chars |
content | string | ✅ | Markdown content; variables are auto-detected | max 32 KB |
Body:
{
"name": "Default Outbound Script",
"content": "## Opening\nHello {{customer_name}}, this is {{agent_name}} from {{brand_name}}...\n\n## Body\n... talk track for the campaign ...\n\n## Closing\nThank you for your time."
}| Field | Type | Description | Valid values |
|---|---|---|---|
id | integer | Newly created script ID | |
name | string | Script name | |
content | string | Markdown content | |
placeholders_detected[] | array<string> | variables parsed from content | |
created_at | datetime | Creation timestamp (ISO 8601 UTC) |
Response 201:
{
"data": {
"id": 12,
"name": "Default Outbound Script",
"content": "## Opening\nHello {{customer_name}}...",
"placeholders_detected": ["customer_name", "agent_name", "brand_name"],
"created_at": "2026-06-06T06:00:00Z"
}
}Response 422 — validation cases:
// Duplicate script name
{
"message": "The given data was invalid.",
"errors": { "name": ["The name has already been taken."] }
}
// Content too long
{
"message": "The given data was invalid.",
"errors": { "content": ["The content may not be greater than 32768 characters."] }
}PUT /api/telesales/scripts/{id} — update Body: same as POST.
Response 200: the updated script object, with placeholders_detected re-computed from the new content.
Response 422 — conflict:
// Script is in use by active campaigns — update is allowed with a warning
{
"data": { "id": 12, ... },
"warnings": ["Script is currently used by 2 active campaigns. Agents will see new content on next call."]
}DELETE /api/telesales/scripts/{id} Response 204 No Content (success).
Response 422 — script is in use by an active campaign:
{
"message": "Cannot delete a script that is in use by a campaign.",
"errors": { "id": ["Script 12 is bound to campaign(s): 36, 42. Detach from campaign first."] }
}POST /api/telesales/scripts/{id}/preview — render preview | Field | Type | Description |
|---|---|---|
id | integer | Script ID to render |
| Field | Type | Required | Description | Valid values |
|---|---|---|---|---|
lead_id | integer | ✅ | Lead ID — supplies from custom_fields | Must exist |
agent_id | integer | ✅ | Agent user ID — supplies from profile | Must exist |
Body:
{
"lead_id": 78912,
"agent_id": 25
}Renders the content with each replaced by the live value from lead.custom_fields + the agent's profile + the customer's brand.
| Field | Type | Description | Valid values |
|---|---|---|---|
id | integer | Script ID | |
rendered_content | string | Markdown content with placeholders substituted | |
missing_placeholders[] | array<string> | Variables that could not be resolved from custom_fields / profile — the UI can warn the agent |
Response 200:
{
"data": {
"id": 12,
"rendered_content": "## Opening\nHello Anh Hung, this is Nguyen Van A from Zorio...\n\n## Body\n...",
"missing_placeholders": ["policy_number"]
}
}| Endpoint | Purpose |
|---|---|
GET /api/telesales/dispositions | List the customer's dispositions |
GET /api/telesales/dispositions/{id} | Detail for one disposition |
POST /api/telesales/dispositions | Create a disposition (admin) |
PUT /api/telesales/dispositions/{id} | Update a disposition (admin) |
DELETE /api/telesales/dispositions/{id} | Delete a disposition (admin) |
GET /api/telesales/dispositions — list | Field | Type | Required | Description | Valid values |
|---|---|---|---|---|
category | string | optional | Filter by outcome group | contact / no_contact / callback / remove |
is_active | bool | optional | Whether to include hidden dispositions | true (default) / false |
data[]) | Field | Type | Description | Valid values |
|---|---|---|---|
id | integer | Disposition ID | |
code | string | Immutable code — agents / API use this to tag calls | [A-Za-z0-9_-], max 32 chars |
label | string | Display label | max 100 chars |
description | string|null | Detailed description | max 500 chars |
category | string | Outcome group | contact / no_contact / callback / remove |
is_final | bool | Is this a final outcome or still retryable | true / false |
trigger_callback | bool | Auto-create a callback when the agent picks this code | true / false (true only when category=callback) |
trigger_dnc | bool | Auto-add the number to DNC when picked | true / false |
default_retry_delay_minutes | integer|null | Minutes to wait before retrying the lead if not final | 1-43200 (minutes) |
color | string | Hex color for UI badge | #RRGGBB |
is_active | bool | Whether it appears in the agent dropdown | true / false |
usage_count_30d | integer | Calls that used this code in the last 30 days | ≥ 0 |
created_at | datetime | Creation timestamp (ISO 8601 UTC) |
Response 200:
{
"data": [
{
"id": 5,
"code": "SALE-OK",
"label": "Sale Success",
"description": "Customer agreed and committed",
"category": "contact",
"is_final": true,
"trigger_callback": false,
"trigger_dnc": false,
"default_retry_delay_minutes": null,
"color": "#10b981",
"is_active": true,
"usage_count_30d": 78,
"created_at": "2026-06-01T06:00:00Z"
}
]
}GET /api/telesales/dispositions/{id} — detail | Field | Type | Description |
|---|---|---|
id | integer | Disposition ID |
Response 200: disposition object (same shape as one element of the list).
Response 404: { "message": "Disposition not found." }
POST /api/telesales/dispositions — create (admin) | Field | Type | Required | Description | Valid values |
|---|---|---|---|---|
code | string | ✅ | Immutable code — unique per account | [A-Za-z0-9_-], max 32 chars |
label | string | ✅ | Display label | max 100 chars |
description | string | optional | Detailed description | max 500 chars |
category | string | ✅ | Outcome group | contact / no_contact / callback / remove |
is_final | bool | optional | Final outcome (no retry) | true / false (default false) |
trigger_callback | bool | optional | Auto-create callback — requires category=callback | true / false (default false) |
trigger_dnc | bool | optional | Auto-add number to DNC | true / false (default false) |
default_retry_delay_minutes | integer | optional | Minutes before retrying the lead | 1-43200 |
color | string | optional | UI color | #RRGGBB |
Body:
{
"code": "SALE-OK",
"label": "Sale Success",
"description": "Customer agreed and committed",
"category": "contact",
"is_final": true,
"trigger_callback": false,
"trigger_dnc": false,
"default_retry_delay_minutes": null,
"color": "#10b981"
}| Field | Type | Description | Valid values |
|---|---|---|---|
id | integer | Newly created disposition ID | |
code | string | Immutable code | |
label | string | Display label | |
category | string | Outcome group | contact / no_contact / callback / remove |
is_final | bool | Final outcome | |
trigger_callback | bool | Auto-create callback | |
trigger_dnc | bool | Auto-add DNC | |
color | string | Color | #RRGGBB |
is_active | bool | Visible in the agent dropdown (default true) | |
created_at | datetime | Creation timestamp (ISO 8601 UTC) |
Response 201:
{
"data": {
"id": 5,
"code": "SALE-OK",
"label": "Sale Success",
"category": "contact",
"is_final": true,
"trigger_callback": false,
"trigger_dnc": false,
"color": "#10b981",
"is_active": true,
"created_at": "2026-06-06T06:00:00Z"
}
}Response 422 — validation cases:
// Duplicate code
{
"message": "The given data was invalid.",
"errors": { "code": ["The code has already been taken."] }
}
// Invalid category
{
"message": "The given data was invalid.",
"errors": { "category": ["The selected category is invalid."] }
}
// trigger_callback=true but category is not 'callback'
{
"message": "The given data was invalid.",
"errors": { "trigger_callback": ["trigger_callback only applies when category='callback'."] }
}Response 403 — caller is not an admin:
{ "message": "You are not allowed to create dispositions. The admin role is required." }PUT /api/telesales/dispositions/{id} — update (admin) | Field | Type | Description |
|---|---|---|
id | integer | Disposition ID |
Same as POST but every field is optional — send only what you want to change. code cannot be changed after creation (immutable identifier); sending a new code returns 422.
| Field | Type | Description | Valid values |
|---|---|---|---|
label | string | Display label | max 100 chars |
description | string|null | Detailed description | max 500 chars |
category | string | Outcome group | contact / no_contact / callback / remove |
is_final | bool | Final outcome | |
trigger_callback | bool | Auto-create callback (true only when category=callback) | |
trigger_dnc | bool | Auto-add DNC | |
default_retry_delay_minutes | integer|null | Retry delay in minutes | 1-43200 |
color | string | Color | #RRGGBB |
is_active | bool | Show/hide in dropdown | true / false |
Response 200: the updated object.
Response 422:
// Attempt to change code
{
"message": "The given data was invalid.",
"errors": { "code": ["Code is immutable. Create a new disposition if a different code is needed."] }
}DELETE /api/telesales/dispositions/{id} — delete (admin) | Field | Type | Description |
|---|---|---|
id | integer | Disposition ID to delete |
Response 204 No Content (success — soft delete; sets is_active=false).
Response 422 — disposition is in use:
{
"message": "Cannot delete a disposition with associated call history.",
"errors": { "id": ["Disposition 5 has 78 calls in last 30 days. Hide instead of deleting (set is_active=false)."] }
}Hide vs Delete
To preserve reporting history, delete defaults to soft delete (set is_active=false). Old dispositions still appear in reports but are hidden from the agent dropdown. Hard delete is only allowed when usage_count = 0.
Each new account is seeded with a generic catalog that is not tied to any industry. Customers can extend or replace it through the API or the admin UI.
| Code | Label | Category | is_final | trigger_callback | trigger_dnc |
|---|---|---|---|---|---|
SUCCESS | Successful Contact | contact | x | ||
CALLBACK_REQ | Customer Asked for Callback | callback | x | ||
REFUSED | Customer Refused | contact | x | ||
NO_ANSWER | No Answer | no_contact | |||
BUSY | Busy | no_contact | |||
VOICEMAIL | Voicemail Left | no_contact | |||
WRONG_NUMBER | Wrong Number | remove | x | ||
DNC_REQUEST | DNC Request | remove | x | x |
For industry-specific catalogs (customer care, service appointments, surveys, medical appointments, ...), see the separate per-customer configuration package — it will pre-seed an extended code set tailored to the business workflow.