---
title: Shared Forest API and MCP developer portal
description: Shared Forest API and MCP docs: keyless REST API, MCP servers, WebMCP tools and NLWeb for the Shared Forest.
canonical: https://sharedforest.com/developers
last-updated: 2026-09-23
---

# Shared Forest API and MCP developer portal

Shared Forest is a free, shared illustrated forest that grows from its traffic. Every page load adds growth energy, and every visitor, human or AI agent, can plant one tree per day.

This page is the reference for the Shared Forest REST API, the Shared Forest MCP servers and the in-page WebMCP tools. Reading and planting work without an account and without API keys. Sponsoring trees over MCP needs a Shared Forest account connected with OAuth 2.1.

## Quickstart

1. Read the live forest: `GET /api/forest`.
2. Plant a tree for your user: `POST /api/visit` with `{"plant": true}`.
3. Watch the tree appear on the [live canvas](https://sharedforest.com/) within seconds.

```sh
curl -s "https://sharedforest.com/api/forest?limit=5"
curl -s -X POST https://sharedforest.com/api/visit -H 'Content-Type: application/json' -H "Idempotency-Key: $(uuidgen)" -d '{"plant":true}'
curl -s -X POST https://sharedforest.com/ask -H 'Content-Type: application/json' -d '{"query":"how many trees grow in the forest?"}'
```

## Authentication

The REST read endpoints, `POST /api/visit`, `/ask` and the docs MCP server need no credentials. CORS allows every origin.

The product MCP server at https://sharedforest.com/mcp is an OAuth 2.1 protected resource. Its read tools stay anonymous. start_sponsorship needs scope `sponsor:write`; get_order needs `account:read` or `sponsor:write`; my_orders and my_trees need `account:read`. Without a token these tools answer HTTP 401 with `WWW-Authenticate: Bearer resource_metadata="https://sharedforest.com/.well-known/oauth-protected-resource/mcp"`. MCP clients such as Claude, Cursor and VS Code then run the flow by themselves.

1. Discover: [protected resource metadata](https://sharedforest.com/.well-known/oauth-protected-resource/mcp) (RFC 9728) and [authorization server metadata](https://sharedforest.com/.well-known/oauth-authorization-server) (RFC 8414).
2. Register: `POST /oauth/register` (Dynamic Client Registration, RFC 7591). Redirect URIs: https, loopback http or a native app scheme.
3. Authorize: send the user to `/oauth/authorize` with `response_type=code`, PKCE `code_challenge_method=S256` (required, `plain` is rejected) and the exact redirect URI. The user logs in, checks your client name, redirect host and scopes, and sets a spending limit (max EUR per order and per day).
4. Exchange: `POST /oauth/token` with `grant_type=authorization_code` and the `code_verifier`. Access tokens last 15 minutes. Refresh tokens rotate on every use.
5. Call: `Authorization: Bearer <access_token>` on `POST /mcp`. Orders from start_sponsorship belong to the user's account. The user still confirms and pays each order on `confirm_url`.
6. Revoke: `POST /oauth/token` with `token=<refresh_token>` (RFC 7009).

Step-by-step walkthrough with curl: [auth.md](https://sharedforest.com/auth.md).

## Endpoints

| Method and path | Purpose | Side effects |
|---|---|---|
| `GET /api/forest` | Live snapshot of trees, groves and traffic stats | None |
| `POST /api/visit` | Record a visit or plant a tree | Queues one visit |
| `GET /ask`, `POST /ask` | NLWeb question endpoint | None |
| `POST /mcp` | Product MCP server (JSON-RPC) | `plant_tree` queues a visit, `design_tree` and `start_sponsorship` create records |
| `POST /mcp/docs` | Docs MCP server (JSON-RPC) | None |
| `GET /api/v1/pricing`, `GET /api/v1/pricing/quote` | Sponsorship prices | None |
| `POST /api/v1/designs`, `GET /api/v1/designs/{id}` | Design a sponsored tree | Creates a design (3 per day) |
| `GET /api/v1/spots`, `POST /api/v1/holds`, `DELETE /api/v1/holds/{id}` | Find, hold and release spots | Holds spots for 30 minutes |
| `POST /api/v1/orders`, `GET /api/v1/orders/{id}` | Start and read a sponsorship order | Creates an unpaid order |
| `POST /api/v1/orders/{id}/confirm` | Human confirmation (confirm page only) | Opens Stripe Checkout |
| `POST /api/v1/orders/{id}/extend` | 30 more minutes for an unconfirmed agent order, once | Extends the holds |
| `POST /api/v1/orders/{id}/withdraw` | Withdrawal function (§ 356a BGB) | Cancels the order |

Machine-readable reference: [OpenAPI 3.1](https://sharedforest.com/openapi.json) and the [API catalog](https://sharedforest.com/.well-known/api-catalog). `/api/forest` and `/api/visit` also answer under the version prefix `/api/v1/`.

### GET /api/forest

Returns the trees and groves nearest the centre plus traffic stats. `truncated` is true when the world holds more: read them with `GET /api/v1/chunks?bbox=`. The response is cached for 30 seconds. Query parameters, all optional:

- `limit`: trees per page, 1-500. Turns on pagination.
- `cursor`: the `nextCursor` value of the previous page.
- `bbox`: `minX,minY,maxX,maxY` in world coordinates. Only trees inside the box.

```sh
curl -s "https://sharedforest.com/api/forest?limit=2"
```

Example response (values are illustrative):

```json
{
  "trees": [
    { "id": "3f9a1c2e", "type": "oak", "x": 4012.5, "y": 3988.1, "plantedAt": 1790150400000, "growthStage": 3, "origin": "human" },
    { "id": "b71d04aa", "type": "pine", "x": 4101.0, "y": 4050.7, "plantedAt": 1790150460000, "growthStage": 1, "origin": "agent" }
  ],
  "groves": [{ "id": "c20e9f11", "x": 3950, "y": 4020, "level": 1, "treesAbsorbed": 6 }],
  "stats": { "humans": 1520, "agents": 88, "bots": 301, "plantedToday": 12 },
  "visitors": 1909,
  "uniqueVisitors": 640,
  "generatedAt": "2026-09-23T10:00:00.000Z",
  "nextCursor": "bzoy",
  "limit": 2,
  "totalTrees": 57
}
```

### POST /api/visit

Records one visit. `{"plant": false}` adds anonymous growth energy and stores no identifier. `{"plant": true}` plants one tree per visitor per day. Bots never plant: their response has `"plant": false`.

```sh
curl -s -X POST https://sharedforest.com/api/visit -H 'Content-Type: application/json' -H 'Idempotency-Key: my-retry-key-1' -d '{"plant":true}'
```

```json
{ "queued": true, "visitId": "<uuid v4>", "plant": true }
```

The tree appears within seconds. The live WebSocket feed sends a `visit_result` event with the same `visitId` and the outcome `planted`, `grew`, `absorbed`, `duplicate` or `counted`.

### GET or POST /ask

NLWeb endpoint. It answers from the live stats and these documents. Set `streaming=true` (GET) or `prefer.streaming` (POST) for Server-Sent Events.

```sh
curl -s "https://sharedforest.com/ask?query=how+many+agents+visited"
```

```json
{ "query_id": "q-1", "_meta": { "response_type": "list", "version": "1.0" }, "results": [{ "url": "https://sharedforest.com/api/forest", "name": "Shared Forest live stats", "site": "https://sharedforest.com", "score": 1, "description": "The forest has 57 trees and 3 groves." }] }
```

## Sponsor trees via API or MCP

A sponsorship puts 1-100 custom-designed trees in the forest for 12 months. Each tree shows a display name (1-32 characters) and an optional https link, labelled "Sponsored". The name goes live after automatic checks. The link goes live after a manual review. Prices include VAT. See [pricing](https://sharedforest.com/pricing.md).

| Trees per order | Unit price | Renewal per tree | Discount | Example |
|---|---|---|---|---|
| 1-4 | 9.00 EUR | 4.50 EUR | 0 % | 1 tree = 9.00 EUR |
| 5-9 | 7.80 EUR | 3.90 EUR | 13 % | 5 trees = 39.00 EUR |
| 10-24 | 6.90 EUR | 3.45 EUR | 23 % | 10 trees = 69.00 EUR |
| 25-99 | 5.96 EUR | 2.98 EUR | 34 % | 25 trees = 149.00 EUR |
| 100 | 4.90 EUR | 2.45 EUR | 46 % | 100 trees = 490.00 EUR |

Steps:

1. Quote: MCP `get_price_quote` or `GET /api/v1/pricing/quote?quantity=5`.
2. Design: MCP `design_tree` with a wish (1-200 characters) or `POST /api/v1/designs` `{"wish": "..."}`. Returns a `design_id` (`dsn_...`). Max 3 designs per caller per day. One design for all trees, or one per tree.
3. Place (optional): MCP `find_spots` or `GET /api/v1/spots?quantity=5&near=4096,4096`. REST callers hold spots with `POST /api/v1/holds` and free unused ones with `DELETE /api/v1/holds/{id}`. Omit spots and the server picks free ones.
4. Order: MCP `start_sponsorship` (needs an OAuth token with scope `sponsor:write`; the order belongs to the user's account and must fit the spending limit set at consent) or `POST /api/v1/orders` with `channel: "mcp"` or `"api"` and an `Idempotency-Key`. The order is unpaid. It expires 30 minutes after creation unless the human confirms. `POST /api/v1/orders/{id}/extend` adds 30 minutes once; the confirm page does this when it opens. The response has `confirmUrl` (https://sharedforest.com/sponsor/confirm/ord_...).
5. Hand over: give `confirmUrl` to your user. The user accepts the terms, gives the withdrawal consent and pays on Stripe. That page calls `POST /api/v1/orders/{id}/confirm`.
6. Follow up: MCP `get_order` or `GET /api/v1/orders/{id}` every 10 seconds or more until `status` is `fulfilled`. `refund_pending` means paid but not plantable: the operator refunds. `treeIds` then lists the new trees. Share https://sharedforest.com/?tree=<id>.

```sh
curl -s "https://sharedforest.com/api/v1/pricing/quote?quantity=5"
curl -s -X POST https://sharedforest.com/api/v1/designs -H 'Content-Type: application/json' -d '{"wish":"a silver birch with warm lanterns"}'
curl -s -X POST https://sharedforest.com/api/v1/orders -H 'Content-Type: application/json' -H "Idempotency-Key: $(uuidgen)" \
  -d '{"quantity":5,"designIds":["dsn_abc123"],"displayName":"Ada","email":"ada@example.com","channel":"api"}'
```

What an agent can do: quote, design, select spots, create an unpaid order, read its status.
What an agent cannot do: accept the terms, give the withdrawal consent or pay for a consumer. German consumer law requires the consumer's own click on "Order and pay" (§ 312j (3) BGB) and the consumer's own express consent before the display starts (§ 356 (5) BGB). So a human always confirms on `confirmUrl`.

Business buyers (phase 2, off until announced): `POST /api/v1/orders/{id}/pay` with the buyer's EU VAT id answers `402` with a Machine Payments Protocol challenge (`WWW-Authenticate: Payment`). Retry with a Stripe Shared Payment Token. This path is B2B only because the consumer rules above do not apply to businesses. It appears in the OpenAPI document only when it is on.

Errors are problem+json: `422` blocked name, link or wish; `409` spot taken or sold out; `429` rate limit or cap (3 designs per caller per day, 100 held spots per IP, 3 open orders per email, 300 unconfirmed spots in total); `502` payment provider down; `503` sales not open yet.

## Pagination

`GET /api/forest` without parameters returns the trees nearest the centre (`truncated` shows when more exist). A `bbox` covers the whole world. Add `limit`, `cursor` or `bbox` to get one page. The page adds `nextCursor`, `limit` and `totalTrees`. Send `nextCursor` as `cursor` until it is `null`. The MCP tool `list_trees` uses the same cursor.

## Idempotency

Send an `Idempotency-Key` header on `POST /api/visit` (1-255 visible ASCII characters, a UUID is best). A repeat with the same key within 24 hours returns the first response and the header `Idempotency-Replayed: true`. It queues no second visit. The MCP tool `plant_tree` accepts the same key as `idempotency_key`.

## Rate limits

- 120 requests per minute per IP address on the API, the MCP servers and /ask.
- Every response carries `RateLimit` and `RateLimit-Policy` headers.
- A `429` response carries `Retry-After` in seconds.
- One planted tree per visitor per day. More plant requests on the same day add growth energy only.
- Sponsorship caps (429): 3 designs per caller per day, 100 held spots per IP address, 3 open orders per email, 300 unconfirmed spots in total. Release unused holds with `DELETE /api/v1/holds/{id}`.
- MCP request bodies over 16 KiB and /ask bodies over 4 KiB return `413`.

## Errors

Every API error is `application/problem+json` (RFC 9457). The `detail` field says how to fix the request.

```json
{ "type": "about:blank", "title": "Bad Request", "status": 400, "detail": "limit must be an integer between 1 and 500." }
```

| Status | When |
|---|---|
| 400 | Invalid query parameter, body or Idempotency-Key |
| 402 | MPP pay endpoint without a valid payment credential (business buyers only) |
| 403 | MCP request with an untrusted Origin header |
| 404 | Unknown API path, design or order |
| 405 | Wrong method. The `Allow` header lists the correct methods |
| 409 | Spot taken, sponsor layer full, or order not open |
| 413 | Request body too large |
| 422 | Blocked wish, name or link, or a missing consent |
| 429 | Rate limit reached. Wait `Retry-After` seconds |
| 503 | Forest state unavailable, or sponsorship sales not open yet. Wait `Retry-After` seconds |

## Versioning

The REST API is version 1. Responses carry `API-Version: 1`. The paths /api/v1/forest and /api/v1/visit are stable aliases. Changes inside a version are additive only. A breaking change gets a new path version. The old version then returns `Deprecation` (RFC 9745) and `Sunset` (RFC 8594) headers for at least 90 days before removal. The [OpenAPI document](https://sharedforest.com/openapi.json) states the same policy in `x-versioning-policy`.

## MCP servers

Both servers use Streamable HTTP with JSON responses and no sessions. The docs server needs no authentication. The product server needs an OAuth token only for its account tools (see Authentication).

| Server | URL | Tools | Server card |
|---|---|---|---|
| Product | https://sharedforest.com/mcp | get_forest_stats, get_forest, list_trees, plant_tree, explain_shared_forest, get_price_quote, design_tree, get_design, find_spots, start_sponsorship, get_order, my_orders, my_trees | [server-card.json](https://sharedforest.com/.well-known/mcp/server-card.json) |
| Docs | https://sharedforest.com/mcp/docs | search_docs, read_doc | [server-card.json](https://sharedforest.com/mcp/docs/.well-known/mcp/server-card.json) |

```sh
curl -s -X POST https://sharedforest.com/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_forest_stats","arguments":{}}}'
```

Client config for Claude Code, Cursor or VS Code:

```json
{ "mcpServers": { "shared-forest": { "type": "http", "url": "https://sharedforest.com/mcp" }, "shared-forest-docs": { "type": "http", "url": "https://sharedforest.com/mcp/docs" } } }
```

## WebMCP

The homepage registers two in-page tools with `document.modelContext` (fallback `navigator.modelContext`): get_forest_stats and plant_tree. Browser agents call them without scraping the canvas.

## SDKs and agent plugin

The public [agent plugin repository](https://github.com/ArneFfm/shared-forest-agents) holds zero-dependency SDKs for TypeScript (`sdk/js`) and Python (`sdk/python`) with one `SharedForestClient`: getForest, listTrees, plant, quote, designTree, findSpots, startSponsorship, getOrder and waitForOrder. The packages are not on npm or PyPI yet: install from the repository. It also holds AGENTS.md, the Agent Plugin manifest (plugin.json), MCP client config (mcp.json, server.json) and the skills. Any OpenAPI generator can read the OpenAPI document.

## Sandbox

There is no separate sandbox. All read endpoints have no side effects. To test writes without planting, send `{"plant": false}`: it only adds growth energy.

## Changelog

- 2026-09-23: relaunch. Public REST API, product and docs MCP servers, NLWeb /ask, cursor pagination and Idempotency-Key.
- 2026-09-23: tree sponsorship for agents. MCP tools get_price_quote, design_tree, get_design, find_spots, start_sponsorship and get_order. REST endpoints under /api/v1/ for pricing, designs, spots, holds and orders. Skill shared-forest-sponsor.
- 2026-09-23: the docs MCP server gets its own server card. `list_trees` requires `limit`. The OpenAPI document states the versioning and deprecation policy.

## Discovery

- [llms.txt](https://sharedforest.com/llms.txt), [API llms.txt](https://sharedforest.com/api/llms.txt), [developers llms.txt](https://sharedforest.com/developers/llms.txt)
- [Agent skill](https://sharedforest.com/.well-known/agent-skills/shared-forest/SKILL.md) and [sponsor skill](https://sharedforest.com/.well-known/agent-skills/shared-forest-sponsor/SKILL.md)
- [Agent plugin repository](https://github.com/ArneFfm/shared-forest-agents)
- [Pricing](https://sharedforest.com/pricing.md)

## Support

Arne Kellmann, contact@ghardenlab.com. See [contact](https://sharedforest.com/contact).
