API REST
Gerencie agentes, chaves, acessos e o registro a partir de um script com uma chave de console.
Nesta página
Endereço e chave
Scripts chamam https://mcp.sallyport.cloud/v1 com uma chave de console. Um proprietário cria uma em Configurações, Chaves de API com Nova chave de API. Ela começa com sp_ck_ e aparece uma única vez.
Envie como Authorization: Bearer $SP_CONSOLE_KEY. A chave age como proprietário da organização inteira. Ela para de funcionar quando é revogada, ou quando o proprietário que a criou deixa de ser proprietário.
Algumas ações precisam de uma pessoa conectada e respondem 403 a uma chave de console. São elas criar chaves de console, responder aprovações, o login OAuth, as configurações de notificação, marcar sinais e baixar respostas guardadas.
Tokens de agente (sp_live_…, sp_test_…) são para /mcp e /p/. Em /v1 eles abrem uma rota só: GET /v1/bodies/{body_ref} para as chamadas do próprio 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/workspacesRespostas e erros
Um sucesso é {"data": …, "request_id": "…"} com 200, ou 201 quando algo foi criado. Corpos de requisição são JSON de até 1 MiB. Um campo que a rota não conhece é ignorado, exceto campos de regra aposentados como class_policy em /bindings, que recebem 422.
Um erro tem o mesmo formato em todas as rotas. message é uma frase curta em inglês; details traz o que um programa precisa.
{
"error": {
"code": "VALIDATION_FAILED",
"message": "Validation failed",
"details": {
"fields": [
{ "field": "workspace_id", "code": "not_a_uuid", "message": "Select a workspace" }
]
},
"request_id": "9e8df29b6763e0ddd8138df2790de68a"
}
}| Status e código | O que significa |
|---|---|
401 AUTH_FAILED | A chave está ausente, errada ou revogada. |
403 FORBIDDEN | Seu papel não basta. details.required_role diz qual basta. |
404 NOT_FOUND, 404 CALL_NOT_FOUND | Nada com esse ID na sua organização, ou o ID não é um UUID. |
409 CONFLICT, 409 CALL_CONFLICT | O objeto está ocupado, mudou ou já está nesse estado. details diz qual. |
410 APPROVAL_EXPIRED | O cartão de aprovação fechou antes da resposta. |
422 VALIDATION_FAILED | Um campo está errado. details.fields lista cada um com field, code e message. |
400 BAD_REQUEST | Um parâmetro de consulta que a rota não aceita. details.accepted lista os que ela aceita. |
503 UNAVAILABLE, 500 INTERNAL | Uma falha do lado do Sallyport. Repita um 503 após details.retry_after_ms. |
Listas
A maioria das listas devolve todas as linhas de uma vez: data é {"items": […], "total": n, "page": 1, "limit": n}.
Chamadas, aprovações e eventos de auditoria vêm em páginas de {"items", "next_cursor", "total", "truncated"}. limit é 50 por padrão e no máximo 200. Devolva next_cursor como cursor até ele ser null.
Um filtro com vários valores os recebe separados por vírgula, como em state=completed,failed. A maioria das rotas de lista recusa um parâmetro que não conhece, então um filtro digitado errado nunca devolve tudo.
Dê a um script o próprio agente
POST /agents recebe name e workspace_id. environment é live (padrão) ou test; description, delegate_user_id e allowed_ips são opcionais. Pegue workspace_id de GET /workspaces.
Um agente novo recebe na hora todas as conexões ativas do seu espaço de trabalho e ambiente, todas rodando sem perguntar. Depois emita um token para ele. O exemplo usa jq para ler o ID do agente novo.
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"O token está em data.token, e esta resposta é o único lugar onde ele aparece. Começa com sp_live_ para um agente live e sp_test_ para um de teste.
label aceita até 100 caracteres, e expires_at é um horário RFC 3339 opcional no futuro. DELETE /agents/{id}/tokens/{token_id} revoga um token.
Adicione uma chave
POST /credentials guarda uma chave e cria as conexões dela no mesmo passo. Todos os agentes do mesmo espaço de trabalho e ambiente as recebem na hora.
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 nomeia o serviço, e GET /recipes lista todos. fields leva o segredo com os nomes de credential_fields da receita; a maioria dos serviços tem um campo só, value. environment é live por padrão.
A resposta descreve a chave sem o segredo. data.used_by.bindings lista as conexões novas: um id dali é o {binding_id} do proxy HTTP.
Nenhuma rota jamais devolve um segredo. Para trocá-lo, envie um objeto fields novo para POST /credentials/{id}/rotate.
Mude como um agente usa uma conexão
Cada par de agente e conexão tem uma regra. class_policy associa read, write e destructive a auto, ask ou deny, e um par novo começa com os três em 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 substitui a regra inteira. Uma classe que você omite vira ask, e um step_up, path_prefix_override, methods_override ou rate_per_hour omitido é apagado. Envie os que você quer manter.
Campos opcionais: step_up (none, ou totp para um código do autenticador ao aprovar uma chamada destrutiva), path_prefix_override, methods_override e rate_per_hour. Os três últimos só podem estreitar a conexão.
DELETE /agents/{id}/bindings/{binding_id} tira uma conexão do agente. POST /agents/{id}/bindings devolve conexões: binding_ids ou "all": true, com um rule opcional no mesmo formato.
Leia as chamadas e o registro
GET /calls percorre as chamadas em páginas, as mais novas primeiro. Filtros: workspace_id, agent_id, binding_id, state, class, decision, channel, host, q, session, from e to.
Uma chamada traz call_id, agent, binding, class, decision (run, ask ou deny), state, summary, upstream_status, duration_ms, contagens de bytes, error_code e body_ref.
Estados: admitted, awaiting_approval, dispatching, executing, completed, denied, failed, outcome_unknown e cancelled. GET /calls/{call_id} acrescenta a intenção, a linha do tempo e os números de sequência da chamada no registro.
GET /audit/events percorre o registro em páginas, o mais novo primeiro. GET /audit/export baixa a cadeia como linhas JSON: um cabeçalho e depois um evento por linha. from_seq e to_seq limitam um intervalo.
Limite um agente a endereços conhecidos
allowed_ips em POST /agents e PATCH /agents/{id} aceita até 100 endereços IP ou faixas CIDR. Uma lista vazia permite qualquer endereço, e um PATCH sem o campo mantém a lista atual.
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 é o endereço de onde saem as requisições do agente. GET /me/ip, rodado nessa máquina, mostra o endereço que o Sallyport vê.
Uma requisição de qualquer outro endereço recebe 403 SOURCE_IP_NOT_ALLOWED em /mcp, /p/ e /v1/bodies. Chamadas já em andamento não são interrompidas.
Apps conectados
GET /oauth/consents lista os clientes de IA que entraram com OAuth, uma linha por pessoa, cliente e agente. Um membro recebe as próprias linhas; um proprietário, um administrador ou uma chave de console recebe as de todos.
Cada linha traz id, client_id, client_name, registration (cimd para um cliente identificado pelo próprio site, dcr para um que se registrou sozinho), verified, domain, publisher, redirect (kind: web, app ou loopback, e host), agent (id, name), workspace_id, connected_by (id, name, email), connected_at, renewed_at, last_used_at e mine.
DELETE /oauth/consents/{id} desconecta a conexão inteira. As chamadas dela param na hora, em todos os computadores de onde o cliente entrou, e o cliente precisa entrar de novo para voltar.
A tela de consentimento lê GET /oauth/consent?request=…, só para uma pessoa conectada. Além do client ID e do endereço de retorno, ela traz client.verified, client.domain, client.publisher, redirect (kind, host) e workspaces. Cada espaço de trabalho tem access: grants (existing, all ou none) e as connections que o agente vai usar, cada uma com id, name, channel e asks_first.
Uma linha também traz sub_agents: os agentes que o cliente criou para as suas máquinas, cada um com id, name e box, a máquina para a qual existe. Em GET /agents, esse agente tem created_by_client com client_id, client_name e box. Ele só tem parent_agent_id sob uma conexão que é ela mesma um agente, feita com scope=mcp agents. Os demais agentes têm null nos dois. Uma linha com manages_agents: true é a conexão de uma plataforma: ela gerencia agentes e não é um agente.
Tokens para as máquinas de uma plataforma
Uma plataforma que roda agentes em máquinas próprias entra uma vez com scope=agents. Esse consentimento é um acesso para gerenciar agentes, não um agente: a pessoa vê um cartão curto sem lista de serviços. Com o access token da conexão, ela cria um agente para cada máquina em POST https://mcp.sallyport.cloud/v1/agents. O agente começa sem acesso: uma pessoa escolhe o que ele pode usar na página dele no 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"}'A resposta traz agent_id e access_url, a página de acesso do agente. Acrescente &return_to= com a sua própria página, e a pessoa volta com um botão. Uma máquina tem um agente: um segundo POST responde 409 com box_has_agent. GET /v1/agents lista os agentes da pessoa com created_by_client, box e paused. PATCH e DELETE /v1/agents/{id} renomeiam, pausam ou excluem só os agentes que este cliente criou. Nenhuma chamada desta API dá ou tira acesso. Envie allowed_ips no POST ou no PATCH, e os tokens do agente funcionam só a partir desses endereços: um token copiado da máquina não vale nada em outro lugar. A lista que a pessoa define no console vale por cima.
Para o token de uma máquina, a plataforma troca o access token da conexão em POST https://mcp.sallyport.cloud/token (RFC 8693) e nomeia o agente em sallyport_agent. O token vale uma 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"A resposta traz access_token, expires_in, sallyport_agent_id, sallyport_agent_name e sallyport_agent_url. Não há refresh token: a plataforma troca de novo. Uma recusa traz 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) recebe token e client_id. O token de uma máquina encerra todos os tokens dessa máquina. O refresh token encerra a conexão inteira, como “Desconectar”.
GET https://mcp.sallyport.cloud/v1/access com Authorization: Bearer responde pelo agente desse token, o de uma máquina: services, o número de conexões que ele pode usar, access_url, a página de acesso dele, e connections, as mesmas entradas de list_access. Enquanto não tiver serviços, message diz isso em uma linha. Um token que não serve recebe 401 com WWW-Authenticate: Bearer e error="invalid_token". O token próprio da conexão recebe services: 0 e uma linha dizendo que ela não usa nenhum serviço. Em /mcp e /p/ ele recebe 403 com error="insufficient_scope" e scope="mcp".
Servidores MCP prontos
GET /v1/mcp-servers lista os servidores MCP remotos que o console oferece ao adicionar um. São os servidores hospedados pelos próprios fornecedores, cada um com seu endereço e sua forma de entrar. Qualquer membro pode ler. Escolher um só preenche o formulário, e o que você salva é uma conexão MCP remota comum, veja Clientes MCP e 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 | O endereço do servidor. Quando url_part não é null, o endereço traz {name}. Essa parte é de cada cliente: label a pede em seis idiomas, example mostra uma. |
auth | oauth entra no fornecedor, bearer aceita um token criado lá, api_key envia uma chave no cabeçalho api_key_header depois de api_key_prefix, none não precisa de nada. |
dynamic_registration | No caminho OAuth o fornecedor registra o Sallyport sozinho. false significa que você informa um ID de cliente criado no fornecedor. |
bearer_alternative, key_url | O servidor OAuth também aceita um token criado no fornecedor, e a página onde você cria esse token ou chave. |
recipe | O serviço do catálogo do mesmo fornecedor, ou null. |
description, docs_url | O que o servidor faz, uma frase em cada um dos seis idiomas, e a página do fornecedor sobre o servidor. |
Todas as rotas
Os caminhos ficam sob https://mcp.sallyport.cloud/v1. Membro significa qualquer papel na organização. Uma chave de console conta como proprietário.
| Rotas | Quem | Para quê |
|---|---|---|
GET /tenants/me, PATCH /tenants/me | membro, proprietário | A organização: nome, configurações, contadores. |
POST /tenants/me/pause, POST /tenants/me/resume | proprietário | Parar e retomar todas as chamadas de agentes. A pausa leva reason. |
GET /me/ip | qualquer um | O endereço que o Sallyport vê para quem chama. |
GET, POST /workspaces; GET, PATCH /workspaces/{id} | membro, administrador | Espaços de trabalho. Crie um com slug, name e kind (team ou client). |
GET /workspaces/{id}/report?period=YYYY-MM | auditor | Um mês de um espaço de trabalho: totais, serviços, chamadas destrutivas. |
GET, POST /agents; GET, PATCH, DELETE /agents/{id} | membro, administrador | Agentes. Excluir responde 409 enquanto uma chamada do agente está em andamento. |
POST /agents/{id}/pause, POST /agents/{id}/resume | administrador | Parar um agente e retomá-lo. A pausa leva 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} | membro, administrador | As conexões que um agente usa e a regra dele em cada uma. |
GET, PUT /agents/{id}/limits | membro, administrador | concurrency_limit, rate_per_hour e read_budget.multiplier. |
GET, POST /credentials; GET, PATCH, DELETE /credentials/{id} | administrador | Chaves. Excluir uma chave exclui as conexões dela. |
POST /credentials/{id}/rotate, PATCH /credentials/{id}/origin, GET /credentials/{id}/usage, POST /credentials/{id}/oauth/start | administrador | Trocar o segredo, registrar se o original está revogado no fornecedor, ver quem usa a chave. oauth/start começa o login no fornecedor e exige uma pessoa conectada ao console. |
GET, POST /bindings; GET, PATCH, DELETE /bindings/{id} | membro, administrador | Conexões. PATCH compara If-Match com config_version quando você o envia. |
POST /bindings/{id}/pause, /resume, /check-key, /oauth/start | administrador | Parar uma conexão, retomá-la, verificar a chave de novo. oauth/start faz login em um servidor MCP remoto e exige uma pessoa conectada ao console. |
GET, POST /ssh-hosts; GET, PATCH, DELETE /ssh-hosts/{id} | administrador | Servidores para comandos SSH. |
POST /database-connections/test | administrador | Testar o endereço e a senha de um banco de dados antes de salvar. |
POST /email-connections/test | administrador | Testar uma caixa de correio e a senha antes de salvar. |
GET /policies/rule-changes, POST /policies/rule-changes/{id}/revert | membro, administrador | O histórico de mudanças de regras, e desfazer uma. |
GET /recipes, GET /recipes/{key} | membro | O catálogo de serviços, veja Receitas. |
GET /mcp-servers | membro | Servidores MCP remotos prontos para uma nova conexão. |
GET, POST /grants; POST /grants/{id}/revoke | membro, administrador | Permissões com prazo de um agente sobre uma conexão. Um aprovador também pode revogá-las. |
GET /approvals, GET /approvals/{call_id}, GET /approvals/{call_id}/payload | aprovador | Cartões de aprovação e a requisição exata por trás de cada um. Um auditor pode ler a lista. |
POST /approvals/{call_id}/open, /decide, /transfer | aprovador | Estender um cartão, respondê-lo, passá-lo a outro aprovador. Responder exige uma pessoa conectada. |
GET /access-requests, GET /access-requests/{request_id} | membro | Pedidos que os agentes fizeram por um serviço que não conseguiram usar. |
POST /access-requests/{request_id}/provision, /decline | administrador | Responder a um pedido com uma chave, ou recusá-lo com reason. |
GET /calls, GET /calls/live, GET /calls/{call_id} | auditor | Histórico de chamadas, chamadas em andamento, uma chamada. |
POST /calls/{call_id}/cancel | administrador | Parar uma chamada que não terminou. |
GET /bodies/{body_ref}/meta, GET /bodies/{body_ref} | auditor; o download também o agente | Tamanho e validade de uma resposta guardada, e a própria resposta. O download aceita Range. |
GET /audit/events, GET /audit/export | auditor | O registro, em páginas ou como um arquivo de linhas JSON. Só com um papel na organização inteira, não em um espaço de trabalho. |
GET /signals, POST /signals/{id}/ack | membro, administrador | Atividade incomum, e marcá-la como vista. |
GET /reports, GET /reports/{id}, GET /insights | membro | Relatórios semanais e estatísticas de aprovações. |
GET /billing, POST /billing/plan | administrador, proprietário | Plano e uso, e pedir outro plano. |
GET, POST /webhooks; PATCH, DELETE /webhooks/{id} | administrador | Eventos do registro enviados ao seu endereço HTTPS. O segredo de assinatura aparece uma vez. |
GET /webhooks/{id}/deliveries, POST /webhooks/{id}/deliveries/{delivery_id}/retry | administrador | Histórico de entregas, e reenviar uma que falhou. |
GET /members, PATCH /members/{user_id} | membro, administrador | Pessoas e seus papéis. |
GET, POST /members/invitations; DELETE /members/invitations/{id} | administrador | Convites por e-mail. |
GET /members/{user_id}/offboarding, POST /members/{user_id}/offboard | administrador | Passar adiante o que uma pessoa que sai tinha. |
GET, POST /guests; POST /guests/{id}/relink; DELETE /guests/{id} | administrador | Aprovadores do cliente que entram por um link de e-mail. |
GET /onboarding, POST /onboarding/steps/{key} | membro | A lista de primeiros passos. |
GET, POST /console-keys; DELETE /console-keys/{id} | proprietário | Chaves de console. Criar uma exige um proprietário conectado. |
GET /oauth/consents, DELETE /oauth/consents/{id} | membro, administrador | Apps conectados, e desconectar um. Um membro cuida dos seus, um administrador dos de todos. |
/notifications/…, /oauth/consent, /guest/step-up | pessoa | Telas do console para uma pessoa conectada. |