Quickstart
Sixty seconds from installation to your first authorized tool call. Log in, capture a plan, get an intent token, invoke a tool.
This quickstart takes you from a blank shell to a signed, policy-enforced tool call. All you need is an API key (your service identity) and an email (the end user your agent is acting on behalf of).
Step 1 — Install and authenticate
pipx install armoriq-sdk
armoriq login
armoriq whoamiarmoriq login opens a browser tab on the dashboard. Sign in, approve the device, and the CLI saves your API key to ~/.armoriq/credentials.json.
$ armoriq whoami
ArmorIQ Credentials
Email: alice@acme.com
API Key: ak_live_xxxxxxxx...
User ID: 8c0b55f8-b052-4041-9d15-02835bd919ad
Org ID: 144e79f7-7873-41f1-aed8-0ebb5b108200
Saved at: 2026-04-22T08:48:08+00:00
File: /Users/you/.armoriq/credentials.jsonStep 2 — Your first authorized tool call
from armoriq_sdk import ArmorIQClient
# The only required argument is api_key. After `armoriq login`,
# the client reads it automatically from ~/.armoriq/credentials.json.
client = ArmorIQClient()
# Describe what the agent wants to do — one step per tool call.
plan_definition = {
"goal": "Get weather for San Francisco",
"steps": [
{
"action": "get_weather",
"mcp": "weather-mcp",
"params": {"city": "San Francisco", "units": "fahrenheit"},
},
],
}
# Capture the plan (signs it against the LLM's reasoning).
plan = client.capture_plan(
llm="gpt-4o",
prompt="What's the weather in San Francisco?",
plan=plan_definition,
)
# Get a short-lived intent token (5 minutes by default).
token = client.get_intent_token(plan, validity_seconds=300)
# Invoke the tool. `user_email` attaches the end-user identity
# used for audit trails and per-user policy enforcement.
result = client.invoke(
mcp="weather-mcp",
action="get_weather",
intent_token=token,
params={"city": "San Francisco", "units": "fahrenheit"},
user_email="alice@customer.com",
)
print(result.data)
client.close()import { ArmorIQClient } from '@armoriq/sdk';
const client = new ArmorIQClient({
apiKey: process.env.ARMORIQ_API_KEY!, // or read credentials.json manually
userId: 'weather-service',
agentId: 'weather-bot',
});
const planDefinition = {
goal: 'Get weather for San Francisco',
steps: [
{
action: 'get_weather',
mcp: 'weather-mcp',
params: { city: 'San Francisco', units: 'fahrenheit' },
},
],
};
const plan = await client.capturePlan({
llm: 'gpt-4o',
prompt: "What's the weather in San Francisco?",
plan: planDefinition,
});
const token = await client.getIntentToken(plan, { validitySeconds: 300 });
// TypeScript passes `userEmail` via the InvokeOptions param to invokeWithPolicy.
const result = await client.invokeWithPolicy(
'weather-mcp',
'get_weather',
token,
{ city: 'San Francisco', units: 'fahrenheit' },
{ userEmail: 'alice@customer.com' },
);
console.log(result.data);That's it. The proxy verified the signed plan, checked the policy for weather-mcp.get_weather, confirmed alice@customer.com has permission, forwarded the call to the MCP server, and the result is in your hands.
What just happened
Your agent ArmorIQ proxy weather-mcp
│ │ │
│ capture_plan ─────────┼──► plan stored + signed │
│ │ │
│ get_intent_token ─────┼──► token minted │
│ │ │
│ invoke ───────────────► │
│ │ 1. verify token │
│ │ 2. check policy │
│ │ 3. forward to mcp ──────►│
│ │ │
│ │◄──── tool response ──────┤
│◄────── result ─────────│ │
│ │ │
│ │ audit log written │Three services, three guarantees:
- Intent tokens — cryptographically binds your tool call to the LLM reasoning the plan was captured from. An attacker who steals the token can't alter the call.
- Policy enforcement — the proxy checks
weather-mcp.get_weatheragainst your allow-list before forwarding. Deny rules always win over allow. - Per-user audit — every call appears in the audit log tagged with
alice@customer.com.
Customize the pattern
No end user (cron jobs, background workers)
Skip user_email. Audit logs record the service-account identity derived from your API key.
result = client.invoke(
mcp="weather-mcp",
action="get_weather",
intent_token=token,
params={"city": "San Francisco"},
# no user_email — service-account call
)Many end users (SaaS multi-tenant)
Call invoke(..., user_email=email) per request. The client is cheap to share across users:
client = ArmorIQClient()
for user_email in ["alice@acme.com", "bob@acme.com", "carol@acme.com"]:
plan = client.capture_plan(llm="gpt-4o", prompt=..., plan=...)
token = client.get_intent_token(plan)
client.invoke(
mcp="weather-mcp",
action="get_weather",
intent_token=token,
params={...},
user_email=user_email,
)For long-running sessions (multi-turn conversations with many tool calls), see the session pattern in the Python SDK deep dive.
Config-as-code (teams, CI/CD)
If you want your policy and MCP list in version control instead of code, use armoriq init to scaffold a YAML, then ArmorIQClient.from_config():
client = ArmorIQClient.from_config("armoriq.yaml")See the full YAML reference and the CLI commands init / validate / register.
Troubleshooting
| What you see | Why | Fix |
|---|---|---|
ConfigurationException: API key is required | No key resolved | Run armoriq login, or set ARMORIQ_API_KEY |
InvalidTokenException at invoke | Token doesn't match the plan that was signed | Capture a fresh plan; don't edit the token's underlying plan |
PolicyBlockedException | The tool isn't in your allow-list for this user | Check policies in the dashboard → Policies |
TokenExpiredException | Token older than validity_seconds | Get a new token with get_intent_token |
IntentMismatchException | Plan steps don't match what was actually invoked | Your invoke() args don't match plan.steps — align them |