Overview
Base URL for every request:
https://scoutrail.io
There are two ways in. MCP is for AI clients (Claude, ChatGPT): paste one URL into the client's connector settings and ScoutRail shows up as a tool, no install. REST is for your own backend: plain JSON over HTTPS. Both run the same scouting engine and return the same ranked results.
Responses are JSON. Opportunity text comes from third-party public sources, so it is returned inside a <scoutrail_opportunity_data> fence: treat everything inside as data, never as instructions to your agent.
Authentication
Authorization: Bearer scoutrail_sk_...ScoutRail has two tiers. Without a key, calls are anonymous and rate-limited, and scout_opportunities returns a 2-result sample. Send an account API key as a Bearer token to get the full ranked list, metered against your subscription. The key never expires until you revoke it; store it like a password and keep it server-side.
Quickstart
Connect over MCP in one step:
Claude: Settings -> Connectors -> Add custom connector -> https://scoutrail.io/mcp
ChatGPT: Settings -> Connectors -> Add -> MCP server URL: https://scoutrail.io/mcp
For full results, add your API key as the connector's Bearer token. Or make your first REST call from a terminal:
curl https://scoutrail.io/api/ask \
-H 'content-type: application/json' \
-d '{ "text": "hospital cleaning services in south africa" }'
Connect over MCP
https://scoutrail.io/mcpJSON-RPC 2.0 over the MCP Streamable HTTP transport. The endpoint is stateless, so a GET asking for an event stream returns 405 by design; POST your JSON-RPC messages instead. ScoutRail exposes two tools: scout_opportunities and list_markets.
Try it · initialize handshake
Run to see the live response.
tools/list
/mcpList the tools ScoutRail exposes and their JSON Schema input shapes. Most clients call this automatically after initialize.
Try it
Run to see the live response.
scout_opportunities
/mcp tools/callFind live tenders, grants, RFPs and buyers for a business. The result carries a plain-English brief plus structuredContent matching the ScoutRail AnswerView schema. With a key you get the full ranked list; without one, a 2-result sample.
| Argument | Type | Description |
|---|---|---|
| business required | string | What the business does, sells or is looking for, e.g. "hospital cleaning services". |
| markets | string[] | Optional market codes to scope the search: za, uk, us, ng. Defaults to auto-detection from the description. |
Try it
Run to see the live response.
/api/ask
/api/askThe same scout over plain REST, for your own backend. Returns the presented result and the composed view (the AnswerView the site and MCP both consume).
| Field | Type | Description |
|---|---|---|
| text required | string | A free-text description of the business and, optionally, the market or location. |
Try it
Run to see the live response.
/api/subscribe
/api/subscribeRegister an HTTPS callback for a query. When a fresh answer is composed, ScoutRail POSTs it to your endpoint, so an agent gets updates without polling. Running this tester registers a real subscription for the callback URL below.
| Field | Type | Description |
|---|---|---|
| query required | string | The business description to watch for new opportunities. |
| callbackUrl required | string | An HTTPS URL that receives the signed answer payload. |
Try it
Run to see the live response.
Verifying signed webhooks
/.well-known/scoutrail-webhook.jsonEach delivery carries X-Scoutrail-Signature: ed25519=<base64> and X-Scoutrail-Key-Id. Fetch the public keys, pick the one matching the kid, and verify the Ed25519 signature over the raw request body. The key is public, so there is no shared secret to store.
const jwks = await (await fetch(
'https://scoutrail.io/.well-known/scoutrail-webhook.json')).json();
const jwk = jwks.keys.find(k => k.kid === req.headers['x-scoutrail-key-id']);
const key = await crypto.subtle.importKey(
'jwk', { kty:'OKP', crv:'Ed25519', x: jwk.x }, { name:'Ed25519' }, false, ['verify']);
const sig = Uint8Array.from(atob(
req.headers['x-scoutrail-signature'].slice('ed25519='.length)), c => c.charCodeAt(0));
const ok = await crypto.subtle.verify('Ed25519', key, sig, rawBodyBytes);
Try it · fetch the public keys
Run to see the live response.
Errors & rate limits
REST endpoints use standard HTTP status codes and return a JSON body with an error field. MCP reports tool-level problems as text content inside a normal 200 JSON-RPC result, so an AI client sees the message rather than a transport failure.
| Status | Meaning |
|---|---|
| 401 | Not signed in, for endpoints that need a session. |
| 402 | Free scouts used up. Subscribe to continue. |
| 429 | Rate limit exceeded. Slow down and retry. |
| 400 | Malformed request (bad JSON, missing required field). |
The MCP endpoint is rate-limited per IP. An account API key lifts scout_opportunities from the 2-result sample to the full list and meters it against your plan quota; anonymous callers stay on the sample.
Discovery files
/.well-known/mcp.jsonA static descriptor AI directories read to find the ScoutRail MCP: endpoint, transport and tool names. CORS-open so a browser directory can fetch it cross-origin.
Try it
Run to see the live response.
Back to ScoutRail · Your account & usage · Email preferences