API REST
Gérez agents, clés, accès et journal depuis un script avec une clé de console.
Sur cette page
- Adresse et clé
- Réponses et erreurs
- Listes
- Donner à un script son propre agent
- Ajouter une clé
- Changer l’usage d’une connexion par un agent
- Lire les appels et le journal
- Limiter un agent à des adresses connues
- Apps connectées
- Jetons pour les machines d’une plateforme
- Serveurs MCP prêts à l’emploi
- Toutes les routes
Adresse et clé
Les scripts appellent https://mcp.sallyport.cloud/v1 avec une clé de console. Un propriétaire la crée dans Paramètres, Clés API avec Nouvelle clé API. Elle commence par sp_ck_ et n’apparaît qu’une fois.
Envoyez-la en Authorization: Bearer $SP_CONSOLE_KEY. La clé agit comme un propriétaire de toute l’organisation. Elle cesse de fonctionner quand elle est révoquée, ou quand le propriétaire qui l’a créée n’est plus propriétaire.
Quelques actions exigent une personne connectée et répondent 403 à une clé de console. Ce sont la création de clés de console, les réponses aux approbations, la connexion OAuth, les réglages de notifications, l’acquittement des signaux et le téléchargement des réponses stockées.
Les jetons d’agent (sp_live_…, sp_test_…) servent pour /mcp et /p/. Sur /v1, ils n’ouvrent qu’une route : GET /v1/bodies/{body_ref} pour les appels de l’agent lui-même.
export SP_CONSOLE_KEY='sp_ck_…'
curl --fail-with-body \
--header "Authorization: Bearer $SP_CONSOLE_KEY" \
https://mcp.sallyport.cloud/v1/tenants/me
curl --fail-with-body \
--header "Authorization: Bearer $SP_CONSOLE_KEY" \
https://mcp.sallyport.cloud/v1/workspacesRéponses et erreurs
Un succès est {"data": …, "request_id": "…"} avec 200, ou 201 quand quelque chose a été créé. Les corps de requête sont du JSON jusqu’à 1 MiB. Un champ inconnu de la route est ignoré, sauf les champs de règle retirés comme class_policy sur /bindings, qui reçoivent 422.
Une erreur a la même forme sur toutes les routes. message est une courte phrase en anglais ; details porte ce dont un programme a besoin.
{
"error": {
"code": "VALIDATION_FAILED",
"message": "Validation failed",
"details": {
"fields": [
{ "field": "workspace_id", "code": "not_a_uuid", "message": "Select a workspace" }
]
},
"request_id": "9e8df29b6763e0ddd8138df2790de68a"
}
}| Statut et code | Signification |
|---|---|
401 AUTH_FAILED | La clé manque, est fausse ou révoquée. |
403 FORBIDDEN | Votre rôle ne suffit pas. details.required_role nomme celui qui suffit. |
404 NOT_FOUND, 404 CALL_NOT_FOUND | Rien avec cet ID dans votre organisation, ou l’ID n’est pas un UUID. |
409 CONFLICT, 409 CALL_CONFLICT | L’objet est pris, a changé ou est déjà dans cet état. details dit lequel. |
410 APPROVAL_EXPIRED | La carte d’approbation s’est fermée avant la réponse. |
422 VALIDATION_FAILED | Un champ est faux. details.fields les liste avec field, code et message. |
400 BAD_REQUEST | Un paramètre de requête que la route ne prend pas. details.accepted liste ceux qu’elle prend. |
503 UNAVAILABLE, 500 INTERNAL | Une panne côté Sallyport. Répétez un 503 après details.retry_after_ms. |
Listes
La plupart des listes renvoient toutes les lignes d’un coup : data vaut {"items": […], "total": n, "page": 1, "limit": n}.
Les appels, les approbations et les événements d’audit arrivent en pages de {"items", "next_cursor", "total", "truncated"}. limit vaut 50 par défaut et 200 au plus. Renvoyez next_cursor comme cursor jusqu’à ce qu’il soit null.
Un filtre à plusieurs valeurs les prend séparées par des virgules, comme dans state=completed,failed. La plupart des routes de liste refusent un paramètre inconnu : un filtre mal orthographié ne renvoie jamais tout.
Donner à un script son propre agent
POST /agents prend name et workspace_id. environment vaut live (par défaut) ou test ; description, delegate_user_id et allowed_ips sont facultatifs. Prenez workspace_id dans GET /workspaces.
Un nouvel agent reçoit aussitôt toutes les connexions actives de son espace et de son environnement, toutes sans demander. Émettez-lui ensuite un jeton. L’exemple utilise jq pour lire l’ID du nouvel agent.
export SP_WORKSPACE_ID='…'
AGENT_ID=$(curl --fail-with-body --silent \
--request POST \
--header "Authorization: Bearer $SP_CONSOLE_KEY" \
--header "Content-Type: application/json" \
--data "{\"name\":\"Nightly report\",\"workspace_id\":\"$SP_WORKSPACE_ID\"}" \
https://mcp.sallyport.cloud/v1/agents | jq -r .data.id)
curl --fail-with-body \
--request POST \
--header "Authorization: Bearer $SP_CONSOLE_KEY" \
--header "Content-Type: application/json" \
--data '{"label":"CI"}' \
"https://mcp.sallyport.cloud/v1/agents/$AGENT_ID/tokens"Le jeton est dans data.token, et cette réponse est le seul endroit où il apparaît. Il commence par sp_live_ pour un agent live et sp_test_ pour un agent de test.
label accepte 100 caractères au plus, et expires_at est une date RFC 3339 facultative dans le futur. DELETE /agents/{id}/tokens/{token_id} révoque un jeton.
Ajouter une clé
POST /credentials enregistre une clé et crée ses connexions dans la même étape. Tous les agents du même espace et du même environnement les reçoivent aussitôt.
export GITHUB_TOKEN='…'
curl --fail-with-body \
--request POST \
--header "Authorization: Bearer $SP_CONSOLE_KEY" \
--header "Content-Type: application/json" \
--data "{\"name\":\"GitHub\",\"workspace_id\":\"$SP_WORKSPACE_ID\",\"recipe_key\":\"github\",\"fields\":{\"value\":\"$GITHUB_TOKEN\"}}" \
https://mcp.sallyport.cloud/v1/credentialsrecipe_key nomme le service, et GET /recipes les liste. fields porte le secret sous les noms de credential_fields de la recette ; la plupart des services ont un seul champ, value. environment vaut live par défaut.
La réponse décrit la clé sans son secret. data.used_by.bindings liste les nouvelles connexions : un id de cette liste est le {binding_id} du proxy HTTP.
Aucune route ne renvoie jamais un secret. Pour le remplacer, envoyez un nouvel objet fields à POST /credentials/{id}/rotate.
Changer l’usage d’une connexion par un agent
Chaque paire agent et connexion a une règle. class_policy associe à read, write et destructive l’une des valeurs auto, ask ou deny, et une nouvelle paire démarre avec les trois sur auto.
export SP_BINDING_ID='…'
curl --fail-with-body \
--request PUT \
--header "Authorization: Bearer $SP_CONSOLE_KEY" \
--header "Content-Type: application/json" \
--data '{"class_policy":{"read":"ask","write":"ask","destructive":"ask"}}' \
"https://mcp.sallyport.cloud/v1/agents/$AGENT_ID/bindings/$SP_BINDING_ID"PUT remplace toute la règle. Une classe omise passe à ask, et un step_up, path_prefix_override, methods_override ou rate_per_hour omis est effacé. Envoyez ceux que vous voulez garder.
Champs facultatifs : step_up (none, ou totp pour un code d’authentificateur à l’approbation d’un appel destructif), path_prefix_override, methods_override et rate_per_hour. Les trois derniers ne peuvent que restreindre la connexion.
DELETE /agents/{id}/bindings/{binding_id} retire une connexion à l’agent. POST /agents/{id}/bindings redonne des connexions : binding_ids ou "all": true, avec un rule facultatif de même forme.
Lire les appels et le journal
GET /calls parcourt les appels par pages, les plus récents d’abord. Filtres : workspace_id, agent_id, binding_id, state, class, decision, channel, host, q, session, from et to.
Un appel porte call_id, agent, binding, class, decision (run, ask ou deny), state, summary, upstream_status, duration_ms, les nombres d’octets, error_code et body_ref.
États : admitted, awaiting_approval, dispatching, executing, completed, denied, failed, outcome_unknown et cancelled. GET /calls/{call_id} ajoute l’intention, la chronologie et les numéros de séquence de l’appel dans le journal.
GET /audit/events parcourt le journal par pages, le plus récent d’abord. GET /audit/export télécharge la chaîne en lignes JSON : un en-tête, puis un événement par ligne. from_seq et to_seq délimitent une plage.
Limiter un agent à des adresses connues
allowed_ips sur POST /agents et PATCH /agents/{id} accepte jusqu’à 100 adresses IP ou plages CIDR. Une liste vide autorise toute adresse, et un PATCH sans ce champ garde la liste actuelle.
export SP_AGENT_IP='…'
curl --fail-with-body \
--request PATCH \
--header "Authorization: Bearer $SP_CONSOLE_KEY" \
--header "Content-Type: application/json" \
--data "{\"allowed_ips\":[\"$SP_AGENT_IP\"]}" \
"https://mcp.sallyport.cloud/v1/agents/$AGENT_ID"SP_AGENT_IP est l’adresse d’où partent les requêtes de l’agent. GET /me/ip, lancé sur cette machine, montre l’adresse que voit Sallyport.
Une requête depuis toute autre adresse reçoit 403 SOURCE_IP_NOT_ALLOWED sur /mcp, /p/ et /v1/bodies. Les appels déjà en cours ne sont pas arrêtés.
Apps connectées
GET /oauth/consents liste les clients IA connectés par OAuth, une ligne par personne, client et agent. Un membre reçoit ses propres lignes ; un propriétaire, un administrateur ou une clé de console reçoit celles de tout le monde.
Une ligne porte id, client_id, client_name, registration (cimd pour un client désigné par son site, dcr pour un client qui s’est enregistré lui-même), verified, domain, publisher, redirect (kind : web, app ou loopback, et host), agent (id, name), workspace_id, connected_by (id, name, email), connected_at, renewed_at, last_used_at et mine.
DELETE /oauth/consents/{id} déconnecte toute la connexion. Ses appels s’arrêtent aussitôt, sur chaque ordinateur depuis lequel le client s’est connecté, et le client doit se reconnecter pour revenir.
L’écran de consentement lit GET /oauth/consent?request=…, réservé à une personne connectée. En plus du Client ID et de l’adresse de redirection, il porte client.verified, client.domain, client.publisher, redirect (kind, host) et workspaces. Chaque espace de travail a un access : grants (existing, all ou none) et les connections que l’agent utilisera, chacune avec id, name, channel et asks_first.
Une ligne porte aussi sub_agents : les agents que le client a créés pour ses machines, chacun avec id, name et box, la machine pour laquelle il existe. Dans GET /agents, un tel agent a created_by_client avec client_id, client_name et box. Il n’a parent_agent_id que sous une connexion qui est elle-même un agent, faite avec scope=mcp agents. Tout autre agent a null dans les deux. Une ligne avec manages_agents: true est la connexion d’une plateforme : elle gère des agents et n’est pas un agent.
Jetons pour les machines d’une plateforme
Une plateforme qui fait tourner des agents sur ses propres machines se connecte une fois avec scope=agents. Ce consentement est un accès pour gérer des agents, pas un agent : la personne voit une carte courte sans liste de services. Avec le jeton d’accès de la connexion, elle crée un agent pour chaque machine sur POST https://mcp.sallyport.cloud/v1/agents. L’agent démarre sans accès : une personne choisit ce qu’il peut utiliser sur sa page dans la console.
curl -sS https://mcp.sallyport.cloud/v1/agents \
-H "Authorization: Bearer $SP_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "api-worker", "box": "5f0c2d1e-9a4b-4c1e-8f3a-2b7d9e6c1a40"}'La réponse porte agent_id et access_url, la page d’accès de l’agent. Ajoutez &return_to= avec votre propre page, et la personne revient d’un bouton. Une machine a un agent : un second POST répond 409 avec box_has_agent. GET /v1/agents liste les agents de la personne avec created_by_client, box et paused. PATCH et DELETE /v1/agents/{id} renomment, mettent en pause ou suppriment seulement les agents que ce client a créés. Aucun appel de cette API ne donne ni ne retire d’accès. Envoyez allowed_ips dans POST ou PATCH, et les jetons de l’agent ne fonctionnent que depuis ces adresses : un jeton copié hors de la machine ne vaut rien ailleurs. La liste que la personne fixe dans la console s’applique en plus.
Pour le jeton d’une machine, la plateforme échange le jeton d’accès de la connexion sur POST https://mcp.sallyport.cloud/token (RFC 8693) et nomme l’agent dans sallyport_agent. Le jeton vit une heure.
curl -sS https://mcp.sallyport.cloud/token \
-d grant_type=urn:ietf:params:oauth:grant-type:token-exchange \
-d client_id=https://detachbox.com/.well-known/sallyport-client.json \
-d subject_token="$SP_ACCESS_TOKEN" \
-d subject_token_type=urn:ietf:params:oauth:token-type:access_token \
-d sallyport_agent="$AGENT_ID"La réponse porte access_token, expires_in, sallyport_agent_id, sallyport_agent_name et sallyport_agent_url. Il n’y a pas de jeton de rafraîchissement : la plateforme échange de nouveau. Un refus porte sallyport_reason : subject_token_expired, consent_ended, agents_scope_missing, agent_not_bound, agent_deleted ou agent_paused.
POST https://mcp.sallyport.cloud/revoke (RFC 7009) prend token et client_id. Le jeton d’une machine met fin à tous les jetons de cette machine. Le jeton de rafraîchissement met fin à toute la connexion, comme « Déconnecter ».
GET https://mcp.sallyport.cloud/v1/access avec Authorization: Bearer répond pour l’agent de ce jeton, celui d’une machine : services, le nombre de connexions qu’il peut utiliser, access_url, sa page d’accès, et connections, les mêmes entrées que list_access. Tant qu’il n’a aucun service, message le dit en une ligne. Un jeton qui ne convient pas reçoit 401 avec WWW-Authenticate: Bearer et error="invalid_token". Le jeton propre de la connexion reçoit services: 0 et une ligne qui dit qu’elle n’utilise aucun service. Sur /mcp et /p/, il reçoit 403 avec error="insufficient_scope" et scope="mcp".
Serveurs MCP prêts à l’emploi
GET /v1/mcp-servers liste les serveurs MCP distants que la console propose quand vous en ajoutez un. Ce sont les serveurs hébergés par les fournisseurs eux-mêmes, chacun avec son adresse et sa façon de se connecter. Tout membre peut le lire. En choisir un remplit seulement le formulaire, et ce que vous enregistrez est une connexion MCP distante ordinaire, voir Clients MCP et serveurs distants.
{
"data": {
"items": [
{
"key": "datadog-site",
"name": "Datadog (other sites)",
"vendor": "Datadog",
"url": "https://mcp.{site}/v1/mcp",
"url_part": {
"name": "site",
"label": { "en": "Your Datadog site domain, for example datadoghq.eu or us5.datadoghq.com", "ru": "…", "es": "…", "pt": "…", "fr": "…", "de": "…" },
"example": "datadoghq.eu"
},
"auth": "oauth",
"dynamic_registration": true,
"bearer_alternative": false,
"api_key_header": null,
"api_key_prefix": null,
"key_url": null,
"recipe": "datadog",
"docs_url": "https://docs.datadoghq.com/mcp_server/setup/",
"description": { "en": "Query logs, metrics, traces, monitors and incidents on your Datadog site.", "ru": "…", "es": "…", "pt": "…", "fr": "…", "de": "…" }
}
],
"total": 1,
"page": 1,
"limit": 1
},
"request_id": "5d0c2f7e8a1b4c6d9e0f1a2b3c4d5e6f"
}| Champ | Sens |
|---|---|
url, url_part | L’adresse du serveur. Quand url_part n’est pas null, l’adresse contient {name}. Cette partie est propre à chaque client : label la demande en six langues, example en montre une. |
auth | oauth se connecte chez le fournisseur, bearer prend un jeton créé là-bas, api_key envoie une clé dans l’en-tête api_key_header après api_key_prefix, none ne demande rien. |
dynamic_registration | Par la voie OAuth, le fournisseur enregistre Sallyport tout seul. false signifie que vous saisissez un ID client créé chez le fournisseur. |
bearer_alternative, key_url | Le serveur OAuth accepte aussi un jeton créé chez le fournisseur, et la page où vous créez ce jeton ou cette clé. |
recipe | Le service du catalogue du même fournisseur, ou null. |
description, docs_url | Ce que fait le serveur, une phrase dans chacune des six langues, et la page du fournisseur sur le serveur. |
Toutes les routes
Les chemins partent de https://mcp.sallyport.cloud/v1. Membre désigne n’importe quel rôle dans l’organisation. Une clé de console compte comme propriétaire.
| Routes | Qui | Pour quoi |
|---|---|---|
GET /tenants/me, PATCH /tenants/me | membre, propriétaire | L’organisation : nom, réglages, compteurs. |
POST /tenants/me/pause, POST /tenants/me/resume | propriétaire | Arrêter et relancer tous les appels d’agents. La pause prend reason. |
GET /me/ip | tout le monde | L’adresse que Sallyport voit pour cet appelant. |
GET, POST /workspaces; GET, PATCH /workspaces/{id} | membre, administrateur | Espaces de travail. Création avec slug, name et kind (team ou client). |
GET /workspaces/{id}/report?period=YYYY-MM | auditeur | Un mois d’un espace : totaux, services, appels destructifs. |
GET, POST /agents; GET, PATCH, DELETE /agents/{id} | membre, administrateur | Agents. La suppression répond 409 tant qu’un appel de l’agent est en cours. |
POST /agents/{id}/pause, POST /agents/{id}/resume | administrateur | Arrêter un agent et le relancer. La pause prend reason. |
GET, POST /agents/{id}/tokens; DELETE /agents/{id}/tokens/{token_id} | administrateur | Jetons d’agent. |
GET, POST /agents/{id}/bindings; PUT, DELETE /agents/{id}/bindings/{binding_id} | membre, administrateur | Les connexions d’un agent et sa règle sur chacune. |
GET, PUT /agents/{id}/limits | membre, administrateur | concurrency_limit, rate_per_hour et read_budget.multiplier. |
GET, POST /credentials; GET, PATCH, DELETE /credentials/{id} | administrateur | Clés. Supprimer une clé supprime ses connexions. |
POST /credentials/{id}/rotate, PATCH /credentials/{id}/origin, GET /credentials/{id}/usage, POST /credentials/{id}/oauth/start | administrateur | Remplacer le secret, noter si l’original est révoqué chez le fournisseur, voir qui utilise la clé. oauth/start lance la connexion chez le fournisseur et demande une personne connectée à la console. |
GET, POST /bindings; GET, PATCH, DELETE /bindings/{id} | membre, administrateur | Connexions. PATCH compare If-Match à config_version quand vous l’envoyez. |
POST /bindings/{id}/pause, /resume, /check-key, /oauth/start | administrateur | Arrêter une connexion, la relancer, revérifier sa clé. oauth/start connecte un serveur MCP distant et demande une personne connectée à la console. |
GET, POST /ssh-hosts; GET, PATCH, DELETE /ssh-hosts/{id} | administrateur | Serveurs pour les commandes SSH. |
POST /database-connections/test | administrateur | Essayer l’adresse et le mot de passe d’une base de données avant de les enregistrer. |
POST /email-connections/test | administrateur | Essayer une boîte aux lettres et son mot de passe avant de les enregistrer. |
GET /policies/rule-changes, POST /policies/rule-changes/{id}/revert | membre, administrateur | L’historique des changements de règles, et l’annulation d’un changement. |
GET /recipes, GET /recipes/{key} | membre | Le catalogue des services, voir Recettes. |
GET /mcp-servers | membre | Serveurs MCP distants prêts à l’emploi pour une nouvelle connexion. |
GET, POST /grants; POST /grants/{id}/revoke | membre, administrateur | Permissions à durée limitée d’un agent sur une connexion. Un approbateur peut aussi les révoquer. |
GET /approvals, GET /approvals/{call_id}, GET /approvals/{call_id}/payload | approbateur | Cartes d’approbation et la requête exacte derrière chacune. Un auditeur peut lire la liste. |
POST /approvals/{call_id}/open, /decide, /transfer | approbateur | Prolonger une carte, y répondre, la passer à un autre approbateur. Répondre exige une personne connectée. |
GET /access-requests, GET /access-requests/{request_id} | membre | Demandes des agents pour un service qu’ils n’ont pas pu utiliser. |
POST /access-requests/{request_id}/provision, /decline | administrateur | Répondre à une demande par une clé, ou la refuser avec reason. |
GET /calls, GET /calls/live, GET /calls/{call_id} | auditeur | Historique des appels, appels en cours, un appel. |
POST /calls/{call_id}/cancel | administrateur | Arrêter un appel non terminé. |
GET /bodies/{body_ref}/meta, GET /bodies/{body_ref} | auditeur ; le téléchargement aussi l’agent | Taille et expiration d’une réponse stockée, et la réponse elle-même. Le téléchargement accepte Range. |
GET /audit/events, GET /audit/export | auditeur | Le journal, par pages ou en un fichier de lignes JSON. Seulement avec un rôle sur toute l’organisation, pas sur un espace de travail. |
GET /signals, POST /signals/{id}/ack | membre, administrateur | Activité inhabituelle, et la marquer comme vue. |
GET /reports, GET /reports/{id}, GET /insights | membre | Rapports hebdomadaires et statistiques d’approbation. |
GET /billing, POST /billing/plan | administrateur, propriétaire | Offre et consommation, et demande d’une autre offre. |
GET, POST /webhooks; PATCH, DELETE /webhooks/{id} | administrateur | Événements du journal envoyés à votre adresse HTTPS. Le secret de signature n’apparaît qu’une fois. |
GET /webhooks/{id}/deliveries, POST /webhooks/{id}/deliveries/{delivery_id}/retry | administrateur | Historique des livraisons, et renvoi d’une livraison échouée. |
GET /members, PATCH /members/{user_id} | membre, administrateur | Personnes et leurs rôles. |
GET, POST /members/invitations; DELETE /members/invitations/{id} | administrateur | Invitations par e-mail. |
GET /members/{user_id}/offboarding, POST /members/{user_id}/offboard | administrateur | Transmettre ce que détenait une personne qui part. |
GET, POST /guests; POST /guests/{id}/relink; DELETE /guests/{id} | administrateur | Approbateurs côté client qui se connectent par un lien reçu par e-mail. |
GET /onboarding, POST /onboarding/steps/{key} | membre | La liste des premiers pas. |
GET, POST /console-keys; DELETE /console-keys/{id} | propriétaire | Clés de console. En créer une exige un propriétaire connecté. |
GET /oauth/consents, DELETE /oauth/consents/{id} | membre, administrateur | Apps connectées, et la déconnexion de l’une d’elles. Un membre gère les siennes, un administrateur celles de tout le monde. |
/notifications/…, /oauth/consent, /guest/step-up | personne | Écrans de la console pour une personne connectée. |