Coming Soon — the Agent Vault is not yet generally available. This documentation describes the feature ahead of its release; details may change before launch.
Every agent gets the same capability over a plain authenticated HTTP API, for runtimes that don't (yet) speak MCP: CI jobs, custom automations, or any language with a solid HTTP client.
Base URL : https://secretstash.cloud/api/v1/agents/{agent}/secretsAuth : Authorization: Bearer <YOUR_AGENT_API_KEY> headerContent type : application/json{agent} is the agent's UUID, shown on its page in the web application (also visible in the REST base URL under Connection on that page).
GET /api/v1/agents/{agent}/secrets
Returns metadata only — variable names and environment slugs the agent may resolve, never values:
{
"data" : [
{
"environment" : { "id" : "..." , "name" : "Production" , "slug" : "production" },
"dek_provisioned" : true ,
"variables" : [{ "name" : "GITHUB_PROD_TOKEN" }, { "name" : "STRIPE_SECRET_KEY" }]
}
]
}
GET /api/v1/agents/{agent}/secrets/{name}?environment=<slug>
environment is optional — omit it to search across all of the agent's authorized environments (the first match wins). Returns:
{ "data" : { "GITHUB_PROD_TOKEN" : "ghp_..." } }
A name outside the agent's scope returns 404.
POST /api/v1/agents/{agent}/secrets/resolve
{
"variables" : [ "GITHUB_PROD_TOKEN" , "STRIPE_SECRET_KEY" ],
"environment" : "production"
}
Returns all requested values in one response:
{ "data" : { "GITHUB_PROD_TOKEN" : "ghp_..." , "STRIPE_SECRET_KEY" : "sk_..." } }
If any requested name is out of the agent's scope, the whole batch is rejected with 403 and an out_of_scope list — no partial results. Split the request if you need a partial resolve.
/api/v1/... is the canonical, supported path. An older unversioned fallback (/api/agents/..., no v1 segment) is served for backward compatibility, but every response through it carries deprecation headers: Deprecation: true, Sunset, Link: <.../api/v1/...>; rel="successor-version", and X-Api-Version: v1. New integrations should always use the versioned path.
Every example below resolves GITHUB_PROD_TOKEN from the production environment using the single-secret endpoint.
curl Python (requests) Python (httpx) Node (axios) Node (fetch) Go PHP (Guzzle) PHP (Laravel HTTP)
curl -H "Authorization: Bearer $AGENT_API_KEY " \
"https://secretstash.cloud/api/v1/agents/ $AGENT_ID /secrets/GITHUB_PROD_TOKEN?environment=production"
import os
import requests
agent_id = os.environ["AGENT_ID"]
token = os.environ["AGENT_API_KEY"]
response = requests.get(
f"https://secretstash.cloud/api/v1/agents/{agent_id}/secrets/GITHUB_PROD_TOKEN",
params={"environment": "production"},
headers={"Authorization": f"Bearer {token}"},
timeout=10,
)
response.raise_for_status()
github_token = response.json()["data"]["GITHUB_PROD_TOKEN"]
import os
import httpx
agent_id = os.environ["AGENT_ID"]
token = os.environ["AGENT_API_KEY"]
with httpx.Client(timeout=10) as client:
response = client.get(
f"https://secretstash.cloud/api/v1/agents/{agent_id}/secrets/GITHUB_PROD_TOKEN",
params={"environment": "production"},
headers={"Authorization": f"Bearer {token}"},
)
response.raise_for_status()
github_token = response.json()["data"]["GITHUB_PROD_TOKEN"]
import axios from "axios" ;
const agentId = process.env. AGENT_ID ;
const token = process.env. AGENT_API_KEY ;
const { data } = await axios. get (
`https://secretstash.cloud/api/v1/agents/${ agentId }/secrets/GITHUB_PROD_TOKEN` ,
{
params: { environment: "production" },
headers: { Authorization: `Bearer ${ token }` },
},
);
const githubToken = data.data. GITHUB_PROD_TOKEN ;
const agentId = process.env. AGENT_ID ;
const token = process.env. AGENT_API_KEY ;
const url = new URL ( `https://secretstash.cloud/api/v1/agents/${ agentId }/secrets/GITHUB_PROD_TOKEN` );
url.searchParams. set ( "environment" , "production" );
const response = await fetch (url, {
headers: { Authorization: `Bearer ${ token }` },
});
if ( ! response.ok) {
throw new Error ( `Failed to resolve secret: ${ response . status }` );
}
const { data } = await response. json ();
const githubToken = data. GITHUB_PROD_TOKEN ;
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
type secretResponse struct {
Data map[string]string `json:"data"`
}
func main() {
agentID := os.Getenv("AGENT_ID")
token := os.Getenv("AGENT_API_KEY")
url := fmt.Sprintf(
"https://secretstash.cloud/api/v1/agents/%s/secrets/GITHUB_PROD_TOKEN?environment=production",
agentID,
)
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
errBody, _ := io.ReadAll(io.LimitReader(resp.Body, 1024))
panic(fmt.Sprintf("failed to resolve secret: %d %s", resp.StatusCode, errBody))
}
var body secretResponse
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
panic(err)
}
githubToken := body.Data["GITHUB_PROD_TOKEN"]
fmt.Println(len(githubToken) > 0)
}
<?php
use GuzzleHttp\Client;
$agentId = getenv('AGENT_ID');
$token = getenv('AGENT_API_KEY');
$client = new Client(['base_uri' => 'https://secretstash.cloud']);
$response = $client->get("/api/v1/agents/{$agentId}/secrets/GITHUB_PROD_TOKEN", [
'query' => ['environment' => 'production'],
'headers' => ['Authorization' => "Bearer {$token}"],
]);
$body = json_decode((string) $response->getBody(), true);
$githubToken = $body['data']['GITHUB_PROD_TOKEN'];
<?php
use Illuminate\Support\Facades\Http;
$agentId = config('services.secretstash.agent_id');
$token = config('services.secretstash.agent_api_key');
$response = Http::withToken($token)
->acceptJson()
->timeout(10)
->get("https://secretstash.cloud/api/v1/agents/{$agentId}/secrets/GITHUB_PROD_TOKEN", [
'environment' => 'production',
])
->throw();
$githubToken = $response->json('data.GITHUB_PROD_TOKEN');
This example reads its credentials from config/services.php, which a default Laravel install doesn't define. Add:
'secretstash' => [
'agent_id' => env('SECRETSTASH_AGENT_ID'),
'agent_api_key' => env('SECRETSTASH_AGENT_API_KEY'),
],
and set SECRETSTASH_AGENT_ID and SECRETSTASH_AGENT_API_KEY in your .env.
None of the examples above log or print the resolved value. Keep it that way — if your agent needs to confirm a secret resolved, log the variable name , not its value.