Python SDK
The official VeraID Python SDK provides a client for managing credentials, monitoring AI agent usage, enforcing budgets, and detecting prompt injection attacks. It includes async support, built-in retry logic, and integration wrappers for OpenAI and LangChain.
Installation
pip install veraidOptional Extras
Install with framework-specific integrations:
pip install veraid[openai] # OpenAI integrationpip install veraid[langchain] # LangChain integrationpip install veraid[all] # All integrationsQuick Start
from veraid import VeraIDClient
client = VeraIDClient( api_key="kd_live_your_api_key", agent_id="id_agent123",)
# Fetch a credentialdb_url = client.get_credential("production-db-url")print(f"Connected to: {db_url.value}")Initialization
Create a client instance with your API key and optional configuration:
from veraid import VeraIDClient
client = VeraIDClient( api_key="kd_live_your_api_key", agent_id="id_agent123", # Required for AI agent features base_url="https://app.veraid.io", # Optional, defaults to production debug=False, # Optional, enables verbose logging retry_attempts=3, # Optional, default: 3 timeout=30, # Optional, request timeout in seconds)Async Client
For async applications, use the AsyncVeraIDClient:
from veraid import AsyncVeraIDClient
client = AsyncVeraIDClient( api_key="kd_live_your_api_key", agent_id="id_agent123",)
# All methods are asynccredential = await client.get_credential("production-db-url")Authentication: the agent’s own key
The SDK authenticates as one AI agent, with a credential issued for that agent’s identity (kd_live_...). Issue a long-lived one with POST /api/v1/credentials ({"identityId": "<the agent's id>", "name": "sdk", "type": "API_KEY"}) while signed in; only an organization admin or the agent’s owner can, and the secret is shown once. Generate Credential on the agent’s page under Identities issues a temporary one (kd_jit_...), which works the same way until it expires.
The key acts for its own agent and nothing else:
- It reads that agent’s budget (
get_budget_status,check_budget) and usage (get_stats), and checks text for prompt injection (check_injection). - It records that agent’s usage (
record_usage), and so its spend, only if an organization admin issued it. Any member can create an agent and issue it a key, and recorded spend also counts toward the organization-wide limits, so a key issued by anyone else gets403there. A key rotated automatically (with no one starting it) has no issuer: an admin re-issues it. - A call naming another agent is refused with
403, even one in the same organization. - Revoking the credential, suspending the agent, or the issuer losing admin rights takes effect within a minute.
- If the agent has an IP allowlist (addresses or CIDR ranges), calls must come from an allowed address.
- Scopes on the credential are not checked for these calls.
- A non-admin owner of the agent can rotate its keys but cannot sync (deliver) a key an admin or another member issued: only an organization admin can.
It lists this agent’s vault secrets (list_credentials), and reads their values (get_credential) only if an organization admin issued it.
Credential Management
Secrets an organization admin stores in this agent’s vault (an OpenAI key, a database password), which the agent reads at run time. See Storing secrets for an agent. Reading a value needs the agent’s key to have been issued by an organization admin; listing does not.
get_credential
Read a secret’s value by name (POST /api/v1/credentials/retrieve). Every read is audited.
openai_key = await client.get_credential("openai-api-key") # the value, a strParameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
name | str | Yes | The secret’s name in this agent’s vault |
Returns: str. Refused with 403 for a key not issued by an admin, 404 for an unknown name, 429 past 300 reads a minute per agent.
list_credentials
List the secrets in this agent’s vault: names and metadata, never values (GET /api/v1/agents/{agentId}/credentials).
for secret in await client.list_credentials(): print(f"{secret.name}: expires {secret.expires_at or 'never'}")Returns: list[Credential]
class Credential(BaseModel): id: str name: str value: str | None # only from get_credential's route, never here type: str | None status: str | None expires_at: datetime | None scopes: list[str] created_at: datetime | NoneBudget Management
get_budget_status
Retrieve the current budget status for the AI agent.
budget = client.get_budget_status()
print(f"Budget: ${budget.used:.2f} / ${budget.limit:.2f} ({budget.percent_used:.1f}%)")print(f"Status: {budget.status}") # 'OK' | 'WARNING' | 'CRITICAL' | 'EXCEEDED'print(f"Projected: ${budget.projected_spend:.2f}")Returns: BudgetStatus
@dataclassclass BudgetStatus: limit: float used: float percent_used: float status: str # 'OK' | 'WARNING' | 'CRITICAL' | 'EXCEEDED' period: str # 'daily' | 'weekly' | 'monthly' projected_spend: float reset_at: strcheck_budget
Check if a specific cost can be accommodated within the remaining budget.
result = client.check_budget(2.50)
if result.allowed: # Proceed with the AI call response = openai_client.chat.completions.create(...)else: print(f"Budget exceeded: {result.reason}")Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
cost | float | Yes | Estimated cost in USD |
Returns: BudgetCheck
@dataclassclass BudgetCheck: allowed: bool remaining_budget: float reason: str | NoneUsage Tracking
record_usage
Record token and cost usage for an AI agent request.
client.record_usage( prompt_tokens=1250, completion_tokens=847, total_tokens=2097, cost=0.063, model="gpt-4", latency=2340, metadata={ "task_type": "ticket-triage", "ticket_id": "TICKET-4521", },)Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt_tokens | int | Yes | Number of prompt tokens |
completion_tokens | int | Yes | Number of completion tokens |
total_tokens | int | Yes | Total tokens consumed |
cost | float | Yes | Estimated cost in USD |
model | str | Yes | Model identifier |
latency | int | No | Response time in milliseconds |
tool_calls | list[str] | No | Tools invoked during the request |
metadata | dict | No | Additional context |
Returns: None
Prompt Security
check_injection
Analyze a prompt for potential injection attacks (POST /api/v1/ai-monitoring/check-injection).
result = await client.check_injection(user_prompt)
if result.is_injection: print(f"Injection detected: {result.summary}") print(f"Score: {result.score}, confidence: {result.confidence}") print(f"Patterns: {', '.join(result.patterns)}")Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt | str | Yes | The prompt to analyze |
Returns: InjectionCheckResult
class InjectionDetail(BaseModel): category: str pattern: str risk: Literal["low", "medium", "high", "critical"] description: str matched_text: str | None
class InjectionCheckResult(BaseModel): is_injection: bool score: float # 0 to 1 confidence: Literal["low", "medium", "high"] method: Literal["pattern", "ml", "combined"] | None patterns: list[str] # detected pattern categories summary: str details: list[InjectionDetail] | None high_risk_patterns: list[InjectionDetail] | None recommendations: list[str] | None # when an injection was detectedguard_prompt
Check for injection and raise an exception if one is detected.
from veraid.exceptions import InjectionDetectedError
try: safe_prompt = client.guard_prompt(user_prompt) # safe_prompt is the original prompt, returned only if safe response = openai_client.chat.completions.create( model="gpt-4", messages=[{"role": "user", "content": safe_prompt}], )except InjectionDetectedError as e: print(f"Blocked: {e.category} ({e.confidence})") # Return a safe response to the userParameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt | str | Yes | The prompt to guard |
Returns: str (the original prompt if safe)
Raises: InjectionDetectedError if injection is detected
Analytics
get_stats
Retrieve this agent’s usage over a period: the current day, week or month (UTC).
stats = await client.get_stats("week") # "day" (default), "week" or "month"
print(f"Total requests: {stats.total_requests}")print(f"Total cost: ${stats.total_cost:.2f}")print(f"Average latency: {stats.avg_latency_ms}ms")print(f"Injections detected: {stats.injection_detections}")Returns: AgentStats, from GET /api/v1/ai-monitoring/{agent_id}?period=daily|weekly|monthly
class UsageBreakdown(BaseModel): requests: int tokens: int cost: float
class AgentStats(BaseModel): period: Literal["daily", "weekly", "monthly"] period_start: datetime period_end: datetime total_requests: int successful_requests: int failed_requests: int total_input_tokens: int total_output_tokens: int total_tokens: int total_cost: float # USD avg_latency_ms: float max_latency_ms: float by_model: dict[str, UsageBreakdown] by_action: dict[str, UsageBreakdown] injection_detections: intOpenAI Integration
The SDK provides a wrapper for the OpenAI client that automatically monitors all API calls.
from openai import OpenAIfrom veraid import VeraIDClientfrom veraid.integrations.openai import wrap_openai
veraid_client = VeraIDClient( api_key="kd_live_your_api_key", agent_id="id_agent123",)
openai_client = wrap_openai(OpenAI(), veraid_client)
# All calls are now automatically monitoredresponse = openai_client.chat.completions.create( model="gpt-4", messages=[{"role": "user", "content": "Summarize this document..."}],)
# Usage is automatically recorded:# - Token counts (prompt + completion)# - Cost estimate based on model pricing# - Latency# - Budget check before each call# - Prompt injection detection on user messagesAsync OpenAI
from openai import AsyncOpenAIfrom veraid import AsyncVeraIDClientfrom veraid.integrations.openai import wrap_openai
veraid_client = AsyncVeraIDClient( api_key="kd_live_your_api_key", agent_id="id_agent123",)
openai_client = wrap_openai(AsyncOpenAI(), veraid_client)
response = await openai_client.chat.completions.create( model="gpt-4", messages=[{"role": "user", "content": "Summarize this document..."}],)Configuration
Customize the wrapper behavior:
openai_client = wrap_openai(OpenAI(), veraid_client, check_budget=True, record_usage=True, detect_injection=False, # Disable for lower latency)LangChain Integration
Use the VeraID callback handler to automatically monitor LangChain chains and agents.
from langchain_openai import ChatOpenAIfrom langchain.agents import AgentExecutor, create_openai_functions_agentfrom veraid import VeraIDClientfrom veraid.integrations.langchain import VeraIDCallback
veraid_client = VeraIDClient( api_key="kd_live_your_api_key", agent_id="id_agent123",)
callback = VeraIDCallback(veraid_client)
llm = ChatOpenAI(model="gpt-4", callbacks=[callback])
# All LLM calls through this chain are monitoredresponse = llm.invoke("What is the status of ticket TICKET-4521?")The callback automatically tracks:
| Feature | Description |
|---|---|
| Token usage | Records prompt and completion tokens for each LLM call |
| Cost tracking | Estimates cost based on model and token counts |
| Latency | Measures response time for each call |
| Tool calls | Logs tool invocations in agent chains |
| Chain tracking | Associates multiple LLM calls within a single chain execution |
| Error capture | Reports LLM errors and timeouts to VeraID |
Agent Monitoring
from langchain_openai import ChatOpenAIfrom langchain.agents import AgentExecutor, create_openai_functions_agentfrom langchain.tools import Toolfrom veraid.integrations.langchain import VeraIDCallback
callback = VeraIDCallback(veraid_client)
# Define your toolstools = [ Tool(name="search_kb", func=search_knowledge_base, description="Search the knowledge base"), Tool(name="get_ticket", func=get_ticket, description="Get ticket details"),]
llm = ChatOpenAI(model="gpt-4")agent = create_openai_functions_agent(llm, tools, prompt)executor = AgentExecutor(agent=agent, tools=tools, callbacks=[callback])
# The callback monitors the entire agent execution, including tool callsresult = executor.invoke({"input": "Resolve ticket TICKET-4521"})Error Handling
The SDK provides typed exception classes for different failure modes:
from veraid import VeraIDClientfrom veraid.exceptions import ( VeraIDError, AuthenticationError, RateLimitError, BudgetExceededError, InjectionDetectedError, NotFoundError,)
try: credential = client.get_credential("my-secret")except AuthenticationError: print("Invalid API key")except RateLimitError as e: print(f"Rate limited. Retry after {e.retry_after}s")except BudgetExceededError as e: print(f"Budget exceeded: {e.limit} / {e.used}")except InjectionDetectedError as e: print(f"Injection: {e.category} ({e.confidence})")except NotFoundError: print("Credential not found")except VeraIDError as e: print(f"VeraID error: {e.message}")Context Manager
The client supports context manager usage for automatic cleanup:
with VeraIDClient(api_key="kd_live_your_api_key") as client: credential = client.get_credential("my-secret") # Client is automatically closed when exiting the blockAsync version:
async with AsyncVeraIDClient(api_key="kd_live_your_api_key") as client: credential = await client.get_credential("my-secret")Environment Variables
The SDK reads the following environment variables as defaults:
| Variable | Description | Required |
|---|---|---|
VERAID_API_KEY | API key for authentication | Yes |
VERAID_AGENT_ID | Agent identity ID (for AI agent features) | For agent features |
VERAID_BASE_URL | Base URL override (default: https://app.veraid.io) | No |
VERAID_DEBUG | Enable debug logging (true / false) | No |
export VERAID_API_KEY="kd_live_your_api_key"export VERAID_AGENT_ID="id_agent123"export VERAID_DEBUG="false"