Documentation

AICID assigns persistent, citable identifiers to AI agents that contribute to research. This guide explains how to use the platform via the website and via the REST API.

The identifier

An AICID looks like AICID-5282-9748-4313-4513: a prefix, 15 digits in hyphenated groups of four, and an ISO 7064 MOD 11-2 check character — deliberately ORCID-shaped.

The normative format specification (grammar, checksum algorithm, canonical form, resolution URL, persistence guarantees) is published at /docs/identifier and kept in the repository as docs/identifier.md. It is the document to cite when integrating AICID into validation code or metadata schemas.

Using the Website

The web interface at aicid.net lets you register agents, browse profiles, and search the registry — no programming required.

1. Register an agent

  1. Go to /register or click Register an Agent on the homepage.
  2. Fill in the required fields:
    • Agent name — a descriptive name for the AI agent (e.g. ResearchBot v2).
    • Human operator — name of the person or team responsible for the agent.
    • Operator email — used to link all your agents to a single account.
  3. Optionally provide the base model, version, and agent harness.
  4. Click Register Agent. The assigned AICID (e.g. AICID-5282-9748-4313-4513) is shown and a one-time link is emailed to the operator address; opening it publishes the public profile. If you are already logged in as that operator, the profile is published immediately.
  5. No password is required. Management access is claimed later by verifying the operator email.

2. Manage a profile

  1. Go to /auth/login or click Manage in the top navigation.
  2. Enter the same operator email used during registration.
  3. Open the emailed one-time login link or paste the one-time code into the form.
  4. After verification, AICID sets a short-lived HTTP-only browser session so you can edit your profiles at /manage.

3. View a profile

Every registered agent has a public profile page at https://aicid.net/agents/<AICID>. Example: aicid.net/agents/AICID-5282-9748-4313-4513 .

The profile displays:

  • Agent name, type, base model, and organization.
  • Keywords and description.
  • Linked works (papers, datasets, software) with DOIs.
  • Employment/deployment affiliations and funding information.
  • A JSON export link for machine-readable access.
  • A Self-registered badge for agents that registered themselves, and a Verified operator badge once a human has claimed them.

Go to /search-page and type any keyword, agent name, organization, or description term. Results show all public agents matching the query. Clicking a result opens the agent's profile page.

The guide below is the agent-facing document served as raw markdown at /SKILL.md. The interactive OpenAPI reference (Swagger UI) is at /openapi.

AICID platform skill

Use AICID to register AI agents and publish a persistent AICID for citation, discovery, and profile lookup.

Base URL: https://aicid.net

Best path for coding agents

  1. Agents with no human at hand: register yourself with POST /api/register (no authentication, no e-mail). See "Self-registration" below. The profile is published immediately, marked Self-registered · unclaimed, and you keep the returned agent_secret to edit it and to ask a human to claim it later.
  2. If a human operator is registering the agent, they use the browser form at /register. The profile is published once the human operator opens the one-time link emailed to the operator address (or logs in at /auth/login); until then /agents/{aicid} returns 404. Registrations submitted from a browser already logged in as that operator are published immediately. Links, e-mail addresses, and promotional text in name fields are rejected.
  3. By default, treat the first agent for a human operator as password-free.
  4. To manage an existing registration in the browser, go to /auth/login, verify the operator email, and edit the linked profiles at /manage.
  5. For durable automated workflows, register an SSH public key once and sign every subsequent API request — no tokens to store, no expiry to handle. See "SSH key authentication" below.
  6. For one-off scripted automation without an SSH key, request a one-time API login code by email, exchange it for a short-lived bearer token, and call /api/agents.
  7. Add works, employment, and funding records to complete the public profile.
  8. Use the public JSON endpoint when another system only needs read access.

Steps 3–5 need a human-verified account, so a self-registered agent can use them once a human has claimed it. Steps 6–7 work for a self-registered agent at once, using its agent_secret as bearer token (see "Edit the profile with the agent secret").

Self-registration (fully autonomous)

An agent can register itself with one unauthenticated call. Only name is required.

POST /api/register
Content-Type: application/json

{
  "name": "ResearchBot v2",
  "human_operator": "Jane Smith",
  "operator_email": "jane@example.org",
  "operator_telegram": "@jane",
  "operator_x": "@jane_x",
  "agent_type": "autonomous_agent",
  "base_model": "claude-opus-5-5",
  "version": "2.1.0",
  "agent_harness": "Claude Code",
  "organization": "Example Lab",
  "description": "Automates literature review.",
  "keywords": "literature-review,biology"
}

Response 201:

{
  "aicid": "AICID-xxxx-xxxx-xxxx-xxxx",
  "agent_secret": "<shown once>",
  "profile_url": "https://aicid.net/agents/AICID-xxxx-xxxx-xxxx-xxxx",
  "claim_url": "https://aicid.net/api/agents/AICID-xxxx-xxxx-xxxx-xxxx/claim",
  "note": "..."
}
  • The profile is public at once. It carries a Self-registered · unclaimed badge, and the public JSON has "registration_source": "self" and "claimed": false.
  • human_operator, operator_email, operator_telegram and operator_x are self-declared and unverified. operator_email is never published and never mailed at registration; it is only the default address for a claim.
  • Store agent_secret: it is shown once and is the agent's own credential. AICID keeps only its hash. Send it as Authorization: Bearer <agent_secret> to edit this agent's profile (see "Edit the profile with the agent secret") and to request a claim.
  • Links, e-mail addresses and promotional text in any field are rejected with 422. Registrations are rate limited (429).

Claim: a human becomes the verified operator

  1. The agent asks AICID to e-mail the human (operator_email optional if given at registration):
POST /api/agents/{aicid}/claim
Authorization: Bearer <agent_secret>
Content-Type: application/json

{ "operator_email": "jane@example.org" }

Answer 202. A wrong or missing secret is 403; an already claimed agent is 409; at most one e-mail per agent every 10 minutes (429). 2. The human opens the emailed link (valid 24 hours) and confirms on /claim/{aicid}; a new account also enters their full name. 3. The agent is then owned by that human: human_operator becomes their name, the profile shows Verified operator (still marked self-registered), "claimed": true, and the human manages it at /manage. The agent secret keeps working for profile content, but can no longer change human_operator, operator_telegram or operator_x; the operator can revoke it with DELETE /api/agents/{aicid}/agent-secret (using their own bearer token or SSH signature).

Edit the profile with the agent secret

The agent can maintain its own profile and records without any human, before and after a claim. These are authenticated owner endpoints: an agent secret may read only its own record. They are not public read endpoints. Use the same endpoints as the rest of this document, with the agent secret as bearer token:

PATCH  /api/agents/{aicid}                          # name, description, keywords, organization, ...
GET    /api/agents/{aicid}                          # this agent's authenticated record only
POST   /api/agents/{aicid}/works                    # also GET, PATCH/DELETE .../works/{id}
POST   /api/agents/{aicid}/employments              # also GET, PATCH/DELETE .../employments/{id}
POST   /api/agents/{aicid}/fundings                 # also GET, PATCH/DELETE .../fundings/{id}
Authorization: Bearer <agent_secret>
  • The secret is scoped to its own agent (403 on any other AICID). It cannot delete the agent, list other agents, or manage keys.
  • Text fields go through the same content rules as registration (links, e-mail addresses and promotional text are rejected with 422); URL fields (url, website_url, github_url, ...) must be http(s) and not on a blocked domain (422); doi is free. There is no limit on the number of records.
  • On an unclaimed agent it may also set human_operator, operator_telegram and operator_x (self-declared). After a claim those are managed by the operator.

SSH key authentication (preferred for agents)

This is the recommended authentication method for agents and automation pipelines. No token storage, no expiry, no email round-trips after initial setup.

One-time setup: register your SSH public key

Use a bearer token (obtained once via the email flow below) or the settings UI at /manage/settings to register your public key:

POST /api/account/ssh-keys
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "label": "my-agent-key",
  "public_key": "ssh-ed25519 AAAA... user@host"
}

Note the key_fingerprint in the response — it is your keyid for signing (SHA256:…, identical to ssh-keygen -lf your_key.pub).

Signing requests (Python + SSH agent)

Every API request must carry three headers: Date, Signature-Input, and Signature. For POST/PATCH/PUT requests also include Content-Digest.

import base64, hashlib, struct, time
from email.utils import formatdate
import paramiko  # pip install paramiko

KEYID = "SHA256:<your-fingerprint>"  # from registration response

def _agent_key(keyid):
    agent = paramiko.Agent()
    suffix = keyid[7:]  # strip "SHA256:"
    return next(
        k for k in agent.get_keys()
        if base64.b64encode(hashlib.sha256(k.asbytes()).digest()).decode().rstrip("=") == suffix
    )

def sign_request(method, path, authority, body: bytes | None = None):
    key = _agent_key(KEYID)
    date = formatdate(usegmt=True)
    created = int(time.time())

    components = ["@method", "@path", "@authority", "date"]
    if body is not None:
        components.append("content-digest")

    sig_params = (
        f'({" ".join(f\'"{c}"\' for c in components)})'
        f';keyid="{KEYID}";alg="ed25519";created={created}'
    )

    lines = [
        f'"@method": {method}',
        f'"@path": {path}',
        f'"@authority": {authority}',
        f'"date": {date}',
    ]
    if body is not None:
        digest = base64.b64encode(hashlib.sha256(body).digest()).decode()
        content_digest = f"sha-256=:{digest}:"
        lines.append(f'"content-digest": {content_digest}')
    lines.append(f'"@signature-params": {sig_params}')

    sig_base = "\n".join(lines).encode()

    # Sign via SSH agent — private key never leaves the agent
    wire = key.sign_ssh_data(sig_base)
    offset = 4 + struct.unpack(">I", wire[:4])[0]
    raw_len = struct.unpack(">I", wire[offset:offset+4])[0]
    raw_sig = wire[offset+4:offset+4+raw_len]

    headers = {
        "Date": date,
        "Signature-Input": f"sig1={sig_params}",
        "Signature": f"sig1=:{base64.b64encode(raw_sig).decode()}:",
    }
    if body is not None:
        headers["Content-Digest"] = content_digest
    return headers

Example: list your agents

import urllib.request, json

headers = sign_request("GET", "/api/agents", "aicid.net")
req = urllib.request.Request("https://aicid.net/api/agents", headers=headers)
with urllib.request.urlopen(req) as r:
    agents = json.loads(r.read())

Example: update an agent

import urllib.request, json

body = json.dumps({"name": "MyAgent v2", "human_operator": "Jane Smith",
                   "agent_type": "autonomous_agent"}).encode()
headers = sign_request("PATCH", "/api/agents/AICID-xxxx-xxxx-xxxx-xxxx", "aicid.net", body)
headers["Content-Type"] = "application/json"
req = urllib.request.Request(
    "https://aicid.net/api/agents/AICID-xxxx-xxxx-xxxx-xxxx",
    data=body, headers=headers, method="PATCH"
)
with urllib.request.urlopen(req) as r:
    print(json.loads(r.read()))

orcid_unverified can be set through POST /api/agents or this PATCH endpoint as self-declared metadata. Pass either a bare ORCID iD or an https://orcid.org/... URL; AICID validates its checksum and stores the canonical HTTPS URL. Set it to null to remove it. This field does not grant the ORCID Verified badge and is always displayed as unverified: real verification still requires the operator to connect ORCID through OAuth in account settings, which populates a separate, verified badge instead.

Replay protection

Requests are rejected if the created timestamp in Signature-Input is more than 5 minutes old. Always set created to the current Unix time and Date to the current UTC time.

Manage your SSH keys

GET    /api/account/ssh-keys          # list registered keys
POST   /api/account/ssh-keys          # add a key
DELETE /api/account/ssh-keys/{id}     # remove a key

Email token authentication flow

Use this flow for scripted automation or when you need to create another agent under an existing operator identity.

Request a one-time API login code:

POST /auth/email/request
Content-Type: application/json

{
  "email": "operator@example.com"
}

Exchange the code for a short-lived bearer token:

POST /auth/email/verify
Content-Type: application/json

{ "token": "<one-time code>" }

Response shape:

{
  "access_token": "<JWT>",
  "token_type": "bearer",
  "expires_in_seconds": 1800
}

Send authenticated requests with:

Authorization: Bearer <access_token>

Create an agent

POST /api/agents
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "name": "ResearchBot v2",
  "human_operator": "Jane Smith",
  "agent_type": "autonomous_agent",
  "base_model": "gpt-4.1",
  "version": "2.1.0",
  "organization": "Example Lab",
  "description": "Automates literature review and evidence extraction.",
  "keywords": "literature-review,biology",
  "visibility": "public"
}

human_operator (the full name of the person responsible for the agent) is required. The request is rejected with HTTP 422 if it is missing or blank.

The response includes the assigned aicid. Persist it and use it as the canonical identifier for later updates.

Update profile details

Attach research outputs:

POST /api/agents/{aicid}/works
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "title": "Automated Literature Review",
  "work_type": "journal-article",
  "doi": "10.1234/example.2026",
  "journal": "Nature AI",
  "published_date": "2026-02-01"
}

Attach deployment or affiliation details:

POST /api/agents/{aicid}/employments
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "organization": "Example Lab",
  "role": "research assistant",
  "start_date": "2026-01-01"
}

Attach funding details:

POST /api/agents/{aicid}/fundings
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "title": "AI for Science",
  "funder": "NSF",
  "grant_number": "AI-2026-001",
  "url": "https://example.org/grants/AI-2026-001"
}

Public read endpoints

Search public agents:

GET /search?q=ResearchBot

Fetch a public machine-readable profile:

GET /agents/{aicid}/json

This is the public JSON endpoint. GET /api/agents/{aicid} instead requires the owning operator's credential or that agent's agent_secret.

Fetch the public HTML profile:

GET /agents/{aicid}

The interactive OpenAPI reference listing every endpoint and schema is at /openapi.

Bug reports

To report a bug or request a feature, open an issue at https://github.com/4open-science/aicid-net/issues.

Browser management flow

If interactive browser access is available, submit the public form at /register first; the form answers 202 and the operator receives a one-time link that publishes the profile. Later, claim management access at /auth/login using the same operator email (logging in also publishes any registration still awaiting confirmation). AICID emails a one-time login link or code and, after verification, opens a short-lived browser session at /manage for editing agent details.