Maps for AI agents, with no human in the loop

# Maps for AI agents, with no human in the loop

> **Note, updated 3 August 2026.** The MCP server exposed twelve tools when this was written. It exposes **32** today, and `geocode` is live on the hosted gateway. The tools named below are all still live; the list has grown since. See [/docs/mcp](/docs/mcp) for the current set.

MapMap's signup is an API call. `POST /v1/keys` with an email address and `"accept_tos": true` returns `201` and a live `snk_` key worth 1,000 calls over 72 hours, with no card, no CAPTCHA and no sales call. Prices sit at `/pricing.json` in machine-readable form, orientation sits at `/llms.txt` on every deployment, errors carry stable type URIs an agent can branch on, and the hosted MCP server at `https://mcp.mapmap.ai/mcp` needs no key to connect. An agent can find us, price us, get a key and route, with no human in the loop. That is not a demo. It is the signup flow.

## Why can't an agent buy a normal mapping API?

Because every step of the purchase assumes a human with a credit card and a calendar. Look at what the incumbents actually require before the first route comes back:

- The price is on a marketing page, rendered by JavaScript, or it is "contact us" for the tier that has the capability you need.
- The key comes from a dashboard behind a card form, a CAPTCHA and sometimes a phone number.
- The premium capability, in our case truck dimensions and ADR dangerous-goods tunnel codes, is quoted rather than published.
- The errors are English prose in a JSON body, with no stable identifier to dispatch on.

An agent has none of the things those steps demand. It has no card, no phone, no patience for a demo booking form, and no reliable way to read a price out of a rendered pricing table. So it does what models do when the facts are not available: it guesses, or it picks the provider whose documentation it could actually parse.

We took the opposite position. Every step a human takes to become a customer has a machine equivalent, published at a fixed path, and served by every deployment including your own self-hosted one.

## How does an agent get from cold start to a route?

Six steps, all plain HTTP, and no human at our end in any of them.

| Step | Call | What comes back |
|---|---|---|
| 1. Orient | `GET /llms.txt` | What the platform does, endpoint names, the instructions-for-agents block. Every gateway deployment serves its own, alongside `/openapi.json`. |
| 2. Price | `GET /pricing.json` | Current prices, bands, quotas and unit rules. Do not scrape the marketing pages. |
| 3. Key | `POST /v1/keys` | `201` with a provisional `snk_` key: 1,000 calls, 72 hours, card-free |
| 4. Call | `Authorization: Bearer snk_…` | Routing, matrix, isochrone, map-matching and ADR checks; geocoding on deployments that enable it |
| 5. Check | `GET /v1/keys/self` | Key state, quota, remaining calls, prepaid balance. Consumes no quota, no credit, no rate limit |
| 6. Handle | `402` and `429` | On `402`, read the x402 body. On `429`, honour `Retry-After` |

Step 3 is the one people do not believe until they run it:

```sh
curl -fsS -X POST "https://api.mapmap.ai/v1/keys" \
-H "Content-Type: application/json" \
-d '{"email": "you@example.co.uk", "accept_tos": true}'
```

```json
{
"key": "snk_…",
"key_id": "3f1c…-uuid",
"state": "provisional",
"calls": 1000,
"expires_in_h": 72,
"verify": "link sent to email; clicking upgrades this key to the free tier",
"tos": "https://mapmap.ai/terms"
}
```

The key is shown once, so store it on receipt. It works immediately, rate-limited to 60 requests a minute, and it expires after 72 hours if nothing verifies it. A magic link goes to the email address separately, and clicking it upgrades that same key to the free tier: 50,000 calls a month, commercial use allowed, still no card. Re-issuing with the same address is safe, because the verification link upgrades every provisional key issued to that email. It is not unlimited: an identity holds at most five active keys, and a single IP can issue three a day. The sixth key is a `409` (`urn:sn-gateway:problem:key-cap`) and the fourth issuance in a day is a `429` (`urn:sn-gateway:problem:issuance-velocity`), so an agent should key once and store it rather than loop.

That verification step is the one place a mailbox is still assumed. An agent with mail access clears it alone. An agent without one gets 1,000 calls and 72 hours to prove the integration works, which is enough to decide.

Step 5 is how an agent knows where it stands without spending anything:

```sh
curl -fsS "https://api.mapmap.ai/v1/keys/self" \
-H "Authorization: Bearer $API_KEY"
```

```json
{
"key_id": "3f1c…-uuid",
"state": "provisional",
"monthly_quota": 1000,
"used_this_month": 1,
"remaining": 999,
"credits_pence": 0,
"credits_millipence": 0,
"expires_at": "2026-07-18T09:00:00Z",
"identity_keys": 1
}
```

Trimmed above: the live response also carries the offline-download allowance and the monthly-active-user counters. `expires_at` appears on provisional keys only, so its absence is how an agent tells a verified key from an unverified one without parsing `state`.

Polling this endpoint costs nothing against quota, credit or the rate limit. That is deliberate: an agent watching for its own verification to land, or watching a prepaid balance before a large job, should not have to spend calls to do it. `credits_millipence` is the full-precision balance, because 1p is 1,000 millipence and per-call prices are fractional.

## What does it cost, and what happens when the free tier runs out?

Prices are published per call, both classes, no quote required. These reconcile with `/pricing.json`, which wins if this page ever drifts.

| Class | Applies to | 50k to 1M/month | 1M to 10M | Beyond 10M |
|---|---|---|---|---|
| Standard | Car, van, bus, two-wheeler and pedestrian routing, geocoding, matrix, isochrone, map-matching | 0.05p | 0.03p | 0.02p |
| Premium | Truck routing and ADR compliance | 1p | 0.7p | 0.4p |

The class is detected per request, so an agent never picks a plan. The free tier is 50,000 included calls a month after verification, and a premium call draws 20 included calls, which makes all-premium free usage 2,500 calls a month. Size-metered requests are explicit: a matrix bills one call per started block of 25 elements and caps at 10,000 elements per request, an isochrone bills one call per contour with a maximum of 10, an optimisation bills a flat 10 calls.

Past the free tier, the answer depends on the key's history, and the distinction matters more than it looks:

```json
{
"x402Version": 1,
"error": "free tier of 50000 calls/month exhausted and prepaid balance is below the request price of 1p",
"accepts": [],
"instructions": "x402 payment is not enabled on this deployment yet; top up prepaid credits at https://api.mapmap.ai/account to continue"
}
```

That `402` is the x402 wire format, not our usual problem+json, so x402 clients parse it directly. A verified key that has never bought credit gets something different: a `429` with problem `type` `urn:sn-gateway:problem:quota-exceeded`, carrying `quota` and `used`. So an agent should not sit waiting for a `402` to discover payment exists. On a quota-exceeded `429`, top up. After the first top-up, an insufficient balance produces `402`s. There is no post-paid overage on any path, which is the point: no credit means the calls stop, never a surprise invoice. The full `402`-versus-`429` matrix is in [the machine payments guide](/docs/x402).

## MCP tools are what an agent can do; skills are what an agent knows

MCP tools let an agent act on the platform; agent skills teach it how to build against one. Conflating the two is why agent integrations produce code that compiles and is still wrong.

The [MCP server](/docs/mcp) is the action layer. Twelve tools: `route`, `check_adr_tunnel`, `geocode`, `matrix`, `optimise_routes`, `order_stops`, `list_style_layers`, `get_style`, `create_style`, `set_palette`, `set_layer_paint`, `check_style_contrast`. All carry full JSON Schemas and structured outputs, so a model calls them correctly first try, and tool errors name the exact problem so it can self-correct. The hosted endpoint speaks streamable HTTP, takes no key to connect and runs under fair use:

```sh
claude mcp add --transport http mapmap https://mcp.mapmap.ai/mcp
```

Cursor, Claude Desktop, Codex, VS Code, Windsurf and the Claude API MCP connector each take the same URL, with no command and no environment variables. Configs for all of them are on [the agents page](/agents), and the build story is in [our MCP server release note](/news/mcp-server).

The [agent skills](/docs/skills) are the knowledge layer, installed separately:

```sh
npx skills add Mapmapai/mapmap-agent-skills
```

Ten Markdown modules, MIT licensed, carrying no credentials and making no network calls, from `mapmap-mcp-setup` and `mapmap-truck-adr-routing` through `mapmap-fleet-optimisation`, `mapmap-x402-payments` and `mapmap-self-host-ops`. A tool lets an agent execute a route. A skill is what stops it burning three attempts finding out that truck parameters `400` on any profile other than `truck`, that bare coordinates on the compatible endpoint are `lon,lat`, that setting `tunnel_code: "D"` already implies `hazmat`, and that slashed codes like `B/D` must be URL-encoded. Install the tools so the agent can act, install the skills so it acts well.

## Why do errors carry stable type URIs?

So an agent branches on an identifier instead of parsing English that we reserve the right to reword. Every gateway error is RFC 9457 `application/problem+json` with a `type` of `urn:sn-gateway:problem:<slug>`.

| `type` | Status | An agent should |
|---|---|---|
| `urn:sn-gateway:problem:unauthorized` | 401 | Reissue at `POST /v1/keys` |
| `urn:sn-gateway:problem:quota-exceeded` | 429 | Verify email or top up credit, do not retry |
| `urn:sn-gateway:problem:rate-limited` | 429 | Back off for `retry_after` seconds, also sent as `Retry-After` |
| `urn:sn-gateway:problem:optimisation-too-large` | 422 | Split the problem, using `max_locations` and `locations` from the body |
| `urn:sn-gateway:problem:costing-conflict` | 400 | Drop the fields named in `conflicting_fields` |

Two `429`s that mean opposite things (wait a second, versus stop and pay) are distinguishable by URN rather than by reading the `detail` string. There are exactly two exceptions to the envelope and we name them rather than hide them: the OSRM-compatible URL endpoint uses its own `{"code": …, "message": …}` shape because OSRM clients dispatch on `code`, and the `402` uses the x402 format above.

## What is not live yet, and what it does not do

Four things fall short of the pitch above, and they are the ones a sales page would leave out.

- **x402 inline payment is not switched on for the hosted gateway.** The gateway implements the standard server-side (protocol version 1, HTTP transport), but accepting payments is configuration-gated on `SN_X402_*`. Without it a deployment is x402-ready: identical wire shape, `accepts` empty, and an `X-PAYMENT` header ignored entirely, so do not retry with payment. Prepaid Stripe credit is the settlement rail that works on every deployment today. Dynamic per-request quoting and the `POST /v1/x402/topup` bridge need a further variable, `SN_X402_MICROUSD_PER_PENCE`, on top of that base configuration. Check [the machine payments guide](/docs/x402) rather than trusting this paragraph, because it changes the day we flip it.
- **The hosted MCP endpoint is for evaluation, not production traffic.** `route`, `matrix`, `check_adr_tunnel`, `list_style_layers` and `get_style` work today. `geocode`, `optimise_routes` and `order_stops` answer with tool errors until their upstreams, the geocoder and the optimisation solver, are enabled on that deployment, and style publishes are metered and may be unavailable. For production, call the REST API with your own key or run the server against your own deployment.
- **The `sn-mcp` binary is not on a package registry.** You build it from source or run it from the self-host distro's Docker image.
- **Provisional keys are limited on purpose.** They cannot draw prepaid credit and cannot download offline territory layers. Verify the email and both open up.

## Try it

Issue a key with the `curl` above, then read `/llms.txt` and `/pricing.json` on whichever deployment you are pointed at, because both are per-deployment rather than global. Connect the MCP server with one command, add the skills with one more, and if you would rather not depend on our uptime at all, run the same gateway, engine and MCP server yourself from the Docker distro. Every distributable is permissive-licence only.

Routing derives from OpenStreetMap, so credit "© OpenStreetMap contributors" when you render or republish it.