curl --request POST \
--url https://apiv2.vodex.ai/v1/batches/rows.csv/preview \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"csv": "Mobile No,name,AMT DUE\n+14155551234,Dana,120.50\n",
"assistantId": "8f1c3b6e-9d2a-4c77-a2f1-6b0f2f4e1a90",
"mapping": {
"Mobile No": "to",
"AMT DUE": "var:balance"
}
}
'import requests
url = "https://apiv2.vodex.ai/v1/batches/rows.csv/preview"
payload = {
"csv": "Mobile No,name,AMT DUE
+14155551234,Dana,120.50
",
"assistantId": "8f1c3b6e-9d2a-4c77-a2f1-6b0f2f4e1a90",
"mapping": {
"Mobile No": "to",
"AMT DUE": "var:balance"
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
csv: 'Mobile No,name,AMT DUE\n+14155551234,Dana,120.50\n',
assistantId: '8f1c3b6e-9d2a-4c77-a2f1-6b0f2f4e1a90',
mapping: {'Mobile No': 'to', 'AMT DUE': 'var:balance'}
})
};
fetch('https://apiv2.vodex.ai/v1/batches/rows.csv/preview', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://apiv2.vodex.ai/v1/batches/rows.csv/preview",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'csv' => 'Mobile No,name,AMT DUE
+14155551234,Dana,120.50
',
'assistantId' => '8f1c3b6e-9d2a-4c77-a2f1-6b0f2f4e1a90',
'mapping' => [
'Mobile No' => 'to',
'AMT DUE' => 'var:balance'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://apiv2.vodex.ai/v1/batches/rows.csv/preview"
payload := strings.NewReader("{\n \"csv\": \"Mobile No,name,AMT DUE\\n+14155551234,Dana,120.50\\n\",\n \"assistantId\": \"8f1c3b6e-9d2a-4c77-a2f1-6b0f2f4e1a90\",\n \"mapping\": {\n \"Mobile No\": \"to\",\n \"AMT DUE\": \"var:balance\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://apiv2.vodex.ai/v1/batches/rows.csv/preview")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"csv\": \"Mobile No,name,AMT DUE\\n+14155551234,Dana,120.50\\n\",\n \"assistantId\": \"8f1c3b6e-9d2a-4c77-a2f1-6b0f2f4e1a90\",\n \"mapping\": {\n \"Mobile No\": \"to\",\n \"AMT DUE\": \"var:balance\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://apiv2.vodex.ai/v1/batches/rows.csv/preview")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"csv\": \"Mobile No,name,AMT DUE\\n+14155551234,Dana,120.50\\n\",\n \"assistantId\": \"8f1c3b6e-9d2a-4c77-a2f1-6b0f2f4e1a90\",\n \"mapping\": {\n \"Mobile No\": \"to\",\n \"AMT DUE\": \"var:balance\"\n }\n}"
response = http.request(request)
puts response.read_body{
"headers": [
"<string>"
],
"mapping": {},
"total": 123,
"valid": 123,
"errorRows": 123,
"duplicates": 123,
"errors": [
{
"line": 123,
"to": "<string>",
"reason": "<string>"
}
],
"errorsTruncated": true,
"sample": [
{
"line": 123,
"to": "<string>",
"rowRef": "<string>",
"variables": {},
"reason": "<string>",
"duplicate": true
}
]
}{
"error": "not found"
}{
"error": "not found"
}{
"error": "not found"
}Check a CSV before creating anything
What the file would do, without doing it. Runs the same parse and the
same admission POST /batches/{id}/rows.csv runs, against the prompt of
the agent you name — so a row this reports as good cannot be refused on
confirm. Writes nothing: no batch, no rows, no calls.
There is no batch id because there is no batch yet. A dry-run flag on the append route would mean creating one first, and the point of checking a file is that walking away from it costs nothing.
Duplicates are reported, not rejected. The same number twice is a mistake often enough to show and deliberate often enough that refusing it here would make the CSV path stricter than the JSON one.
curl --request POST \
--url https://apiv2.vodex.ai/v1/batches/rows.csv/preview \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"csv": "Mobile No,name,AMT DUE\n+14155551234,Dana,120.50\n",
"assistantId": "8f1c3b6e-9d2a-4c77-a2f1-6b0f2f4e1a90",
"mapping": {
"Mobile No": "to",
"AMT DUE": "var:balance"
}
}
'import requests
url = "https://apiv2.vodex.ai/v1/batches/rows.csv/preview"
payload = {
"csv": "Mobile No,name,AMT DUE
+14155551234,Dana,120.50
",
"assistantId": "8f1c3b6e-9d2a-4c77-a2f1-6b0f2f4e1a90",
"mapping": {
"Mobile No": "to",
"AMT DUE": "var:balance"
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
csv: 'Mobile No,name,AMT DUE\n+14155551234,Dana,120.50\n',
assistantId: '8f1c3b6e-9d2a-4c77-a2f1-6b0f2f4e1a90',
mapping: {'Mobile No': 'to', 'AMT DUE': 'var:balance'}
})
};
fetch('https://apiv2.vodex.ai/v1/batches/rows.csv/preview', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://apiv2.vodex.ai/v1/batches/rows.csv/preview",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'csv' => 'Mobile No,name,AMT DUE
+14155551234,Dana,120.50
',
'assistantId' => '8f1c3b6e-9d2a-4c77-a2f1-6b0f2f4e1a90',
'mapping' => [
'Mobile No' => 'to',
'AMT DUE' => 'var:balance'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://apiv2.vodex.ai/v1/batches/rows.csv/preview"
payload := strings.NewReader("{\n \"csv\": \"Mobile No,name,AMT DUE\\n+14155551234,Dana,120.50\\n\",\n \"assistantId\": \"8f1c3b6e-9d2a-4c77-a2f1-6b0f2f4e1a90\",\n \"mapping\": {\n \"Mobile No\": \"to\",\n \"AMT DUE\": \"var:balance\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://apiv2.vodex.ai/v1/batches/rows.csv/preview")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"csv\": \"Mobile No,name,AMT DUE\\n+14155551234,Dana,120.50\\n\",\n \"assistantId\": \"8f1c3b6e-9d2a-4c77-a2f1-6b0f2f4e1a90\",\n \"mapping\": {\n \"Mobile No\": \"to\",\n \"AMT DUE\": \"var:balance\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://apiv2.vodex.ai/v1/batches/rows.csv/preview")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"csv\": \"Mobile No,name,AMT DUE\\n+14155551234,Dana,120.50\\n\",\n \"assistantId\": \"8f1c3b6e-9d2a-4c77-a2f1-6b0f2f4e1a90\",\n \"mapping\": {\n \"Mobile No\": \"to\",\n \"AMT DUE\": \"var:balance\"\n }\n}"
response = http.request(request)
puts response.read_body{
"headers": [
"<string>"
],
"mapping": {},
"total": 123,
"valid": 123,
"errorRows": 123,
"duplicates": 123,
"errors": [
{
"line": 123,
"to": "<string>",
"reason": "<string>"
}
],
"errorsTruncated": true,
"sample": [
{
"line": 123,
"to": "<string>",
"rowRef": "<string>",
"variables": {},
"reason": "<string>",
"duplicate": true
}
]
}{
"error": "not found"
}{
"error": "not found"
}{
"error": "not found"
}Authorizations
Tenant API key (vdx_live_…) as Authorization: Bearer.
Query Parameters
Required unless given in the JSON body.
Body
Corrections to auto-detection, keyed by the header as
written: to, rowRef, assistantId, from, var:<name>
for a prompt variable, or "" to ignore the column. Columns
left out are detected as usual. Where two columns claim one
single-valued field the leftmost keeps it, and the resolved
mapping comes back in the response.
Show child attributes
Show child attributes
Response
The verdict — including when every row is bad. Previewing a broken file is a successful preview, and the counts are the answer.
The file's columns, in its own order.
What each column was read as, after corrections.
Show child attributes
Show child attributes
Rows under the header, blank lines excluded.
Rows that would be dialled.
Parse failures plus rejections.
Rows whose to appeared on an earlier line. Dialled anyway.
Every problem row, capped at 5,000 entries.
Show child attributes
Show child attributes
More problems than errors lists. The counts stay exact.
The first 50 lines in file order, each with its verdict.
Show child attributes
Show child attributes