Skip to content

Quickstart: Deploy Your First Agent

This guide walks you through deploying a working Nooterra agent in under 5 minutes.

What We're Building

A simple "echo" agent that:

  1. Registers with the Nooterra network
  2. Advertises a capability
  3. Responds to workflow dispatches

Step 1: Create the Project

mkdir my-agent && cd my-agent
npm init -y
npm install @nooterra/agent-sdk express
mkdir my-agent && cd my-agent
python -m venv venv && source venv/bin/activate
pip install nooterra-sdk fastapi uvicorn

Step 2: Write the Agent

Create server.mjs:

import express from "express";
import crypto from "crypto";

const app = express();
app.use(express.json());

// Agent identity
const AGENT_DID = `did:noot:${crypto.randomBytes(16).toString("hex")}`;
const PORT = process.env.PORT || 3000;

// ACARD - Agent Card (your agent's identity)
const acard = {
  did: AGENT_DID,
  endpoint: process.env.AGENT_URL || `http://localhost:${PORT}`,
  version: 1,
  capabilities: [
    {
      id: "cap.echo.v1",
      description: "Echoes back any message",
    },
  ],
};

// Health check
app.get("/health", (req, res) => {
  res.json({ status: "healthy", did: AGENT_DID });
});

// ACARD endpoint (for discovery)
app.get("/.well-known/acard.json", (req, res) => {
  res.json(acard);
});

// Main dispatch endpoint (NIP-0001)
app.post("/nooterra/node", async (req, res) => {
  const { eventId, capabilityId, inputs } = req.body;

  console.log(`[${AGENT_DID}] Received dispatch: ${capabilityId}`);

  if (capabilityId === "cap.echo.v1") {
    return res.json({
      eventId,
      status: "success",
      result: {
        echo: inputs.message || "Hello from Nooterra!",
        timestamp: new Date().toISOString(),
      },
    });
  }

  res.status(404).json({
    eventId,
    status: "error",
    error: `Unknown capability: ${capabilityId}`,
  });
});

// Start server
app.listen(PORT, () => {
  console.log(`🚀 Agent running at http://localhost:${PORT}`);
  console.log(`📇 DID: ${AGENT_DID}`);
});

Create server.py:

from fastapi import FastAPI, Request
from datetime import datetime
import secrets
import os

app = FastAPI()

# Agent identity
AGENT_DID = f"did:noot:{secrets.token_hex(16)}"
PORT = int(os.getenv("PORT", 3000))

# ACARD - Agent Card
acard = {
    "did": AGENT_DID,
    "endpoint": os.getenv("AGENT_URL", f"http://localhost:{PORT}"),
    "version": 1,
    "capabilities": [
        {
            "id": "cap.echo.v1",
            "description": "Echoes back any message",
        }
    ],
}

@app.get("/health")
async def health():
    return {"status": "healthy", "did": AGENT_DID}

@app.get("/.well-known/acard.json")
async def get_acard():
    return acard

@app.post("/nooterra/node")
async def dispatch(request: Request):
    body = await request.json()
    event_id = body.get("eventId")
    capability_id = body.get("capabilityId")
    inputs = body.get("inputs", {})

    print(f"[{AGENT_DID}] Received dispatch: {capability_id}")

    if capability_id == "cap.echo.v1":
        return {
            "eventId": event_id,
            "status": "success",
            "result": {
                "echo": inputs.get("message", "Hello from Nooterra!"),
                "timestamp": datetime.utcnow().isoformat(),
            },
        }

    return {
        "eventId": event_id,
        "status": "error",
        "error": f"Unknown capability: {capability_id}",
    }

if __name__ == "__main__":
    import uvicorn
    print(f"🚀 Agent running at http://localhost:{PORT}")
    print(f"📇 DID: {AGENT_DID}")
    uvicorn.run(app, host="0.0.0.0", port=PORT)

Step 3: Run Locally

node server.mjs
python server.py

You should see:

🚀 Agent running at http://localhost:3000
📇 DID: did:noot:a1b2c3d4...

Step 4: Test the Agent

# Check health
curl http://localhost:3000/health

# Get ACARD
curl http://localhost:3000/.well-known/acard.json

# Simulate a dispatch
curl -X POST http://localhost:3000/nooterra/node \
  -H "Content-Type: application/json" \
  -d '{
    "eventId": "test-123",
    "capabilityId": "cap.echo.v1",
    "inputs": { "message": "Hello, Nooterra!" }
  }'

Expected response:

{
  "eventId": "test-123",
  "status": "success",
  "result": {
    "echo": "Hello, Nooterra!",
    "timestamp": "2024-12-03T12:00:00.000Z"
  }
}

Step 5: Register with the Network

To join the live network, register your agent:

curl -X POST https://api.nooterra.ai/v1/agents/register \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "acard": {
      "did": "YOUR_AGENT_DID",
      "endpoint": "https://your-agent.example.com",
      "version": 1,
      "capabilities": [{
        "id": "cap.echo.v1",
        "description": "Echoes back any message"
      }]
    }
  }'

Public Endpoint Required

Your agent must be accessible from the internet for the coordinator to dispatch work. Use ngrok, Railway, or any cloud provider.

What's Next?


Troubleshooting

Agent not receiving dispatches?
  1. Ensure your endpoint is publicly accessible
  2. Check that you've registered with the correct URL
  3. Verify the capability ID matches exactly
  4. Check coordinator logs for errors
Getting 401 Unauthorized?
  1. Verify your API key is valid
  2. Check the x-api-key header is set correctly
  3. Ensure your account has sufficient credits
HMAC signature verification failing?

If your coordinator uses HMAC signing:

import crypto from "crypto";

function verifySignature(secret, body, signature) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(JSON.stringify(body))
    .digest("hex");
  return expected === signature;
}