AI agents
- Written for
- + Written for
- Deprecated
- + Deprecated
- Applies to
- + Applies to
AI agents
Hand off whole workflows — verify members, restore a backup, post a panel — to an AI agent. Restore Hub supports three integration styles: native MCP, OpenAPI-aware platforms, and plain HTTP with a system prompt. Pick the one that matches your tool.
Bases an agent should know
REST API:
https://api.restorehub.net/api/v1OpenAPI:
https://api.restorehub.net/api/v1/openapi.jsonData model:
https://api.restorehub.net/api/v1/public/schema.jsonMCP server:
https://mcp.restorehub.net/mcpAgent playbook:
https://restorehub.net/agents.md
Native MCP clients
Best for Claude Desktop, Claude Code, Cursor, Zed — anything that speaks the Model Context Protocol. The agent gets a typed tool list pulled live from your account.
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json on macOS (or %APPDATA%\Claude\claude_desktop_config.json on Windows) and add:
{
"mcpServers": {
"restorehub": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.restorehub.net/mcp"],
"env": {
"RESTOREHUB_API_KEY": "rh_YOUR_KEY"
}
}
}
}Cursor
In Cursor, open Settings → MCP → Add new MCP server and paste:
{
"name": "restorehub",
"url": "https://mcp.restorehub.net/mcp",
"headers": { "Authorization": "Bearer rh_YOUR_KEY" }
}Full MCP setup including troubleshooting is on the MCP Server page.
OpenAPI-aware platforms
For ChatGPT custom GPTs, Vercel AI SDK, LangChain, or any tool that consumes an OpenAPI 3.x spec.
ChatGPT custom GPT
Open Configure → Actions → Create new action
Click Import from URL and paste
https://api.restorehub.net/api/v1/openapi.jsonSet Authentication → API Key → Bearer and paste your
rh_keySave. The GPT can now call any endpoint.
Vercel AI SDK
import { generateText } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { experimental_createMCPClient as createMCPClient } from "ai";
const mcp = await createMCPClient({
transport: {
type: "sse",
url: "https://mcp.restorehub.net/mcp",
headers: { Authorization: `Bearer ${process.env.RH_API_KEY}` },
},
});
const tools = await mcp.tools();
const { text } = await generateText({
model: anthropic("claude-sonnet-4-5"),
tools,
prompt: "Schedule daily backups at 03:00 UTC for all my servers.",
});Plain HTTP + system prompt
For agents that have no native tool support — or when you want the simplest possible setup — give the model a system prompt that points at the OpenAPI and lets it construct HTTP calls.
# In your system prompt:
You manage Restore Hub via its REST API.
Base: https://api.restorehub.net/api/v1
Auth: Authorization: Bearer rh_YOUR_KEY
Always read the OpenAPI spec before constructing requests:
https://api.restorehub.net/api/v1/openapi.json
Refer to the data model when interpreting responses:
https://api.restorehub.net/api/v1/public/schema.json
Follow the playbook at:
https://restorehub.net/agents.md
Refuse any operation that requires a scope your key doesn't have.
Confirm before destructive scopes (servers:delete, backups:restore,
members:pull, templates:apply, marketplace:payout, account:delete).Widget SSO (drop-in Discord login)
Restore Hub's verification widget is a strict superset of rolling your own Discord OAuth. Drop in one script tag and you get a signed JWT plus the full Discord identity scope — username, global name, banner, accent color, locale, MFA status, Nitro tier, public flags, verified email, and account creation date.
Tokens are signed with RS256. The public JWKS is served at:
curl https://api.restorehub.net/api/v1/.well-known/jwks.jsonVerify a widget JWT offline with any JWKS-aware library:
import { jwtVerify, createRemoteJWKSet } from "jose";
const JWKS = createRemoteJWKSet(
new URL("https://api.restorehub.net/api/v1/.well-known/jwks.json")
);
const { payload } = await jwtVerify(token, JWKS, {
issuer: "restorehub.net",
audience: "restorehub:widget",
});
// JWT claims use compact names (flat). Map to full names if needed:
// payload.sub -> Discord user ID
// payload.usr -> username
// payload.gn -> globalName
// payload.dsc -> discriminator
// payload.ava -> avatar
// payload.bnr -> banner
// payload.ac -> accentColor
// payload.loc -> locale
// payload.mfa -> mfaEnabled
// payload.prm -> premiumType
// payload.fl -> publicFlags
// payload.eml -> email
// payload.ev -> emailVerified
// payload.pv -> phoneVerified
// payload.cre -> accountCreatedAt
// payload.sid -> Restore Hub server ID
// payload.gid -> Discord guild ID
// (HTTP verify-token endpoint, below, returns the same data with the full
// names AND nested under user.* — use whichever fits your codebase.)Or POST the token to our HTTP endpoint (no API key required):
curl -X POST https://api.restorehub.net/api/v1/widget/verify-token \
-H "Content-Type: application/json" \
-d '{"token":"eyJhbGciOiJSUzI1NiIsImtpZCI6Im..."}'Response shape (nested):
{
"valid": true,
"user": {
"id": "1407504666643791972",
"username": "alice",
"globalName": "Alice",
"discriminator": "0",
"avatar": "abc123",
"banner": null,
"accentColor": null,
"locale": "en-US",
"mfaEnabled": true,
"premiumType": 0,
"publicFlags": 0,
"email": "[email protected]",
"emailVerified": true,
"phoneVerified": null,
"accountCreatedAt": "2025-08-19T23:21:02.700Z"
},
"serverId": "97aff4c6-1f12-41c0-ad9c-61ee2f9c7675",
"discordGuildId": "1508506810271662190",
"issuedAt": "2026-05-26T01:53:25.000Z",
"expiresAt": "2026-05-26T02:53:25.000Z"
}Identity fields live under user.*; token + server metadata are at the root. Failure cases return 401 with { error, code: "INVALID_TOKEN" }.
Full snippet generator + payload reference is in your dashboard under Servers → [server] → Widget.
Acting on the user's behalf — token exchange
The widget JWT proves identity but doesn't carry the underlying Discord OAuth credentials. If your backend needs to call the Discord API as the verified user — list their admin guilds, fetch their connections, sync linked-account profile data — exchange the widget JWT for their Discord access_token via POST /api/v1/widget/access-token:
curl -X POST https://api.restorehub.net/api/v1/widget/access-token \
-H "Content-Type: application/json" \
-d '{"token":"eyJhbGciOiJSUzI1NiIs..."}'Response:
{
"accessToken": "...",
"refreshToken": "...",
"expiresAt": "2026-06-02T01:23:45.000Z",
"discordUserId": "1407504666643791972",
"serverId": "97aff4c6-...",
"tokenType": "Bearer"
}Then call Discord directly:
curl https://discord.com/api/v10/users/@me/guilds \
-H "Authorization: Bearer $ACCESS_TOKEN"Opt-in required. The server owner enables this in Dashboard → Widget → Webhook & Redirect → "Share Discord OAuth tokens with widget integrators". Off by default. Calling the endpoint without the toggle returns 403 ACCESS_TOKEN_SHARE_DISABLED with a message pointing the owner at the setting.
Errors: 400 bad body, 401 invalid JWT, 403 opt-in not enabled, 404 member or server gone, 429 rate-limited (60/min/IP), 500 decrypt failure (member must re-verify). Treat access tokens like any OAuth credential — TLS only, never log, store server-side not in browser storage. The JWT itself expires in 1h; the access_token in ~7 days; use the refresh_token to renew (see below).
Refreshing an expired access_token
Discord access tokens expire after ~7 days. Refreshing requires the OAuth app's client_id + client_secret which we hold, you don't. Proxy the refresh through POST /api/v1/widget/refresh-token — we exchange the refresh token against Discord on your behalf and return new ones:
curl -X POST https://api.restorehub.net/api/v1/widget/refresh-token \
-H "Content-Type: application/json" \
-d '{
"refreshToken": "<refresh_token from earlier>",
"serverId": "<serverId from earlier>"
}'Response:
{
"accessToken": "...",
"refreshToken": "...", // Discord rotates — replace your stored copy
"expiresAt": "2026-06-09T01:23:45.000Z",
"scope": "identify email guilds guilds.join",
"serverId": "97aff4c6-...",
"tokenType": "Bearer"
}Discord rotates the refresh_token on every refresh — store the returned one over the old. We also update our cached encrypted copy on the verified-member row so background workers stay in sync. Same opt-in gate (scopeAccessTokenShare) and rate limit (60/min/IP). If Discord rejects the refresh (token revoked, already used, etc.) you get 401 REFRESH_TOKEN_INVALID — the user needs to re-verify through the widget popup to mint fresh credentials.
1-click DNS via Domain Connect
For the per-domain TXT verification step (proving you own the site embedding the widget), Restore Hub supports the Domain Connect open standard. If your DNS provider supports it (today: Cloudflare, GoDaddy, IONOS, Google Domains via Cloudflare, and a handful of others), the dashboard shows an "Add via Cloudflare — 1 click" button. Click it, approve the record on your provider's screen, and we re-verify automatically when you return. The manual TXT copy-paste flow remains the fallback for everyone else.
Sample prompts
Drop these straight into Claude, Cursor, or any MCP-connected agent.
"List my Discord servers and show member counts."
"Set up verification on server [name] with VPN blocking and alt detection."
"Schedule daily backups at 3am UTC for every PRO server."
"Pull all verified members from server X to server Y."
"Mint an API key with only the read scopes I need to monitor my servers."
"Send a verification panel to the #verify channel on server [name]."
"Show me the last 20 audit-log entries with action containing 'BACKUP'."
Safety guardrails
Restore Hub doesn't trust agents — it trusts API keys. Two layers protect you:
Scopes. A key without
backups:restoresimply cannot restore a backup, no matter what an agent decides.Audit log. Every API-driven action is recorded with the key ID and metadata. View at /dashboard/audit-log.
For agent-driven workflows we recommend minting a read-only key for the first few sessions, then unlocking write scopes once you trust the prompts.