For builders

The identity primitive
for any AI surface.

One call. Your agent meets the person instead of a stranger. It carries their word, their rhythm, and how to meet them, always with their consent. The pressure points never leave the person's own read. Patent pending. Yours to import.

Claude SDK Vercel AI SDK LangChain MCP REST

The four modes that matter

ModeWhat it returnsWhen to use it
system_promptClaude-ready system addendum tuned to the user.Prepend at every conversation start.
identity_tokenES256 JWT carrying word, rhythm, force. Offline-verifiable, and revocable by the person.Hand-off between services, minted from the person's own signed-in session. Verify against /.well-known/jwks.json or with /api/identity-verify. For a first-party app that needs its own consent screen, use Sign in with Pupul below.
scrubVoice-clean text. No em-dashes, no AI tells.Run on every model output before showing the user.
draftRewrite text in the user's compressed voice.When the user asks the agent to write something on their behalf.

REST. The thinnest path.

If your agent can do an HTTP request, you can use Noctara.

curl -X POST https://noctaracorp.com/api/mcp/system_prompt \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","style":"full"}'

Returns:

{
  "ok": true,
  "system_prompt": "# Identity context (Noctara behavioral read).\n\nYou are speaking to one person...",
  "has_read": true
}

Read anyone, statelessly.

No account needed. Send the person's answers, with their consent, and get the read back. This is the embed path. one call, nothing stored.

curl -X POST https://noctaracorp.com/api/read \
  -H "Content-Type: application/json" \
  -H "X-Read-Key: YOUR_KEY" \
  -d '{"consent": true, "answers": ["...", "..."]}'

Returns:

{
  "ok": true,
  "read": { "word": "ARCHITECT", "force": "Independence", "rhythm": "Builder",
            "posture": "...", "mirror": "...", "half_truth": "...",
            "departure": "...", "risk_line": "...", "lever": "..." }
}

This one endpoint is the exception to self-serve: /api/read runs on a manually issued partner key, so email hello@noctaracorp.com for one. The person being read must consent in your app first. If what you want is a read of a person who already has one, take the self-serve OAuth rail below instead. no email, no key request.

The registry. Four claims, and the evidence for each.

When you do not want the person's contents, only whether there is a person there. This answers four questions from the accruing record and shows the working behind every one.

Run it right now, no key, no signup. This returns the exact response shape from the same code paths, over a fixture record that says so in its first field:

curl "https://noctaracorp.com/api/v1/registry-demo"

Real records need a key and the person's recorded consent to your org:

To test the authed happy-path without a real consented client, query the reserved sandbox tag with your key. It returns a labeled demo:true fixture with visible:true and full claims, so you can wire and parse a positive response before a real person consents:

curl -H "Authorization: Bearer noctara_lk_YOURKEY" "https://noctaracorp.com/api/v1/registry?tag=sandbox"
curl "https://noctaracorp.com/api/v1/registry?tag=CLIENT_TAG" \
  -H "Authorization: Bearer noctara_lk_..."

Returns:

{
  "ok": true, "tag": "flmd7", "visible": true,
  "claims": {
    "is_human": true, "is_continuous": true,
    "has_deviated": false, "stands_behind": true
  },
  "provenance": {
    "beats": 2, "distinct_weeks": 2, "sources": ["tap_in","palace"],
    "first_beat": "...", "last_beat": "...", "drift_observations": 0,
    "thresholds": { "is_human": ">= 2 beats, or a measured live typing session",
                    "is_continuous": ">= 2 distinct weeks" },
    "liveness": { "basis": "keystroke_liveness_v1", "score": 0.81,
                  "calibration": "synthetic corpora; conservative threshold; ..." }
  },
  "consent": { "granted": true, "scope": "coach:17", "granted_at": "...",
               "revocable_at": "https://noctaracorp.com/consent" }
}

A claim we cannot evidence is null, never false. This is the most important line on this page. false means we looked and it is not so. null means we do not know. If our ledger read fails you get a 503 and no claims at all, with a note saying so, because answering is_human: false off a broken query would be us telling you a real person is not one. Expect nulls, and treat them as "insufficient record", never as a negative.

is_human is a presence signal, not a biometric verdict. It says this record holds beats of a kind a present person produces, across more than one sitting, above a threshold we publish in the response so you can judge it yourself. We will not dress that up as identity proof.

The liveness provenance, when present, means the record carries a measured typing session and our scorer read it as live. The scorer's verdicts are live or null, never negative: its calibration basis is stated inside the response, and until real-stream calibration earns published error rates we will not tell you a person is not one. Liveness can only ever add evidence to is_human; a record without it falls back to the beat threshold exactly as before.

No grant, no claims. If the person has not granted your org you get visible: false and nothing else. Not a reduced set, not nulls with hints. They can revoke at any time and this endpoint stops answering the moment they do.

What it costs. Free through 1,000 active records a month, then five cents per active record per month, no minimum and no contract. An active record is one distinct person your key asked about at least once that month, counted once no matter how many times you ask, so re-checking on every session costs nothing extra.

Check the meter yourself. GET /api/v1/usage with the same key returns your active-record count for the month, the free ceiling, the rate, and what we think you owe, along with the working behind it. We count only calls that actually returned claims: a call we refused for want of consent, or could not complete, is logged but never billed. If we cannot read your usage we return 503 and withhold the number rather than showing you a zero we did not verify.

curl -H "Authorization: Bearer noctara_lk_..." https://noctaracorp.com/api/v1/usage

{ "active_records": 2, "free_included": 1000, "billable_records": 0,
  "rate_usd_per_active_record": 0.05, "estimated_usd": 0,
  "basis": { "unit": "one distinct person you asked about at least once this month,
                      counted once no matter how many times you ask",
             "privacy": "we count salted one-way tokens, not identities" } }

What it cannot answer yet. Measured 2026-08-02: 39 people across our whole base carry the two or more beats is_human requires, and four of five real consented records answered all-nulls. The endpoint is correct and the records under it are thin. The version of this worth paying for is the one where your own users have accrued something worth grading, which takes weeks of them showing up.

What you may not use it for.

Every registry response carries a permitted_use block, and the boundary is not optional. This is a continuity signal, not a consumer report. You may use it to check that the same continuous, consenting identity stands behind an account, a message, or an action. You may not use it for any FCRA-covered decision (credit, employment or hiring, housing, insurance), for background checks, character or risk scoring, or for any purpose the person did not consent to. Noctara is not a consumer reporting agency and these claims are not a consumer report. The person can revoke at any time, and the endpoint stops answering the moment they do. Full terms at /terms.

MCP. For Claude Desktop, Cursor, Cline, OpenClaw.

One block in your MCP config and the eighteen Noctara modes are tools your model can call.

{
  "mcpServers": {
    "noctara": {
      "command": "npx",
      "args": ["-y", "noctara-mcp-server"],
      "env": {
        "NOCTARA_API_BASE": "https://noctaracorp.com/api",
        "NOCTARA_USER_EMAIL": "user@example.com",
        "NOCTARA_CLIENT_ID": "your-app"
      }
    }
  }
}

Full Claude install guide covers Desktop, Code, Cursor, Cline, and OpenClaw with the exact paths for each.

Official SDK.

If you want a typed wrapper instead of raw fetch, Python has one:

pip install noctara
from noctara import Noctara

n = Noctara
sp = n.system_prompt(email="user@example.com")

The SDK is MIT-licensed. The patent does not encumber the SDK, only the underlying behavioral primitive.

There is no npm package yet. In TypeScript, call the endpoint directly; it is one fetch, and the Claude example below shows the whole shape.

Claude SDK (TypeScript). The cleanest example.

import Anthropic from "@anthropic-ai/sdk";

async function noctaraSystemPrompt(email: string) {
  const r = await fetch("https://noctaracorp.com/api/mcp/system_prompt", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ email, style: "full" }),
  });
  const j = await r.json();
  return j.system_prompt as string;
}

const claude = new Anthropic;
const identity = await noctaraSystemPrompt("user@example.com");

const msg = await claude.messages.create({
  model: "claude-opus-4-7",
  max_tokens: 1024,
  system: identity + "\n\n# Your task.\nYou are a helpful agent for this person.",
  messages: [{ role: "user", content: "What should I do tomorrow?" }],
});

Vercel AI SDK. Same shape.

import { generateText } from "ai";
import { anthropic } from "@ai-sdk/anthropic";

const identity = await noctaraSystemPrompt(userEmail);

const { text } = await generateText({
  model: anthropic("claude-opus-4-7"),
  system: identity,
  prompt: userMessage,
});

LangChain (Python).

import httpx
from langchain_anthropic import ChatAnthropic
from langchain_core.messages import SystemMessage, HumanMessage

def noctara_system_prompt(email: str) -> str:
    r = httpx.post("https://noctaracorp.com/api/mcp/system_prompt",
        json={"email": email, "style": "full"})
    return r.json["system_prompt"]

llm = ChatAnthropic(model="claude-opus-4-7")
identity = noctara_system_prompt("user@example.com")
resp = llm.invoke([
    SystemMessage(content=identity),
    HumanMessage(content="What should I do tomorrow?"),
])

Sign in with Pupul. The production rail, ES256.

If the person is your user and the token has to leave your walls, use the OAuth rail rather than the MCP token below. It carries its own consent screen and its own client registration, so you never have to hold the person's Noctara session. Access tokens from /api/oauth/token are signed ES256 (P-256) by api/_lib/sign.js, and the public half is published at /.well-known/jwks.json. Any third party verifies a Noctara token offline, with the public key alone, never calling us and never holding a secret of ours. Discovery lives at /.well-known/oauth-authorization-server.

curl -X POST https://noctaracorp.com/api/oauth/register \
  -H "Content-Type: application/json" \
  -d '{"app_name":"My AI App","email":"you@yourapp.com",
       "redirect_uris":["https://yourapp.com/callback"]}'
# -> { "ok": true, "client_id": "app_...", "client_secret": "psk_..." }

# then ordinary OAuth: /api/oauth/authorize -> /api/oauth/token -> /api/pupul-context

Self-serve, no sales call, no human in the loop. First 1,000 reads a month are free per app. Full integration docs: pupulcorp.com/identity/docs.

Identity tokens. ES256, minted from the person's own session.

Which crypto is which, plainly. identity_token is an ES256 JWT, signed with the same issuer key as the OAuth rail above, so anyone verifies it offline against /.well-known/jwks.json with the public key alone. There is no shared signing secret. Every token carries a jti, is recorded as a grant on the person's own consent page at /consent, and stops verifying the moment they close that door. /api/identity-verify still accepts it if you would rather we check, and that path checks the revocation list too.

The mint requires the person's own signed-in session. Your server cannot mint a token for a user it merely names, because a token that asserts someone's identity can only be created by that someone. Send the session that person is already holding, as an Authorization: Bearer header, a session_token field in the body, or the same-origin noctara_token cookie. The email or mark you pass has to be the one that session belongs to. Anything else returns 401 unauthorized, including a call carrying only an email.

If your stack has more than one service, mint an identity_token once at the edge the person signed in at, and verify it everywhere else.

// service A: mint, from the session the person is already holding.
// A call with only an email and no session returns 401.
const r = await fetch("https://noctaracorp.com/api/mcp/identity_token", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${userSessionToken}`,
  },
  body: JSON.stringify({ email: "user@example.com", audience: "my-agent" }),
});
const { token, jti, expires_at } = await r.json();

// service B: verify. Offline against the published JWKS with any ES256 JWT
// library, or ask us, which also checks the jti against the revocation list.
const v = await fetch("https://noctaracorp.com/api/identity-verify", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ token }),
});
const { valid, claims } = await v.json();
// claims.word, claims.rhythm, claims.force, claims.sub, claims.jti

Voice scrub. The cheapest thing in the stack.

Every model output runs through this before it hits the user.

const r = await fetch("https://noctaracorp.com/api/mcp/scrub", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ text: modelOutput }),
});
const { text } = await r.json();

Strips em-dashes, en-dashes, smart quotes, ellipsis chars, and the most reliable AI tells. The cleanup is invisible until your user notices their agent stopped sounding like an LLM.

Open source. Patent licensed for non-commercial use.

The SDK and reference implementation are on GitHub at github.com/noctara/sdk (MIT). The behavioral identity primitive itself is covered by a US provisional patent application (Systems and Methods for Behavioral Biometric Identity Assessment and Authentication). Non-commercial use is free. Commercial use at scale: calkire@noctaracorp.com.

Versioning and the deprecation promise

The registry API is versioned in the path (/api/v1/...). The response shape is the contract: we add fields, we do not remove or rename them within a version. A breaking change means a new version number, and the old version keeps serving.

The promise: when a version is deprecated, we give a minimum of 90 days notice before it stops serving, announced here and by email to every registered key. You will never wake up to a silently changed contract.

Changelog

The pitch in one sentence

Every conversation with your AI starts cold. Yours doesn't have to.

The architecture compounds.
Claude install · OpenClaw install · The spec · Firm license · GitHub