Skip to content
NagentNagent
Log inSign upHire your AI Growth Team
NagentNagent
Log inSign up

Your agents, from your own code

Run your Nagent agents from a script, a backend, Claude Code, Cursor or any MCP client. A run from outside is governed exactly as one inside Nagent: the same knowledge, tools, autonomy level and approvals, and spend through your budget.

These are the developer docs. Setting up and running your AI teams in the workspace itself is covered in the platform docs.

Quickstart

  1. Create an API key in Nagent at Settings, API & MCP (steps below).
  2. Run an agent:
curl -X POST https://nagent.ai/api/v1/agents/nora/runs \
  -H "Authorization: Bearer $NAGENT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"input": "Research retail-1.example and draft a first email"}'

The response is the finished run: its output, the actions it took, and any it is waiting on a person to approve.

Create an API key

  1. Sign in to Nagent and open https://nagent.ai/admin/settings/api (Settings, API & MCP). Viewing it needs settings:read; creating a key needs settings:write.
  2. Choose Create key. Name it after what will use it, for example Zapier production or Cursor, Priya.
  3. Pick live or test. A test key runs agents without tools, so it never sends or writes anything: use it while you build.
  4. Give it scopes (agents:run and runs:read to run agents) and the agents it may reach. Choose personal if it should act as you, which deciding approvals needs.
  5. Optionally set an expiry, an IP allowlist (Business) or a lower rate limit.
  6. Copy the key. It is shown once, with every snippet on this page filled in. Store it as NAGENT_API_KEY in your secret manager or shell.

Rotate makes a successor with the same access and keeps the old key working for 24 hours. Revoke stops it at once. Keys start on the Team plan.

From any language

The API is plain HTTPS and JSON with a bearer key, so any stack can call it. Official SDKs exist for Python and TypeScript. For anything else, generate a client from the OpenAPI document at /api/v1/openapi.json, or import that URL into Postman, Insomnia or Bruno to try every call with your key.

Python

# pip install nagent
import os
from nagent import Nagent

nagent = Nagent(api_key=os.environ["NAGENT_API_KEY"], base_url="https://nagent.ai")
run = nagent.runs.create("nora", input="Research retail-1.example and draft a first email")
print(run["status"], run["output"])

TypeScript

// npm install @nagent/sdk
import { Nagent } from '@nagent/sdk';

const nagent = new Nagent({ apiKey: process.env.NAGENT_API_KEY!, baseUrl: 'https://nagent.ai' });
const run = await nagent.runs.create('nora', { input: 'Research retail-1.example and draft a first email' });
console.log(run.status, run.output);

Go, Java, C#, Ruby, PHP and the rest

# Any language: generate a client from the OpenAPI document
npx @openapitools/openapi-generator-cli generate \
  -i https://nagent.ai/api/v1/openapi.json -g go -o ./nagent-go
# -g java | csharp | ruby | php | kotlin | swift5 | rust | …

Keys and scopes

A key belongs to your workspace and is made by a member. It can hold no scope its maker lacks, reaches only the agents it is given, and is checked on every call against the plan the workspace is on that day. Keys start nag_live_ or nag_test_; a test key runs agents without tools, so it never sends or writes anything. Rotate makes a successor and keeps the old key working for 24 hours; revoke stops it at once.

ScopeWhat it allowsFrom
agents:readList the agents the key reaches and read their agent.md.Team
agents:runStart a governed run. Actions below the autonomy floor wait for a person.Team
runs:readRead a run’s status, output and the actions it took or is waiting on.Team
approvals:readList the actions waiting for a person.Business
approvals:decideApprove or reject a waiting action, as the member the key belongs to. Personal keys only.Business
webhooks:manageAdd and remove the endpoints Nagent posts events to.Business
knowledge:readList, search and read the Knowledge Hub documents the key’s member may read.Team
knowledge:writeAdd a document to the Knowledge Hub from text or a web address.Team
leads:readList and read leads, with contact fields as the member’s role shows them.Team
leads:writeCreate leads and change their stage, owner and details.Team
ops:triggersList the triggers of the agents the key reaches, fire one now, or subscribe an agent to an event.Team

Plans and limits

PlanAPI and MCPRequests a minuteWebhook endpoints
SoloThe settings page and snippets; keys start on TeamNoneNone
TeamLive keys, agents and runs scopes601
BusinessEvery scope, personal keys that decide approvals, IP allowlists6005
EnterpriseEvery scope, personal keys, IP allowlists, unlimited call log6,00025

See pricing for the plans themselves.

REST reference

Base URL https://nagent.ai/api/v1. JSON in and out; errors are RFC 9457 problem documents; every response carries X-Request-Id, Nagent-Api-Version and RateLimit-*. The machine-readable reference is /api/v1/openapi.json.

CallWhat it doesScope
GET /agentsList the agents this key reachesagents:read
GET /agents/{key}Read one agentagents:read
GET /agents/{key}/agent.mdThe agent as a markdown specificationagents:read
POST /agents/{key}/runsRun an agentagents:run
GET /runs/{id}Read a run, with its output once it has oneruns:read
GET /runs/{id}/eventsA run as server-sent eventsruns:read
GET /approvalsActions waiting for a personapprovals:read
POST /approvals/{id}/decideApprove or reject a waiting actionapprovals:decide
GET /webhooksList webhook endpointswebhooks:manage
POST /webhooksAdd a webhook endpointwebhooks:manage
DELETE /webhooks/{id}Remove a webhook endpointwebhooks:manage
GET /knowledgeKnowledge Hub documents the key’s member may readknowledge:read
POST /knowledgeAdd a documentknowledge:write
GET /knowledge/{id}Read a document with its textknowledge:read
GET /leadsLeads, newest firstleads:read
POST /leadsCreate a leadleads:write
GET /leads/{id}Read a leadleads:read
PATCH /leads/{id}Change a leadleads:write
GET /triggersTriggers of the agents this key reachesops:triggers
POST /triggers/{id}/fireFire a trigger nowops:triggers

Runs, sessions and async

POST /agents/{key}/runs takes input (up to 9000 characters) and optionally:

  • A run’s output is returned to the key that started it; another key reads the run without it.
  • sessionId: continue a conversation. The same id sends the agent the last turns this key ran in it, so a chat in your product keeps its thread. Sessions belong to the key.
  • metadata: up to 16 string labels of up to 500 characters, returned on the run and on its webhook events, for your own ticket or user ids.
  • Header Idempotency-Key: a retry within 24 hours is refused with 409 rather than run twice.
  • Header Prefer: respond-async: answer 202 at once with the run running and a Location; with the same key, poll GET /runs/{id}, follow GET /runs/{id}/events, or wait for the run.completed webhook. Use it for long runs and from serverless functions with short timeouts.
curl -X POST https://nagent.ai/api/v1/agents/nora/runs \
  -H "Authorization: Bearer $NAGENT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Prefer: respond-async" \
  -d '{"input": "Audit retail-1.example", "sessionId": "crm-4711", "metadata": {"ticket": "T-88"}}'

# 202 Accepted, Location: /api/v1/runs/<id>, status "running". Then:
curl https://nagent.ai/api/v1/runs/<id> -H "Authorization: Bearer $NAGENT_API_KEY"

Lists page with a cursor: pass nextCursor back as cursor until hasMore is false.

curl "https://nagent.ai/api/v1/approvals?status=pending&limit=50" -H "Authorization: Bearer $NAGENT_API_KEY"
# { "object": "list", "data": [...], "hasMore": true, "nextCursor": "MTc2…" }
curl "https://nagent.ai/api/v1/approvals?status=pending&limit=50&cursor=MTc2…" -H "Authorization: Bearer $NAGENT_API_KEY"

Knowledge, leads and triggers (Beta)

These scopes start on the Team plan, and the key acts as its member: it reads and writes what that person may in Nagent, no more.

  • knowledge:read lists and reads the Knowledge Hub documents the member may read, matched on title and topics, or by meaning with mode=semantic, which draws a little from the workspace’s credits. A document they may not read answers 404.
  • knowledge:write adds a document from title and text, or from a url Nagent reads. A full Knowledge Hub answers 402.
  • leads:read lists and reads leads, with contact fields as the member’s role shows them in the CRM.
  • leads:write creates a lead from an email, phone or LinkedIn URL, owned by the member, and updates one through the CRM’s own update. A lead that already exists answers 409 with its address in Location, so a sync can retry safely. A lead made here is not sent outreach on its own; run an agent for that, and its sends wait for approval as usual.
  • ops:triggers lists the triggers of the agents the key reaches and fires one now. A trigger that is off or paused answers 409, one past its hourly limit 429, and the fire is governed as when it fires on schedule: the outcome is fired, or queued when its action waits for a person.

MCP for coding agents

The server is https://nagent.ai/api/mcp/nagent over Streamable HTTP, with the same key as a bearer header. It works with Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Devin and any client that speaks MCP; a client that only starts local servers reaches it through mcp-remote. Each agent the key reaches is a <agent>_run tool (with optional sessionId and metadata); nagent_list_agents, nagent_get_run, nagent_wait_run and, on a personal key, nagent_decide_approval follow the key’s scopes, as do nagent_search_knowledge, nagent_read_knowledge, nagent_add_knowledge, nagent_list_leads, nagent_get_lead, nagent_create_lead, nagent_update_lead, nagent_list_triggers and nagent_fire_trigger. Each agent’s agent.md and card are resources. Discovery: /.well-known/mcp.json. Set NAGENT_API_KEY in the environment the client starts from, or paste the key where the snippet names it.

Claude Code

claude mcp add --transport http nagent https://nagent.ai/api/mcp/nagent --header "Authorization: Bearer $NAGENT_API_KEY"

Codex in ~/.codex/config.toml

# Set NAGENT_API_KEY in the environment Codex starts from.
[mcp_servers.nagent]
url = "https://nagent.ai/api/mcp/nagent"
bearer_token_env_var = "NAGENT_API_KEY"

Cursor in .cursor/mcp.json

{
  "mcpServers": {
    "nagent": {
      "url": "https://nagent.ai/api/mcp/nagent",
      "headers": {
        "Authorization": "Bearer ${env:NAGENT_API_KEY}"
      }
    }
  }
}

VS Code in .vscode/mcp.json

{
  "inputs": [
    {
      "type": "promptString",
      "id": "nagent-api-key",
      "description": "Nagent API key",
      "password": true
    }
  ],
  "servers": {
    "nagent": {
      "type": "http",
      "url": "https://nagent.ai/api/mcp/nagent",
      "headers": {
        "Authorization": "Bearer ${input:nagent-api-key}"
      }
    }
  }
}

Windsurf in ~/.codeium/windsurf/mcp_config.json

{
  "mcpServers": {
    "nagent": {
      "serverUrl": "https://nagent.ai/api/mcp/nagent",
      "headers": {
        "Authorization": "Bearer ${env:NAGENT_API_KEY}"
      }
    }
  }
}

Gemini CLI in ~/.gemini/settings.json

{
  "mcpServers": {
    "nagent": {
      "httpUrl": "https://nagent.ai/api/mcp/nagent",
      "headers": {
        "Authorization": "Bearer $NAGENT_API_KEY"
      }
    }
  }
}

Claude Desktop in claude_desktop_config.json

{
  "mcpServers": {
    "nagent": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://nagent.ai/api/mcp/nagent",
        "--header",
        "Authorization:${NAGENT_AUTH}"
      ],
      "env": {
        "NAGENT_AUTH": "Bearer <your key>"
      }
    }
  }
}

Devin and others in the client’s MCP settings

Transport: Streamable HTTP
URL:       https://nagent.ai/api/mcp/nagent
Header:    Authorization: Bearer <your key>

A client that only runs local (stdio) servers can use:
npx -y mcp-remote https://nagent.ai/api/mcp/nagent --header "Authorization:Bearer <your key>"

Webhooks

Add an https endpoint in Settings, API & MCP. Nagent signs every delivery in the Standard Webhooks scheme. A delivery that fails is retried after 1 minute, 5, 30, then 2, 5 and 10 hours; after 24 hours the endpoint pauses and its maker is emailed. De-duplicate on webhook-id, which is the same on every retry.

EventWhen
run.startedA run started through the API or MCP began. Carries the run id, agent, session and metadata.
run.completedA run started through the API or MCP finished. Carries the run without its output.
run.failedA run started through the API or MCP did not finish, and why.
approval.requestedA run is waiting for a person to approve one or more actions, by approval id.
approval.decidedA waiting action was approved or rejected over the API.

Verifying a delivery: TypeScript

import crypto from 'node:crypto';

// secret: the whsec_… shown when you added the endpoint
export function verify(secret: string, headers: Headers, body: string): boolean {
  const id = headers.get('webhook-id') ?? '';
  const ts = Number(headers.get('webhook-timestamp'));
  if (!id || Math.abs(Date.now() / 1000 - ts) > 300) return false;
  const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64');
  const expected = crypto.createHmac('sha256', key).update(`${id}.${ts}.${body}`).digest();
  return (headers.get('webhook-signature') ?? '').split(' ').some(entry => {
    const [v, sig] = entry.split(',');
    const got = Buffer.from(sig ?? '', 'base64');
    return v === 'v1' && got.length === expected.length && crypto.timingSafeEqual(got, expected);
  });
}

Verifying a delivery: Python

import base64, hashlib, hmac, time

# secret: the whsec_… shown when you added the endpoint
def verify(secret: str, headers: dict, body: bytes) -> bool:
    msg_id = headers.get("webhook-id", "")
    ts = int(headers.get("webhook-timestamp", "0"))
    if not msg_id or abs(time.time() - ts) > 300:
        return False
    key = base64.b64decode(secret.removeprefix("whsec_"))
    expected = hmac.new(key, f"{msg_id}.{ts}.".encode() + body, hashlib.sha256).digest()
    for entry in headers.get("webhook-signature", "").split(" "):
        version, _, sig = entry.partition(",")
        if version == "v1" and hmac.compare_digest(base64.b64decode(sig), expected):
            return True
    return False

Any Standard Webhooks library verifies these too; the SDKs include the check.

Approvals

A run is one governed pass of an agent over your input. Each agent works at an autonomy level, L1 Observe to L5 Lead; an action below its level does not run on its own but waits for a person, and the run comes back waiting_for_approval naming the approvals. A member decides them in Nagent, or a personal key with approvals:decide decides them as that member. Every run is checked against the workspace budget first.

Versioning

This is version 1, named in the path and in the Nagent-Api-Version header. Within v1, fields and operations are only added, so ignore fields you do not know. A breaking change ships as /api/v2. An operation being retired answers with Deprecation, a Sunset date at least six months out, and a Link to what replaces it. The older /api/public/agent/run is the website widget’s endpoint, not this API; called with a key it answers Deprecation and points here.

Errors

StatusTypeMeaning
401#missing-keyNo `Authorization: Bearer` header, or not a key.
401#invalid-keyNo workspace has this key.
401#key-revokedThe key was revoked.
401#key-expiredThe key passed its expiry.
401#key-rotatedThe key was rotated and its 24 hours of grace are over.
403#planThe workspace plan does not include the API or MCP.
403#ip-not-allowedThe call came from outside the key’s allowlist.
403#scope-missingThe key was not given the scope this call needs.
403#scope-withheldThe key holds the scope, but the plan no longer includes it.
403#agent-not-allowedThe key was not given this agent.
404#agent-not-foundNo agent with that key in the workspace.
409#agent-pausedThe agent is paused in the workspace.
409#duplicate-requestThe Idempotency-Key was used in the last 24 hours.
400#invalid-inputThe run request is missing its input, or a session id or metadata is malformed.
400#invalid-cursorThe cursor was not one a previous page returned.
429#rate-limitedOver the key’s requests a minute. Retry after the Retry-After seconds.

SDKs

Both are thin clients over the REST API with no dependencies. Tests hold each to the OpenAPI document, so a method is one operation. Errors raise the problem document with its request id. Both are published at 1.0.0: nagent on PyPI and @nagent/sdk on npm, an ES module you load with import. The REST API and its OpenAPI document above work from any other stack.

Python: pip install nagent

# pip install nagent
import os
from nagent import Nagent

nagent = Nagent(api_key=os.environ["NAGENT_API_KEY"])
accepted = nagent.runs.create("nora", input="Research retail-1.example", session_id="crm-4711", wait=False)
run = nagent.runs.wait(accepted["id"])
print(run["status"], run["output"])

for approval in nagent.approvals.iter():
    print(approval["id"], approval["action"])

TypeScript and JavaScript: npm install @nagent/sdk

// npm install @nagent/sdk
import { Nagent } from '@nagent/sdk';

const nagent = new Nagent({ apiKey: process.env.NAGENT_API_KEY! });
const accepted = await nagent.runs.create('nora', { input: 'Research retail-1.example', sessionId: 'crm-4711' }, { async: true });
const run = await nagent.runs.wait(accepted.id!);
if (run.status === 'waiting_for_approval') console.log('Waiting on', run.waitingOn);
else console.log(run.output);

for await (const approval of nagent.approvals.all()) console.log(approval.id, approval.action);