Run a batch end to end in TypeScript
⚠️
Preview — this feature is being built; the design below is the spec.
The same overnight classification job in Node.js 20+, using built-in fetch.
import { readFile, writeFile } from 'node:fs/promises'
const API = 'https://app.syntic.ai/v1'
const H = { Authorization: `Bearer ${process.env.SYNTIC_API_KEY}` }
// 1. Build the JSONL input
const tickets = await loadTickets() // your data
const lines = tickets.map((t) => JSON.stringify({
custom_id: `ticket-${t.id}`,
endpoint: '/v1/chat/completions',
body: {
model: 'Amara Quick',
max_tokens: 5,
messages: [
{ role: 'system', content: 'Reply with one word: billing, technical, account or other.' },
{ role: 'user', content: t.text },
],
},
}))
await writeFile('tickets.jsonl', lines.join('\n') + '\n')
// 2. Upload
const form = new FormData()
form.append('purpose', 'batch')
form.append('file', new Blob([await readFile('tickets.jsonl')]), 'tickets.jsonl')
const file = await (await fetch(`${API}/files`, { method: 'POST', headers: H, body: form })).json()
// 3. Create the batch
let batch = await (await fetch(`${API}/batches`, {
method: 'POST',
headers: { ...H, 'Content-Type': 'application/json', 'Idempotency-Key': 'tickets-2026-11-05' },
body: JSON.stringify({ input_file_id: file.id }),
})).json()
// 4. Wait
while (!['completed', 'expired', 'canceled', 'failed'].includes(batch.status)) {
await new Promise((r) => setTimeout(r, 60_000))
batch = await (await fetch(`${API}/batches/${batch.id}`, { headers: H })).json()
}
// 5. Stream the results line by line
const res = await fetch(`${API}/batches/${batch.id}/results`, { headers: H })
const labels = new Map<string, string>()
const failed: string[] = []
let buf = ''
for await (const part of res.body!.pipeThrough(new TextDecoderStream())) {
buf += part
let i
while ((i = buf.indexOf('\n')) >= 0) {
const line = buf.slice(0, i); buf = buf.slice(i + 1)
if (!line) continue
const row = JSON.parse(line)
if (row.result.type === 'succeeded') labels.set(row.custom_id, row.result.response.body.choices[0].message.content.trim())
else failed.push(row.custom_id)
}
}
console.log(labels.size, 'classified;', failed.length, 'to resubmit')See Batches for limits and pricing.
Was this page helpful?