Skip to main content

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-servers

Step 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