REST API
Управляйте агентами, ключами, доступом и журналом из скрипта с ключом консоли.
На этой странице
Адрес и ключ
Скрипты обращаются к https://mcp.sallyport.cloud/v1 с ключом консоли. Его создаёт владелец в разделе «Настройки», «Ключи API» кнопкой «Новый ключ API». Ключ начинается с sp_ck_ и показывается один раз.
Передавайте его как Authorization: Bearer $SP_CONSOLE_KEY. Ключ действует как владелец всей организации. Он перестаёт работать, когда его отзывают или когда создавший его владелец перестаёт быть владельцем.
Несколько действий требуют человека, вошедшего в консоль, и на ключ консоли отвечают 403. Это создание ключей консоли, ответ на подтверждения, вход через OAuth, настройки уведомлений, отметка сигналов и скачивание сохранённых ответов.
Токены агентов (sp_live_…, sp_test_…) нужны для /mcp и /p/. В /v1 они открывают один маршрут: GET /v1/bodies/{body_ref} для вызовов самого агента.
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/workspacesОтветы и ошибки
Успешный ответ выглядит как {"data": …, "request_id": "…"} со статусом 200 или 201, если что-то создано. Тела запросов в JSON до 1 МиБ. Неизвестные маршруту поля пропускаются, кроме снятых полей правил вроде class_policy на /bindings: на них ответ 422.
Ошибка на всех маршрутах устроена одинаково. В message короткая фраза на английском, в details данные для программы.
{
"error": {
"code": "VALIDATION_FAILED",
"message": "Validation failed",
"details": {
"fields": [
{ "field": "workspace_id", "code": "not_a_uuid", "message": "Select a workspace" }
]
},
"request_id": "9e8df29b6763e0ddd8138df2790de68a"
}
}| Статус и код | Что значит |
|---|---|
401 AUTH_FAILED | Ключа нет, он неверный или отозван. |
403 FORBIDDEN | Вашей роли не хватает. Нужная роль названа в details.required_role. |
404 NOT_FOUND, 404 CALL_NOT_FOUND | В вашей организации нет объекта с таким ID, или ID не является UUID. |
409 CONFLICT, 409 CALL_CONFLICT | Объект занят, изменился или уже в этом состоянии. Подробности в details. |
410 APPROVAL_EXPIRED | Карточка подтверждения закрылась раньше ответа. |
422 VALIDATION_FAILED | Поле заполнено неверно. details.fields перечисляет каждое с field, code и message. |
400 BAD_REQUEST | Параметр запроса, которого маршрут не принимает. Допустимые перечислены в details.accepted. |
503 UNAVAILABLE, 500 INTERNAL | Сбой на стороне Sallyport. После 503 повторите через details.retry_after_ms. |
Списки
Большинство списков отдаёт все строки сразу: в data лежит {"items": […], "total": n, "page": 1, "limit": n}.
Вызовы, подтверждения и события аудита приходят страницами вида {"items", "next_cursor", "total", "truncated"}. limit по умолчанию 50, самое большее 200. Передавайте next_cursor обратно в cursor, пока он не станет null.
Фильтр с несколькими значениями принимает их через запятую, например state=completed,failed. Большинство маршрутов списков отклоняет незнакомый параметр, так что опечатка в фильтре не вернёт всё подряд.
Заведите скрипту своего агента
POST /agents принимает name и workspace_id. environment бывает live (по умолчанию) или test; description, delegate_user_id и allowed_ips необязательны. workspace_id возьмите из GET /workspaces.
Новый агент сразу получает все активные подключения своего пространства и среды, и все они работают без вопросов. Затем выпустите ему токен. В примере ID нового агента читается через jq.
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"Токен лежит в data.token, и больше нигде он не появится. У агента среды live он начинается с sp_live_, у тестового с sp_test_.
label до 100 символов, expires_at необязателен и задаётся временем RFC 3339 в будущем. DELETE /agents/{id}/tokens/{token_id} отзывает токен.
Добавьте ключ
POST /credentials сохраняет ключ и тут же создаёт его подключения. Все агенты того же пространства и среды получают их сразу.
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 называет сервис, список даёт GET /recipes. fields несёт секрет под именами из credential_fields рецепта; у большинства сервисов одно поле value. environment по умолчанию live.
В ответе описан ключ без секрета. data.used_by.bindings перечисляет новые подключения: id оттуда и есть {binding_id} для HTTP-прокси.
Ни один маршрут не возвращает секрет. Чтобы заменить его, отправьте новый объект fields в POST /credentials/{id}/rotate.
Настройте, как агент пользуется подключением
У каждой пары агента и подключения есть правило. class_policy задаёт для read, write и destructive одно из auto, ask или deny, новая пара начинает со всех трёх на 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 заменяет правило целиком. Класс, который вы не указали, становится ask, а неуказанные step_up, path_prefix_override, methods_override и rate_per_hour сбрасываются. Передавайте те, что хотите сохранить.
Необязательные поля: step_up (none или totp, код из аутентификатора при подтверждении необратимого вызова), path_prefix_override, methods_override и rate_per_hour. Последние три могут только сузить подключение.
DELETE /agents/{id}/bindings/{binding_id} забирает подключение у агента. POST /agents/{id}/bindings возвращает подключения: binding_ids или "all": true, по желанию с rule того же вида.
Читайте вызовы и журнал
GET /calls отдаёт вызовы страницами, новые первыми. Фильтры: workspace_id, agent_id, binding_id, state, class, decision, channel, host, q, session, from и to.
У вызова есть call_id, agent, binding, class, decision (run, ask или deny), state, summary, upstream_status, duration_ms, счётчики байтов, error_code и body_ref.
Состояния: admitted, awaiting_approval, dispatching, executing, completed, denied, failed, outcome_unknown и cancelled. GET /calls/{call_id} добавляет intent, ход вызова и номера его записей в журнале.
GET /audit/events отдаёт журнал страницами, новые записи первыми. GET /audit/export выгружает цепочку файлом JSON Lines: строка заголовка, затем по событию в строке. from_seq и to_seq задают диапазон.
Ограничьте агента известными адресами
allowed_ips в POST /agents и PATCH /agents/{id} принимает до 100 IP-адресов или CIDR-сетей. Пустой список разрешает любой адрес, а PATCH без этого поля оставляет текущий список.
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 это адрес, с которого приходят запросы агента. GET /me/ip, выполненный на той же машине, покажет адрес, который видит Sallyport.
Запрос с любого другого адреса получит 403 SOURCE_IP_NOT_ALLOWED на /mcp, /p/ и /v1/bodies. Уже идущие вызовы не прерываются.
Подключённые приложения
GET /oauth/consents возвращает ИИ-клиенты, вошедшие через OAuth, по одной строке на человека, клиента и агента. Участник получает свои строки, владелец, администратор или ключ консоли получают все.
В строке есть id, client_id, client_name, registration (cimd для клиента, которого называет его сайт, dcr для зарегистрировавшегося самостоятельно), verified, domain, publisher, redirect (kind: web, app или loopback, и host), agent (id, name), workspace_id, connected_by (id, name, email), connected_at, renewed_at, last_used_at и mine.
DELETE /oauth/consents/{id} отключает подключение целиком. Его вызовы сразу прекращаются на всех компьютерах, с которых входил клиент, и чтобы вернуться, клиент входит заново.
Экран согласия читает GET /oauth/consent?request=…, только для человека, вошедшего в консоль. Кроме ID клиента и адреса возврата там есть client.verified, client.domain, client.publisher, redirect (kind, host) и workspaces. У каждого рабочего пространства есть access: grants (existing, all или none) и connections, которыми будет пользоваться агент, каждое с id, name, channel и asks_first.
В строке есть и sub_agents: агенты, которых клиент создал для своих машин, каждый с id, name и box, машиной, для которой он создан. В GET /agents у такого агента есть created_by_client с client_id, client_name и box. parent_agent_id у него есть, только если подключение само агент, со scope=mcp agents. У остальных агентов в обоих полях null. Строка с manages_agents: true означает подключение платформы: оно управляет агентами и само не агент.
Токены для машин платформы
Платформа, которая запускает агентов на своих машинах, входит один раз со scope=agents. Это согласие даёт доступ к управлению агентами и не создаёт агента: человек видит короткую карточку без списка сервисов. С access-токеном подключения она создаёт агента для каждой машины в POST https://mcp.sallyport.cloud/v1/agents. Агент начинает без доступа: чем ему пользоваться, человек выбирает на его странице в консоли.
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"}'В ответе agent_id и access_url, страница доступа агента. Добавьте &return_to= со своей страницей, и человек вернётся одной кнопкой. У машины один агент: второй POST отвечает 409 с box_has_agent. GET /v1/agents показывает агентов человека с created_by_client, box и paused. PATCH и DELETE /v1/agents/{id} переименовывают, ставят на паузу или удаляют только агентов, которых создал этот клиент. Ни один вызов этого API не даёт и не забирает доступ. Передайте allowed_ips в POST или PATCH, и токены агента заработают только с этих адресов: токен, скопированный с машины, бесполезен где-либо ещё. Список, который человек задал в консоли, действует поверх.
За токеном машины платформа обменивает access-токен подключения в POST https://mcp.sallyport.cloud/token (RFC 8693) и называет агента в sallyport_agent. Токен живёт час.
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"В ответе access_token, expires_in, sallyport_agent_id, sallyport_agent_name и sallyport_agent_url. Refresh-токена нет: платформа обменивает снова. В отказе есть sallyport_reason: subject_token_expired, consent_ended, agents_scope_missing, agent_not_bound, agent_deleted или agent_paused.
POST https://mcp.sallyport.cloud/revoke (RFC 7009) принимает token и client_id. Токен машины закрывает все токены этой машины. Refresh-токен закрывает всё подключение, как «Отключить».
GET https://mcp.sallyport.cloud/v1/access с Authorization: Bearer отвечает за агента этого токена, агента машины: services, число подключений, которыми он может пользоваться, access_url, его страница доступа, и connections, те же записи, что у list_access. Пока сервисов нет, message говорит об этом одной строкой. Токен, который не подходит, получает 401 с WWW-Authenticate: Bearer и error="invalid_token". Собственный токен подключения получает services: 0 и строку о том, что подключение ничем не пользуется. На /mcp и /p/ он получает 403 с error="insufficient_scope" и scope="mcp".
Готовые MCP-серверы
GET /v1/mcp-servers отдаёт удалённые MCP-серверы, которые консоль предлагает при добавлении подключения. Это серверы самих поставщиков, у каждого адрес и способ входа. Читать список может любой участник. Выбор сервера только заполняет форму, а сохраняется обычное подключение к удалённому MCP-серверу, см. MCP-клиенты и удалённые серверы.
{
"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"
}| Поле | Что значит |
|---|---|
url, url_part | Адрес сервера. Если url_part не null, в адресе стоит {name}. Эта часть у каждого клиента своя: label спрашивает её на шести языках, example показывает пример. |
auth | oauth означает вход у поставщика, bearer принимает токен, выпущенный там, api_key отправляет ключ в заголовке api_key_header после api_key_prefix, none не требует ничего. |
dynamic_registration | При входе через OAuth поставщик сам регистрирует Sallyport. false значит, что вы вводите ID клиента, созданный у поставщика. |
bearer_alternative, key_url | OAuth-сервер принимает и токен, выпущенный у поставщика, и страница, где выпускают этот токен или ключ. |
recipe | Сервис каталога того же поставщика или null. |
description, docs_url | Что делает сервер, по одному предложению на шести языках, и страница поставщика об этом сервере. |
Все маршруты
Пути указаны относительно https://mcp.sallyport.cloud/v1. «Участник» значит любую роль в организации. Ключ консоли считается владельцем.
| Маршруты | Кто | Зачем |
|---|---|---|
GET /tenants/me, PATCH /tenants/me | участник, владелец | Организация: название, настройки, счётчики. |
POST /tenants/me/pause, POST /tenants/me/resume | владелец | Остановить и снова запустить все вызовы агентов. Для паузы нужен reason. |
GET /me/ip | любой | Адрес, который Sallyport видит у этого клиента. |
GET, POST /workspaces; GET, PATCH /workspaces/{id} | участник, администратор | Рабочие пространства. Создаются с slug, name и kind (team или client). |
GET /workspaces/{id}/report?period=YYYY-MM | аудитор | Месяц работы пространства: итоги, сервисы, необратимые вызовы. |
GET, POST /agents; GET, PATCH, DELETE /agents/{id} | участник, администратор | Агенты. Удаление отвечает 409, пока у агента идёт вызов. |
POST /agents/{id}/pause, POST /agents/{id}/resume | администратор | Остановить одного агента и запустить снова. Для паузы нужен reason. |
GET, POST /agents/{id}/tokens; DELETE /agents/{id}/tokens/{token_id} | администратор | Токены агента. |
GET, POST /agents/{id}/bindings; PUT, DELETE /agents/{id}/bindings/{binding_id} | участник, администратор | Подключения агента и его правило на каждом. |
GET, PUT /agents/{id}/limits | участник, администратор | concurrency_limit, rate_per_hour и read_budget.multiplier. |
GET, POST /credentials; GET, PATCH, DELETE /credentials/{id} | администратор | Ключи сервисов. Удаление ключа удаляет его подключения. |
POST /credentials/{id}/rotate, PATCH /credentials/{id}/origin, GET /credentials/{id}/usage, POST /credentials/{id}/oauth/start | администратор | Заменить секрет, отметить, отозван ли исходный ключ у поставщика, узнать, кто пользуется ключом. oauth/start начинает вход у поставщика, для него нужен человек, вошедший в консоль. |
GET, POST /bindings; GET, PATCH, DELETE /bindings/{id} | участник, администратор | Подключения. PATCH сверяет If-Match с config_version, если заголовок передан. |
POST /bindings/{id}/pause, /resume, /check-key, /oauth/start | администратор | Остановить подключение, запустить снова, заново проверить ключ. oauth/start выполняет вход в удалённый MCP-сервер, для него нужен человек, вошедший в консоль. |
GET, POST /ssh-hosts; GET, PATCH, DELETE /ssh-hosts/{id} | администратор | Серверы для SSH-команд. |
POST /database-connections/test | администратор | Проверить адрес и пароль базы данных до сохранения. |
POST /email-connections/test | администратор | Проверить почтовый ящик и его пароль до сохранения. |
GET /policies/rule-changes, POST /policies/rule-changes/{id}/revert | участник, администратор | История изменений правил и отмена одного из них. |
GET /recipes, GET /recipes/{key} | участник | Каталог сервисов, см. Рецепты. |
GET /mcp-servers | участник | Готовые удалённые MCP-серверы для нового подключения. |
GET, POST /grants; POST /grants/{id}/revoke | участник, администратор | Разрешения агенту на подключение с ограниченным сроком. Отозвать может и подтверждающий. |
GET /approvals, GET /approvals/{call_id}, GET /approvals/{call_id}/payload | подтверждающий | Карточки подтверждения и точный запрос за каждой. Список может читать и аудитор. |
POST /approvals/{call_id}/open, /decide, /transfer | подтверждающий | Продлить карточку, ответить на неё, передать другому. Ответить может только человек, вошедший в консоль. |
GET /access-requests, GET /access-requests/{request_id} | участник | Запросы агентов на сервисы, которыми они не смогли воспользоваться. |
POST /access-requests/{request_id}/provision, /decline | администратор | Ответить на запрос ключом или отказать с reason. |
GET /calls, GET /calls/live, GET /calls/{call_id} | аудитор | История вызовов, идущие вызовы, один вызов. |
POST /calls/{call_id}/cancel | администратор | Остановить незавершённый вызов. |
GET /bodies/{body_ref}/meta, GET /bodies/{body_ref} | аудитор; скачивание ещё и сам агент | Размер и срок хранения сохранённого ответа и сам ответ. Скачивание понимает Range. |
GET /audit/events, GET /audit/export | аудитор | Журнал страницами или одним файлом JSON Lines. Нужна роль на всю организацию, роли в одном рабочем пространстве мало. |
GET /signals, POST /signals/{id}/ack | участник, администратор | Необычная активность и отметка «просмотрено». |
GET /reports, GET /reports/{id}, GET /insights | участник | Еженедельные отчёты и статистика подтверждений. |
GET /billing, POST /billing/plan | администратор, владелец | Тариф и расход, запрос другого тарифа. |
GET, POST /webhooks; PATCH, DELETE /webhooks/{id} | администратор | События журнала на ваш HTTPS-адрес. Секрет подписи показывается один раз. |
GET /webhooks/{id}/deliveries, POST /webhooks/{id}/deliveries/{delivery_id}/retry | администратор | История доставок и повторная отправка неудачной. |
GET /members, PATCH /members/{user_id} | участник, администратор | Люди и их роли. |
GET, POST /members/invitations; DELETE /members/invitations/{id} | администратор | Приглашения по почте. |
GET /members/{user_id}/offboarding, POST /members/{user_id}/offboard | администратор | Передать дела уходящего человека. |
GET, POST /guests; POST /guests/{id}/relink; DELETE /guests/{id} | администратор | Подтверждающие со стороны клиента, которые входят по ссылке из письма. |
GET /onboarding, POST /onboarding/steps/{key} | участник | Список первых шагов. |
GET, POST /console-keys; DELETE /console-keys/{id} | владелец | Ключи консоли. Создать ключ может только владелец, вошедший в консоль. |
GET /oauth/consents, DELETE /oauth/consents/{id} | участник, администратор | Подключённые приложения и отключение одного из них. Участник работает со своими, администратор со всеми. |
/notifications/…, /oauth/consent, /guest/step-up | человек | Экраны консоли для человека, вошедшего в неё. |