// Customs document pack as a SequentialWorkflow: read the invoice, classify
// each line, write the declaration in the broker's format. A broker reviews
// and files it; the swarm never submits anything to a customs authority.
const SWARMS_API = "https://api.swarms.world/v1/swarm/completions"
type InvoiceLine = { description: string; qty: number; unitValueUsd: number; origin: string }
type Shipment = { reference: string; shipper: string; consignee: string; incoterm: string; lines: InvoiceLine[] }
type SwarmResponse = {
output: unknown
usage: { input_tokens: number; output_tokens: number; total_tokens: number }
}
export async function buildCustomsPack(s: Shipment): Promise<SwarmResponse> {
const lines = s.lines.map((l, i) => `${i + 1}. ${l.description}, qty ${l.qty}, $${l.unitValueUsd} each, origin ${l.origin}`).join("\n")
const res = await fetch(SWARMS_API, {
method: "POST",
headers: {
"x-api-key": process.env.SWARMS_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: `Customs Pack ${s.reference}`,
swarm_type: "SequentialWorkflow",
task:
`Shipment ${s.reference}. Shipper ${s.shipper}, consignee ${s.consignee}, ${s.incoterm}. ` +
`Invoice lines:\n${lines}\n\nProduce the entry data for the import declaration and list every line where the classification is uncertain.`,
agents: [
{
agent_name: "Invoice Reader",
system_prompt: "You extract parties, Incoterm, currency, total value and each line item from a commercial invoice into structured fields. Flag missing fields.",
model_name: "claude-haiku-5-5", // extraction, fast and cheap
max_loops: 1,
},
{
agent_name: "HS Code Agent",
system_prompt: "You classify each line to a 6-digit HS heading, state the General Rule of Interpretation you applied, and mark any line where two headings are plausible as UNCERTAIN with both candidates.",
model_name: "gpt-5.4", // classification mistakes are expensive, stronger model
max_loops: 1,
},
{
agent_name: "Declaration Writer",
system_prompt: "You fill the import entry fields (parties, Incoterm, value per line, HS per line, origin per line) as JSON and write a short cover note for the broker listing the UNCERTAIN lines first. Mark the output DRAFT.",
model_name: "claude-sonnet-5-5",
max_loops: 1,
},
],
max_loops: 1,
}),
})
if (!res.ok) throw new Error(`Swarms API ${res.status}: ${await res.text()}`)
return (await res.json()) as SwarmResponse
}
buildCustomsPack({
reference: "MSKU7781204",
shipper: "Hualian Storage Technology Co.",
consignee: "Nordwind Data Centers BV",
incoterm: "FOB Shanghai",
lines: [
{ description: "Solid-state storage units, 7.68 TB, for servers", qty: 240, unitValueUsd: 410, origin: "CN" },
{ description: "Rack rail kits, steel", qty: 60, unitValueUsd: 38, origin: "CN" },
{ description: "Power cables C13 to C14, 2 m", qty: 480, unitValueUsd: 3.2, origin: "VN" },
],
}).then((r) => {
console.log(r.output)
console.log("tokens:", r.usage.total_tokens)
})