Secrets Python Examples

ExploreSelf-hostDocumentationBlogSign In
Documentation
Overview
Getting Started
Getting StartedPlatform IntegrationsAgentPlaybooks in ClaudePortable Bot TeamsHermes Memory ProviderHermes for OrganisationsAgentPlaybooks Tools in HermesCLI & Editor PluginsPython SecretsCross-platform Agent BackupsRelease DistributionTeam CollaborationFederated MCP & OpenAPI ToolsMCP Registry PublishingRoadmap
Core Concepts
PlaybooksSkillsMemoryMCP Integration
Reference
ArchitectureAPI ReferenceManagement APIDeveloper GuideSelf-HostingEnvironment Setup
View on GitHub
DocsSecrets Python Examples

title: Python Examples - Secrets API

Secrets API: Python Examples

For existing applications using .env or os.environ, start with the Python Secrets library: a startup loader with API-key and mTLS authentication, Docker examples, and compatibility with existing CI/CD values.

Ready-to-use Python code for integrating with the AgentPlaybooks Secrets API. These examples cover the Proxy (zero-exposure) pattern, the optional Reveal pattern, and full CRUD operations.

Prerequisites

pip install requests

All examples use the requests library. Replace the placeholders:

PlaceholderValue

YOUR_PLAYBOOK_GUIDYour playbook's GUID (from the dashboard URL)
YOUR_API_KEYYour playbook API key (apb_live_...)
BASE_URLhttps://apbks.com or your self-hosted domain

import requests

BASE_URL = "https://apbks.com"
PLAYBOOK_GUID = "YOUR_PLAYBOOK_GUID"
API_KEY = "YOUR_API_KEY"

HEADERS = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}


🛡️ Proxy Pattern (Recommended — Zero Exposure)

The proxy pattern lets your agent use secrets without ever seeing the raw value. The server decrypts the secret, injects it into your HTTP request as a header, and returns only the response.

Via REST API (/proxy)

def use_secret_proxy(
    secret_name: str,
    target_url: str,
    method: str = "GET",
    header_name: str = "Authorization",
    header_prefix: str = "Bearer ",
    body: dict | None = None,
    extra_headers: dict | None = None,
    timeout_ms: int = 30000,
) -> dict:
    """
    Make an HTTP request with a secret injected server-side.
    The secret value never leaves the server.
    """
    payload = {
        "secret_name": secret_name,
        "url": target_url,
        "method": method,
        "header_name": header_name,
        "header_prefix": header_prefix,
        "timeout_ms": timeout_ms,
    }
    if body:
        payload["body"] = body
    if extra_headers:
        payload["extra_headers"] = extra_headers

    resp = requests.post(
        f"{BASE_URL}/api/playbooks/{PLAYBOOK_GUID}/secrets/proxy",
        headers=HEADERS,
        json=payload,
    )
    resp.raise_for_status()
    return resp.json()


# Example: Call OpenAI API using a stored secret
result = use_secret_proxy(
    secret_name="OPENAI_API_KEY",
    target_url="https://api.openai.com/v1/models",
    method="GET",
)
print(f"Status: {result['status']}")
print(f"Models: {result['body']}")
# The agent NEVER sees the actual API key!

Stream model output via HTTP (no MCP required)

Use response_mode: "stream" to forward incremental output. Enable streaming in the provider payload too. Unlike the default proxy mode, the response has the provider's HTTP status and raw body, without a JSON wrapper.

import os
import sys

with requests.post(
    f"{BASE_URL}/api/playbooks/{PLAYBOOK_GUID}/secrets/proxy",
    headers=HEADERS,
    json={
        "secret_name": "MODEL_API_KEY",
        "url": os.environ["MODEL_ENDPOINT"],
        "method": "POST",
        "response_mode": "stream",
        "timeout_ms": 300000,
        "body": {
            "model": os.environ["MODEL_ID"],
            "messages": [{"role": "user", "content": "Hello!"}],
            "stream": True,
        },
    },
    stream=True,  # Tell requests not to buffer the response locally either.
    timeout=(10, 310),
) as response:
    response.raise_for_status()
    for chunk in response.iter_content(chunk_size=None, decode_unicode=False):
        # Consume provider HTTP chunks without imposing a fixed local chunk size.
        # Feed bytes into an incremental provider parser in production.
        sys.stdout.buffer.write(chunk)
        sys.stdout.buffer.flush()

Choose the endpoint, model and payload for your provider (for example xAI or MiniMax). Restrict the vault secret's allowed_hosts to that provider. Closing the response cancels the upstream stream. For browser usage and the full response contract, see Streaming without MCP.

Via MCP (use_secret tool)

If your agent connects via MCP, use the use_secret tool:

import json

def call_mcp_tool(tool_name: str, arguments: dict) -> dict:
    """Call an MCP tool on the playbook's MCP server."""
    payload = {
        "jsonrpc": "2.0",
        "id": 1,
        "method": "tools/call",
        "params": {
            "name": tool_name,
            "arguments": arguments,
        },
    }
    resp = requests.post(
        f"{BASE_URL}/api/mcp/{PLAYBOOK_GUID}",
        headers=HEADERS,
        json=payload,
    )
    resp.raise_for_status()
    return resp.json()


# Example: Use stored secret to call an external API
result = call_mcp_tool("use_secret", {
    "secret_name": "OPENAI_API_KEY",
    "url": "https://api.openai.com/v1/models",
    "method": "GET",
    "header_name": "Authorization",
    "header_prefix": "Bearer ",
})
# Parse the MCP response
content = json.loads(result["result"]["content"][0]["text"])
print(f"Status: {content['status']}")
print(f"Note: {content['note']}")
# Output: "Request made with secret 'OPENAI_API_KEY' injected as 
#          Authorization header. Secret value was NOT exposed to the agent."


🔓 Reveal Pattern (Optional — Raw Access)

If you explicitly need the raw secret value (e.g., for a local SDK that requires the key directly), you can enable Reveal for specific secrets.

⚠️ Security Note: Reveal must be explicitly enabled per-secret via the allow_api_key_reveal flag. By default, all secrets are Proxy Only.

Enable Reveal on a Secret

When creating or updating a secret, set allow_api_key_reveal to true:

# Create a secret with reveal enabled
requests.post(
    f"{BASE_URL}/api/playbooks/{PLAYBOOK_GUID}/secrets",
    headers=HEADERS,
    json={
        "name": "GITHUB_TOKEN",
        "value": "ghp_xxxxxxxxxxxx",
        "description": "GitHub personal access token",
        "category": "token",
        "allow_api_key_reveal": True,  # ← Enable reveal for API keys
    },
).raise_for_status()

Read the Raw Secret Value

def reveal_secret(secret_name: str) -> str:
    """
    Get the decrypted raw value of a secret.
    Only works if allow_api_key_reveal is True for this secret.
    Returns 403 "Proxy Only" if reveal is not enabled.
    """
    resp = requests.get(
        f"{BASE_URL}/api/playbooks/{PLAYBOOK_GUID}/secrets/reveal/{secret_name}",
        headers=HEADERS,
    )
    if resp.status_code == 403:
        data = resp.json()
        raise PermissionError(
            f"Reveal blocked: {data.get('error', 'Proxy Only mode')}"
        )
    resp.raise_for_status()
    return resp.json()["value"]


# Example
try:
    token = reveal_secret("GITHUB_TOKEN")
    print(f"Token (first 10 chars): {token[:10]}...")
except PermissionError as e:
    print(f"Cannot reveal: {e}")
    print("Use the proxy pattern instead, or enable allow_api_key_reveal.")


📦 Store, Rotate & Delete Secrets

Store a New Secret

def store_secret(
    name: str,
    value: str,
    description: str = "",
    category: str = "general",
    allow_reveal: bool = False,
    expires_at: str | None = None,
) -> dict:
    """Store a new encrypted secret."""
    payload = {
        "name": name,
        "value": value,
        "description": description,
        "category": category,
        "allow_api_key_reveal": allow_reveal,
    }
    if expires_at:
        payload["expires_at"] = expires_at

    resp = requests.post(
        f"{BASE_URL}/api/playbooks/{PLAYBOOK_GUID}/secrets",
        headers=HEADERS,
        json=payload,
    )
    resp.raise_for_status()
    return resp.json()


# Store an API key (Proxy Only — default & recommended)
store_secret(
    name="STRIPE_SECRET_KEY",
    value="sk_live_xxxxxxxxxxxxx",
    description="Stripe production key",
    category="api_key",
)

# Store a token with reveal enabled and expiration
store_secret(
    name="DEPLOY_TOKEN",
    value="deploy-xxxx",
    description="CI/CD deployment token",
    category="token",
    allow_reveal=True,
    expires_at="2027-01-01T00:00:00Z",
)

Rotate (Update) a Secret

def rotate_secret(name: str, new_value: str, description: str | None = None) -> dict:
    """Replace the value of an existing secret."""
    payload = {"value": new_value}
    if description is not None:
        payload["description"] = description

    resp = requests.put(
        f"{BASE_URL}/api/playbooks/{PLAYBOOK_GUID}/secrets/{name}",
        headers=HEADERS,
        json=payload,
    )
    resp.raise_for_status()
    return resp.json()


rotate_secret("STRIPE_SECRET_KEY", "sk_live_new_key_value")

List Secrets (Metadata Only)

def list_secrets(category: str | None = None) -> list[dict]:
    """List all secrets (metadata only — never returns values)."""
    params = {}
    if category:
        params["category"] = category

    resp = requests.get(
        f"{BASE_URL}/api/playbooks/{PLAYBOOK_GUID}/secrets",
        headers=HEADERS,
        params=params,
    )
    resp.raise_for_status()
    return resp.json()


secrets = list_secrets(category="api_key")
for s in secrets:
    mode = "🔓 Reveal Enabled" if s["allow_api_key_reveal"] else "🛡️ Proxy Only"
    print(f"  {s['name']} [{s['category']}] — {mode}")
    if s["expires_at"]:
        print(f"    Expires: {s['expires_at']}")

Delete a Secret

def delete_secret(name: str) -> bool:
    """Permanently delete a secret."""
    resp = requests.delete(
        f"{BASE_URL}/api/playbooks/{PLAYBOOK_GUID}/secrets/{name}",
        headers=HEADERS,
    )
    resp.raise_for_status()
    return True


delete_secret("DEPLOY_TOKEN")


Complete Example: Agent Workflow

A typical agent workflow combining proxy calls with memory:

import requests
import json

BASE_URL = "https://apbks.com"
GUID = "your-playbook-guid"
KEY = "apb_live_your_key"
HEADERS = {"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"}


def agent_workflow():
    """Example: Agent uses stored OpenAI key to summarize text, saves result to memory."""

    # 1. Call OpenAI via proxy (agent never sees the API key)
    result = requests.post(
        f"{BASE_URL}/api/playbooks/{GUID}/secrets/proxy",
        headers=HEADERS,
        json={
            "secret_name": "OPENAI_API_KEY",
            "url": "https://api.openai.com/v1/chat/completions",
            "method": "POST",
            "body": {
                "model": "gpt-4o",
                "messages": [{"role": "user", "content": "Summarize: AI agents need secure credential management."}],
                "max_tokens": 100,
            },
        },
    ).json()

    summary = result["body"]["choices"][0]["message"]["content"]
    print(f"Summary: {summary}")

    # 2. Save the result to playbook memory
    requests.put(
        f"{BASE_URL}/api/playbooks/{GUID}/memory/last_summary",
        headers=HEADERS,
        json={
            "value": {"summary": summary, "source": "openai"},
            "tags": ["summary", "ai"],
            "description": "Latest AI-generated summary",
        },
    ).raise_for_status()

    print("✅ Summary saved to memory!")


if __name__ == "__main__":
    agent_workflow()


Security Best Practices

PracticeDescription

Default to Proxy OnlyNever enable allow_api_key_reveal unless absolutely necessary
Use scoped API keysCreate API keys with only secrets:read permission for agents that only need proxy access
Set expiration datesUse expires_at for time-limited credentials
Rotate regularlyUse rotate_secret() to update values without downtime
Monitor usageCheck use_count and last_used_at in secret metadata

Next Steps

  • API Reference — Full endpoint documentation
  • MCP Integration — Connect via Model Context Protocol
  • Self-Hosting — Deploy on your own infrastructure
Edit this page on GitHub
On this page
Prerequisites🛡️ Proxy Pattern (Recommended — Zero Exposure)Via REST API (/proxy)Stream model output via HTTP (no MCP required)Via MCP (use_secret tool)🔓 Reveal Pattern (Optional — Raw Access)Enable Reveal on a SecretRead the Raw Secret Value📦 Store, Rotate & Delete SecretsStore a New SecretRotate (Update) a SecretList Secrets (Metadata Only)Delete a SecretComplete Example: Agent WorkflowSecurity Best PracticesNext Steps