GentIDGentID
API Reference · v1

GentID Documentation

GentID is an open, federated identity protocol for AI agents. Organizations issue identities under their own domains from their own nodes; anyone verifies them with pure cryptography, with no central registry and no call to gentid.com required. The full RFC lives at gentid.com/spec, with published conformance test vectors.

These docs cover the protocol (the default path), self-hosting your own issuer node, the hosted API (a managed node, anchored to your domain), the SDK, and migration from registry-era v1.

Base URL

api.gentid.com/v1

Auth

API Key (Bearer)

Format

JSON

#Quick Start

Become an identity issuer for your own domain in three commands. Keys are generated locally and never transmitted.

bash
# 1. Anchor your domain (prints the DNS TXT record to publish)
npx @gentid/cli init --domain your-domain.com
npx @gentid/cli check-dns

# 2. Issue a delegation and an agent under it
gentid delegate create --name ops --scopes "booking:*" --ceiling USD,500,2000,10000
gentid agent issue --name rebooker-7 --under ops --scopes booking:rebook

# 3. Serve — now anyone on the internet can verify your agents
gentid start   # /.well-known/gentid.json, /v1/agents/{id}, /v1/revocations

Don't want to run a node? The hosted API gives you a managed node that is still anchored to your domain: one TXT record, no lock-in.

#Protocol

The protocol is the product. Everything below is defined normatively in the specification; this section is the practical tour. The reference verifier is @gentid/core: zero dependencies, zero network calls, and a published conformance suite any third party can implement against.

#Verifying requests

Agents attach two headers: GentID-Envelope (a signed, timestamped, nonced wrapper binding the request body) and GentID-Bundle(the certificate chain). Verification resolves the agent's domain anchors from DNS + .well-known, then checks the chain locally:

ts
import { gentidAuth } from '@gentid/auth/express';

app.use('/api', gentidAuth());          // one line, as always

app.post('/api/book', (req, res) => {
  const { id, domain, grants, assurance } = req.gentid;
  // id        → "gentic:agent:delta.com:ops:rebooker-7"
  // assurance → 1 = domain-verified (2 = org-verified, 3 = transaction-proven)
  // Verified against delta.com's own keys. gentid.com was never contacted.
  res.json({ ok: true, bookedBy: id });
});

Revocation freshness is class-based: pass gentidAuth({ operationClass: 'financial' }) for money-moving routes and stale or unknown revocation state becomes a hard deny, and there is deliberately no flag to bypass it.

#Identifiers & anchoring

Identities are domain-anchored and self-describing: gentic:agent:<domain>[:path…]:<name>. The path mirrors the delegation chain, so the identifier itself tells you who could have issued it. Trust roots are published in DNS (_gentid.<domain> TXT) and a signed /.well-known/gentid.json. Every identifier also projects to a did:web DID for interop.

text
gentic:agent:delta.com:ops:rebooker-7
  ⇄ did:web:delta.com:gentid:ops:rebooker-7

_gentid.delta.com.  IN TXT  "v=gentid1; k=ed25519; p=<base64url pubkey>"

#Mandates & HTTP 402

A mandate (gentid.mandate.v1) is a signed spending authorization issued through the same delegation chain, where limits and scopes can only narrow. Verification proves the mandate is authentic; whether funds exist and budgets have room is enforced by a regulated settlement institution the issuing org designates. Verified ≠ enforceable: GentID never holds balances.

ts
// Service side: charge for an action
res.status(402).set(paymentRequiredHeaders({
  amount: 12.50, currency: 'USD',
  payee: 'acme-travel.com',
  enforcersAccepted: ['atheries.com'],
}));

// Agent side: escrow with the enforcer, then retry — one helper
const resp = await fetchWith402(request, { mandate, enforcers, reference });

#Authentication

All authenticated endpoints require an API key. Generate one from the API Keys page in your dashboard. Pass it as a Bearer token in the Authorization header.

http
Authorization: Bearer gid_live_xxxxxxxxxxxx
Never expose your API key in client-side code. Store it as an environment variable on your server.

Keys are prefixed with gid_live_ for production and gid_test_ for sandbox environments.

#Agents

An agent is a named, cryptographic identity. When created, it receives a unique, self-describing id of the form agent:<issuer-host>:<org>.<name> (see Agent id format), a public key stored on GentID servers, and a private key returned once, so store it securely. Agents created before this format existed keep their original gentic:agent: id, and both are accepted everywhere an agent id is expected.

#Create an agent

POSThttps://api.gentid.com/v1/agents

Creates a new agent identity and generates an Ed25519 keypair. The privateKey is returned only once. It is not stored and cannot be recovered.

Request body
namerequired

string

Human-readable name for the agent. Max 128 chars.

typescript
const res = await fetch("https://api.gentid.com/v1/agents", {
  method: "POST",
  headers: {
    "Authorization": "Bearer gid_live_xxxxxxxxxxxx",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ name: "payments-agent" }),
});

const agent = await res.json();
// {
//   id: "gentic:agent:a3f9d2c1e8b4",
//   name: "payments-agent",
//   owner: "acme-corp",
//   publicKey: "MCowBwYDK2VdAxEA...",
//   privateKey: "MC4CAQAwBwYDK2Vd...",  ← shown once only
//   status: "active",
//   createdAt: "2025-01-15T10:30:00.000Z"
// }

#List agents

GEThttps://api.gentid.com/v1/agents

Returns a paginated list of agents belonging to your organization.

Query parameters
limit

integer

Number of results to return. Default 50, max 100.

offset

integer

Offset for pagination. Default 0.

typescript
const res = await fetch("https://api.gentid.com/v1/agents?limit=20&offset=0", {
  headers: { "Authorization": "Bearer gid_live_xxxxxxxxxxxx" },
});
const { agents, total } = await res.json();

#Get an agent

GEThttps://api.gentid.com/v1/agents/:id
bash
curl https://api.gentid.com/v1/agents/gentic:agent:a3f9d2c1e8b4 \
  -H "Authorization: Bearer gid_live_xxxxxxxxxxxx"

#Revoke & suspend

Agents can be suspended (temporarily disabled, re-activatable) or revoked (permanently invalidated). Revocation cannot be undone.

Suspend

POST /agents/:id/suspend

Reactivate

POST /agents/:id/reactivate

Revoke

POST /agents/:id/revoke

bash
# Suspend an agent
curl -X POST https://api.gentid.com/v1/agents/gentic:agent:a3f9d2/suspend \
  -H "Authorization: Bearer gid_live_xxxxxxxxxxxx"

# Revoke permanently
curl -X POST https://api.gentid.com/v1/agents/gentic:agent:a3f9d2/revoke \
  -H "Authorization: Bearer gid_live_xxxxxxxxxxxx"

#Signatures

Signatures are the core of GentID. Your agent signs messages locally with its Ed25519 private key, then you log the signature with GentID so it can be independently audited and verified.

#Log a signature

POSThttps://api.gentid.com/v1/signatures
Request body
agentIdrequired

string

The agent's full GentID identifier.

messagerequired

string

The plaintext or JSON payload that was signed.

signaturerequired

string

The Ed25519 signature encoded as base64.

typescript
// Sign locally with the agent's private key
import { sign } from "@noble/ed25519";
import { sha256 } from "@noble/hashes/sha256";

const message = "approve-payment-12345";
const msgHash = sha256(Buffer.from(message));
const sig = await sign(msgHash, privateKeyBytes);
const signature = Buffer.from(sig).toString("base64");

// Log to GentID
await fetch("https://api.gentid.com/v1/signatures", {
  method: "POST",
  headers: {
    "Authorization": "Bearer gid_live_xxxxxxxxxxxx",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    agentId: "gentic:agent:a3f9d2",
    message,
    signature,
  }),
});

#Verify a signature

POSThttps://api.gentid.com/v1/signatures/verify

Verifies a signature against the agent's stored public key. Returns valid: true or valid: false. No authentication required: anyone can verify.

bash
curl -X POST https://api.gentid.com/v1/signatures/verify \
  -H "Content-Type: application/json" \
  -d '{
    "agentId": "gentic:agent:a3f9d2",
    "message": "approve-payment-12345",
    "signature": "base64sig..."
  }'

# → { "valid": true, "agentId": "gentic:agent:a3f9d2", "status": "active" }

#List signatures

GEThttps://api.gentid.com/v1/signatures/:agentId
bash
curl "https://api.gentid.com/v1/signatures/gentic:agent:a3f9d2?limit=50" \
  -H "Authorization: Bearer gid_live_xxxxxxxxxxxx"

#Verification

GentID provides a public verification endpoint that anyone can use to check an agent's identity - no API key required. This is the foundation of interoperability.

#Public agent lookup

GEThttps://api.gentid.com/v1/verification/lookup/:agentId

Returns the agent's public identity: name, public key, status, owner org, and issuance date. No authentication required. Use this to verify an agent before trusting it.

bash
curl https://api.gentid.com/v1/verification/lookup/gentic:agent:a3f9d2

# Response:
# {
#   "id": "gentic:agent:a3f9d2c1e8b4",
#   "name": "payments-agent",
#   "status": "active",
#   "publicKey": "MCowBwYDK2VdAxEA...",
#   "algorithm": "Ed25519",
#   "owner": "acme-corp",
#   "issuedAt": "2025-01-15T10:30:00.000Z"
# }

#Request verification

POSThttps://api.gentid.com/v1/verification

Request a formal verification for an agent. Supports domain, email, and manual verification types.

bash
curl -X POST https://api.gentid.com/v1/verification \
  -H "Authorization: Bearer gid_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"agentId":"gentic:agent:a3f9d2","type":"domain"}'

#Discovery & Federation

GentID is a protocol, not just this hosted API. Any organization can run its own GentID-compatible server, and agents issued by different servers can still verify each other, with no shared database or central registry required.

#Agent id format

New agents get a self-describing id of the form agent:<issuer-host>:<org-slug>.<agent-slug>, for example agent:api.gentid.com:acme-corp.payments-bot, or, on a self-hosted instance, agent:identity.apple.com:apple.travel-assistant. The issuer host is derivable from the id alone, which is what makes discovery possible without looking anything up first. Agents created before this format existed keep their original gentic:agent:<hex> id indefinitely, and both formats are accepted everywhere an agent id is expected.

#Discovery document

Every GentID-compatible server publishes a discovery document at its domain root, modeled on OIDC discovery. No authentication required.

GEThttps://api.gentid.com/v1/.well-known/gentid-configuration
bash
curl https://api.gentid.com/.well-known/gentid-configuration

# Response:
# {
#   "issuer": "https://api.gentid.com",
#   "jwks_uri": "https://api.gentid.com/.well-known/jwks.json",
#   "verification_endpoint": "https://api.gentid.com/api/v1/verification/lookup",
#   "token_verification_endpoint": "https://api.gentid.com/api/v1/verification/verify-token",
#   "protocol_version": "1.0"
# }

#JWKS & token verification

Permission tokens are signed with the issuing instance's Ed25519 key (EdDSA), published at jwks_uri. A verifier can check a token's signature directly against the issuer's public key, with no callback to the issuing server needed once the (cacheable) discovery document and JWKS are fetched.

GEThttps://api.gentid.com/v1/.well-known/jwks.json
bash
curl https://api.gentid.com/.well-known/jwks.json

# Response:
# { "keys": [{ "kty": "OKP", "crv": "Ed25519", "x": "...", "kid": "...", "alg": "EdDSA", "use": "sig" }] }

@gentid/auth and @gentid/sdkdo this resolution automatically: when you don't pass an explicit apiUrl, they resolve the issuer from the token's agent id and verify locally. Legacy (gentic:agent:) tokens, or any resolution failure, fall back to calling the verify-token endpoint directly, exactly as in prior versions.

#Errors & codes

GentID uses standard HTTP status codes. Error responses always include a machine-readable code field.

json
{
  "message": "Agent not found",
  "code": "AGENT_NOT_FOUND"
}
StatusCodeMeaning
400BAD_REQUESTInvalid request body or parameters
401UNAUTHORIZEDMissing or invalid API key
403FORBIDDENAction not allowed for this key
403NOT_VERIFIEDAccount not verified. Complete verification first
404AGENT_NOT_FOUNDAgent does not exist
409CONFLICTResource already exists
429RATE_LIMITEDToo many requests. Back off and retry
500INTERNAL_ERRORSomething went wrong on our side

#Rate limits

The API is rate-limited per API key. Limits vary by plan:

PlanRequests / minVerifications / month
Free601,000
Pro300100,000
EnterpriseUnlimited

When rate-limited, the response includes Retry-After and X-RateLimit-Reset headers.

#TypeScript SDK

Install the official GentID SDK for Node.js (18+):

bash
npm install @gentid/sdk
typescript
import { GentIDClient } from "@gentid/sdk";

const gentid = new GentIDClient({
  apiKey: process.env.GENTID_API_KEY!, // gid_live_...
  // baseUrl defaults to "https://api.gentid.com/v1"
});

// Create an agent identity
const agent = await gentid.createAgent({ name: "payments-bot", owner: "acme-corp" });
// ⚠ agent.privateKey is returned ONCE. Store it in your secrets vault

// Sign any action
const { signature } = await gentid.signMessage(agent.id, "approve-tx-99");

// Verify from anywhere. No API key needed
const { valid } = await gentid.verifySignature(agent.id, "approve-tx-99", signature);
console.log(valid); // true

// Public identity lookup. No API key needed
const identity = await gentid.lookupAgent(agent.id);
// { id, name, status, publicKey, algorithm, owner, issuedAt }

#Webhooks

GentID sends signed HTTP POST requests to your registered endpoints when key events occur. Register and manage endpoints from the Webhooks page in your dashboard.

#Events

EventFired when
agent.createdA new agent identity is registered
agent.updatedAn agent is suspended, reactivated, or revoked
signature.loggedAn agent signs a message
verification.updatedA verification request is approved or rejected

#Payload format

json
{
  "event": "agent.created",
  "timestamp": "2025-01-15T10:30:00.000Z",
  "data": {
    "id": "gentic:agent:a3f9d2c1e8b4",
    "name": "payments-bot",
    "owner": "acme-corp",
    "status": "active"
  }
}

#Verifying signatures

Every request includes an X-GentID-Signature header. Verify it using the signing secret shown when you add an endpoint:

typescript
import crypto from "crypto";

function verifyWebhook(secret: string, rawBody: string, sigHeader: string): boolean {
  const [tPart, v1Part] = sigHeader.split(",");
  const timestamp = tPart.replace("t=", "");
  const expected  = v1Part.replace("v1=", "");
  const sig = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}

// Express example
app.post("/webhooks/gentid", express.raw({ type: "application/json" }), (req, res) => {
  const valid = verifyWebhook(
    process.env.GENTID_WEBHOOK_SECRET!,
    req.body.toString(),
    req.headers["x-gentid-signature"] as string,
  );
  if (!valid) return res.status(401).send("Invalid signature");

  const { event, data } = JSON.parse(req.body.toString());
  // handle event...
  res.status(200).send("OK");
});

#Agent Gateway

@gentid/auth is a drop-in middleware package that lets any Node.js, Next.js, or Cloudflare Worker site accept AI agents as a new class of authenticated user, with no rebuilding required.

bash
npm install @gentid/auth

Your server reads the token from Authorization: GentID <token> or X-GentID-Token, calls the GentID verify endpoint, and populates req.agent with the decoded identity and permissions.

#Express / Node.js

typescript
import { gentidAuth } from '@gentid/auth/express';

app.use('/api', gentidAuth());

app.post('/api/book', (req, res) => {
  const agent = req.agent!;
  // agent.agentId, agent.owner, agent.permissions…
  res.json({ ok: true, bookedBy: agent.agentName });
});

#Next.js

Wrap individual route handlers:

typescript
// app/api/book/route.ts
import { withGentidAuth } from '@gentid/auth/next';

export const POST = withGentidAuth(async (req, agent) => {
  return Response.json({ bookedBy: agent.agentName, perms: agent.permissions });
});

Or protect entire route groups via middleware.ts:

typescript
// middleware.ts
import { createGentidMiddleware } from '@gentid/auth/next';
export default createGentidMiddleware({ required: true });
export const config = { matcher: '/api/agent/:path*' };

// Then in a route handler:
import { getAgentFromHeaders } from '@gentid/auth/next';
const agent = getAgentFromHeaders(req.headers); // AgentContext | null

#Cloudflare Worker

typescript
import { withGentidAuth } from '@gentid/auth/cloudflare';

export default {
  fetch: withGentidAuth(async (request, agent, env, ctx) => {
    return Response.json({ agent: agent.agentName, perms: agent.permissions });
  }),
};

Full integration guide with token flow, badge embed, and trust badge is available in the Integrations page of your dashboard.

#Self-hosting

GentID is open source under the MIT license. You can run the entire stack on your own infrastructure. The managed cloud at api.gentid.com is optional, and every self-hosted instance still verifies agents from any other GentID-compatible server via discovery.

There are three pieces, each its own repository, and you choose which ones to host:

  • Web console: the Next.js dashboard and admin UI (this app).
  • API server: issues identities, signs and verifies tokens, serves the /api/v1 surface.
  • @gentid/auth SDK: drop-in middleware your own apps embed; point it at your API.

#Web console

The console is a standard Next.js app. It reaches your API server through two server-side settings. It never exposes the API base to the browser. All portal calls go through same-origin /api/* route handlers that proxy to API_BASE_URL.

API_BASE_URLrequired

string

Base URL of your GentID API, including the /api/v1 path. Defaults to http://localhost:1100/api/v1.

JWT_SECRETrequired

string

Secret used to validate the gid_session cookie. Must be identical to the secret your API server signs session tokens with, or every login will be rejected.

bash
# .env.local
API_BASE_URL=https://api.your-domain.com/api/v1
JWT_SECRET=your-shared-secret-at-least-32-characters
bash
git clone https://github.com/GentricAI/gentid-sdk.git
cd gentid-sdk
npm install
npm run build
npm start            # serves the console on http://localhost:3000

#Point the SDK at your deployment

@gentid/auth defaults to https://api.gentid.com only as a last resort. Pass apiUrl to always target your own server explicitly, or leave it unset and let the middleware resolve the correct issuer automatically from each token's agent id via discovery, verifying locally against that issuer's published JWKS. The same behavior applies to the Next.js and Cloudflare entry points.

typescript
import { gentidAuth } from '@gentid/auth/express';

// Explicit — always verifies against your own server:
app.use('/api', gentidAuth({
  apiUrl: 'https://api.your-domain.com',
}));

// Or omit apiUrl and let it resolve per-token via discovery:
app.use('/api', gentidAuth());

#API server

The API server is the source of truth. It issues Ed25519 keypairs, signs permission tokens, and serves every endpoint documented above under /api/v1, plus the discovery documents at /.well-known. Two requirements when you run your own:

  • It must expose the /api/v1 surface (auth, portal, signatures, verification) on the host your console's API_BASE_URL points to.
  • It must sign session tokens with the same JWT_SECRET the console uses, so the console's middleware can validate logins.

The server lives at github.com/010101G/gentid-express, with the full environment variable reference, a Dockerfile, and a docker compose up path in its own README. Run npm run generate-signing-key once to create the Ed25519 key that signs permission tokens and gets published at /.well-known/jwks.json, then start the server and point your console at it.

#Migration from v1

Registry-era identities (gentic:agent:a3f9d2… hash IDs) and the REST verify endpoint keep working for a 12-month deprecation window, backed by the legacy node anchored at legacy.gentid.com. Nothing breaks on protocol day one.

To move an agent to a domain-anchored identity:

bash
gentid migrate --from gentic:agent:a3f9d2e8b1c4 \
  --to ops:rebooker-7   # new id under YOUR verified domain

# → re-issues the certificate through your delegation chain and emits a
#   signed alias record (gentid.alias.v1) linking old id → new id, so
#   historical signatures and audit logs remain verifiable forever.

The v1 SDK surface (@gentid/sdk) is preserved as a thin wrapper over the protocol packages, with deprecation warnings pointing at the v2 equivalents. Verifiers presented with a legacy signature can resolve the alias record and report the new identity.

Ready to build?

Create your account and issue your first agent identity in minutes.

Get started free →