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.

bash
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

bash
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

json
{
  "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 their config. A nested value such as opening hours by day can be a text field with "config": { "editor": "json" }.
  • locationMode is required, optional or none. Optional keys: description, parent (the apiKey of a parent type; list parents first), required and helpText on 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:

bash
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":

bash
spatibase import public/stores.json --type store --images photos --publish

Translations from one file per language:

bash
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:

bash
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/projects with {"name": "…", "ref": "…"} (no sign-in) answers 201 with projectId, token, mcpUrl, deliveryUrl, appUrl, expiresAt and limits. The sb_temp_… token lasts 24 hours and is sent as Authorization: Bearer <token>.
  • GET /api/agent/projects/<id> with the token answers claimState (unclaimed or claimed), expiresAt, emailGiven, entries and uploadBytes.
  • POST /api/agent/projects/<id>/claim-email with {"email": "…"} sends Keep your project and answers expiresAt and emailSentTo.
  • 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-RPC POST each.

The step-by-step version for agents is the playbook, with the curl calls in its appendix.

Updated

Docs support

How can we help?

A person on our team reads every message. We include the page you are on, so you don't have to explain where.

Or write to hello@spatibase.com. For outages, check System status.

Talk to us

Not sure it’s a fit? Let’s find out

Tell us about your use case and what you need, from data model to security and deployment. In 30 minutes we’ll look at it together and tell you honestly if we can help.

Or write to hello@spatibase.com.