Manual integration guide
Complete guide for integrating with AIM without using the Python SDK. This covers direct API integration, cryptographic signing, capability declaration, and MCP server registration.
When to use manual integration
Manual integration is recommended when:
- You're using a language other than Python
- You need fine-grained control over the integration
- You're building a custom SDK for your organization
- You're integrating AIM into existing infrastructure
Note: The Python SDK handles all of this automatically. Use it when possible.
Choose your language
Step 1: generate base64 public key
Public key generation guide
AIM requires Ed25519 public keys in base64 format (exactly 32 bytes). Here's how to generate them correctly:
# Generating Base64 Public Keys for AIM
## Method 1: Using OpenSSL (Recommended)
# Generate Ed25519 key pair
openssl genpkey -algorithm ed25519 -out private_key.pem
openssl pkey -in private_key.pem -pubout -out public_key.pem
# Extract raw public key bytes and convert to base64
# Note: Ed25519 public keys are exactly 32 bytes
openssl pkey -in private_key.pem -pubout -outform DER | tail -c 32 | base64
# Or extract from PEM file (removes headers and newlines)
PUBLIC_KEY=$(cat public_key.pem | grep -v "PUBLIC KEY" | tr -d '\n')
## Method 2: Using Python (nacl library)
import nacl.signing
import nacl.encoding
import base64
# Generate keypair
signing_key = nacl.signing.SigningKey.generate()
verify_key = signing_key.verify_key
# Get base64-encoded public key (32 bytes)
public_key_b64 = verify_key.encode(encoder=nacl.encoding.Base64Encoder).decode('utf-8')
print(f"Base64 public key: {public_key_b64}")
# Example output: "YXNkZmFzZGZhc2RmYXNkZmFzZGZhc2RmYXNkZmFzZGY="
## Method 3: Using Node.js
const crypto = require('crypto');
// Generate Ed25519 key pair
const { publicKey, privateKey } = crypto.generateKeyPairSync('ed25519');
// Export public key as base64 (raw 32 bytes)
const publicKeyDer = publicKey.export({ type: 'spki', format: 'der' });
// Skip the SPKI header (12 bytes) to get raw Ed25519 public key (32 bytes)
const rawPublicKey = publicKeyDer.slice(-32);
const publicKeyBase64 = rawPublicKey.toString('base64');
console.log('Base64 public key:', publicKeyBase64);
## Method 4: Using Go
import (
"crypto/ed25519"
"crypto/rand"
"encoding/base64"
)
// Generate key pair
publicKey, privateKey, _ := ed25519.GenerateKey(rand.Reader)
// Convert public key to base64 (32 bytes)
publicKeyBase64 := base64.StdEncoding.EncodeToString(publicKey)
fmt.Printf("Base64 public key: %s\n", publicKeyBase64)
## Key Format Requirements for AIM:
# MUST be exactly 32 bytes when decoded from base64
# MUST be raw Ed25519 public key bytes (not PEM, not DER with headers)
# MUST be standard base64 encoding (not URL-safe)
# Example valid format: "YXNkZmFzZGZhc2RmYXNkZmFzZGZhc2RmYXNkZmFzZGY="
## Validating Your Public Key:
# Check if your base64 public key is valid
echo "YOUR_BASE64_PUBLIC_KEY" | base64 -d | wc -c
# Should output: 32
# Verify it's valid Ed25519 format (Python)
import base64
public_key_b64 = "YOUR_BASE64_PUBLIC_KEY"
public_key_bytes = base64.b64decode(public_key_b64)
assert len(public_key_bytes) == 32, f"Invalid length: {len(public_key_bytes)} (expected 32)"
print("Valid Ed25519 public key format")Step 2: agent registration
Generate keys and register agent
First, generate an Ed25519 key pair and register your agent with AIM:
# Step 1: Get your API key from AIM Dashboard (Settings → API Keys)
# The API key is used to authenticate the registration request
# Step 2: Register agent with AIM
# Note: AIM generates Ed25519 keys for you - no need to create them manually!
curl -X POST https://your-aim-server.com/api/v1/public/agents/register \
-H "Content-Type: application/json" \
-H "X-AIM-API-Key: your-api-key-here" \
-d '{
"name": "my-agent",
"displayName": "My Agent",
"description": "A secure AI agent for data processing",
"agentType": "custom",
"version": "1.0.0",
"repositoryUrl": "https://github.com/myorg/my-agent",
"documentationUrl": "https://docs.myorg.com/my-agent"
}'
# Response (SAVE THESE CREDENTIALS - privateKey is only returned ONCE!)
{
"agentId": "550e8400-e29b-41d4-a716-446655440000",
"name": "my-agent",
"displayName": "My Agent",
"publicKey": "MCowBQYDK2VwAyEA...",
"privateKey": "MC4CAQAwBQYDK2VwBCIEIA...",
"aimUrl": "https://your-aim-server.com",
"status": "pending",
"trustScore": 60,
"message": "Agent registered successfully. Awaiting verification."
}Step 3: sign API requests
Ed25519 request signing
Sign each request to the sdk-api routes with the private key returned at registration. The signature authenticates the agent on each of those requests:
# Ed25519 Authentication for API Calls
# The agent signs each request with its Ed25519 private key; no OAuth tokens are used.
# Load credentials saved during registration
AGENT_ID=$(cat .aim_credentials.json | jq -r '.agentId')
PRIVATE_KEY=$(cat .aim_credentials.json | jq -r '.privateKey')
PUBLIC_KEY=$(cat .aim_credentials.json | jq -r '.publicKey')
AIM_URL=$(cat .aim_credentials.json | jq -r '.aimUrl')
# For API calls, sign your request with Ed25519
# The signature proves you control the agent's private key
# Example: Get agent details
# The sdk-api group routes, such as this one, read X-Agent-ID, X-Timestamp, X-Signature
# and X-Public-Key.
# X-Public-Key is required and must equal the base64 public key registered for the
# agent; X-Algorithm is optional (Ed25519 is the default algorithm)
curl -X GET "${AIM_URL}/api/v1/sdk-api/agents/${AGENT_ID}" \
-H "Content-Type: application/json" \
-H "X-Agent-ID: ${AGENT_ID}" \
-H "X-Timestamp: $(date +%s)" \
-H "X-Signature: <ed25519-signature>" \
-H "X-Public-Key: ${PUBLIC_KEY}"
# The signature is computed over method, path, timestamp and body, in that order, joined by newlines:
# <method>\n<path>\n<timestamp>\n<body>
# The path includes the query string when the request has one. A request with no body,
# such as the GET above, leaves out the body and the newline before it:
# <method>\n<path>\n<timestamp>
# X-Timestamp must be within 30 seconds of the
# server's clock, and X-Signature carries the signature base64-encoded
# Use the Ed25519 private key from registration to sign
# Example: Read a verification by ID
# Set VERIFICATION_ID to the ID AIM returned when this agent created the verification
VERIFICATION_ID="<verification-id>"
# This route reads X-AIM-Agent-ID, X-AIM-Timestamp and X-AIM-Signature; the signed
# message is "GET\n/api/v1/sdk-api/verifications/<id>\n<agent-id>\n<timestamp>"
curl -X GET "${AIM_URL}/api/v1/sdk-api/verifications/${VERIFICATION_ID}" \
-H "X-AIM-Agent-ID: ${AGENT_ID}" \
-H "X-AIM-Timestamp: $(date +%s)" \
-H "X-AIM-Signature: <ed25519-signature>"Step 4: declare capabilities
Manual capability declaration
Declare what your agent can do - these are the operations it can perform:
# Manual Capability Declaration
# Method 1: During Registration
curl -X POST https://api.aim.example.com/v1/agents/register \
-H "Content-Type: application/json" \
-d '{
"name": "my-agent",
"public_key": "'$PUBLIC_KEY'",
"capabilities": [
{
"name": "text_analysis",
"description": "Analyze text for sentiment and entities",
"parameters": {
"text": {
"type": "string",
"required": true,
"description": "Text to analyze"
},
"language": {
"type": "string",
"required": false,
"default": "en",
"description": "Language code"
}
},
"returns": {
"type": "object",
"properties": {
"sentiment": "string",
"entities": "array",
"confidence": "number"
}
}
},
{
"name": "data_export",
"description": "Export data to various formats",
"parameters": {
"format": {
"type": "string",
"enum": ["json", "csv", "parquet"],
"required": true
},
"filters": {
"type": "object",
"required": false
}
}
}
]
}'
# Method 2: Update Capabilities After Registration
curl -X PUT https://api.aim.example.com/v1/agents/my-agent/capabilities \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"capabilities": [
{
"name": "new_capability",
"description": "A new capability",
"parameters": {...}
}
]
}'
# Method 3: Add Individual Capability
curl -X POST https://api.aim.example.com/v1/agents/my-agent/capabilities \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "process_image",
"description": "Process and analyze images",
"parameters": {
"image_url": "string",
"operations": "array"
}
}'Step 5: register MCP servers
MCP server registration
Register MCP servers that your agent communicates with:
# Manual MCP Server Declaration
# 1. Register MCP Server with Agent
curl -X POST https://api.aim.example.com/v1/agents/my-agent/mcp-servers \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "custom-processor",
"url": "http://localhost:8080",
"public_key": "-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
-----END PUBLIC KEY-----",
"capabilities": [
"text_processing",
"data_analysis",
"report_generation"
],
"metadata": {
"version": "1.0.0",
"protocol": "mcp-v1",
"description": "Custom data processing MCP server"
}
}'
# 2. Update MCP Server Capabilities
curl -X PATCH https://api.aim.example.com/v1/mcp-servers/custom-processor \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"capabilities": {
"add": ["new_capability"],
"remove": ["old_capability"]
}
}'
# 3. List Agent's MCP Servers
curl -H "Authorization: Bearer $ACCESS_TOKEN" \
https://api.aim.example.com/v1/agents/my-agent/mcp-serversStep 6: health and monitoring
Health checks and heartbeats
# Health Check & Monitoring
# 1. Agent Health Check
curl -H "Authorization: Bearer $ACCESS_TOKEN" \
https://api.aim.example.com/v1/agents/my-agent/health
# Response
{
"status": "healthy",
"uptime": 86400,
"last_activity": "2024-01-20T10:30:00Z",
"trust_score": 0.92,
"capabilities_count": 5,
"mcp_servers_count": 2,
"token_expires_in": 842,
"metrics": {
"requests_total": 1523,
"requests_success": 1501,
"requests_failed": 22,
"avg_response_time": 45
}
}
# 2. Heartbeat Endpoint (Keep-Alive)
while true; do
curl -X POST https://api.aim.example.com/v1/agents/my-agent/heartbeat \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"status": "active",
"capabilities_available": ["process_data", "analyze_text"],
"mcp_servers_connected": ["custom-processor"],
"resource_usage": {
"cpu_percent": 15.2,
"memory_mb": 256,
"connections": 5
}
}'
sleep 30 # Send heartbeat every 30 seconds
done
# 3. Metrics Reporting
curl -X POST https://api.aim.example.com/v1/agents/my-agent/metrics \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'",
"metrics": {
"operations_performed": 42,
"data_processed_mb": 156.8,
"errors_encountered": 2,
"average_latency_ms": 35
}
}'Integration checklist
Common integration issues
401 on a signed API call
On the sdk-api routes that read X-Signature, check that the signature was made with the private key returned at registration and covers the method, path, timestamp and body exactly as sent, and that X-Timestamp is within 30 seconds of the server's clock. Changing the body after signing breaks the signature. The verification read route signs a different message, shown in its example above.
MCP server not verified
Ensure the MCP server responds to verification challenges with proper Ed25519 signatures.
Rate limiting
Implement exponential backoff. Default limit: 100 requests per minute.
Security best practices
Key management
- Never commit private keys to version control
- Use secure key storage (HSM, KMS, Vault)
- Rotate keys periodically
- Use different keys per environment
Request security
- Sign each sdk-api request; never send the private key
- Use secure transport (HTTPS only)
- Send the current timestamp with each signed request