AI assistants (MCP)
Partner API v1 · revision 1.13 · 9 November 2026 · What changed
ChatGPT, Claude, Claude Code, Cursor, Codex or VS Code over MCP: sign in once, then run your customers in a conversation.
All the guides · OpenAPI spec and explorer · Need more? Talk to us · Partner terms
This guide connects an AI assistant to your partner account: ChatGPT, Claude, a coding agent such as Claude Code, Cursor or Codex, a VS Code chat, or a script. The assistant can read your account and run your customers in a conversation: add a customer, give one 20 GB more, turn one off, hand a link over, see which incidents touched your routes. It does what the dashboard does, with the same rules, limits and refusals, because its tools call the dashboard's own code. You connect an assistant by signing in to ProofKit and pressing Allow, with no key to copy; a script or a bot signs in with the partner API key your backend already uses. For what a customer's limits mean, see Without code: the dashboard.
What it is
- The address is
https://proofkit.org/, with no trailing slash. It speaks MCP over HTTP and keeps no session: every request is a POST that gets one JSON answer, and a GET is refused withmcp/ partner 405, except a browser's: opened in a browser, the address shows a page on how to connect. Any client that can send a header to an HTTP MCP server can connect. - Two ways in. Signing in is for a person: the assistant opens proofkit.org in your browser, you sign in to your ProofKit account and press Allow, and the assistant works within what you allowed until you disconnect it. The key is for scripts and bots: your partner API key, the
pk_…one, in theX-Partner-Keyheader, inX-API-Keywhere a client offers only that name, such as Claude's connector, or asAuthorization: Bearer pk_…where a client sends a bearer and nothing else. A bearer that starts withpk_is a key; any otherAuthorizationcarries the sign-in's token. A key reaches through this server what it reaches on the REST API, and nothing more (see What a key cannot do). - Who can sign in. The account that owns the partner, with two-factor sign-in turned on (an authenticator app or codes from Telegram). The Allow page asks for the code if your session did not pass it when you signed in.
- The protocol. The server speaks both generations of MCP on this one address: the revisions that open with
initialize, which most clients use today, and 2026-07-28. A client needs no setting for it. - What the assistant is told. When it connects, the server gives it five lines of rules: ask you before it removes access, resets a link, narrows countries or changes limits; a customer's link is that customer's access and goes only to the person who asked for it; no tool moves money; a refusal comes with a code and a sentence to say to you.
- What ProofKit keeps. For each assistant you connect: its client's name, the address it returns to, what you allowed and the dates it was connected and last used. For 30 days, each call made through an assistant, or with your key, leaves a line in a log: which assistant or key made it (a key by its first characters,
pk_and eight more), the tool, the customer's number when the call names one, how it ended and when. No address and no other argument. What you write to an assistant is processed by that assistant's provider, not by us. The dashboard shows the connected assistants and the last calls under AI assistants at the end of the Overview tab.
Connect
Open the Partner page: at the end of the Overview tab, AI assistants has a button or a command for each assistant below, with the address filled in. Every example connects the server under the name proofkit-partner. In each, the assistant starts the sign-in by itself and your browser opens the Allow page on proofkit.org:
- It names the assistant and the address it returns to after you press Allow: read the address, because a name is only what the assistant calls itself.
- It names your partner and the account you are signed in as. Sign in first if you are not, and give the two-factor code if it asks.
- It shows two permissions. Read your partner account and your customers is always on. Manage, to add, change and turn off customers and to change what every customer gets, is a box that appears, ticked, when the assistant asks for it: untick it to let the assistant only look.
- It states what no assistant can do: move money, see or change your API key, change your password, email or two-factor sign-in, or delete your account.
Press Allow and the assistant is connected. Press Deny and it is told you refused. Connecting the same assistant again from the same place replaces its earlier connection. Each install of a desktop assistant is a connection of its own, listed under AI assistants on the Partner page: Claude Code on two machines is two connections, and Disconnect ends one of them.
ChatGPT
Open chatgpt.com/plugins, press + and choose Add custom MCP server. Paste the address https://proofkit.org/ and choose OAuth for authentication. Press Create as a plugin: ChatGPT opens proofkit.org, where you sign in and press Allow. ChatGPT has no prefilled link.
Claude
ProofKit Partner is in Claude's connector directory. On claude.ai or in the Claude desktop app, press Add to Claude on the Partner page, or open https://claude.ai/, and connect it from that page: Claude opens proofkit.org, where you sign in and press Allow. By hand, it is a custom connector: Settings → Connectors → Add custom connector, the name ProofKit Partner and the address; where Claude asks how people sign in, choose Sign in now.
Claude Code
claude mcp add --transport http proofkit-partner https://proofkit.org/mcp/partner
Run /mcp in Claude Code, choose proofkit-partner and follow the prompt to sign in: your browser opens proofkit.org, where you sign in and press Allow. Do not add an Authorization header to the server: Claude Code does not start a sign-in for a server that has one.
Cursor
Press Add to Cursor on the Partner page and press Install when Cursor asks. By hand, in .cursor/, or ~/ for every project:
{
"mcpServers": {
"proofkit-partner": {
"url": "https://proofkit.org/mcp/partner"
}
}
}
In Cursor's MCP settings, press Connect next to proofkit-partner: your browser opens proofkit.org, where you sign in and press Allow.
Codex
codex mcp add proofkit-partner --url https://proofkit.org/mcp/partner
Codex starts the sign-in by itself: your browser opens proofkit.org, where you sign in and press Allow. If the browser does not open, run codex mcp login proofkit-partner. The same server in ~/:
[mcp_servers.proofkit-partner]
url = "https://proofkit.org/mcp/partner"
VS Code
Press Add to VS Code on the Partner page and press Install when VS Code asks; if your browser does not hand the link to VS Code, press Add through vscode.dev beside it. By hand, in .vscode/, or the user-level mcp.json:
{
"servers": {
"proofkit-partner": {
"type": "http",
"url": "https://proofkit.org/mcp/partner"
}
}
}
When VS Code asks to sign in to the server, accept: your browser opens proofkit.org, where you sign in and press Allow.
Check it
Ask the assistant about your balance. It calls get_account and answers in a sentence that names your partner, your balance and how many customers you have.
Read and manage
What the assistant may do is what you ticked on the Allow page. Read lets it look: the account, usage, countries, the customers and a customer's card, devices, Torrent Guard's endings, incidents and the subscription settings. Read includes a customer's link, which a tool returns when you ask for it (see Customers' links in a chat). Manage lets it change things: add customers, change a customer's limits, turn one off or on, give one a new link, change a number, remove one, and change what every customer gets. An assistant connected with Read alone is not even shown the tools that change something, and a call to one is refused with HTTP 403 and INSUFFICIENT_SCOPE. To give it more, connect it again and leave Manage ticked.
Every call is checked as the dashboard checks you: the partner must still be yours, with two-factor on, and not switched off. An assistant cannot do what you could not do in the dashboard, and an hour of its writes spends the same budgets as yours (see Limits).
How long a connection lasts
An assistant renews its sign-in by itself, so you are not asked again. It stops working when:
- you disconnect it (below);
- you change your password or sign out everywhere, which ends every connection of the account;
- you turn two-factor sign-in off;
- its renewal token is used twice, which means a copy of it exists: ProofKit ends the connection at once;
- it goes 60 days without a call;
- the partner is deleted.
A connection to a partner that is switched off answers with the dashboard's own refusal, and works again when the partner does.
Disconnecting
On the Partner page, AI assistants at the end of the Overview tab lists each connected assistant with its name, where it returns to, Read or Read and manage, when it was connected and last used, and its calls of the last 30 days. Disconnect asks first and ends the connection at once: the assistant's next call is refused and it has to sign in again. Below the list, What assistants did shows the last calls of the 30 days, newest first, each with who made it, what it was and the customer's number if it named one; a call that was refused or failed is marked.
When an assistant is connected, ProofKit tells the owner: a notification in the bell and, when the account has a verified email address, an email. It names the assistant, where it returns to, what it may do and when. If you did not connect it, disconnect it under AI assistants and change your password.
When sign-in fails
- The Allow page says to turn on two-factor sign-in. Open the security settings it links to in another tab, turn an authenticator app or Telegram on, come back to the page and press Check again. The request waits for 30 minutes.
- It says the account does not run a partner. Only the account that owns a partner can allow an assistant. Sign out and sign in with that account (the one you use for the Partner page), then start the connection again from the assistant.
- It says the request has expired or was already answered. A sign-in request lives 30 minutes and is answered once: a second tab or a double press finds it gone. Start the connection again from the assistant.
- The Allow page never opens, or the assistant reports a connection error. The sign-in server is its own address,
https://auth.proofkit.org. The assistant, or the machine it runs on, must be able to reach it andhttps://proofkit.org. Allow both in a company proxy or firewall, or connect a script with the key. - Claude Code starts no sign-in. Remove an
Authorizationheader from the server's configuration and run/mcpagain. - It worked and stopped, with
INVALID_TOKEN. The connection ended: see How long a connection lasts. Sign in again. - Claude shows the docs search alone. The connector was added with No sign-in, or before the server asked for a sign-in, and Claude keeps a connector's way of signing in for as long as it exists. Remove the connector and add it again.
With the key, for scripts and bots
A partner API key connects a script or a bot with no sign-in: if you have none, see Getting access. The key goes in the X-Partner-Key header, and it reaches the 15 tools the REST API gives a key, besides the docs search. Claude and Claude Code keep the key in their own settings; the others keep it out of the file: they read it from the environment or ask for it.
Claude, with a key
On claude.ai or in the Claude desktop app, open Settings → Connectors → Add custom connector, name it proofkit-partner and paste the address. Under Authentication, choose No sign-in. Under Request headers, press Add header and pick x-api-key in the list, not Custom header, which needs Anthropic's approval. Its value is your partner API key. Claude stores the header with the connector and does not show it again: anyone who can use that connector uses your key. Claude does not offer Request headers to every account: without them, sign in as above, or connect from Claude Code, Cursor, VS Code or Codex.
Claude Code, with a key
claude mcp add --transport http proofkit-partner https://proofkit.org/mcp/partner --header "X-Partner-Key: pk_…"
Run /mcp in Claude Code: the server is listed, with its tools. The command keeps the key in Claude Code's own configuration, outside your project. Do not add --scope project: that writes the key into .mcp.json, a file meant to be committed with the project.
Cursor, with a key
In .cursor/, or ~/ for every project:
{
"mcpServers": {
"proofkit-partner": {
"url": "https://proofkit.org/mcp/partner",
"headers": { "X-Partner-Key": "${env:PROOFKIT_PARTNER_KEY}" }
}
}
}
Set PROOFKIT_PARTNER_KEY in the environment Cursor is started from.
Codex, with a key
In ~/:
[mcp_servers.proofkit-partner]
url = "https://proofkit.org/mcp/partner"
env_http_headers = { "X-Partner-Key" = "PROOFKIT_PARTNER_KEY" }
env_http_headers names the environment variable that holds the header's value. Set PROOFKIT_PARTNER_KEY in the environment Codex is started from.
VS Code, with a key
In .vscode/, or the user-level mcp.json:
{
"inputs": [
{
"type": "promptString",
"id": "proofkit-partner-key",
"description": "ProofKit partner API key",
"password": true
}
],
"servers": {
"proofkit-partner": {
"type": "http",
"url": "https://proofkit.org/mcp/partner",
"headers": { "X-Partner-Key": "${input:proofkit-partner-key}" }
}
}
}
VS Code asks for the key when it first starts the server, hides what you type and does not write it into the file.
A script
Any MCP client library works. By hand, a call is one POST:
curl -s https://proofkit.org/mcp/partner \
-H "X-Partner-Key: $KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_account","arguments":{}}}'
The answer is one JSON-RPC response. A tool's result is in result: content holds a sentence and then the same data as JSON text, and structuredContent holds it as an object. A request that carries a sign-in's token is read by the token alone: a key header beside it is not read.
Tools
The server lists 21 tools: the docs search, which anyone may call, and 20 that need the key or a sign-in. With neither, the server asks an assistant to sign in when it connects; a direct call of the docs search still answers. A key may call 15 of the 20; the other five are for an assistant you signed in as the owner, and a key is refused with OWNER_ONLY. With no credential only the docs search answers, and with the key of a partner the server is not open for, every other tool answers NOT_AVAILABLE. A key is shown the 16 tools it may call; a sign-in is shown the tools its permissions reach: all 21, or, when it was given Read alone, only the ones that change nothing. A customer is named by its number, the same one the dashboard and the API's external_id use, and a number is read trimmed. A GB in any tool, going in or coming out, is 1,073,741,824 bytes, as everywhere in ProofKit. The tools' names, inputs and outputs keep their meaning once published, as the Partner API's do: a tool or an optional input may be added, none is renamed or retyped.
| Tool | What it does | Changes something |
|---|---|---|
search_partner_docs | Finds the sections of these partner docs that answer a question, with a link to each. Needs no key. | no |
get_account | Your plan, balance and stake, how many days a prepaid balance lasts at the last week's pace, your customers by state, the last 30 days of charges and your olcRTC use. Says where to top up, in words. | no |
get_usage | Traffic and cost over a period, in total and for the 20 customers who used most. | no |
list_countries | The exit countries a customer can get, with the price per GB each starts from, direct and through an entry. | no |
list_subscribers | Finds customers by any part of the number, filters by state (active, expired, used up, disabled), newest or oldest first, 25 a page, or up to 50 when asked, with a cursor for the next. Carries no links. | no |
get_subscriber | One customer: its state and limits, 30 days of traffic, the routes it used in the last 24 hours and its devices. Returns the customer's link. | no |
hand_over_subscriber | The customer's link and a line stating its limits, ready to send. A customer that is cut off is said to be, first. Returns the link. | no |
list_subscriber_devices | The devices that fetched a customer's list: the app, the system and its version, when each was first and last seen, and whether it holds one of the limit's places. | no |
get_guard_events | Every time Torrent Guard ended a session of one of your customers, kept for 30 days, oldest first: the customer's number, why, when, the exit's country, the transport the connections came in through and the numbers behind the decision. Takes an optional since time and a page size of up to 50, and says where to read on. The rows are those of GET /. | no |
add_subscribers | Adds 1 to 100 customers by number, each with an optional traffic limit, end date or length in days, countries and device limit. A number that exists is left as it is, and said so; a removed one comes back fresh, as in the dashboard. Needs your subscriptions switched on, as the dashboard's add does. Returns the links of the customers it made or brought back. | yes |
update_subscriber_limits | Changes a customer's traffic limit, end date, countries or device limit: what you leave out stays, and null clears it. A traffic limit written starts the allowance again from now, and, once limits are switched on for your partner, taking a country away ends the customer's routes through it. | yes |
add_traffic | Gives a customer N GB more than it has left: its limit becomes what remains plus N, from now. A customer with no traffic limit is refused. | yes |
set_subscriber_enabled | Turns a customer off, which ends its connections at once and makes its link open nothing, or on again with the same link. | yes |
reset_subscriber_link | Gives a customer a new link. The old one stops at once, the customer's connections end and its apps lose the servers. Returns the new link. | yes |
remove_subscriber_device | Takes a device off a customer: it gets no list from its next refresh, and its place is free for another. | yes |
allow_subscriber_device | Lets a removed device fetch the list again, when a place of the limit is free. | yes |
get_incidents | The incidents of the last 7 days that touched your customers' routes, ongoing first, each with what to tell a customer. A network is named by its AS number or a mobile operator's name, a route by its country and side, never by a node. Signed-in owner only. | no |
get_subscription_settings | What every customer gets, as the dashboard's Subscription tab shows it: exit countries, entry, transports, title, support link, refresh hours, setup page, device mode and fixed exit address. Signed-in owner only. | no |
update_subscription_settings | Changes those settings: what you leave out stays as it is. A change can cost more: an entry for every customer costs more per GB than direct, and the dashboard shows the prices. Signed-in owner only. | yes |
change_subscriber_number | Gives a customer another number; its link stays. Signed-in owner only. | yes |
remove_subscriber | The dashboard's Remove: the customer's sessions end and its number stays reserved for it. Signed-in owner only. | yes |
A tool that can end or overwrite what a customer has, or what every customer gets, is marked destructive in the hints the server sends with it, so a client asks you before it runs one, and the server tells the assistant to ask first. A tool that changes nothing is marked read-only, which many clients run without asking.
What a key cannot do
An assistant on a key does what the key does on the REST API, and no more. A key is meant to sit in a bot's environment, and a leaked one must not be able to change how every customer is served or to empty the list. So five tools are for an assistant you signed in as the owner, and a key is refused with OWNER_ONLY:
- Settings.
get_subscription_settingsandupdate_subscription_settings: exit countries, entry, transports, title, support link, refresh hours, setup page, device mode and fixed exit address. The reference makes the same promise of the API key: see Subscription settings. - Remove.
remove_subscriber, taking a customer off the list for good. The tools a key may call can turn a customer off, which keeps its link and limits for later, or give it a new link, but not remove it. - Change number.
change_subscriber_number, giving a customer another number. - Incidents.
get_incidents, the Overview's list of incidents that touched your customers' routes.
Money stays with the dashboard for every credential, a sign-in included: no tool tops up the balance, withdraws it, links a wallet or shows a payment link. get_account shows your balance and says where to top up, in words: top up in your dashboard on proofkit.org, under Balance.
No credential can make, list, rotate or revoke an API key, change the account's password, email or two-factor sign-in, or delete the account or the partner. An assistant's sign-in opens this server and nothing else: it cannot call the dashboard's or the Partner API's routes. The provisioning calls a bot uses (starting a route to a named country or node and reading its configs, closing a session, the olcRTC door) stay with the REST API: see Partner API reference.
Customers' links in a chat
A customer's link is the customer's access: whoever holds it can use it. Four tools return one, because you ask for it in order to hand it over: get_subscriber, hand_over_subscriber, add_subscribers and reset_subscriber_link. The other tools carry none, and the list of customers has no links in it. The first two are reads: an assistant connected with Read alone can show a link when you ask.
A link that appears in a chat stays in that chat. The assistant's provider keeps the conversation's history under its own terms, and a client may save a transcript on your machine; ProofKit can take neither back. So:
- ask for a link when you are about to send it, and send it to the person it belongs to;
- do not ask for links in a chat other people can read, or in a project the whole team shares;
- for a test customer, give it an end date and a small traffic limit (
valid_daysandquota_gbinadd_subscribers), so that a link left in a transcript soon opens nothing; - if a link has been shown where it should not have been,
reset_subscriber_linkgives the customer a new one, and the old one stops at once.
Limits
- Per key or connection: 300 requests a minute and 60 writes a minute, a write being one of the requests too. Past the first the server answers
429withRetry-After; past the second a write comes back as a refusal,RATE_LIMITED. A read spends only the request budget. - The dashboard's hourly budgets: every write also spends the budget the dashboard spends for the same kind of write, so an assistant can do no more in an hour than you can with the dashboard open. The budgets are shared, never added: what you did in the dashboard this hour is spent for the assistant too, and the other way round.
- Sizes: a request's body holds at most 256 KiB,
add_subscriberstakes 1 to 100 numbers, andlist_subscribersgives at most 50 customers a page.
| What the assistant does | The hour's budget |
|---|---|
add_subscribers | 300 customers, each number counting once |
update_subscriber_limits, add_traffic, remove_subscriber_device, allow_subscriber_device | 100 changes |
remove_subscriber | 100 removals |
set_subscriber_enabled, reset_subscriber_link, change_subscriber_number, update_subscription_settings | 30 changes, shared with the saves of the dashboard's subscription settings and the dashboard's Change number |
If a call is refused
A tool answers a refusal as a result, not as a protocol error: isError is true, the text reads CODE: sentence, and the same code and sentence follow as {"error": {"code": "…", "message": "…"}} in JSON text. There is no structuredContent. Say the sentence to the person, and branch on the code. The codes the server gives itself:
INVALID_PARTNER_KEY(HTTP401): the key is wrong, was rotated out, or belongs to a partner that is switched off. It is the answer to any request, whatever was asked.KEY_REQUIRED(HTTP401): a tool that needs a credential was called with none. Sign in, or send a partner key.INVALID_TOKEN(HTTP401): the sign-in's token is not one this server accepts, or its connection has ended: sign in again. AnAuthorizationheader whose value is neither a sign-in's token nor a partner key (Bearer pk_…) gets this too; a partner key also goes inX-Partner-KeyorX-API-Key.CONFLICTING_KEYS(HTTP401): two ofX-Partner-Key,X-API-KeyandAuthorization: Bearer pk_…carry two different keys. Send the key in one of them, or the same key in each.INSUFFICIENT_SCOPE(HTTP403): the assistant was connected with Read alone and called a tool that changes something. Connect it again and leave Manage ticked.OWNER_ONLY: one of the five owner's tools was called with a key. Sign in as the owner.NOT_AVAILABLE: the server is not open for your partner. The docs search still answers.RATE_LIMITED: a budget is spent. It is HTTP429withRetry-Afterfor the minute's requests, and a refusal for the minute's writes and for the dashboard's hourly budgets.PAYLOAD_TOO_LARGE(HTTP413): a request body past 256 KiB.
Any other code is the dashboard's own for the same action, such as NOT_FOUND for a number that is not yours, TWO_FACTOR_REQUIRED for an account with no two-factor sign-in or TRIAL_LIMIT at a free trial's subscriber cap: the sentence says what to do. A tool the server does not have is a JSON-RPC error instead. Arguments that do not fit the tool's schema come back as a result with isError set and one text block that says what did not fit, with no code and no JSON block.