Skip to main content
A custom source puts your own system in the Company Brain without waiting for a built-in source: an internal wiki, a ticketing system, a document store behind your own API. Agents search it and read from it like any other source, with citations, scope, timeout, cache and the access log. Your system decides what each person may see. Every call xpander makes to it carries a short-lived token, signed by your xpander install, that names the person who asked: their user id, email and groups. Your system verifies the token and answers only what that person may open, so an item it hides from someone never reaches them. There are two ways to connect a system:

Add a custom source

1

Pick Custom

In Platform > Agents > Knowledge, open Sources, select Add source and pick Custom under Type.
2

Pick the contract

Under Contract, choose REST endpoint or MCP server.For REST endpoint, enter the Endpoint: the base URL that /search and /read sit under, such as https://kb.example.com/xpander. Under Authentication, choose API key (enter the Header and the API key), Each person signs in (OAuth) (pick the Connection people sign in through), or None (the signed identity only).For MCP server, pick the server from the Skills registry and enter the names of its Search tool and Read tool (search and read by default).
3

Name it

Under Name, give the source the name people will see in citations and in the access log, then select Add and test.
4

Give your system the identity values

The dialog shows the Audience (this source’s id), the JWKS URL and the Issuer. Your system checks these on every call; copy them into its configuration.
5

Test the connection

Select Test connection. xpander runs a sample search as you, with your signed identity, and shows what came back or why it failed. Type a word you know is in the source under Sample search to test with it. Select Done when it works.
Add source dialog with Type Custom, Contract REST endpoint selected, Endpoint https://kb.acme.example/xpander, Authentication API key, Header Authorization, an API key, Name Acme KB and the Add and test button

Add source with Custom and REST endpoint: the base URL, an API key in the Authorization header, and the name people see. Shown with sample data.

Add source dialog with the MCP contract selected, the Acme Wiki MCP entry picked, and the default search and read function names

Custom over an MCP connector: an entry from the Skills registry and the names of its search and read functions. Shown with sample data.

The source is added, with copyable Audience, JWKS URL and Issuer fields, and Test connection showing Connected in 182 ms with two results

After Add and test: the values your system verifies, and a Test connection that found two items. Shown with sample data.

The API key is stored encrypted and never shown again. The source’s card shows only that a key is set and its last four characters; to change it, select Edit on the card’s Connection tab and type a new one. Leaving the field blank keeps the stored key.

The REST contract

Your system answers two POST requests under the base URL. Both take and return JSON. The full OpenAPI 3.1 description is below.
Answer with the items this person may see, best match first:

Read

text is plain text or Markdown; xpander keeps the first 200,000 characters. title, url, modified and path are optional.

Status codes

The identity token

Every call carries X-Xpander-Identity: a JSON Web Token signed with ES256 by your xpander install. Its header names the signing key (kid) and has typ: xpander-brain-identity+jwt. The public keys are at the JWKS URL shown on the source: https://api.xpander.ai/.well-known/brain-identity-jwks.json on xpander cloud, and your own API host’s /.well-known/brain-identity-jwks.json on a self-hosted install. Cache the key set and fetch it again when a token names a kid you have not seen. Each install has its own signing key, and it is used for nothing but this token. Verify the signature, aud, exp and iss on every call, then apply your own permissions with sub, email and groups.

A minimal REST source

A complete source in Python (FastAPI) that verifies the token with person() above and filters by group:
Run it behind TLS (uvicorn app:app behind your HTTPS proxy), use its base URL as the source’s Endpoint, and a person outside Finance never gets the forecast.

The MCP option

Pick an MCP server that is already in the Skills registry. xpander calls it over streamable HTTP: initialize, then tools/call on the Search tool with {"query": "...", "limit": 25, "scope": {...}} and on the Read tool with {"ref": "..."}. The functions answer in the same shapes as the REST contract: the search answer is {"items": [...]} and the read answer is {"title", "text", "url", "modified"}. xpander reads structuredContent when the answer has it, and otherwise parses the text content as JSON; a read may also answer plain text, which becomes the item’s text. A call that answers isError counts as a failure. The server’s authentication is the one set in the Skills registry: an API key, custom headers, each person’s OAuth sign-in (each person signs in once, from the source’s Connection tab or from Settings > Knowledge), or the person’s SSO token. Every call also carries X-Xpander-Identity, so the server can apply its own permissions even with a shared key. A server that supports only the older SSE transport cannot be used as a source.

Scope

A custom source cannot be browsed, so its scope takes typed places. On the card’s Scope tab, choose what to include or leave out and use Add by path:
  • A ref prefix, such as wiki:eng/. It matches every ref that starts with it at a / or : boundary: wiki:eng/ matches wiki:eng/setup, not wiki:engineering.
  • A path, such as /Engineering/Runbooks. It matches the path your system sends with each item. An item without a path is left out when the scope has a path rule.
Every item is checked against the scope on search and again on read, whatever your system answered. Test as a user shows which items a question would read for a chosen person, and which the scope left out.

Settings shared with every source

The source’s card has the same settings as the built-in sources: Sensitive (no cache), the cache time, the timeout, who can read it, and its entries in the access log. Test connection on the card’s Connection tab runs a sample search as you at any time. When the source uses each person’s sign-in, Disconnect on the Connection tab signs you out of it.
Acme KB source card on the Connection tab with Contract REST endpoint, Endpoint, Authentication API key in Authorization, the Audience, JWKS URL and Issuer fields, and Test connection

A custom source's Connection tab: the contract and endpoint, the values your system verifies, and Test connection. Shown with sample data.

Network rules

xpander calls a custom source from your install:
  • HTTPS only. On xpander cloud the endpoint must be reachable on the public internet; private, loopback and cloud metadata addresses are refused.
  • Self-hosted installs may call endpoints inside your network over HTTPS. Plain http:// is accepted only when the install sets BRAIN_CUSTOM_SOURCES_ALLOW_HTTP=true on agent-controller, and only for an address inside your network. Metadata addresses are never called.
  • No redirects to another host. A redirect to the same host with 307 or 308 is followed; any other redirect fails the call. Use the final URL as the endpoint.
  • Size and time. A search answer may be up to 2 MB and a read answer up to 5 MB. A call that takes longer than the source’s timeout is dropped for that question.
  • The calls do not go through an HTTPS_PROXY.
Air-gapped installations. Custom sources are the live sources an air-gapped installation can use: your system lives in your network, the JWKS is served by your own xpander API, and nothing leaves the installation. Add source offers Custom there.

Troubleshooting

Test connection showing The source refused the credential or this person (401/403). Check the API key or sign-in, with the detail the source refused the configured credential

Test connection when the source refused the API key: what failed, and the source's own words. Shown with sample data.

A token that fails to verify on your side is usually one of: the JWKS fetched from another install’s API host, aud compared with something other than the source’s id, or a clock more than 30 seconds off.

OpenAPI description