Chunk
POST /v1/chunk turns a GAEB file into embed-ready chunks. The chunking is
hierarchy-based, not length-based: chunk boundaries follow the
bill-of-quantities structure, and overlap is unnecessary because a position or
group is already a semantically closed unit. How much structure and context
each chunk carries is your decision, made through the request parameters.
Pass a model and the chunks come back vectorized in the same call, ready
to store in your index. Without a model, you get text chunks that pipe into
/v1/embeddings whenever you choose. Billing
follows inference: text-only chunking is free, embedded chunks bill their
tokens once at the model’s rate (see Pricing).
Request
Section titled “Request”curl https://api.flinq.ai/v1/chunk \ -H "Authorization: Bearer flq_your_key_here" \ -F "file=@LV_Rohbau.x83" \ -F "level=position" \ -F "context=path" \ -F "model=flinq-pilot-otter"import requests
with open("LV_Rohbau.x83", "rb") as f: r = requests.post( "https://api.flinq.ai/v1/chunk", headers={"Authorization": "Bearer flq_your_key_here"}, files={"file": f}, data={"level": "position", "context": "path", "model": "flinq-pilot-otter"}, )chunks = r.json()["chunks"] # each chunk carries text, metadata and its vectorimport { readFile } from 'node:fs/promises';
const form = new FormData();form.append('file', new Blob([await readFile('LV_Rohbau.x83')]), 'LV_Rohbau.x83');form.append('level', 'position');form.append('context', 'path');form.append('model', 'flinq-pilot-otter');
const r = await fetch('https://api.flinq.ai/v1/chunk', { method: 'POST', headers: { Authorization: 'Bearer flq_your_key_here' }, body: form,});const { chunks } = await r.json(); // each chunk carries text, metadata and its vector| Parameter | Default | Meaning |
|---|---|---|
level | position | position makes one chunk per BoQ position. group:N makes one chunk per category subtree at hierarchy depth N, merging everything below it. |
context | path | Inherited context prepended to each chunk: none, path (the group-label chain, e.g. Rohbau > Beton) or headers (the full inherited group descriptions). |
include_long_text | true | Whether long texts and sub-descriptions join the chunk text. |
max_chunk_size_tokens | 800 | A hard cap. Chunks over it split at sentence boundaries within their position or group; a single over-cap sentence (tables, schedules) splits at whitespace as a last resort, with a warning. Hierarchy is never merged across chunks, only split within; each part repeats the context prefix and carries a part index. |
model | none | Embedding model id (list with GET /v1/models). When set, every chunk comes back with its vector, billed once at the model’s rate. Without it, chunking is free and returns text only. |
output | text / both | What each chunk carries: text, embeddings or both. Defaults to text without a model and both with one. embeddings drops the chunk text to slim the payload; metadata always stays. Contradictory combinations (vectors without a model, a model with output=text) return 422. |
dimensions | native | Matryoshka truncation for the chunk vectors, exactly as on /v1/embeddings. Needs a model. |
Which knobs are right depends on your corpus and your retrieval task. Category
headers sometimes carry the meaning a position text lacks (context=headers),
and sometimes they are noise (context=none). A coarse index wants
level=group:2; position-level matching wants the default.
Response
Section titled “Response”{ "object": "chunk", "format": "gaeb", "strategy": { "type": "gaeb_hierarchy", "version": 1, "level": "position", "context": "path", "include_long_text": true, "max_chunk_size_tokens": 800 }, "model": "flinq-pilot-otter", "chunks": [ { "index": 0, "text": "Rohbau > Beton\n01.02.0010 Ortbeton C25/30\nOrtbeton der Festigkeitsklasse C25/30, Expositionsklasse XC2.", "tokens": 46, "metadata": { "rno": "01.02.0010", "path": ["Rohbau", "Beton"], "unit": "m2", "qty": 12.5, "part": 0 }, "embedding": [0.0182, -0.0441, 0.0093] } ], "warnings": [], "usage": { "prompt_tokens": 4321, "total_tokens": 4321 }}Chunk metadata keeps the position number, the group path, unit and quantity,
so you can store them alongside the vector and filter or attribute results
later without re-parsing the file. usage reports the billed tokens: the
embedded chunk tokens when a model was given, 0 for free text-only
chunking (each chunk still carries its own tokens count for sizing).
The strategy contract
Section titled “The strategy contract”Chunk boundaries are a contract, not an implementation detail: if they moved
silently, chunks embedded today would stop lining up with chunks embedded next
month, and retrieval against a stored index would quietly degrade. The
strategy object echoes every effective parameter plus a version, and any
change that would move chunk boundaries bumps that version. A stored index
depends on the pair (model, strategy.version): store both with your index
and re-chunk on your own schedule when either changes.
Recipe: file to searchable index in one call
Section titled “Recipe: file to searchable index in one call”curl https://api.flinq.ai/v1/chunk \ -H "Authorization: Bearer flq_your_key_here" \ -F "file=@LV_Rohbau.x83" \ -F "context=path" \ -F "model=flinq-pilot-otter" \ -F "dimensions=256"import requests
with open("LV_Rohbau.x83", "rb") as f: chunks = requests.post( "https://api.flinq.ai/v1/chunk", headers={"Authorization": "Bearer flq_your_key_here"}, files={"file": f}, data={"context": "path", "model": "flinq-pilot-otter", "dimensions": "256"}, ).json()["chunks"]
for c in chunks: index.upsert(id=c["metadata"]["rno"], vector=c["embedding"], payload=c["metadata"])import { readFile } from 'node:fs/promises';
const form = new FormData();form.append('file', new Blob([await readFile('LV_Rohbau.x83')]), 'LV_Rohbau.x83');form.append('context', 'path');form.append('model', 'flinq-pilot-otter');form.append('dimensions', '256');
const { chunks } = await ( await fetch('https://api.flinq.ai/v1/chunk', { method: 'POST', headers: { Authorization: 'Bearer flq_your_key_here' }, body: form, })).json();
for (const c of chunks) { await index.upsert({ id: c.metadata.rno, vector: c.embedding, payload: c.metadata });}Prefer the two-step flow (chunk first, embed later, perhaps through the OpenAI
SDK) when you want to inspect or post-process chunk texts before spending
tokens on them; the produced chunks pipe into /v1/embeddings unchanged, and
text-only chunking costs nothing.
Billing
Section titled “Billing”Billing follows inference. Text-only chunking (no model) is free. With a
model, the produced chunk tokens bill once at that model’s standard rate,
exactly as if you had sent them to /v1/embeddings, never twice. Note that
context=headers repeats header text into every chunk by design, which raises
the embedded token count accordingly.