INTREXA AXIS

Integration guide

Quick Start

By the end of this guide your agent will be registered with a verified identity and making real authorization calls — with a signed audit record produced for every decision.

Before you start

  • An AXIS account with an active plan — sign up on the pricing page
  • A terminal with curl installed
  • Python 3 with pip install agf-sdk — used only to generate a signing key and a delegation chain; the authorization calls themselves are plain curl
  • About 10 minutes

Three things to know before you code

Actions are strings you define; scope lives in the delegation chain

There is no fixed action vocabulary — use any consistent format (file:write, payments.transfer, emails.send). Every /v1/decide call carries a signed JWT delegation chain, and the chain's own scope claim is the hard ceiling: an action outside the chain's effective scope is denied immediately, before policy is evaluated.

did is optional — AXIS can generate one for you

Register an agent without a did and AXIS assigns one (did:agf:<random hex>). Supply your own only if you already have a stable identifier for it elsewhere — you can't change it later without re-registering the agent.

New orgs get a default policy automatically

AXIS provisions a default policy for every new org that allows any in-scope action when trust_score is 0.5 or above. You only need custom policies when you want finer control — time windows, amount caps, resource patterns. Contact us to configure those.

01

Get your API key and org ID

After signing up, AXIS emails you an API key and an org ID. Export the API key — every curl command in this guide sends it via the X-AGF-Key header automatically. Your org is resolved from the key itself, so you won't type the org ID into any request.

Terminal — replace with your real API key from the email
export AGF_API_KEY="agfk_xxxxxxxxxxxxxxxxxxxxxxxx"   # replace with your real key
Choose a plan
02

Generate a signing key for your agent

Every authorization request needs a signed JWT delegation chain proving who's asking. For a single agent acting on its own behalf, the simplest setup is a self-signed EC P-256 key pair: the agent signs its own chain, and AXIS verifies it against the public key you register in the next step. This is the one place this guide uses code instead of curl — generating and PEM-encoding an EC key isn't practical by hand.

Note: Persist the private key (env var, secrets manager) and reuse it across restarts. A freshly generated key each run won't match whatever public key you registered previously for the same agent.

Python
from agf import generate_keypair

private_key_pem, public_key_pem = generate_keypair()
print(private_key_pem)   # save this — you'll sign chains with it
print(public_key_pem)    # this goes in the register call in step 03
03

Register your first agent

Call POST /v1/agents once per agent. Your org is resolved from the API key, so it never goes in the request body — only name is required. Pass the public_key_pem from step 02 as keypair_public so AXIS can verify chains this agent signs.

Note: did is optional. Omit it and AXIS generates one (did:agf:<random hex>); supply your own if you already have a stable identifier for this agent elsewhere. Either way, save the did that comes back — it's what you sign into the delegation chain in step 04.

Note: The response's id (a UUID) is different from did — id is this record's row key, used in the URL for later calls like suspend or retire. did is the identity embedded in delegation chains and audit records.

Request — replace keypair_public with the public_key_pem from step 02
curl -X POST https://api.agentgovernancefoundation.com/v1/agents \
  -H "X-AGF-Key: $AGF_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "report-writer-v1",
    "keypair_public": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
  }'
Response — save id and did for the next steps
{
  "id":         "5b1f9e3a-6c2d-4a8b-9f01-2c3d4e5f6a7b",
  "did":        "did:agf:8f2a1c9b0e4d7f31",
  "name":       "report-writer-v1",
  "status":     "active",
  "created_at": "2026-06-23T10:00:00Z"
}
04

Build a delegation chain

Sign a one-hop JWT with the private key from step 02 — issuer and subject are both the agent's did, and scope is the action you intend to take. This is what proves to AXIS who's asking and what they're allowed to do; /v1/decide rejects any request without one.

Note: This chain is self-signed and single-hop (iss == sub) — the agent authorizing itself, with no separate root authority delegating to it. Multi-hop delegation (a root issuing to an agent, or an agent to a sub-agent) uses the same JWT shape but is out of scope for this quick start.

Python
from agf import build_self_signed_chain

chain = build_self_signed_chain(
    private_key_pem,
    agent_id="did:agf:8f2a1c9b0e4d7f31",   # the did from step 03
    action="file:write",
)
print(chain[0])   # paste this JWT into the chain array in step 05
05

Make your first authorization request

Call POST /v1/decide with the chain from step 04. action.type must match the action string you signed into the chain's scope — a mismatch is denied immediately, before policy is evaluated. resource is a free-form identifier you define; AXIS doesn't connect to or validate it, only stores and matches it against policy rules. audience must match the chain's aud claim ("agf" by default, unless you passed a different audience to build_self_signed_chain).

Note: The context object is optional. If you include it, AXIS uses session_id and ip to improve trust scoring. You can omit it entirely.

Request — replace chain with the JWT from step 04
curl -X POST https://api.agentgovernancefoundation.com/v1/decide \
  -H "X-AGF-Key: $AGF_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "chain": ["eyJhbGciOi..."],
    "action": {
      "type":     "file:write",
      "resource": "s3://corp-data/reports/q2.csv"
    },
    "audience": "agf",
    "context": {
      "session_id": "sess_xyz",
      "ip":         "10.0.1.5"
    }
  }'
06

Read the decision and act on it

The response arrives in under 10 ms, wrapped in a data/meta envelope. Check data.decision: ALLOW means proceed, DENY means block, REVIEW_REQUIRED means a human needs to approve first (data.approval_request_id tells you where). data.reasoning is a stage-by-stage trace from the policy engine (revocation, identity, constraints, risk), not prose. trust_score (0–100) reflects how trusted this chain is right now. Every call produces an artifact_id: a permanent, signed record of this exact decision, stored automatically for audit.

Response
{
  "data": {
    "decision":      "ALLOW",
    "trust_score":    91,
    "risk_score":     12.4,
    "policy_version": "pol_default",
    "artifact_id":    "dec_1750000000_a1b2c3",
    "reasoning":     ["revocation:passed", "identity:checked", "constraints:passed", "risk:12.4:ALLOW", "final:ALLOW"],
    "approval_request_id": null
  },
  "meta": { "request_id": "req_9hmn5st", "timestamp": 1750000273 }
}
Example handler
// Always gate on data.decision — do not compare trust_score directly
if (response.data.decision === "ALLOW") {
  // proceed with the action
} else if (response.data.decision === "REVIEW_REQUIRED") {
  // wait for a human — see response.data.approval_request_id
} else {
  // DENY — see step 07 for how to handle it
}
07

Handle a denied decision

When data.decision is DENY, check data.error_code and data.error_message first, not data.reasoning. A mismatch between action.type and the chain's signed scope — the most common first-time denial — is rejected before policy runs at all: error_code comes back SCOPE_INSUFFICIENT with the specifics in error_message, and reasoning is left empty. Log the artifact_id either way — it's the signed proof of the denial for your audit trail.

Note: A DENY that policy itself produced (rather than the scope precheck) populates data.reasoning with a trace instead — the same field ALLOW and REVIEW_REQUIRED responses use — and leaves error_code/error_message null. Check both fields; which one is populated tells you whether policy ever ran.

Note: A trust_score below your policy's minimum threshold is one way policy itself can deny. This happens when the agent shows unusual behaviour — high request volume, unusual hours, anomaly signals. It recovers with normal behaviour over time.

Response — scope mismatch (rejected before policy runs)
{
  "data": {
    "decision":      "DENY",
    "trust_score":    0,
    "risk_score":     100.0,
    "policy_version": "",
    "artifact_id":    "dec_1750000000_a1b2c3",
    "reasoning":     [],
    "error_code":    "SCOPE_INSUFFICIENT",
    "error_message": "Action 'file:delete' not in effective scope ['file:write']",
    "approval_request_id": null
  },
  "meta": { "request_id": "req_8gln4rra", "timestamp": 1750000401 }
}
Contact us
08

Use the Python SDK (optional)

The AXIS Python SDK wraps the REST API so you can integrate authorization into any Python agent framework — LangChain, CrewAI, AutoGen, or plain Python — without hand-rolling HTTP calls, key generation, or chain signing. Install it from PyPI and pass your API key via the api_key constructor argument.

Note: authorize() never raises for deny or review decisions — it always returns an AuthResult object; check result.allowed before proceeding. It can still raise for everything else: AGFAuthError for a bad key, AGFConnectionError if the runtime is unreachable, or AGFError if no chain and no private_key_pem was configured. Use AGFDeniedError and AGFReviewRequiredError if you prefer exception-based control flow for the decision itself, via the lower-level AGFClient.

Note: The SDK ships LangChain and CrewAI adapters. agf.langchain_tool() adds an authorization gate to your agent's tool list. AGFGuardedTool wraps individual tools so every call is policy-checked automatically.

Install
pip install agf-sdk                  # core
pip install agf-sdk[langchain]       # + LangChain adapter
pip install agf-sdk[crewai]          # + CrewAI adapter
Python — authorize an action
import os
from agf import AgentGovernance

agf = AgentGovernance(
    api_key=os.environ["AGF_API_KEY"],
    org_id="org_acme",
)

result = agf.authorize(
    agent_id="did:agf:agt_01abc",
    action="file:write",
    resource="s3://corp-data/q2.csv",
)

if result.allowed:
    # proceed with the action
    pass
else:
    raise PermissionError(f"Denied: {result.reason}")
LangChain — authorization gate tool
from agf import AgentGovernance
from langchain.agents import initialize_agent, AgentType
from langchain_openai import ChatOpenAI

agf = AgentGovernance(api_key=os.environ["AGF_API_KEY"], org_id="org_acme")

# agf_tool is a BaseTool the agent calls before sensitive operations
agf_tool = agf.langchain_tool(agent_id="did:agf:agt_01abc")

agent = initialize_agent(
    tools=[agf_tool, *your_other_tools],
    llm=ChatOpenAI(),
    agent=AgentType.OPENAI_FUNCTIONS,
)
LangChain — per-tool guard (enforced on every call)
from agf import AGFClient
from agf.langchain import AGFGuardedTool
from langchain_community.tools import ShellTool

client = AGFClient(api_key=os.environ["AGF_API_KEY"])

guarded_shell = AGFGuardedTool(
    tool=ShellTool(),
    client=client,
    agent_id="did:agf:agt_01abc",
    action_type="exec:shell",
    resource="local-shell",
)