API REST
Gestiona agentes, claves, accesos y el registro desde un script con una clave de consola.
En esta página
Dirección y clave
Los scripts llaman a https://mcp.sallyport.cloud/v1 con una clave de consola. Un propietario la crea en Configuración, Claves API con Nueva clave API. Empieza por sp_ck_ y se muestra una sola vez.
Envíala como Authorization: Bearer $SP_CONSOLE_KEY. La clave actúa como propietario de toda la organización. Deja de funcionar cuando se revoca o cuando el propietario que la creó deja de serlo.
Algunas acciones necesitan una persona con sesión iniciada y responden 403 a una clave de consola. Son crear claves de consola, responder aprobaciones, el inicio de sesión OAuth, los ajustes de notificaciones, marcar señales y descargar respuestas guardadas.
Los tokens de agente (sp_live_…, sp_test_…) son para /mcp y /p/. En /v1 abren una sola ruta: GET /v1/bodies/{body_ref} para las llamadas del propio agente.
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/workspacesRespuestas y errores
Un éxito es {"data": …, "request_id": "…"} con 200, o 201 cuando se creó algo. Los cuerpos de solicitud son JSON de hasta 1 MiB. Un campo que la ruta no conoce se ignora, salvo campos de regla retirados como class_policy en /bindings, que reciben 422.
Un error tiene la misma forma en todas las rutas. message es una frase corta en inglés; details lleva lo que necesita un programa.
{
"error": {
"code": "VALIDATION_FAILED",
"message": "Validation failed",
"details": {
"fields": [
{ "field": "workspace_id", "code": "not_a_uuid", "message": "Select a workspace" }
]
},
"request_id": "9e8df29b6763e0ddd8138df2790de68a"
}
}| Estado y código | Qué significa |
|---|---|
401 AUTH_FAILED | La clave falta, es incorrecta o está revocada. |
403 FORBIDDEN | Tu rol no basta. details.required_role nombra el rol que sí basta. |
404 NOT_FOUND, 404 CALL_NOT_FOUND | No hay nada con ese ID en tu organización, o el ID no es un UUID. |
409 CONFLICT, 409 CALL_CONFLICT | El objeto está ocupado, cambió o ya está en ese estado. details dice cuál. |
410 APPROVAL_EXPIRED | La tarjeta de aprobación se cerró antes de la respuesta. |
422 VALIDATION_FAILED | Un campo está mal. details.fields enumera cada uno con field, code y message. |
400 BAD_REQUEST | Un parámetro de consulta que la ruta no admite. details.accepted enumera los que sí. |
503 UNAVAILABLE, 500 INTERNAL | Un fallo del lado de Sallyport. Repite un 503 tras details.retry_after_ms. |
Listas
La mayoría de las listas devuelve todas las filas de una vez: data es {"items": […], "total": n, "page": 1, "limit": n}.
Las llamadas, las aprobaciones y los eventos de auditoría llegan en páginas de {"items", "next_cursor", "total", "truncated"}. limit es 50 por defecto y 200 como máximo. Devuelve next_cursor como cursor hasta que sea null.
Un filtro con varios valores los recibe separados por comas, como en state=completed,failed. La mayoría de las rutas de lista rechaza un parámetro que no conoce, así que un filtro mal escrito nunca devuelve todo.
Da a un script su propio agente
POST /agents recibe name y workspace_id. environment es live (por defecto) o test; description, delegate_user_id y allowed_ips son opcionales. Toma workspace_id de GET /workspaces.
Un agente nuevo recibe enseguida todas las conexiones activas de su espacio y entorno, y todas funcionan sin preguntar. Después emítele un token. El ejemplo usa jq para leer el ID del agente nuevo.
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"El token está en data.token, y esta respuesta es el único lugar donde aparece. Empieza por sp_live_ para un agente live y por sp_test_ para uno de prueba.
label admite hasta 100 caracteres, y expires_at es una hora RFC 3339 opcional en el futuro. DELETE /agents/{id}/tokens/{token_id} revoca un token.
Añade una clave
POST /credentials guarda una clave y crea sus conexiones en el mismo paso. Todos los agentes del mismo espacio y entorno las reciben enseguida.
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 nombra el servicio, y GET /recipes los enumera. fields lleva el secreto con los nombres de credential_fields de la receta; la mayoría de los servicios tiene un solo campo, value. environment es live por defecto.
La respuesta describe la clave sin su secreto. data.used_by.bindings enumera las conexiones nuevas: un id de ahí es el {binding_id} del proxy HTTP.
Ninguna ruta devuelve jamás un secreto. Para sustituirlo, envía un objeto fields nuevo a POST /credentials/{id}/rotate.
Cambia cómo usa un agente una conexión
Cada par de agente y conexión tiene una regla. class_policy asigna a read, write y destructive uno de auto, ask o deny, y un par nuevo empieza con los tres en 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 sustituye la regla entera. Una clase que omites pasa a ask, y un step_up, path_prefix_override, methods_override o rate_per_hour omitido se borra. Envía los que quieras conservar.
Campos opcionales: step_up (none, o totp para un código del autenticador al aprobar una llamada destructiva), path_prefix_override, methods_override y rate_per_hour. Los tres últimos solo pueden estrechar la conexión.
DELETE /agents/{id}/bindings/{binding_id} quita una conexión al agente. POST /agents/{id}/bindings devuelve conexiones: binding_ids o "all": true, con un rule opcional de la misma forma.
Lee las llamadas y el registro
GET /calls recorre las llamadas por páginas, las más nuevas primero. Filtros: workspace_id, agent_id, binding_id, state, class, decision, channel, host, q, session, from y to.
Una llamada lleva call_id, agent, binding, class, decision (run, ask o deny), state, summary, upstream_status, duration_ms, conteos de bytes, error_code y body_ref.
Estados: admitted, awaiting_approval, dispatching, executing, completed, denied, failed, outcome_unknown y cancelled. GET /calls/{call_id} añade la intención, la cronología y los números de secuencia de la llamada en el registro.
GET /audit/events recorre el registro por páginas, lo más nuevo primero. GET /audit/export descarga la cadena como líneas JSON: una cabecera y luego un evento por línea. from_seq y to_seq acotan un rango.
Limita un agente a direcciones conocidas
allowed_ips en POST /agents y PATCH /agents/{id} admite hasta 100 direcciones IP o rangos CIDR. Una lista vacía permite cualquier dirección, y un PATCH sin el campo conserva la lista actual.
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 es la dirección de la que salen las solicitudes del agente. GET /me/ip, ejecutado en esa máquina, muestra la dirección que ve Sallyport.
Una solicitud desde cualquier otra dirección recibe 403 SOURCE_IP_NOT_ALLOWED en /mcp, /p/ y /v1/bodies. Las llamadas que ya están en curso no se detienen.
Apps conectadas
GET /oauth/consents devuelve los clientes de IA que iniciaron sesión con OAuth, una fila por persona, cliente y agente. Un miembro recibe sus filas; un propietario, un administrador o una clave de consola reciben las de todos.
Cada fila lleva id, client_id, client_name, registration (cimd para un cliente identificado por su sitio web, dcr para uno que se registró por su cuenta), verified, domain, publisher, redirect (kind: web, app o loopback, y host), agent (id, name), workspace_id, connected_by (id, name, email), connected_at, renewed_at, last_used_at y mine.
DELETE /oauth/consents/{id} desconecta la conexión entera. Sus llamadas se detienen al instante en todos los ordenadores desde los que el cliente inició sesión, y el cliente vuelve iniciando sesión de nuevo.
La pantalla de consentimiento lee GET /oauth/consent?request=…, solo para una persona con sesión iniciada. Además del client ID y la dirección de redirección, trae client.verified, client.domain, client.publisher, redirect (kind, host) y workspaces. Cada espacio de trabajo tiene access: grants (existing, all o none) y las connections que usará el agente, cada una con id, name, channel y asks_first.
Una fila también trae sub_agents: los agentes que el cliente creó para sus máquinas, cada uno con id, name y box, la máquina para la que existe. En GET /agents ese agente tiene created_by_client con client_id, client_name y box. Tiene parent_agent_id solo bajo una conexión que es en sí un agente, hecha con scope=mcp agents. Los demás agentes tienen null en ambos. Una fila con manages_agents: true es la conexión de una plataforma: gestiona agentes y no es un agente.
Tokens para las máquinas de una plataforma
Una plataforma que ejecuta agentes en máquinas propias inicia sesión una vez con scope=agents. Ese consentimiento es un acceso para gestionar agentes, no un agente: la persona ve una tarjeta corta sin lista de servicios. Con el access token de la conexión crea un agente para cada máquina en POST https://mcp.sallyport.cloud/v1/agents. El agente empieza sin acceso: una persona elige qué puede usar en su página de la consola.
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 respuesta trae agent_id y access_url, la página de acceso del agente. Añada &return_to= con su propia página, y la persona vuelve con un botón. Una máquina tiene un agente: un segundo POST responde 409 con box_has_agent. GET /v1/agents lista los agentes de la persona con created_by_client, box y paused. PATCH y DELETE /v1/agents/{id} renombran, pausan o eliminan solo los agentes que creó este cliente. Ninguna llamada de esta API da ni quita acceso. Envíe allowed_ips en POST o PATCH, y los tokens del agente funcionan solo desde esas direcciones: un token copiado fuera de la máquina no sirve en ningún otro sitio. La lista que la persona fije en la consola se aplica encima.
Para el token de una máquina, la plataforma cambia el access token de la conexión en POST https://mcp.sallyport.cloud/token (RFC 8693) y nombra el agente en sallyport_agent. El token dura una hora.
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 respuesta trae access_token, expires_in, sallyport_agent_id, sallyport_agent_name y sallyport_agent_url. No hay refresh token: la plataforma intercambia de nuevo. Un rechazo trae sallyport_reason: subject_token_expired, consent_ended, agents_scope_missing, agent_not_bound, agent_deleted o agent_paused.
POST https://mcp.sallyport.cloud/revoke (RFC 7009) recibe token y client_id. El token de una máquina termina todos los tokens de esa máquina. El refresh token termina la conexión entera, como «Desconectar».
GET https://mcp.sallyport.cloud/v1/access con Authorization: Bearer responde por el agente de ese token, el de una máquina: services, el número de conexiones que puede usar, access_url, su página de acceso, y connections, las mismas entradas que list_access. Mientras no tenga servicios, message lo dice en una línea. Un token que no sirve recibe 401 con WWW-Authenticate: Bearer y error="invalid_token". El token propio de la conexión recibe services: 0 y una línea que dice que no usa ningún servicio. En /mcp y /p/ recibe 403 con error="insufficient_scope" y scope="mcp".
Servidores MCP listos
GET /v1/mcp-servers enumera los servidores MCP remotos que la consola ofrece al añadir uno. Son los servidores alojados por los propios proveedores, cada uno con su dirección y su forma de iniciar sesión. Cualquier miembro puede leerlo. Elegir uno solo rellena el formulario, y lo que guardas es una conexión MCP remota normal, ver Clientes MCP y servidores remotos.
{
"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"
}| Campo | Significado |
|---|---|
url, url_part | La dirección del servidor. Si url_part no es null, la dirección lleva {name}. Esa parte es propia de cada cliente: label la pide en seis idiomas, example muestra una. |
auth | oauth inicia sesión en el proveedor, bearer acepta un token creado allí, api_key envía una clave en la cabecera api_key_header tras api_key_prefix, none no necesita nada. |
dynamic_registration | Por la vía OAuth el proveedor registra Sallyport por sí mismo. false significa que introduces un ID de cliente creado en el proveedor. |
bearer_alternative, key_url | El servidor OAuth también acepta un token creado en el proveedor, y la página donde creas ese token o clave. |
recipe | El servicio del catálogo del mismo proveedor, o null. |
description, docs_url | Lo que hace el servidor, una frase en cada uno de los seis idiomas, y la página del proveedor sobre el servidor. |
Todas las rutas
Las rutas cuelgan de https://mcp.sallyport.cloud/v1. Miembro significa cualquier rol en la organización. Una clave de consola cuenta como propietario.
| Rutas | Quién | Para qué |
|---|---|---|
GET /tenants/me, PATCH /tenants/me | miembro, propietario | La organización: nombre, ajustes, contadores. |
POST /tenants/me/pause, POST /tenants/me/resume | propietario | Detener y reanudar todas las llamadas de agentes. La pausa lleva reason. |
GET /me/ip | cualquiera | La dirección que Sallyport ve para quien llama. |
GET, POST /workspaces; GET, PATCH /workspaces/{id} | miembro, administrador | Espacios de trabajo. Se crean con slug, name y kind (team o client). |
GET /workspaces/{id}/report?period=YYYY-MM | auditor | Un mes de un espacio: totales, servicios, llamadas destructivas. |
GET, POST /agents; GET, PATCH, DELETE /agents/{id} | miembro, administrador | Agentes. Borrar responde 409 mientras una llamada del agente está en curso. |
POST /agents/{id}/pause, POST /agents/{id}/resume | administrador | Detener un agente y reanudarlo. La pausa lleva reason. |
GET, POST /agents/{id}/tokens; DELETE /agents/{id}/tokens/{token_id} | administrador | Tokens de agente. |
GET, POST /agents/{id}/bindings; PUT, DELETE /agents/{id}/bindings/{binding_id} | miembro, administrador | Las conexiones que usa un agente y su regla en cada una. |
GET, PUT /agents/{id}/limits | miembro, administrador | concurrency_limit, rate_per_hour y read_budget.multiplier. |
GET, POST /credentials; GET, PATCH, DELETE /credentials/{id} | administrador | Claves. Borrar una clave borra sus conexiones. |
POST /credentials/{id}/rotate, PATCH /credentials/{id}/origin, GET /credentials/{id}/usage, POST /credentials/{id}/oauth/start | administrador | Sustituir el secreto, anotar si el original está revocado en el proveedor, ver quién usa la clave. oauth/start inicia el inicio de sesión en el proveedor y necesita una persona con sesión iniciada. |
GET, POST /bindings; GET, PATCH, DELETE /bindings/{id} | miembro, administrador | Conexiones. PATCH compara If-Match con config_version cuando lo envías. |
POST /bindings/{id}/pause, /resume, /check-key, /oauth/start | administrador | Detener una conexión, reanudarla, volver a comprobar su clave. oauth/start inicia sesión en un servidor MCP remoto y necesita una persona con sesión iniciada. |
GET, POST /ssh-hosts; GET, PATCH, DELETE /ssh-hosts/{id} | administrador | Servidores para comandos SSH. |
POST /database-connections/test | administrador | Probar la dirección y la contraseña de una base de datos antes de guardarlas. |
POST /email-connections/test | administrador | Probar un buzón y su contraseña antes de guardarlos. |
GET /policies/rule-changes, POST /policies/rule-changes/{id}/revert | miembro, administrador | El historial de cambios de reglas, y deshacer uno. |
GET /recipes, GET /recipes/{key} | miembro | El catálogo de servicios, ver Recetas. |
GET /mcp-servers | miembro | Servidores MCP remotos listos para una conexión nueva. |
GET, POST /grants; POST /grants/{id}/revoke | miembro, administrador | Permisos con plazo de un agente sobre una conexión. Un aprobador también puede revocarlos. |
GET /approvals, GET /approvals/{call_id}, GET /approvals/{call_id}/payload | aprobador | Tarjetas de aprobación y la solicitud exacta detrás de cada una. Un auditor puede leer la lista. |
POST /approvals/{call_id}/open, /decide, /transfer | aprobador | Prolongar una tarjeta, responderla, pasarla a otro aprobador. Responder exige una persona con sesión iniciada. |
GET /access-requests, GET /access-requests/{request_id} | miembro | Solicitudes que hicieron los agentes por un servicio que no pudieron usar. |
POST /access-requests/{request_id}/provision, /decline | administrador | Responder a una solicitud con una clave, o rechazarla con reason. |
GET /calls, GET /calls/live, GET /calls/{call_id} | auditor | Historial de llamadas, llamadas en curso, una llamada. |
POST /calls/{call_id}/cancel | administrador | Detener una llamada que no ha terminado. |
GET /bodies/{body_ref}/meta, GET /bodies/{body_ref} | auditor; la descarga también el agente | Tamaño y caducidad de una respuesta guardada, y la respuesta misma. La descarga admite Range. |
GET /audit/events, GET /audit/export | auditor | El registro, por páginas o como un archivo de líneas JSON. Solo con un rol en toda la organización, no en un espacio de trabajo. |
GET /signals, POST /signals/{id}/ack | miembro, administrador | Actividad inusual, y marcarla como vista. |
GET /reports, GET /reports/{id}, GET /insights | miembro | Informes semanales y estadísticas de aprobaciones. |
GET /billing, POST /billing/plan | administrador, propietario | Plan y uso, y pedir otro plan. |
GET, POST /webhooks; PATCH, DELETE /webhooks/{id} | administrador | Eventos del registro enviados a tu dirección HTTPS. El secreto de firma se muestra una vez. |
GET /webhooks/{id}/deliveries, POST /webhooks/{id}/deliveries/{delivery_id}/retry | administrador | Historial de entregas, y reenviar una fallida. |
GET /members, PATCH /members/{user_id} | miembro, administrador | Personas y sus roles. |
GET, POST /members/invitations; DELETE /members/invitations/{id} | administrador | Invitaciones por correo. |
GET /members/{user_id}/offboarding, POST /members/{user_id}/offboard | administrador | Traspasar lo que tenía una persona que se va. |
GET, POST /guests; POST /guests/{id}/relink; DELETE /guests/{id} | administrador | Aprobadores del cliente que entran con un enlace por correo. |
GET /onboarding, POST /onboarding/steps/{key} | miembro | La lista de primeros pasos. |
GET, POST /console-keys; DELETE /console-keys/{id} | propietario | Claves de consola. Crear una exige un propietario con sesión iniciada. |
GET /oauth/consents, DELETE /oauth/consents/{id} | miembro, administrador | Apps conectadas, y desconectar una. Un miembro gestiona las suyas, un administrador las de todos. |
/notifications/…, /oauth/consent, /guest/step-up | persona | Pantallas de la consola para una persona con sesión iniciada. |