Bring your own data

BYOD (bring-your-own-data) layers let the agent read and render customer-authored GeoJSON alongside the built-in stack. A sales territories overlay, a delivery zones layer, customer pin sets, isochrone polygons computed by your backend — anything you can express as a GeoJSON FeatureCollection.

Each BYOD source becomes a byod entry in agent state. The model can then reference it by id from any unified data tool (analyseData, processData), toggle its visibility on the map, or recall its contents.

Lifecycle

Your databyod-N entry in stateRendered on the mapanalyseData / processData inputrecallByodURL-hosted .geojsonInline FeatureCollectionstate.byod.addEntry(...) addByodLayer({ url })addByodLayer({ data })no tool callupdateByodDisplaybyodEntryIDs: [id]{ id }

Three ways to ingest

1. URL-hosted layer (via the addByodLayer tool)

The most conversational path: the model is told the URL by the user and calls addByodLayer. URL fetching is policy-gated for safety — only http:// and https:// are allowed, responses are capped at 25 MB, the request times out at 15 seconds, and the body is parsed as GeoJSON before being committed to state.

addByodLayer({
label: 'Sales territories',
url: 'https://example.com/territories.geojson',
show: true,
});

The tool returns the new entry’s id (byod-0, byod-1, …) and a one-line summary the model relays back to the user.

2. Inline FeatureCollection (also via addByodLayer)

When the data is already in scope — pasted into the chat, generated by a previous tool, or held by your application — pass data instead of url:

addByodLayer({
label: 'Customer pins 2026-Q1',
data: { type: 'FeatureCollection', features: [/* … */] },
});

No fetch policy applies — the data was never out on the network. Geometry-type defaults still apply (Point → circle, LineString → line, Polygon → fill); pass layers to override.

3. Programmatic ingest (via state.byod.addEntry)

For preloading data at app startup, or attaching layers from your own UI controls outside the chat turn cycle, bypass the tool layer entirely:

agent.state.byod.addEntry(
featureCollection,
'Sales territories',
{ source: { kind: 'integrator' } },
);

This skips the URL fetch policy (no network involved) and sidesteps the tool-execution path. The returned id is the same byod-N shape, so the model can reference it later via recallByod or analyseData.

The source: { kind: 'integrator' } provenance marker tells the model that this entry came from outside the chat — useful when surfacing it via the help or recallByod tools.

Inspecting what’s loaded

// List every BYOD entry (compact summary)
recallByod();
// Full FeatureCollection of one entry
recallByod({ id: 'byod-0' });

Like every recall tool, recallByod is opt-in: it returns the full data only when an id is provided, so the LLM doesn’t accidentally pull a megabyte of features into context.

Toggling visibility

// Show one entry
updateByodDisplay({ entryIds: ['byod-0'], action: 'show' });
// Hide everything BYOD-related
updateByodDisplay({ action: 'hide', clearAll: true });

Visibility is independent of presence in state — hiding an entry doesn’t remove it from byodEntryIDs inputs to analyseData / processData. That’s deliberate: the model can compute against the data without rendering it.

Using BYOD in unified data tools

Once an entry exists, the model can pass it into analyseData / processData like any other entry kind:

analyseData({
byodEntryIDs: ['byod-0'],
placesEntryIDs: ['places-2'],
name: 'territory-coverage',
code: `
const counts = {};
for (const territory of byod.features) {
const inside = places.features.filter((p) =>
turf.booleanPointInPolygon(p, territory),
);
counts[territory.properties.name] = inside.length;
}
return counts;
`,
});

The injected byod identifier is a merged FeatureCollection over every id passed in byodEntryIDs. Mixed-geometry collections are normal — Point, LineString, and Polygon features can all live in one BYOD entry.

For the cross-kind operations the sandbox supports (turf.booleanPointInPolygon, turf.buffer, h3.polygonToCells, etc.), see Code generation .

Disabling BYOD entirely

For deployments where you don’t want any BYOD surface at all — for example, an embed where you trust only TomTom-sourced data — switch the kind off via dataEntries:

const agent = createMapAgent(map, {
model: openai('gpt-4o'),
dataEntries: {
byod: { enabled: false },
},
});

This drops addByodLayer, recallByod, and updateByodDisplay from the registry AND removes byod from analyseData / processData scope. The classifier won’t pick BYOD tools, the model won’t see them in its prompt, and your application’s state.byod slice still exists if you want to manage data programmatically (via state.byod.addEntry) — but nothing the agent does will touch it.

Custom layer styling

By default, the slice picks MapLibre layer specs from geometry type (Point → circle, LineString → line, Polygon → fill). For richer rendering, pass explicit layers to addByodLayer or addEntry:

agent.state.byod.addEntry(
featureCollection,
'High-priority territories',
{
layers: [
{ type: 'fill', paint: { 'fill-color': '#ff0000', 'fill-opacity': 0.3 } },
{ type: 'line', paint: { 'line-color': '#ff0000', 'line-width': 2 } },
],
},
);

Mixed-geometry collections get one layer per kind present unless you override.