The spatibase command
spatibase is a small command-line tool that moves the places in a repo into Spatibase and publishes them through the Content Delivery API. It is made for coding agents, so its output is short and plain, it never asks questions, and its errors say what to do next. You can use it yourself too. It needs Node.js 18.3 or newer and has no dependencies.
npx --yes spatibase@0.1 --help
On this page spatibase stands for npx --yes spatibase@0.1. Pin the version as shown: 0.1 keeps you on compatible releases. The prompt makes your coding agent run exactly these commands.
A typical run
spatibase create "Acme stores" # temporary project, saved in .spatibase/
spatibase model .spatibase/model.json # types and fields from a small JSON file
spatibase import public/stores.json --type store --publish # drafts, marked ready, published as a version
spatibase verify # counts in the live API
spatibase keep dev@example.com # send the link that keeps the project
Then point your app at the Delivery API address that create printed.
Where it keeps its state
spatibase create writes .spatibase/project.json in the current folder (the project id, its addresses, the secret token and the expiry) and a .spatibase/.gitignore containing *, so the folder is never committed. Run the other commands from the same folder. The token never appears in the output.
The API address is --api <url>, else the SPATIBASE_API environment variable, else https://api.spatibase.com. Setting it points the tool at another Spatibase server, for example a local one.
Commands
| Command | What it does |
|---|---|
create "<name>" [--ref <label>] [--new] |
Makes a temporary project and saves it. Prints the project id, the Delivery address, the expiry and the limits. Refuses when .spatibase/ already holds a project, unless --new |
link <code> [--new] |
Connects the folder to a project a person made in the app, with the one-time code from New project, Coding agent. Saves the project and a key in .spatibase/ like create does, but the project has no expiry and nothing to keep: keep refuses on it and status shows what the key can do. Refuses when .spatibase/ already holds a project, unless --new. The code works once, for 15 minutes |
describe |
Prints the content model: types, fields with their apiKey and kind, groups |
model <spec.json> [--preview] |
Creates fields and types from a JSON file. Safe to run again: existing apiKeys are skipped, and fields new in the file are added to existing types. The file is checked first (field kinds, locationMode, geometryKinds, parents, one kind per apiKey), so a mistake stops it before anything is created. --preview shows what would be created and changes nothing |
import <file> --type <apiKey> [options] |
Reads a file and imports it as drafts of a type, optionally marking them ready and publishing the project |
publish [--type <apiKey>] [--dry-run] |
Marks all drafts ready (500 at a time), then publishes the project as one new version. The publish ships everything that is ready, also what other people marked, plus staged unpublishes, deletes and group changes. Entries that fail validation stay drafts and are listed with the reason. --dry-run lists what would be marked and what would ship |
verify |
Compares the places in the public Delivery API with the project and lists drafts still waiting, and unpublishes, deletes and group changes staged for the next publish. Types switched off in the app are listed apart and not checked. Exit code 1 when something is not live |
status |
Shows the saved project: expiry, whether an email was given, whether it was kept, places and upload size |
settings [--name "<name>"] [--description "<text>"] [--add-locale <code>] [--remove-locale <code>] [--locales <list>] [--preview] |
Shows or changes the project’s name, description and languages. --add-locale de turns a language on and keeps the default; --locales en,de sets the whole list, the first is the default and cannot change later. Renaming keeps the project id and the API addresses |
keep <email> |
Sends Keep your project to that address (at most 3 different addresses per project) |
tools [<tool>] |
Lists the MCP tools the project’s key can use, or one tool’s description and arguments |
call <tool> ['<json>' | --args-file <file>] [--preview] |
Calls any tool of the project’s MCP server and prints the answer as JSON. A mistyped tool name is reported as unknown, with the closest names |
Options for every command: --json prints one JSON object instead of text (errors too, on stderr), --api <url>, -h or --help (spatibase <command> --help for one command), --version.
The model file
{
"types": [
{
"name": "Store", "apiKey": "store", "locationMode": "required", "geometryKinds": ["Point"],
"fields": [
{ "label": "Store id", "apiKey": "store_id", "kind": "identifier" },
{ "label": "Phone", "apiKey": "phone", "kind": "phone" },
{ "label": "Category", "apiKey": "category", "kind": "choice", "config": { "options": ["Cafe", "Shop"] } },
{ "label": "Photos", "apiKey": "photos", "kind": "images" }
]
}
]
}
- Field kinds:
text,markdown,number,boolean,date,choice,link,phone,contact,identifier,color,images,files,reference,geo,embed,location. A kind that does not exist stops the command with the closest match. Elements describes them and theirconfig. A nested value such as opening hours by day can be atextfield with"config": { "editor": "json" }. locationModeisrequired,optionalornone. Optional keys:description,parent(the apiKey of a parent type; list parents first),requiredandhelpTexton a field.- The title and the coordinates are not fields: every entry has a title, and the type’s location holds the coordinates.
- A field listed in several types is created once.
Import options
spatibase import <file> --type <apiKey> reads a .geojson or .json GeoJSON file, a .json array of objects, or a .csv or .tsv with a header row. It sends 1,000 rows at a time as drafts.
| Option | Meaning |
|---|---|
--type <apiKey> |
The type to import into (required; it must exist: run model first) |
--title <prop> |
The property that holds the title. Default name, title or label |
--map field=prop |
Fill the field field from the property prop. Repeat for several. Properties already named like a field’s apiKey need no --map |
--match-by <field> |
An identifier field: rows whose value matches update that entry instead of creating a duplicate. Use it to run an import again |
--locale <code> |
The rows are translations into this project language. Needs --match-by; creates nothing; rows without a match are skipped with a warning |
--images <prop>[=<dir>] |
The property holds image file paths. Each distinct file is read from <dir> (default ./public if it exists, else .; a leading / means “inside <dir>”), uploaded once (up to 3.5 MB each, larger ones are skipped with a warning) and the value becomes the uploaded images. The type needs an images field with that apiKey, or use --map <field>=<prop>. http(s) URLs are left as they are |
--publish |
Mark exactly the entries of this run ready and publish the project in the same run, as one version (which also ships anything already marked ready). Other drafts of the type stay drafts. Not with --locale: publish translations afterwards with publish |
--dry-run |
Check only: counts and warnings, and with --images the files are checked on disk (found, size). Nothing is written, uploaded or published |
Before anything is uploaded or written, import checks the options against the project: the type must exist, every --map field must be a field of the type, --match-by must be one of its identifier fields, --images must be an images field and --locale a non-default project language. A mistake stops the command with the fields or types that do exist. Properties the import would drop (no field with that apiKey and no --map) are listed, so you can map them.
Latitude and longitude are read from the columns lat/latitude and lng/lon/long/longitude (any case) or from a nested location or coordinates object. Rows without usable coordinates are imported without a location and skipped by types that require one, with a warning. A temporary project holds 1,000 places: further rows are not sent, with a warning. Warnings and value conversion follow the Import API.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Done |
| 1 | The command failed or, for verify, something is not live. The message says what to do |
| 2 | Wrong usage: a missing argument or an unknown option |
Examples
Import a CSV with different column names, then run it again after fixing the data:
spatibase import places.csv --type store --title shop_name --map phone=tel --map store_id=id --match-by store_id --dry-run
spatibase import places.csv --type store --title shop_name --map phone=tel --map store_id=id --match-by store_id --publish
Images stored in public/images and referenced as "/images/01.webp":
spatibase import public/stores.json --type store --images photos --publish
Translations from one file per language:
spatibase settings --locales en,de # the first is the default; later languages: --add-locale es
spatibase import i18n/stores.de.json --type store --match-by store_id --locale de
spatibase publish
Any other tool, for example the first drafts. tools shows which tools the key has and what they take:
spatibase tools
spatibase tools search_drafts
spatibase call search_drafts '{"limit": 5}'
call adds "confirm": true to the model tools (create_type, create_element, assign_elements, create_group, update_group, update_project_settings) unless you pass --preview. The import, mark_ready, publish_project and restore_version tools only dry-run unless you send "dryRun": false; prefer import and publish.
Without the command
Everything the command does is an HTTP call, for environments without Node.js:
POST https://api.spatibase.com/api/agent/projectswith{"name": "…", "ref": "…"}(no sign-in) answers201withprojectId,token,mcpUrl,deliveryUrl,appUrl,expiresAtandlimits. Thesb_temp_…token lasts 24 hours and is sent asAuthorization: Bearer <token>.GET /api/agent/projects/<id>with the token answersclaimState(unclaimedorclaimed),expiresAt,emailGiven,entriesanduploadBytes.POST /api/agent/projects/<id>/claim-emailwith{"email": "…"}sends Keep your project and answersexpiresAtandemailSentTo.- Model, import, publish and upload are tools of the project’s MCP server (
import_features,mark_ready,publish_project,upload_asset, …), called as one JSON-RPCPOSTeach.
The step-by-step version for agents is the playbook, with the curl calls in its appendix.
Related
Updated