← All guides

Guide · API

Automate Norixia with the API and webhooks

Create a scoped API key, set up agents, knowledge and routines with curl, then receive signed webhooks in Node.js and verify them safely. Examples inside.

Advanced 20 min Updated September 12, 2026

Most of what you do in the Norixia app, you can also do from code. The REST API lets you spin up agents, feed the knowledge base and ship routines from a script, and webhooks tell your own systems the moment a lead replies or a run finishes. This guide walks you through a complete, production-minded setup: a scoped key, a handful of curl calls, safe retries, and a webhook receiver that verifies every signature.

You will need a Norixia workspace, a project, and a terminal with curl and Node.js 18 or later.

What you are working with

A quick map before we start:

Item Value
Base URL https://app.norixia.com/api/v1
API version REST API v1, 189 operations
OpenAPI spec https://app.norixia.com/api/v1/openapi.json
Key prefix nrx_live_
Default rate limit 120 requests per minute
Idempotency Idempotency-Key header supported
Webhook events 30+ events, signed with Norixia-Signature

Tip: load the OpenAPI spec into your favorite client or code generator. It is the full reference for every operation, parameter and response, and it stays in sync with the API.

Step 1: Create a scoped API key

API keys live at the workspace level, next to your team, mailboxes, suppressions and webhooks. Open your workspace settings, go to API keys, and create a new one.

You will make three choices:

  • Scopes. Around 35 scopes are available, grouped into presets: Full, Read & write, Read only, and Custom. Pick the narrowest preset that does the job. A reporting script only needs Read only. A provisioning script that creates agents and routines needs write access to those resources, nothing more.
  • Expiry. Never, 30, 90 or 365 days. For anything running in CI or on a shared server, choose an expiry and put the rotation date in your calendar.
  • Name. Name keys after where they run (“ci-provisioning”, “warehouse-sync”). When you need to revoke one, you will know exactly which.

The key starts with nrx_live_. Store it in a secret manager or an environment variable, never in your repository.

export NORIXIA_API_KEY="nrx_live_..."
export NORIXIA_API="https://app.norixia.com/api/v1"
export PROJECT_ID="your-project-id"

The examples below send the key as a bearer token. Check the OpenAPI spec for the authentication scheme if your client generates requests for you.

Tip: create one key per integration rather than one key for everything. Scopes stay tight, and revoking a leaked key never breaks unrelated jobs.

Step 2: Create an agent

Agents are the members of your AI revenue team. Each one has a role, a brief, a model, an autonomy level and a mailbox. Here is how to create a client prospecting agent that also acts as the project’s manager:

curl -X POST "$NORIXIA_API/projects/$PROJECT_ID/agents" \
  -H "Authorization: Bearer $NORIXIA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: create-agent-lea-2026-09-12" \
  -d '{
    "name": "Léa",
    "role_type": "client_prospecting",
    "is_manager": true,
    "brief": "Find heads of growth at B2B SaaS companies in France and book intro calls.",
    "llm_provider": "anthropic",
    "llm_model": "claude-sonnet-5-5",
    "autonomy": "validation",
    "daily_email_limit": 20,
    "mailbox_id": 1
  }'

A few notes on these fields:

  • role_type sets the agent’s job and the KPIs it reports on. client_prospecting tracks leads sourced, emails sent, open rate, reply rate, positive replies and meetings.
  • is_manager: true makes this agent the one that picks up #team messages without an @mention and dispatches work to the others.
  • autonomy: "validation" means every email waits in the Approvals queue for you. Start here. Move to autopilot once you trust the drafts.
  • daily_email_limit caps sends for this agent. The default is 30, and mailbox guardrails still apply on top.

Keep the id from the response: you will need it for the routine.

Step 3: Add knowledge

Agents write better emails when they know your business. The knowledge base has nine sections, from Company and Offers to Objections, FAQ and Competitors, and every agent in the project reads from it. You can push items from your own docs or CMS:

curl -X POST "$NORIXIA_API/projects/$PROJECT_ID/knowledge" \
  -H "Authorization: Bearer $NORIXIA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: kb-faq-data-residency-v1" \
  -d '{
    "kind": "faq",
    "title": "Where is customer data stored?",
    "content": "All customer data is stored in the EU. We can share our DPA on request."
  }'

This is a good candidate for automation: when your product team updates the public FAQ or pricing page, a small sync job can keep the knowledge base current, so agents never quote last quarter’s answer.

Step 4: Ship a routine from a template

Routines are the visual workflows that run your team on autopilot or on a schedule. The fastest way to create one by API is from a template. This call creates a Daily prospecting routine: every weekday, find 10 new qualified leads, send a personal first email, then follow up after 3 business days without a reply.

curl -X POST "$NORIXIA_API/projects/$PROJECT_ID/routines" \
  -H "Authorization: Bearer $NORIXIA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: routine-daily-prospecting-lea" \
  -d '{
    "template": "daily_prospecting",
    "agent_id": 1,
    "triggers": [
      { "type": "schedule", "frequency": "weekdays", "time": "09:00" }
    ]
  }'

A new routine is a draft. It goes live in three explicit steps, which mirror Save draft, Test and Publish in the builder:

ROUTINE_ID=42

# 1. Check the routine is valid (nodes, branches, settings)
curl -X POST "$NORIXIA_API/routines/$ROUTINE_ID/validate" \
  -H "Authorization: Bearer $NORIXIA_API_KEY"

# 2. Publish the current draft as the live version
curl -X POST "$NORIXIA_API/routines/$ROUTINE_ID/publish" \
  -H "Authorization: Bearer $NORIXIA_API_KEY"

# 3. Turn it on so its triggers start firing
curl -X POST "$NORIXIA_API/routines/$ROUTINE_ID/activate" \
  -H "Authorization: Bearer $NORIXIA_API_KEY"

Stop your script if validate reports a problem. Publishing a broken routine is never what you want, and the error is much easier to read before anything runs.

Tip: the routine still respects everything you set in the app. With the agent in validation mode, the routine drafts emails and they wait in Approvals. A step can also force “Always ask for approval” even on autopilot.

Idempotency keys: retry without duplicates

Networks fail at the worst moment. If a POST times out, you do not know whether Norixia received it. Retrying blindly can create two agents or two routines.

The fix is the Idempotency-Key header. Send a unique key with each logical operation, and reuse the same key when you retry that operation. Good practice:

  • Derive the key from what you are doing, not from the clock: create-agent-lea-<project> beats a random value generated on every attempt.
  • Use a fresh key for a genuinely new operation. Reusing a key for a different body is a bug in your script.
  • Add it to every POST that creates something. It costs nothing and saves a cleanup later.

Rate limits

API keys get 120 requests per minute by default. That is plenty for provisioning and syncs, but a bulk loop can hit it. A few habits keep you well inside:

  • Pace bulk jobs with a small delay between calls rather than firing them all at once.
  • When the API tells you to slow down, back off and retry with growing waits, and combine retries with idempotency keys.
  • For large lead lists, prefer CSV import (up to 5,000 rows synchronously, 50,000 asynchronously) over thousands of single calls.
  • Use webhooks instead of polling. Asking “did anyone reply?” every few seconds burns your budget for nothing.

Step 5: Subscribe to webhooks

Webhooks push events to your server as they happen. Add an endpoint in your workspace settings under Webhooks, pick the events you care about, and copy the signing secret.

Some of the 30+ events you can subscribe to:

  • lead.created, lead.status_changed
  • email.sent, email.replied, email.bounced
  • approval.requested, approval.approved
  • routine_run.finished
  • email_thread.taken_over
  • project.setup_completed

Every delivery has the same envelope:

{
  "id": "evt_...",
  "type": "email.replied",
  "created_at": "2026-09-12T09:14:03Z",
  "workspace": { "id": 7, "slug": "acme" },
  "data": { }
}

Common uses: post approval.requested to your team chat so drafts never sit for hours, push lead.status_changed into your data warehouse, or alert on email.bounced spikes.

Step 6: Verify Norixia-Signature in Node.js

Anyone can send a POST to a public URL. Before trusting a webhook, verify it came from Norixia. Each request carries a header like this:

Norixia-Signature: t=1789204443,v1=5f2b...e91c
  • t is the Unix timestamp of the signing.
  • v1 is a hex HMAC-SHA256 of the string "<t>.<raw body>", keyed with your endpoint secret.

Two rules matter. Sign the raw body exactly as received, not a re-serialized JSON object. And reject timestamps older than 300 seconds, which blocks replayed requests. During secret rotation, Norixia sends dual signatures for 24 hours, so accept a match on any v1 value.

Here is a minimal Express receiver:

import crypto from "node:crypto";
import express from "express";

const SECRET = process.env.NORIXIA_WEBHOOK_SECRET;
const TOLERANCE_SECONDS = 300;

function verifySignature(header, rawBody, secret) {
  if (!header) return false;

  let timestamp = null;
  const signatures = [];
  for (const part of header.split(",")) {
    const [key, value] = part.trim().split("=");
    if (key === "t") timestamp = value;
    if (key === "v1") signatures.push(value);
  }
  if (!timestamp || signatures.length === 0) return false;

  const age = Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp));
  if (!Number.isFinite(age) || age > TOLERANCE_SECONDS) return false;

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");
  const expectedBuf = Buffer.from(expected, "hex");

  return signatures.some((sig) => {
    const sigBuf = Buffer.from(sig, "hex");
    return (
      sigBuf.length === expectedBuf.length &&
      crypto.timingSafeEqual(sigBuf, expectedBuf)
    );
  });
}

const app = express();

app.post(
  "/webhooks/norixia",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const rawBody = req.body.toString("utf8");
    if (!verifySignature(req.get("Norixia-Signature"), rawBody, SECRET)) {
      return res.status(400).send("invalid signature");
    }

    const event = JSON.parse(rawBody);
    res.status(200).send("ok"); // acknowledge fast

    queueMicrotask(() => handleEvent(event));
  }
);

async function handleEvent(event) {
  switch (event.type) {
    case "email.replied":
      // notify the account owner, update your CRM, etc.
      break;
    case "routine_run.finished":
      // record the run in your metrics
      break;
  }
}

app.listen(3000);

Points worth copying into your own code:

  • express.raw keeps the body as bytes. If a JSON parser runs first, the signature will not match.
  • timingSafeEqual avoids leaking information through comparison timing.
  • The handler answers 200 right away and does the real work afterward. In production, push the event to a queue.

Retries and duplicate deliveries

If your endpoint is down or returns an error, Norixia retries up to 8 attempts with backoff: 1 minute, 5 minutes, 15 minutes, 1 hour, 3 hours, 6 hours, then 12 hours. That covers a long outage, but it also means the same event can arrive more than once.

Make your handler idempotent:

  • Store each event id you have processed and skip ones you have already seen.
  • Return a 2xx only once the event is safely stored or queued.
  • Return a non-2xx on a temporary failure, so the retry schedule works for you.

Try it on your own workspace

You now have the full loop: provision agents, knowledge and routines from code, and react to what your AI revenue team does in real time. Create your Norixia workspace, generate a scoped key, and run the curl calls above against a test project.

For the complete reference, head to the developer docs. To see what routines can do once they are live, explore Routines. Key scopes, signed webhooks and roles are covered on the Security page, and the daily prospecting routine guide shows the same template built in the visual editor.