DocsOpen Moon AI
Browse the docs

Resources

Add, test and share the organization’s MCP servers, set up its own file storage, and share knowledge bases and agents through the admin API.

On this page

Three kinds of resources are managed here: the MCP servers offered to members in the chat, the organization’s own storage for uploaded files, and the sharing of knowledge bases and agents with groups, roles or labels.

All routes are under https://chat.realmoon.ai/api/v1/orgs/{org_id}. Scopes: resources:read to read, resources:write to write. The permissions required are those of the key’s creator: manage connectors (MCP servers), manage storage, manage the library (knowledge bases) or publish to the catalog (agents). Creating, turning on and sharing require a managed organization (otherwise 409 org_not_managed).

The sharing blockLink to the The sharing block section

MCP servers and shares use the same sharing object:

JSON
{ "all": false, "group_ids": ["<group id>"], "role_keys": ["admin", "custom:<id>"], "label_ids": ["<label id>"] }
FieldTypeDescription
allbooleantrue: the whole workspace.
group_idslistOrganization groups, up to 200.
role_keyslistBuilt-in roles (owner, admin, manager, billing, member) or custom roles (custom:<id>), up to 50.
label_idslistMember labels (not sensitivity labels), up to 200.

A missing or null block means “shared with no one”. An unknown group, role or label returns 404 not_found (messages Groupe introuvable., Rôle introuvable., Étiquette introuvable.: group, role or label not found). Errors on these routes can carry a field field (the field at fault) in addition to fields.

MCP serversLink to the MCP servers section

An organization MCP server is offered in the chat to the members it is shared with, when it is turned on and its last test passed (offered: true), and when their rules allow it. An organization can have up to 50 servers. The flow: create, test, then turn on.

The MCP server objectLink to the The MCP server object section

JSON
{
  "id": "6f1c2a9e-3b7d-4e21-9a0c-5d8e7f6a1b2c",
  "name": "CRM interne",
  "url": "https://mcp.example-crm.com/mcp?…",
  "transport": "streamable_http",
  "header_name": "Authorization",
  "auth": "token",
  "token_configured": true,
  "token_last4": "9xQa",
  "readable": true,
  "oauth_client_configured": false,
  "members_connected": null,
  "my_oauth_status": null,
  "my_oauth_host": null,
  "tools": [
    { "name": "search_contacts", "description": "Recherche des contacts par nom.", "read_only": true, "hash": "3f5a9c…" }
  ],
  "tools_pending": [],
  "tools_allowed": ["search_contacts"],
  "approval": { "search_contacts": "auto" },
  "sharing": { "all": false, "group_ids": ["2d4c6e8a-1b3d-4f5a-9c7e-0a2b4c6d8e1f"], "role_keys": ["admin"], "label_ids": [] },
  "sensitivity_label_id": null,
  "enabled": true,
  "status": "ok",
  "last_error": null,
  "tested_at": 1793440000,
  "offered": true,
  "created_at": 1793436400,
  "updated_at": 1793440000
}
FieldDescription
urlThe URL, masked: the query string becomes ?… and long path segments …. The full URL is never returned.
transportauto, streamable_http or sse; after a successful test, the detected transport.
authtoken (fixed token), none or oauth (each member signs in with their own account).
token_configured, token_last4A token is saved; its last 4 characters (for a token of 8 characters or more). The token is never returned.
readableThe saved connection details can be read.
members_connectedOAuth server: number of connected members; null otherwise.
my_oauth_statusOAuth server: none, ok or expired for the caller’s connection (for a key, its creator’s).
toolsTools approved at the last test (up to 40): name, description, read_only, hash.
tools_pendingTools that are new (kind: "new") or changed (kind: "changed") since then, set aside until the next test.
tools_allowedTools offered; null: all of them.
approvalPer tool: ask (ask every time), auto (without asking) or never.
sensitivity_label_idSensitivity label: only the roles in sharing.role_keys (or else the owner and admins) can then use the server.
statusuntested, ok or failed; last_error gives the message of the last failure.
offeredenabled and status set to ok.

List MCP serversLink to the List MCP servers section

GET /resources/mcp · resources:read

Parameters: page, limit. Sorted by name. The response also includes max_servers (50).

Add an MCP serverLink to the Add an MCP server section

POST /resources/mcp · resources:write · managed organization

FieldTypeRequiredDescription
namestringyes1 to 80 characters, unique in the organization (case-insensitive).
urlstringyesAn http(s) URL without credentials, 8 to 2,048 characters. It must be public: this is checked during the test.
transportstringnoauto (default), streamable_http or sse.
authstringnotoken (default), none or oauth.
header_namestringnoHeader for the token, Authorization by default (letters, digits and hyphens, up to 64). On Authorization, a token without a space is sent as Bearer <token>.
tokenstringif auth is tokenThe access token, up to 8,192 characters. Ignored for none and oauth.
tools_allowedlistnoTools offered (up to 200 names); missing: all of them.
approvalobjectno{"<tool>": "ask"}, with ask, auto or never, up to 200 entries.
sharingobjectnoSharing block; missing: no one.
sensitivity_label_idstringnoA sensitivity label of the organization.

The server is created turned off and untested.

cURL
curl https://chat.realmoon.ai/api/v1/orgs/$ORG_ID/resources/mcp \
  -H "Authorization: Bearer $MOONAI_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CRM interne",
    "url": "https://mcp.example-crm.com/mcp",
    "auth": "token",
    "token": "<server token>",
    "sharing": {"all": true}
  }'
Python
import os
import requests

BASE = f"https://chat.realmoon.ai/api/v1/orgs/{os.environ['ORG_ID']}"
HEADERS = {"Authorization": f"Bearer {os.environ['MOONAI_ADMIN_KEY']}"}

server = requests.post(
    f"{BASE}/resources/mcp",
    headers=HEADERS,
    json={
        "name": "CRM interne",
        "url": "https://mcp.example-crm.com/mcp",
        "auth": "token",
        "token": os.environ["CRM_MCP_TOKEN"],
        "sharing": {"all": True},
    },
    timeout=30,
).json()

test = requests.post(f"{BASE}/resources/mcp/{server['id']}/test", headers=HEADERS, timeout=60)
test.raise_for_status()
requests.patch(f"{BASE}/resources/mcp/{server['id']}", headers=HEADERS, json={"enabled": True}, timeout=30).raise_for_status()
JavaScript
const BASE = `https://chat.realmoon.ai/api/v1/orgs/${process.env.ORG_ID}`;
const HEADERS = { Authorization: `Bearer ${process.env.MOONAI_ADMIN_KEY}`, 'Content-Type': 'application/json' };

const server = await (await fetch(`${BASE}/resources/mcp`, {
  method: 'POST',
  headers: HEADERS,
  body: JSON.stringify({
    name: 'CRM interne',
    url: 'https://mcp.example-crm.com/mcp',
    auth: 'token',
    token: process.env.CRM_MCP_TOKEN,
    sharing: { all: true },
  }),
})).json();

const test = await fetch(`${BASE}/resources/mcp/${server.id}/test`, { method: 'POST', headers: HEADERS });
if (!test.ok) throw new Error(await test.text());
await fetch(`${BASE}/resources/mcp/${server.id}`, { method: 'PATCH', headers: HEADERS, body: JSON.stringify({ enabled: true }) });
StatuscodeCause
400invalid_requestEmpty name, invalid URL, header refused, token missing, invalid tool name, more than 50 servers, label that is not a sensitivity label.
409invalid_requestUn serveur porte déjà ce nom. (a server already has this name)
409org_not_managedThe organization is not managed.

Read an MCP serverLink to the Read an MCP server section

GET /resources/mcp/{server_id} · resources:read

Returns the server object. 404 not_found for an unknown server.

Update an MCP serverLink to the Update an MCP server section

PATCH /resources/mcp/{server_id} · resources:write

Same fields as on creation, all optional, plus enabled (boolean). Only the fields you send change.

  • token: a string replaces the token, "" deletes it.
  • Changing the URL, auth, transport, header_name or the token resets status to untested: the server is no longer offered until the next successful test. Changing the URL, auth or transport also deletes the members’ personal connections.
  • sharing: null removes sharing. For an OAuth server, removing a member from the sharing deletes their connection.
  • enabled: true requires a managed organization and a successful last test, otherwise 409 test_required (message Testez le serveur avant de l’activer., test the server before turning it on). enabled: false is always allowed.

Delete an MCP serverLink to the Delete an MCP server section

DELETE /resources/mcp/{server_id} · resources:write

Deletes the server, its call log, the members’ connections and its label. Response: {"success": true}.

Test an MCP serverLink to the Test an MCP server section

POST /resources/mcp/{server_id}/test · resources:write · managed organization

Connects to the server, reads its tools, approves them and returns the server object with status: "ok". The test does not turn the server on. An OAuth server is tested with the caller’s connection (for a key, its creator’s).

Each test counts toward the limit of 20 tests in 10 minutes per person, shared with storage and webhook tests (429 rate_limited).

StatuscodeCause
400org_mcp_test_failedThe connection failed; message says why, in French (URL not public, access refused, no server at this URL, timeout…). The server moves to failed.
400oauth_sign_in_requiredOAuth server: the caller has no valid connection to this server.
400oauth_requiredAn auth: "none" server that requires sign-in: set auth to oauth. Carries host and server_host.

Connect an account to an OAuth serverLink to the Connect an account to an OAuth server section

POST /resources/mcp/{server_id}/oauth/start · resources:write

Starts the caller’s personal OAuth connection and returns authorize_url, host and server_host. The flow finishes in the browser that started it: use “Test with my account” in the admin console rather than calling this route from a server. Optional body: client_id (512 characters) and client_secret (1,024), for a provider that requires a client ID.

Read the call logLink to the Read the call log section

GET /resources/mcp/{server_id}/calls · resources:read

Who called which tool, when, and with what result; never the arguments or the content. The key’s creator must be able to manage connectors or read the audit log (the key then needs audit:read).

ParameterTypeDescription
user_idstringOnly this member’s calls.
page, limitintegerPagination. Most recent first.
JSON
{
  "items": [
    {
      "id": "c0a1e2f3-4b5c-4d6e-8f70-91a2b3c4d5e6",
      "server_id": "6f1c2a9e-3b7d-4e21-9a0c-5d8e7f6a1b2c",
      "user_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
      "user_name": "Camille Martin",
      "tool": "search_contacts",
      "outcome": "ok",
      "approved": false,
      "duration_ms": 842,
      "created_at": 1793441200
    }
  ],
  "total": 1,
  "page": 1,
  "limit": 50
}

outcome is ok, error or refused; approved is true for a call made after the member approved it.

Organization storageLink to the Organization storage section

Files uploaded by members can be kept in the organization’s own storage: Amazon S3 or compatible, Azure Blob Storage or Google Cloud Storage. Credentials are encrypted and never returned.

The storage objectLink to the The storage object section

Without storage, the response is {"configured": false}. Otherwise:

JSON
{
  "configured": true,
  "provider": "s3",
  "config": { "prefix": "moonai", "bucket": "acme-moonai-files", "region": "eu-west-3", "endpoint_url": null, "addressing_style": "auto" },
  "credentials": { "configured": true, "hint": "7QXZ" },
  "enabled": true,
  "status": "ok",
  "last_error": null,
  "tested_at": 1793440000,
  "active": true,
  "files": 1240,
  "bytes": 5368709120,
  "pending_deletes": 0,
  "last_ok_at": 1793448000,
  "last_failure_at": null,
  "last_failure_code": null,
  "updated_at": 1793440000
}
FieldDescription
credentials.hintTo recognize the credentials: last 4 characters of the access key (S3) or of the account key (Azure), address of the service account (Google).
statusuntested, ok or failed; last_error gives the code of the last failed test.
activeenabled and status set to ok: new files go there.
files, bytesFiles kept in this storage and their total size.
pending_deletesObjects waiting to be deleted.

Read the storageLink to the Read the storage section

GET /resources/storage · resources:read

Configure the storageLink to the Configure the storage section

PUT /resources/storage · resources:write · managed organization for a creation

FieldTypeRequiredDescription
providerstringyess3, azure or gcs.
configobjectyesDepends on the provider (below).
credentialsobjecton creation, or to change provider or targetMissing: the saved credentials are kept.
enabledbooleannofalse by default: a PUT without enabled turns the storage off.
Providerconfigcredentials
s3bucket, region, endpoint_url (https://, public; omit for AWS), addressing_style (auto, path or virtual), prefixaccess_key_id, secret_access_key
azureaccount_url (https://), container, prefixaccount_key
gcsbucket, prefixservice_account: the JSON key of a Google service account, as an object or as text

prefix is a folder in the bucket (letters, digits, ., _, -, /, up to 200 characters).

A change of provider, configuration or credentials resets the storage to untested and turns it off: the response then carries test_required: true and disabled_by_change. To turn it on: test it, then send the same configuration again with enabled: true.

StatuscodeCause
400invalid_requestInvalid configuration; field names the field (bucket, prefix, endpoint_url, credentials…).
409storage_in_useChanging the bucket, container or URL while files are stored there; files gives their number. Change only the credentials, or turn this storage off.
409test_requiredenabled: true without a successful test.
409org_not_managedCreation in an organization that is not managed.

Test the storageLink to the Test the storage section

POST /resources/storage/test · resources:write

Writes, reads back and deletes a small file, then returns the storage object. The test does not turn the storage on. A failure returns 400 org_storage_test_failed with reason:

reasonCause
configThe configuration is invalid.
deniedThe service refuses access: check the credentials and their permissions (write, read, delete).
not_foundBucket or container not found.
unreachableThe service does not respond.
mismatchThe file read back does not match the file written.
errorAnother failure.

A failure takes the storage out of use for new uploads; files already stored remain readable.

Delete the storageLink to the Delete the storage section

DELETE /resources/storage · resources:write

Removes the configuration, without deleting any object from the bucket. Refused while files are stored there (409 storage_in_use: turn it off instead) or while objects are waiting to be deleted (409 storage_pending_deletes, with pending: test it first so they get deleted). Response: {"success": true}.

SharesLink to the Shares section

An organization knowledge base (knowledge) or agent (agent) can be shared with groups, roles or labels, and can receive a sensitivity label. An unknown resource_type returns 404.

The share objectLink to the The share object section

JSON
{
  "resource_type": "knowledge",
  "resource_id": "4f2a8c1e-6b3d-4e5f-9a7b-0c1d2e3f4a5b",
  "name": "Procédures RH",
  "owner_id": "91b3d5f7-2a4c-4e6f-8b0d-1e3f5a7c9b2d",
  "sharing": { "all": false, "group_ids": ["2d4c6e8a-1b3d-4f5a-9c7e-0a2b4c6d8e1f"], "role_keys": [], "label_ids": [] },
  "sensitivity_label_id": null,
  "updated_at": 1793452000
}

List sharesLink to the List shares section

GET /resources/shares · resources:read

Parameters: resource_type (knowledge or agent, optional), page, limit. Most recently modified first. Each item carries resource_type, resource_id, name (null if the resource no longer exists), sharing, sensitivity_label_id and updated_at.

List what can be sharedLink to the List what can be shared section

GET /resources/shares/candidates · resources:read

Parameters: resource_type (knowledge by default, or agent), q (searches the name, 128 characters), page, limit. Each item: id, name, owner_id, owner_name. The agents listed are those their owner has shared with the workspace.

Read, set and remove a shareLink to the Read, set and remove a share section

GET /resources/shares/{resource_type}/{resource_id} · resources:read

PUT /resources/shares/{resource_type}/{resource_id} · resources:write · managed organization

FieldTypeRequiredDescription
sharingobjectnoSharing block; missing: no one. Replaces the whole share.
sensitivity_label_idstringnoSensitivity label; missing or null: removed.

For a knowledge base, its access settings are rewritten: all: false removes read access for the whole workspace, even if its owner had opened it; shares with individual people remain. With a sensitivity label, only the roles in role_keys (or else the owner and admins) can read and edit it.

An agent is shared with the workspace from its “Agents” page; here, you only choose the roles that can use a confidential agent.

StatuscodeCause
400agent_share_all_unsupportedall: true on an agent.
400invalid_requestGroups or labels on an agent.
400role_share_needs_sensitivityRoles without a sensitivity label.

DELETE /resources/shares/{resource_type}/{resource_id} · resources:write

Removes the organization’s share. Response: {"success": true}; 404 if there is none.

List possible recipientsLink to the List possible recipients section

GET /resources/audience · resources:read

ParameterTypeDescription
kindstringgroups (default), labels (member labels), sensitivity (sensitivity labels) or roles.
qstringSearches the name (or a label’s key and value), 128 characters.
page, limitintegerPagination.

For roles, items gives the custom roles (role_key custom:<id>, name) and, on the first page, builtin gives the five built-in roles with their names according to the type of organization.

See alsoLink to the See also section

Moon AI

Moon AI in action

0:00

Explore

Screenshots of chat.realmoon.ai taken on October 29, 2026, with fictional data.

Moon AI

Your preferences

Visits counted without cookies.

Our server counts visits without cookies or trackers. Your theme and your answer about ad measurement are remembered on this device.

Ad measurement

With your consent, our ad networks’ trackers (Google) measure our ads, from the visit to the sign-up and then to the first subscription and its amount, and may show you our ads. Your answer is valid for 13 months; you can change it here at any time.

No answer saved on this device.

After you decline, no tag is loaded anymore and the site deletes the cookies they had set on realmoon.ai. Those on the ad networks’ own domains are managed with them or in your browser.

The Moon AI app has its own privacy settings.