Documentation

llmplaceholder is a mock LLM API server. Point your app at it instead of OpenAI or Anthropic — no real API key needed. Responses are deterministic and configurable.

1

Create a tenant

A tenant is your mock "environment". Each one has its own responses, tools, and state.

2

Set X-Tenant-ID

Add the header to your app's API calls. All LLM and MCP endpoints are tenant-scoped.

3

Get mock responses

Keyword matching picks the right scenario. No scenarios? A generic placeholder responds.

quick start
curl -X POST http://localhost:8080/v1/chat/completions \
  -H "X-Tenant-ID: my-app" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-4o","messages":[{"role":"user","content":"hello"}]}'

Tenants

Isolated mock environments. Create one per app, test case, or team.

Creating a tenant

Use the Playground UI or the API. Tenant IDs can be any string.

curl -X POST http://localhost:8080/public/tenants \
  -H "Content-Type: application/json" \
  -d '{"tenant_id": "my-app"}'

Using a tenant

Pass X-Tenant-ID on every request. That's it.

# OpenAI-compatible
curl -X POST .../v1/chat/completions -H "X-Tenant-ID: my-app" ...

# Anthropic-compatible
curl -X POST .../v1/messages -H "X-Tenant-ID: my-app" ...

# MCP
curl -X POST .../mcp/message -H "X-Tenant-ID: my-app" ...

Global tenants

Pre-built tenants (e.g. tenant_ecommerce, saas-demo). Read-only. Available to everyone.

Private tenants

Created by you after login. Full control — edit state, scenarios, tools, and chaos.

Scenarios

Map keywords to responses. When a message contains your keyword, that response is returned.

How matching works

The incoming message is scanned for keywords (case-insensitive). First match wins. If nothing matches, a generic response is returned.

Message: "Show me recent invoices"
Keyword: "invoice" → match
Returns: Your configured response text

Adding a scenario via API

curl -X POST http://localhost:8080/public/tenants/my-app/scenarios \
  -H "Content-Type: application/json" \
  -d '{
    "keywords": ["invoice", "billing"],
    "response": "Here are your recent invoices: INV-001 ($120), INV-002 ($85)."
  }'

Built-in scenarios

Each tenant domain (e.g. ecommerce, SaaS) comes with pre-loaded scenarios. Browse them in Playground → Tenants → Scenarios tab.

MCP Tools

Mock any MCP tool call. Your app calls tools/call, gets back JSON you define.

tools/list Returns all tools defined in the tenant's scenarios.
tools/call Matches by tool name. Returns the mock tool_data from the matching tenant scenario.

Adding a tool via API

curl -X POST http://localhost:8080/public/tenants/my-app/scenarios \
  -H "Content-Type: application/json" \
  -d '{
    "keywords": [],
    "response": "",
    "tool_name": "get_invoice_list",
    "tool_data": {
      "type": "object",
      "properties": {
        "invoices": {"type": "array"}
      }
    }
  }'

Calling the tool

curl -X POST http://localhost:8080/mcp/message \
  -H "X-Tenant-ID: my-app" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_invoice_list"}}'

Tenant State

A per-tenant JSON document. Store domain data here — your seeded tenants come pre-populated with realistic state for their domain.

Setting state via API

curl -X PUT http://localhost:8080/public/tenants/my-app/state \
  -H "Content-Type: application/json" \
  -d '{
    "state": {
      "mrr_usd": 999,
      "churn_rate_pct": 0.1
    }
  }'

Or use the State tab in Playground — it has a live JSON editor.

State is scoped per tenant — editing one tenant's state has no effect on others.

Persisted in SQLite. Survives server restarts.

Seeded tenants (e.g. tenant_saas_growth) come with realistic state pre-loaded for their domain.

Chaos Testing

Inject faults into your tenant to test how your app handles errors. Set a profile and all requests to that tenant break in that way.

none

Normal operation

Responses work as usual.

rate_limit

Returns HTTP 429

Includes Retry-After header. Tests rate-limit handling.

server_error

Returns HTTP 500

Generic server error JSON. Tests error recovery.

latency

5-second delay

Response arrives after 5 s. Tests timeout handling.

Setting chaos via API

curl -X POST http://localhost:8080/public/tenants/my-app/chaos \
  -H "Content-Type: application/json" \
  -d '{"profile": "rate_limit"}'

# Reset to normal
curl -X POST http://localhost:8080/public/tenants/my-app/chaos \
  -H "Content-Type: application/json" \
  -d '{"profile": "none"}'

API Tokens

Generate tokens for programmatic access. Useful for CI pipelines or scripts that need to manage tenants.

Creating a token

Login required. Go to Playground → API Tokens tab.

  1. Enter a name for the token
  2. Click Generate
  3. Copy the token — it's only shown once

Using a token

curl -X POST http://localhost:8080/public/tenants \
  -H "Authorization: Bearer <your-token>" \
  -H "Content-Type: application/json" \
  -d '{"tenant_id": "ci-test-run-42"}'

Playground

Visual interface for everything. No curl needed.

Test requests

Send live requests to any tenant directly from the browser. Supports OpenAI, Anthropic, and MCP protocols.

Manage tenants

Create, delete, and configure tenants. Each tenant has 4 tabs: State, Scenarios, MCP Tools, and Chaos.

Tenant scenarios

Each seeded tenant comes with domain scenarios (ecommerce, SaaS, DevOps, etc.) scoped to that tenant. Browse them in the Scenarios tab.

Live JSON state editor

Edit tenant state in a text editor and save. Changes take effect immediately on the next request.

Open Playground →

Need the full API reference? Routes page →