PBN.LTD API docs
View as Markdown

MCP (AI assistants)

Connect Claude, ChatGPT, Cursor, VS Code, Windsurf, Gemini CLI or any other MCP client to your PBN.LTD account: ask in plain words ("which of my sites are offline?", "add a TXT record to example.com", "why is my site paused?") and the assistant uses the same operations as this API, with the same permissions and limits.

Server URL: https://app.pbn.ltd/mcp (live)

Authentication: the assistant opens a PBN.LTD login and asks you to approve access (OAuth). Clients that do not support OAuth can send an API key instead: Authorization: Bearer pbn_... (use a scoped key). Everything the assistant does is in the call log of that key/app, and you can disconnect it at any time on API keys.

Claude (claude.ai and Claude Desktop)

  1. Settings > Connectors (or Customize > Connectors) > + > Add custom connector.
  2. Name: PBN.LTD; URL: https://app.pbn.ltd/mcp. Click Add, then Connect and approve on PBN.LTD.

Vendor guide: Get started with custom connectors using remote MCP and Connect to remote MCP servers.

Claude Code

claude mcp add --transport http pbn https://app.pbn.ltd/mcp
# then run /mcp in Claude Code, pick "pbn" and log in; or use a key:
claude mcp add --transport http pbn https://app.pbn.ltd/mcp --header "Authorization: Bearer $PBN_API_KEY"

Vendor guide: Connect Claude Code to tools via MCP.

ChatGPT

  1. Settings > Security and login: switch on Developer mode.
  2. Open Plugins (Apps) > +, give it the name PBN.LTD, choose the public endpoint option and enter https://app.pbn.ltd/mcp.
  3. Approve access on PBN.LTD when asked.

Vendor guides: MCP in ChatGPT and Connect a server to ChatGPT.

Cursor

Add to ~/.cursor/mcp.json (or .cursor/mcp.json in a project):

{"mcpServers": {"pbn": {"url": "https://app.pbn.ltd/mcp"}}}

To use a key instead of OAuth: {"mcpServers": {"pbn": {"url": "https://app.pbn.ltd/mcp", "headers": {"Authorization": "Bearer pbn_..."}}}}. Vendor guide: Model Context Protocol (MCP) in Cursor.

VS Code (GitHub Copilot)

Command Palette > MCP: Add Server > HTTP, URL https://app.pbn.ltd/mcp - or in .vscode/mcp.json:

{"servers": {"pbn": {"type": "http", "url": "https://app.pbn.ltd/mcp"}}}

Vendor guide: Add and manage MCP servers in VS Code.

Windsurf

In Cascade's MCP settings (mcp_config.json):

{"mcpServers": {"pbn": {"serverUrl": "https://app.pbn.ltd/mcp"}}}

Vendor guide: Cascade MCP.

Gemini CLI

gemini mcp add --transport http pbn https://app.pbn.ltd/mcp
# or with a key:
gemini mcp add --transport http --header "Authorization: Bearer $PBN_API_KEY" pbn https://app.pbn.ltd/mcp

Vendor guide: MCP servers with Gemini CLI.

Other MCP clients

Any client that supports remote MCP servers over Streamable HTTP works: give it https://app.pbn.ltd/mcp and either let it do the OAuth login or send an Authorization: Bearer pbn_... header. About MCP: modelcontextprotocol.io.

What the assistant can do

The tools mirror the API reference one to one (tool sites_list = GET /sites, dns_create = POST /sites/{site_id}/dns, ...). Actions that delete or overwrite (delete a site, restore, reinstall, delete files) are marked destructive, so the assistant asks you before running them. A scoped key or a limited OAuth approval limits the assistant exactly like any other key.

What the server is

Server URLhttps://app.pbn.ltd/mcp (POST only)
TransportStreamable HTTP
Protocol revisions2026-07-28, 2025-11-25, 2025-06-18, 2025-03-26
Tools85
Resources4 fixed, 5 templates, one per docs page
Prompts12
AuthenticationOAuth 2.1 (PKCE, dynamic client registration) or an API key as Authorization: Bearer pbn_...
Server name / versionpbn.ltd 1.0.0

The endpoint serves both shapes of the protocol on the same URL. Revision 2026-07-28 made MCP stateless (no initialize handshake, no sessions, per-request metadata, server/discover); revisions 2025-11-25, 2025-06-18 and 2025-03-26 use the initialize handshake. You do not have to care which one your client speaks - it is detected from the request.

Because the server holds no session, GET and DELETE on https://app.pbn.ltd/mcp answer 405, no Mcp-Session-Id is issued, and streams are not resumable. Nothing is lost by that: every answer is complete in its own response.

Authentication

Two ways in. Both end up as the same thing: a key with scopes, an owner, a call log and a rate limit.

1. OAuth 2.1 (what a connector does for you). Point the client at https://app.pbn.ltd/mcp and it will find the login by itself: the first call answers 401 with

WWW-Authenticate: Bearer realm="PBN.LTD", resource_metadata="https://app.pbn.ltd/.well-known/oauth-protected-resource"

From there the client reads its protected-resource metadata (RFC 9728), then the authorization-server metadata (RFC 8414), registers itself (RFC 7591 dynamic client registration), and opens https://app.pbn.ltd/oauth/authorize in your browser. You log in as yourself, see exactly which permissions the app is asking for, and approve or refuse. PKCE with S256 is required, the resource parameter (RFC 8707) is honoured, authorization codes last 60 seconds, access tokens 1 hour and refresh tokens 90 days with rotation.

2. An API key. Create one at API keys, give it only the permissions the assistant needs, and put it in the client's configuration as the Authorization: Bearer pbn_... header. Use this for clients that have no OAuth support, and for anything running unattended.

Either way you can end the connection at any moment: revoke the key (or the app) on the API keys page and the next call fails. A key created through delegated access also dies the moment that access is withdrawn.

What you can ask for

The tools cover the whole job, not only reading:

Everything is done with the account's own permissions, and anything that replaces or deletes is marked destructive so your assistant asks you first.

Permissions (scopes)

An assistant can only do what the connection was granted. tools/list returns only the tools the connection may actually use, so the assistant never offers something it cannot do. Ask for the narrowest set that does the job:

A :write scope includes the matching :read. sites:delete and sites:login are deliberately separate from sites:write, so an assistant can be allowed to change a site without being able to delete it or to log in as its administrator. If a tool needs a permission the connection does not have, an OAuth connection is asked to step up (403 with WWW-Authenticate: ... error="insufficient_scope"), and an API-key connection gets a tool error that names the missing scope.

Limits

MCP calls share the API's limits exactly - they are the same requests. The defaults are 300 requests a minute, 10000 an hour and 90 writes a minute per key or per connected app, with X-RateLimit-* headers on the answer; see Rate limits and sizes. They were set for exactly this kind of work: an assistant building a site or writing a plugin makes a burst of small writes. A bulk upload counts as one request however many files are inside it, so files_upload_archive is the way to put a built site up, not one files_write per file. A tool that hits the limit returns a tool error saying how long to wait - a well-behaved assistant waits rather than retrying at once. File uploads are limited to 25 MB and file reads to 5 MB (the exact numbers for your key are in the meta_limits tool).

Every tool call is written to the call log of the key that made it, with the site it touched, and write calls keep what was asked for as an audit trail. You can read it on your key's activity page.

Tools

85 tools, one for every operation of the REST API plus jobs_wait. The complete reference - every argument, what comes back, and a worked example for each - is on the MCP tool reference page.

Tools are named after the API endpoints with dots replaced by underscores, so sites_list is GET /sites and dns_create is POST /sites/{site_id}/dns (paths relative to https://app.pbn.ltd/api/v1). Each one carries a JSON-Schema for its input and for its output, so a client can check the answer, and behaviour annotations (readOnlyHint, destructiveHint, idempotentHint) so your assistant knows what needs your say-so first.

Answers carry structuredContent (the JSON the API would return) as well as a text block, so an assistant can work with the data rather than re-reading prose.

Things that finish later

Creating a site, taking or restoring a backup, deleting a site, reinstalling and publishing DNS all start a job. The tool answers immediately with a job block; jobs_wait then waits for it (up to 5 minutes at a time) and jobs_get / jobs_list show it on demand. If the client sends a progressToken, those calls stream notifications/progress while they wait, so nothing looks frozen.

Resources

Read-only context an assistant can pull in without spending a tool call:

URIWhat it holds
pbn://accountWho this connection belongs to and what the connected key may do.
pbn://account/limitsSite slots, subscription state, the default per-site limits, paused sites and the add-ons on the account.
pbn://billingSubscription, payment due, next renewal and the add-ons that are being paid for.
pbn://sitesEvery site on the account with its status, type, platform and nameserver state.
pbn://site/{site_id}One site in full: status, type, CDN, HTTPS, PHP version, nameservers, SEO.
pbn://site/{site_id}/healthIs the site up, what the last checks found and what is wrong.
pbn://site/{site_id}/dnsThe DNS records published for this site.
pbn://invoice/{number}One invoice as a PDF.
pbn://docs/{slug}A page of the PBN.LTD API / MCP documentation as Markdown. Slugs: see pbn://docs/index.

pbn://docs/index lists a pbn://docs/<page> resource for every page of this documentation, so an assistant can read the API reference itself before answering a question about it.

Prompts

Ready-made jobs. In Claude they appear under the + menu (or as / commands) once the connector is added; other clients show them in a command palette.

PromptWhat it does
Diagnose a site (diagnose_site)Work out why a site is down, slow, showing an error or paused, and say what to do about it.
Check a site is ready to go live (going_live_check)Check the nameservers, DNS records, CDN and HTTPS of a site that is waiting to go live, and give the customer the exact steps left.
Create a new site (create_site)Create a site of any type (WordPress, Joomla, Drupal, PrestaShop, OpenCart, Grav, MediaWiki, PHP hosting or static HTML) and follow it through to ready.
Clean up a site (clean_up_site)Use Site Cleaner to find what is filling a site up, and clean it after showing the customer what would go.
Review my account (account_review)A plain-language review of the whole account: plan, what is owed, slots, usage, paused sites, sites that are not live and anything that needs attention.
Answer a billing question (billing_questions)Answer a question about the plan, an invoice, a payment or a renewal from the real billing record, with the invoice links.
Read the error log and fix it (fix_from_error_log)Read a site's error log, explain the errors in plain words and propose the safest fix (never changing a file without asking first).
Back up or restore a site (backup_and_restore)Take a backup now, or restore one - with the confirmation and the warnings a restore needs.
Build me a site (build_site)Create a site of any type, put the pages on it, set the home page and check that it really serves - end to end.
Write and publish a post (publish_post)Write a post or page and publish it to a site of ANY type, with its pictures, and check the published page.
Make me a plugin (make_plugin)Write a WordPress plugin (or theme) into the site, check it, switch it on and prove the site still works - and take it back off if it does not.
Find help / open a ticket (get_help)Search the PBN.LTD knowledge base for the customer's question and, only if it is not answered there, open a support ticket with the right detail in it.

Try it without a client

Everything below works with a plain API key. Replace $PBN_API_KEY with a key from API keys.

List the tools:

curl -s https://app.pbn.ltd/mcp \
  -H "Authorization: Bearer $PBN_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

Call one (this one is free of side effects - it is the cheapest way to prove a key works):

curl -s https://app.pbn.ltd/mcp \
  -H "Authorization: Bearer $PBN_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
       "params":{"name":"account_me","arguments":{}}}'

Search your sites:

curl -s https://app.pbn.ltd/mcp \
  -H "Authorization: Bearer $PBN_API_KEY" -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call",
       "params":{"name":"sites_list","arguments":{"search":"example.com","limit":5}}}'

Read a resource:

curl -s https://app.pbn.ltd/mcp \
  -H "Authorization: Bearer $PBN_API_KEY" -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":4,"method":"resources/read","params":{"uri":"pbn://account"}}'

The handshake a client does first, if you want to see it (revision 2025-11-25):

curl -s https://app.pbn.ltd/mcp \
  -H "Authorization: Bearer $PBN_API_KEY" -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-11-25",
       "capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'

…and the same thing on revision 2026-07-28, which needs no handshake at all:

curl -s https://app.pbn.ltd/mcp \
  -H "Authorization: Bearer $PBN_API_KEY" -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "MCP-Protocol-Version: 2026-07-28" -H "Mcp-Method: server/discover" \
  -d '{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{
       "io.modelcontextprotocol/protocolVersion":"2026-07-28",
       "io.modelcontextprotocol/clientInfo":{"name":"curl","version":"1"},
       "io.modelcontextprotocol/clientCapabilities":{}}}}'

On 2026-07-28 the MCP-Protocol-Version, Mcp-Method and (for tools/call, resources/read and prompts/get) Mcp-Name headers must agree with the body, or the answer is 400 with JSON-RPC error -32020. An unsupported version is 400 with -32022 and the list of versions we do support; an unknown method is 404 with -32601.

More clients

Anything that speaks remote MCP over Streamable HTTP works. A few more, with their own documentation:

Checking the connection yourself

The reference client from the MCP project lists the tools and calls one without any assistant in the way:

npx @modelcontextprotocol/inspector --cli https://app.pbn.ltd/mcp --transport http   --header "Authorization: Bearer $PBN_API_KEY" --method tools/list

npx @modelcontextprotocol/inspector --cli https://app.pbn.ltd/mcp --transport http   --header "Authorization: Bearer $PBN_API_KEY"   --method tools/call --tool-name account_me --format json

Leave out --cli for the graphical version in a browser, which also walks through the OAuth login.

When something is wrong

What you seeWhat it means
401 with WWW-AuthenticateNo key, a revoked or expired key, or a token issued for something else. Reconnect, or check the key on the API keys page.
403 insufficient_scopeThe connection was not granted that permission. An OAuth client will ask you to approve it; a key has to be edited.
A tool answer with isErrorThe call reached us and was refused or failed - the text says why and what to do (wrong site id, site busy, plan expired, over a limit).
429 / a rate-limit tool errorToo many calls for this key in the last minute or hour. Wait the seconds it names.
503 mcp_disabledThe MCP server is switched off for maintenance. The REST API is unaffected.

What it cannot do