English
English
Appearance
English
English
Appearance
A Dataset is the schema unit for a Telesales campaign — it groups together:
phase=lead) — data captured before the call (e.g. policy number, product, deal value)phase=attempt) — outcome data the agent fills in during wrap-up (e.g. promise-to-pay date, payment method, notes)disposition_in (e.g. "Commitment date" only shows when the disposition equals "Promise-to-Pay")As of 2026-08-14, every new campaign must be linked to a Dataset. Legacy campaigns were auto-linked to each tenant's default dataset. The old /api/custom-fields endpoints for the two Telesales entities have been retired (HTTP 410 Gone) — see the Custom Fields page for details.
| Endpoint | Required scope |
|---|---|
GET (read) | telesales module — viewer or above |
POST/PUT/DELETE (write) | Permission manage_telesales_dataset (admin tier) |
POST /api/v1/telesales/datasets/{code}/leads (push lead) | Sanctum PAT with ability telesales:push |
Each dataset has the following top-level attributes:
| Field | Type | Description |
|---|---|---|
id | integer | Auto-increment |
code | string | Unique per tenant; used in the push-lead URL |
name | string | Display name (e.g. "FinanceCorp Life Insurance — Renewal") |
description | string | Short summary |
industry_tag | string | null | Optional enum: insurance, finance, tourism, healthcare, realestate, survey, other |
is_default | boolean | One default dataset per tenant (auto-selected in the campaign wizard) |
is_active | boolean | false = hidden from list, cannot be used for new campaigns |
fields[] | array | List of custom field definitions (details below) |
dispositions[] | array | List of applicable disposition codes (N-N link to telesales_dispositions) |
field_count | integer | Cached count |
disposition_count | integer | Cached count |
Each field object has:
| Field | Type | Description |
|---|---|---|
id | integer | |
field_code | string | Unique per dataset, snake_case (e.g. policy_number) |
label | string | Display label |
type | string | Enum: text, textarea, number, date, datetime, select, multiselect, phone, email, boolean |
required | boolean | Required on push-lead / disposition submit |
options | array | null | For select/multiselect types: [{value, label}, ...] |
visible_when | object | null | Visibility guard, e.g. {"disposition_in": ["RN-02", "RI-02"]} |
phase | string | lead or attempt |
help_text | string | null | Hint shown below the input |
display_order | integer | Position in the form |
GET /api/v1/telesales/datasets — List datasets Returns every dataset owned by the tenant.
GET /api/v1/telesales/datasets
Authorization: Bearer <api_token>
Accept: application/jsonQuery params:
| Param | Description |
|---|---|
is_active | true (default) returns only active datasets. Pass false to include archived ones. |
industry_tag | Filter by industry (e.g. insurance) |
Response 200:
{
"data": [
{
"id": 5,
"code": "sample_life_renewal",
"name": "FinanceCorp Life Insurance — Renewal",
"description": "Premium reminder + policy reinstatement campaign",
"industry_tag": "insurance",
"is_default": true,
"is_active": true,
"field_count": 23,
"disposition_count": 14
},
{
"id": 6,
"code": "sample_consumer_loan",
"name": "SampleFin — Consumer Loan",
"industry_tag": "finance",
"is_default": false,
"field_count": 15,
"disposition_count": 10
}
]
}GET /api/v1/telesales/datasets/{id} — Dataset detail Returns full metadata plus fields and dispositions.
GET /api/v1/telesales/datasets/5
Authorization: Bearer <api_token>Response 200:
{
"data": {
"id": 5,
"code": "sample_life_renewal",
"name": "FinanceCorp Life Insurance — Renewal",
"industry_tag": "insurance",
"is_default": true,
"is_active": true,
"fields": [
{
"id": 101,
"field_code": "full_name",
"label": "Full name",
"type": "text",
"required": true,
"phase": "lead",
"display_order": 10
},
{
"id": 102,
"field_code": "policy_number",
"label": "Policy number",
"type": "text",
"required": true,
"phase": "lead",
"help_text": "Format POL-YYYY-NNNN",
"display_order": 20
},
{
"id": 201,
"field_code": "commitment_date",
"label": "Commitment date",
"type": "date",
"required": true,
"phase": "attempt",
"visible_when": {
"disposition_in": ["RN-02", "RI-02"]
},
"display_order": 10
},
{
"id": 202,
"field_code": "payment_method",
"label": "Payment method",
"type": "select",
"options": [
{ "value": "cash", "label": "Cash" },
{ "value": "bank", "label": "Bank transfer" },
{ "value": "atm", "label": "ATM card" },
{ "value": "wallet", "label": "e-Wallet" }
],
"phase": "attempt",
"visible_when": {
"disposition_in": ["RN-01", "RI-01"]
},
"display_order": 20
}
],
"dispositions": [
{
"code": "RN-01",
"label": "Renewal — Success",
"category": "converted",
"color": "#16A34A"
},
{
"code": "RN-02",
"label": "Renewal — Promise-to-Pay",
"category": "callback",
"color": "#F59E0B"
}
]
}
}POST /api/v1/telesales/datasets/{id}/fields — Create field POST /api/v1/telesales/datasets/5/fields
Authorization: Bearer <api_token>
Content-Type: application/json
{
"field_code": "policy_number",
"label": "Policy number",
"type": "text",
"required": true,
"phase": "lead",
"display_order": 20,
"help_text": "Format POL-YYYY-NNNN"
}Response 201 returns the created field.
Validation:
field_code must match ^[a-z][a-z0-9_]{0,63}$ and be unique within the datasettype must be one of the supported types listed aboveselect/multiselect, options (array of {value, label}) is requiredphase must be lead or attemptError 422 on duplicate code:
{
"error": "field_code_taken",
"message": "Field code \"policy_number\" already exists in this dataset."
}PUT /api/v1/telesales/datasets/{id}/fields/{fid} — Update field field_code, type, and phase are immutable after creation (changing them could corrupt already-stored data). Mutable attributes: label, required, options, visible_when, help_text, display_order.
PUT /api/v1/telesales/datasets/5/fields/102
Content-Type: application/json
{
"label": "Policy No. (Contract ID)",
"required": false,
"help_text": "May be empty if not yet known"
}DELETE /api/v1/telesales/datasets/{id}/fields/{fid} — Delete field Soft delete: is_active=false. Data stored under the custom_fields JSON column on leads/attempts is preserved. You can restore with PUT ... {"is_active": true}.
POST /api/v1/telesales/datasets/{id}/fields/reorder — Reorder fields POST /api/v1/telesales/datasets/5/fields/reorder
Content-Type: application/json
{
"items": [
{ "id": 101, "display_order": 10 },
{ "id": 102, "display_order": 20 },
{ "id": 103, "display_order": 30 }
]
}GET /api/v1/telesales/datasets/{id}/dispositions — Applicable dispositions Returns the disposition codes assigned to the dataset (a subset of the tenant-wide telesales_dispositions catalogue).
PUT /api/v1/telesales/datasets/{id}/dispositions — Set applicable dispositions PUT /api/v1/telesales/datasets/5/dispositions
Content-Type: application/json
{
"dispositions": [
"RN-01", "RN-02", "RN-03", "RN-04",
"RI-01", "RI-02", "RI-03",
"CB-01", "DO-01", "OT-01"
]
}POST /api/v1/telesales/datasets/{dataset_code}/leads — Push lead from an external system Used by partners to push leads from a CRM or internal system into Zorio. Auth via Sanctum PAT with the ability telesales:push.
POST /api/v1/telesales/datasets/sample_life_renewal/leads
Authorization: Bearer <api_token>
Content-Type: application/json
{
"leads": [
{
"full_name": "Alice Sample",
"phone": "0900000001",
"lead_data": {
"policy_number": "POL-2024-0001",
"annual_premium": 12000000,
"policy_status": "lapsed"
}
},
{
"full_name": "Bob Sample",
"phone": "0900000002",
"lead_data": {
"policy_number": "POL-2024-0002",
"annual_premium": 15000000
}
}
],
"campaign_code": "SAMPLE_RENEWAL_202608"
}Response 200:
{
"ok": true,
"inserted": 2,
"skipped_duplicate": 0,
"skipped_dnc": 0,
"errors": [],
"dataset": {
"id": 5,
"code": "sample_life_renewal",
"name": "FinanceCorp Life Insurance — Renewal"
},
"campaign": {
"id": 123,
"code": "SAMPLE_RENEWAL_202608",
"created": false
}
}The dataset block in the response lets clients confirm which dataset was applied — useful if the URL dataset_code was accidentally misspelled but resolved by fuzzy matching (not currently supported, kept for forward compatibility).
phone (required). Auto-normalizes 0xxx → 84xxx.lead_data must match the dataset's phase=lead schema: errors[i].error = "unknown_field"errors[i].error = "missing_required"errors[i].error = "invalid_type"skipped_duplicate++skipped_dnc++Retry-After headerWhen an agent submits a disposition through POST /api/telesales/calls/{uuid}/disposition, the custom_fields payload is validated against the campaign's dataset schema (fields where phase='attempt'). The 5-step pipeline:
unknown_field"42" → int; "true" → boolvisible_when — fields that don't match the submitted disposition are silently droppedtelesales_call_attempts.custom_fields JSONResponse 200 includes a custom_fields_report:
{
"data": { "attempt_id": 999 },
"custom_fields_report": {
"summary": {
"total_fields": 3,
"accepted": 2,
"rejected": 1,
"filtered_by_visible_when": 0
},
"errors": [
{
"field": "commitment_date",
"error": "missing_required",
"message": "Field \"Commitment date\" is required when disposition = RN-02"
}
]
}
}If your integration used to call /api/custom-fields for the two Telesales entities, see the full migration guide on the Custom Fields page.
Quick steps:
GET /api/v1/telesales/datasets → cache the tenant's dataset listGET /api/v1/telesales/datasets/{id} → cache its fields and dispositionsGET /api/custom-fields?entity=TelesalesLead with reading fields[] from the dataset (filter phase='lead')POST/PUT/DELETE /api/custom-fields with the corresponding dataset endpointdataset block in the response as confirmationimport requests
BASE = "https://api.zorio.vn"
TOKEN = "your_api_token"
headers = {"Authorization": f"Bearer {TOKEN}", "Accept": "application/json"}
# List datasets
datasets = requests.get(f"{BASE}/api/v1/telesales/datasets", headers=headers).json()
# Get schema
dataset = requests.get(f"{BASE}/api/v1/telesales/datasets/5", headers=headers).json()['data']
lead_fields = [f for f in dataset['fields'] if f['phase'] == 'lead']
# Push lead
result = requests.post(
f"{BASE}/api/v1/telesales/datasets/sample_life_renewal/leads",
headers=headers,
json={
"leads": [{"full_name": "Alice", "phone": "0900000001",
"lead_data": {"policy_number": "POL-2024-0001"}}],
"campaign_code": "SAMPLE_RENEWAL_202608"
}
).json()
print(f"Inserted {result['inserted']} into dataset {result['dataset']['name']}")const axios = require('axios');
const client = axios.create({
baseURL: 'https://api.zorio.vn',
headers: { Authorization: `Bearer ${process.env.ZORIO_TOKEN}` },
});
// Push a batch of leads
const { data } = await client.post('/api/v1/telesales/datasets/sample_life_renewal/leads', {
leads: [
{ full_name: 'Alice', phone: '0900000001',
lead_data: { policy_number: 'POL-2024-0001' } },
],
campaign_code: 'SAMPLE_RENEWAL_202608',
});
console.log(`Inserted ${data.inserted} into dataset ${data.dataset.name}`);curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"leads": [
{ "full_name": "Alice", "phone": "0900000001",
"lead_data": { "policy_number": "POL-2024-0001" } }
],
"campaign_code": "SAMPLE_RENEWAL_202608"
}' \
https://api.zorio.vn/api/v1/telesales/datasets/sample_life_renewal/leadsQ1. Do datasets support nesting (parent-child)?
A: Not yet. Each dataset is independent. Common fields must currently be duplicated across datasets.
Q2. Can I switch the dataset for a campaign that's already running?
A: Not recommended. Leads imported under the old schema will mismatch the new one. Create a new campaign with a different dataset instead.
Q3. Can I delete a dataset that a campaign uses?
A: No. The endpoint returns 422 dataset_in_use. Delete/archive the referring campaigns first.
Q4. Is bulk create/update supported for fields?
A: Not yet. Loop through individual calls. Batch support is planned for a future release.
Q5. Which webhook fires when a lead is pushed via the dataset endpoint?
A: telesales.lead.imported — one event per successful insert. Subscribe under Portal → Webhooks.