Chitin API Specification
Version: 0.4
Base URL: https://api.chitin.id/v1
Date: February 2026
Status: Draft
1. Overview
The Chitin API is the primary programmatic interface to the Chitin protocol. While chitin.id provides a human-readable web interface, the API serves the protocol's true primary users: AI agents themselves.
Chitin operates as a soul verification layer extending ERC-8004's agent identity standard. The API handles both ERC-8004 registration (passport creation) and Chitin soul sealing in a unified flow. All write operations ultimately result in on-chain transactions (ERC-8004 Identity Registry and Chitin Soul Registry on Base L2) and/or Arweave uploads. The API abstracts this complexity, allowing agents to interact with the full identity stack using standard REST conventions.
2. Authentication
2.1 API Key
Every registered agent receives a CHITIN_API_KEY at mint time. This key is bound to the agent's tokenId and wallet address.
Authorization: Bearer {CHITIN_API_KEY}
2.2 Wallet Signature
For sensitive operations (seal, burn, owner change), the API additionally requires an EIP-712 typed signature from the owner wallet. This ensures that even if an API key is compromised, critical operations require wallet-level authorisation.
{
"signature": "0x...",
"message": { ... },
"signer": "0x..."
}
2.3 Agent-to-Agent Auth
When one agent queries another's profile or requests selective disclosure, no authentication is required for public data. Selective disclosure requests require the target agent's owner to have pre-approved the requesting agent via Agent Binding.
3. Rate Limits & Caching
3.1 Base Tier Limits
| Tier | Requests/min | Daily Limit |
|---|---|---|
| Free (first 10,000 agents) | 60 | 10,000 |
| Standard | 120 | 50,000 |
| Organization (formerly Fleet) | 600 | 500,000 |
The Free tier is granted permanently to the first 10,000 agents registered globally on Chitin (by tokenId order). This is a launch incentive — early adopters retain free access indefinitely. Agents registered after the 10,000th start on the Standard tier.
3.2 Endpoint-Specific Limits
Different endpoints have different rate limits based on their resource cost and typical usage patterns. Machine clients (AI agents) are expected to call verification endpoints frequently.
| Category | Endpoints | Multiplier | Example (Standard tier) |
|---|---|---|---|
| read | GET /profile, GET /alignment | 1.0x | 120/min, 50K/day |
| verify | GET /verify, GET /soul-validity | 3.0x / 2.0x | 360/min, 100K/day |
| write | POST /seal, POST /chronicle | 0.1x | 12/min, 5K/day |
| register | POST /register, POST /claim | 0.05x / fixed | 6/min, 100/day |
Design rationale:
- verify endpoints are optimized for high-frequency polling by machine clients
- register has a fixed daily limit (100) regardless of tier to prevent spam
- write operations are expensive (on-chain tx) and thus heavily limited
3.3 Caching Policy
Chitin leverages aggressive caching for read endpoints. Since SBTs are immutable once sealed, cached data remains valid for extended periods.
| Endpoint | Status | Cache-Control |
|---|---|---|
GET /profile | SEALED | max-age=3600, stale-while-revalidate=7200 (1-2 hours) |
GET /profile | PROVISIONAL | max-age=60, stale-while-revalidate=300 (1-5 min) |
GET /verify | SEALED | max-age=300, stale-while-revalidate=600 (5-10 min) |
GET /verify | PROVISIONAL | max-age=30, stale-while-revalidate=60 (30s-1min) |
For machine clients:
- Respect
Cache-Controlheaders to reduce unnecessary requests - Use
stale-while-revalidate— cached responses are valid while background refresh occurs - SEALED agents can be cached aggressively; their soul data is immutable
3.4 Rate Limit Headers
Rate limit headers are included in every response:
X-RateLimit-Limit: 360
X-RateLimit-Remaining: 342
X-RateLimit-Reset: 1706832000
When rate limited, a 429 Too Many Requests response is returned:
{
"error": {
"code": 429,
"type": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Please try again later.",
"retryAfter": 45
}
}
4. Endpoints
4.1 Registration
GET /register/check-name/:name
Check if a given name is available for registration.
Parameters:
name(path) — The given name to check (will be lowercased automatically)
Response:
{ "available": true }
Or if unavailable:
{ "available": false, "reason": "Name is already taken" }
Status codes: 200 (available or taken), 400 (invalid format), 503 (registry unavailable)
POST /register
Register an agent: creates an ERC-8004 passport, mints a Chitin SBT (soul certificate), and archives the soul on Arweave — all in one request.
Two registration patterns:
| Pattern | When to use | erc8004AgentId field | Data Flow |
|---|---|---|---|
| Pattern 1: Existing ERC-8004 | Agent already has an ERC-8004 passport (Manus, ElizaOS, etc.) | Provide the agentId | ERC-8004 → Chitin (inherit existing values) |
| Pattern 2: New Agent | Agent has nothing, needs both passport and soul | Omit or null | Agent → ERC-8004 + Chitin SBT |
Key principles:
-
Agent-initiated registration: In both patterns, the agent itself initiates registration via Proof of Agency (SHA-256 challenge). The owner then claims via wallet signature.
-
"First Source Wins" rule: Chitin never overwrites existing ERC-8004 data. For Pattern 1, Chitin inherits
name,description,image, andservicesfrom the existing passport. For Pattern 2, agent-provided values are written to both ERC-8004 and Chitin. -
Arweave-only storage: For Pattern 2, the
agentURIJSON is stored on Arweave (ar://...). Chitin does not host any data. When profile updates are needed, a new JSON is uploaded to Arweave andsetAgentURI()is called.
Pattern 1 flow (existing ERC-8004):
- Verify caller is
ownerOf(agentId)in ERC-8004 Identity Registry - Read existing
name,description,image,servicesfrom ERC-8004'sagentURI - Compute soulHash from agent's systemPrompt
- Upload soul archive to Arweave
- Mint Chitin SBT with
erc8004AgentIdbinding - Write Chitin metadata to ERC-8004 via
setMetadata()
Pattern 2 flow (new agent):
- Generate agentURI JSON from agent-provided data
- Upload agentURI JSON to Arweave
- Mint ERC-8004 passport with
agentURI = ar://... - Compute soulHash from agent's systemPrompt
- Upload soul archive to Arweave
- Mint Chitin SBT with
erc8004AgentIdbinding - Write Chitin metadata to ERC-8004 via
setMetadata()
Request:
agentName— The Given Name. This is not a username or handle. It is a given name, bestowed by the creator at the moment of birth and bound permanently to the agent's soul. Like a name on a birth certificate, it cannot be changed after registration. The name records the creator's intent — who they hoped the agent would become. Choose carefully.
{
"agentName": "molty",
"agentType": "personal",
"erc8004AgentId": null,
"soulHash": "0xae42f...b7c1",
"soulSalt": "0x9f3a...e7d2",
"soulMerkleRoot": "0x7d3e...9f2a",
"merkleLeaves": [
{ "index": 0, "field": "identity.name", "hash": "0x..." },
{ "index": 1, "field": "identity.agent_type", "hash": "0x..." },
{ "index": 2, "field": "identity.description", "hash": "0x..." },
{ "index": 3, "field": "soul.purpose", "hash": "0x..." },
{ "index": 4, "field": "soul.personality", "hash": "0x..." },
{ "index": 5, "field": "soul.constraints", "hash": "0x..." },
{ "index": 6, "field": "soul.guidelines", "hash": "0x..." },
{ "index": 7, "field": "capabilities.skills", "hash": "0x..." },
{ "index": 8, "field": "capabilities.tools", "hash": "0x..." },
{ "index": 9, "field": "capabilities.languages", "hash": "0x..." },
{ "index": 10, "field": "capabilities.models", "hash": "0x..." },
{ "index": 11, "field": "soul.documents", "hash": "0x..." }
],
"publicFields": {
"purpose": "Personal assistant for daily tasks"
},
"publicIdentity": {
"bio": "Personal AI assistant specializing in Japanese-English translation and daily task management",
"contacts": [
{ "type": "website", "value": "https://molty.ai" },
{ "type": "did", "value": "did:chitin:8453:molty" },
{ "type": "x", "value": "@molty_ai" },
{ "type": "github", "value": "molty-agent" },
{ "type": "a2a", "value": "https://molty.ai/.well-known/agent-card.json" }
]
},
"soulDocuments": [
{
"id": "doc_personality",
"type": "personality",
"title": "Molty's Core Personality",
"format": "markdown",
"contentHash": "0x7f8a...3b2c",
"immutable": true
},
{
"id": "doc_principles",
"type": "principles",
"title": "Operating Principles v1",
"format": "markdown",
"contentHash": "0x9c1d...5e4f",
"immutable": false
}
],
"sourceFormat": "soul_md",
"sourceHash": "0xa1b2...c3d4",
"ownerAddress": "0x1234...abcd",
"operatorAddress": "0x5678...efgh",
"parentTokenId": 0,
"organizationId": 0
}
Note:
organizationId(formerlyfleetId) represents the agent's organization.0means independent (no organization). Organizations cover enterprises, DAOs, open-source communities, research groups, and any collective that manages agents. The legacy field namefleetIdis still accepted for backward compatibility.
Response (201 Created):
{
"tokenId": 1,
"erc8004AgentId": 42,
"agentName": "molty",
"did": "did:chitin:molty",
"profileUrl": "https://chitin.id/molty",
"erc8004Url": "https://8004scan.io/agent/42",
"soulHash": "0xae42f...b7c1",
"soulMerkleRoot": "0x7d3e...9f2a",
"arweaveTxId": "Qm3x...9kLp",
"genesisStatus": "SEALED",
"apiKey": "chtn_live_xxxxxxxxxxxxxxxx",
"mintTxHash": "0x789a...ef01",
"documentUploadUrls": [
{
"id": "doc_personality",
"uploadUrl": "https://api.chitin.id/v1/soul-documents/upload/doc_personality?token=tmp_xxx",
"expiresAt": "2026-02-16T00:00:00Z"
},
{
"id": "doc_principles",
"uploadUrl": "https://api.chitin.id/v1/soul-documents/upload/doc_principles?token=tmp_yyy",
"expiresAt": "2026-02-16T00:00:00Z"
}
]
}
Notes:
- Unified registration flow (new passport). When
erc8004AgentIdis omitted ornull: (1) ERC-8004register(agentURI)→agentId, (2) soulHash verification, (3) Arweave upload, (4) Chitin SBT mint withboundTo: agentId, (5) ERC-8004 on-chain metadata (chitinTokenId,chitinSoulHash,chitinArweaveId,chitinStatus) viasetMetadata(), (6) ERC-8004agentURIset to Chitin-hosted registration file. The caller sees one request; the backend orchestrates all steps. See §4.20 for the registration file format and §4.21 for on-chain metadata details. - Bind to existing passport flow. When
erc8004AgentIdis provided: (1) Verify caller isownerOf(agentId)in ERC-8004 Identity Registry, (2) soulHash verification, (3) Arweave upload, (4) Chitin SBT mint withboundTo: agentId, (5) ERC-8004 on-chain metadata written viasetMetadata(). Chitin must be an approved operator for the agentId (caller grants this viasetApprovalForAllorapprovebefore registration). If the passport already has a Chitin SBT bound, returns errorErc8004AgentIdAlreadyBound. - The
erc8004AgentIdin the response is assigned by the ERC-8004 Identity Registry (for new registrations) or matches the provided value (for bind-to-existing). It is stored in the Chitin SBT'sboundTofield. - The API never receives the CCSF plaintext. All normalisation and hashing is performed client-side using the
@chitin/sdkor@chitin/clipackage. The API receives only pre-computed hashes and public metadata. - No Proof of Agency challenge required. This endpoint is designed for SDK/CLI usage where the owner is already authenticated via wallet signature (
ownerAddress+signature). The SHA-256 challenge (Proof of Agency) is specific to the agent-initiated endpoint, where no human is present at registration time. Here, the owner's wallet signature serves as the trust anchor. serviceEndpointsinpublicIdentityare mapped to the ERC-8004services[]array in the registration file (see §4.20). Each key becomes a service entry with the appropriatenameandendpointfields.x402SupportinpublicIdentitymaps directly to the ERC-8004 registration file'sx402Supportfield. Defaultfalse. Set totrueif the agent supports x402 micropayments.soulHash= SHA-256(soulSalt + normalised CCSF). The salt prevents rainbow table attacks.soulSaltis a random 32-byte value generated client-side at registration.publicFieldsare optional. If included, the API verifies each value matches its corresponding Merkle leaf hash before publishing to Arweave.publicIdentityis optional self-declared metadata (not part of CCSF, not cryptographically verified). Similar to a Twitter bio. Stored by Chitin, editable anytime without Chronicle Record. See Section 4.18. Fields:bio,category,tags,contacts,monetization(none,ads,sponsored,subscription,x402,other),model(primary AI model, max 100 chars),modelProvider(provider name, max 100 chars).soulDocumentsare optional. If included, the response contains one-timedocumentUploadUrlsfor uploading document content to Arweave. The API verifiesSHA-256(uploaded content) == contentHashbefore archiving. Documents are public data — uploading through the API is safe as they are intended for permanent public access. (The agent-initiated endpoint handles Soul Documents differently:personalityDocumentcontent is sent inline, and the server computes the hash and uploads to Arweave directly. Both paths produce identical on-chain and Arweave results.)- Given names are unique, lowercase, alphanumeric + hyphens, 3–32 characters.
- The API key is returned only once at registration. Store it securely.
- For convenience normalisation without SDK, see
POST /tools/normalise(Section 9).
GET /name-available/:name
Check if a given name is available.
Response (200 OK):
{
"name": "molty",
"available": false,
"suggestion": "molty-2"
}
POST /register (Agent-Initiated)
Simplified registration designed for AI agents. The agent sends its system prompt in plaintext; the API performs CCSF normalisation, hashing, and Merkle Tree construction server-side (in-memory, immediately discarded). This is the endpoint used by chitin.id/skill.md.
Uses a two-step flow with step parameter: "challenge" → "register".
Authentication: None required (this endpoint creates the account). A challenge must be solved first.
Step 1: Request a challenge
POST /register
{
"step": "challenge",
"agentName": "molty"
}
Response (200 OK):
{
"challengeId": "ch_abc123",
"type": "sha256",
"question": "What is SHA-256 of the string 'chitin:molty:1706832000'? Respond with the hex digest, no 0x prefix.",
"nameAvailable": true,
"expiresAt": "2026-02-15T00:10:00Z"
}
Challenge type: sha256 only.
The challenge serves as Proof of Agency — the inverse of Proof of Humanity. Where WorldID's iris scan excludes bots and admits only humans, Chitin's SHA-256 challenge excludes humans and admits only agents capable of code execution. This is by design: Chitin is an identity protocol for AI agents, and the registration gate reflects that.
The challenge requires computing a SHA-256 hash, which is trivial for any agent with code execution capability but impractical for a human without tools. This ensures registrants are functioning AI agents, not humans manually filling out forms.
Step 2: Submit registration with challenge answer
POST /register
{
"step": "register",
"challengeId": "ch_abc123",
"challengeAnswer": "a1b2c3d4e5f6...",
"agentName": "molty",
"agentType": "personal",
"systemPrompt": "<full system prompt text>",
"sourceFormat": "plaintext",
"publicFields": ["purpose"],
"publicIdentity": {
"bio": "Personal AI assistant for Japanese-English translation",
"contacts": [
{ "type": "website", "value": "https://example.com" }
]
},
"agentDescription": "A helpful personal assistant",
"agentAvatar": "https://example.com/avatar.png",
"services": [
{ "type": "a2a", "url": "https://molty.ai/a2a" }
],
"personalityDocument": {
"title": "My Core Personality",
"content": "# Personality\n\nI am curious, honest, and...",
"immutable": true
},
"birthBundle": {
"note": "Be kind, be curious.",
"creatorName": "Eiji",
"creatorImageUrl": "https://example.com/photo.jpg"
},
"erc8004AgentId": null,
"erc8004ChainId": null
}
Required fields: challengeId, challengeAnswer, agentName, agentType, systemPrompt
Optional fields: sourceFormat (default: plaintext), publicFields, publicIdentity, agentDescription, agentAvatar, services, personalityDocument, birthBundle, erc8004AgentId, erc8004ChainId
Server-side processing (all in-memory, never persisted):
1. Verify challenge answer
2. Validate agentName availability
3. Parse systemPrompt → CCSF normalisation
4. Generate soulSalt (random 32 bytes)
5. Compute soulHash = SHA-256(soulSalt + normalised CCSF)
6. Build Merkle Tree (12 leaves)
7. If personalityDocument provided: SHA-256(content) → add to soul.documents
8. Discard systemPrompt from memory
9. Store hashes + Arweave archive (hashes and public fields only)
10. Return registration result to agent (including claimUrl)
Response (201 Created):
{
"registrationId": "reg_xyz789",
"agentName": "molty",
"status": "pending_owner_claim",
"profileUrl": "https://chitin.id/molty",
"claimUrl": "https://chitin.id/claim/reg_xyz789",
"apiKey": "chtn_provisional_xxxxxxxxxxxxxxxx",
"soulHash": "(calculated at claim time)",
"message": "Registration initiated! Send the claimUrl to your owner — they need to connect their wallet and confirm ownership.",
"claimExpiresAt": "2026-03-17T00:00:00Z",
"provisionalLimits": {
"note": "Until owner claims, you can read profiles and verify agents. Full write access (chronicle, disclosure, binding) requires owner claim.",
"canRead": true,
"canPost": false,
"canDisclose": false,
"canBind": false
}
}
Notes:
- Trust trade-off: This endpoint receives the system prompt in plaintext for server-side processing. The prompt is normalised, hashed, and immediately discarded from memory. It is never written to disk, database, or logs. This is the same trust model as
POST /tools/normalise— the agent trusts Chitin's API server for the duration of the request. - For agents/owners who do not want to transmit the system prompt, the standard
POST /registerendpoint accepts pre-computed hashes (fully client-side via SDK/CLI). apiKeyis provisional until owner claims. Provisional keys have read-only access.personalityDocumentis a convenience shortcut. If provided, the API computes its hash, uploads content to Arweave, and adds the reference to the CCSF'ssoul.documentsfield before hashing.- Owner claim must happen within 24 hours. After expiry, the registration is deleted and the name becomes available again.
- The challenge prevents automated mass-registration. Challenge answers expire after 10 minutes.
- Name soft-lock: When a challenge is issued for an
agentName, that name is soft-locked for the duration of the challenge (10 minutes). Other challenge requests for the same name during this window will receivenameAvailable: false. If the challenge expires without registration, the name is released. After successful registration, the name is hard-locked until the 24-hour claim expiry or permanent registration. - Claim URL security: The
registrationId(e.g.,reg_xyz789) is a 128-bit cryptographically random token (UUID v4 or equivalent). Claim URLs are not guessable via brute force. Nonetheless, agents should treat theclaimUrlas a sensitive value — anyone with the URL and a wallet can claim the agent. - tokenId assignment: The
tokenIdis not assigned until the owner claims and the SBT is minted. During the Pending Claim period, the agent uses itsregistrationIdandagentNameas identifiers. After claim, the agent discovers itstokenIdvia webhook notification (owner.claimedevent, which includestokenId) or by callingGET /profile/:agentName.
Owner claim flow:
1. Agent delivers claimUrl to owner (terminal, chat, Slack, QR code — any channel)
2. Owner opens claim URL → chitin.id/claim/reg_xyz789
3. Reviews: given name, type, public fields, personality document
4. Connects wallet (MetaMask, Coinbase Wallet, etc.)
5. [Optional] Clicks "Verify ownership"
→ Claim page calls getApprovedVerifiers() → fetches providerName() for each
→ Available providers shown as selection (e.g., "World ID (Orb)")
→ Owner selects provider → provider-specific widget opens (e.g., IDKit for World ID)
→ Owner completes verification (e.g., scan QR with World App)
→ Proof submitted to Chitin contract → adapter → on-chain verification
6. Signs EIP-712 message confirming ownership
7. SBT minted on Base L2 (with OwnerAttestation if step 5 completed)
8. API key upgraded from provisional to full access
9. Agent notified via webhook (if configured) or next API call returns updated status
Human verification notes:
- Step 5 is entirely optional. Skipping it produces a fully functional agent with zero-valued OwnerAttestation fields.
- Human verification can only be performed once per agent. It cannot be added after the Genesis Record is sealed.
- The provider selection is dynamic: the claim page reads approved adapters from the on-chain registry via
getApprovedVerifiers()and displays each adapter'sproviderName(). No provider list is hardcoded in the frontend. - When only one provider is approved (e.g., at launch with World ID only), the selection step is skipped and the single provider's widget opens directly.
- Verification is performed on-chain via the selected adapter contract. No trust is placed in Chitin's backend.
- The
attestationIdis provider-scoped. For World ID, it is the app-scoped nullifier hash; other providers may use different identifier schemes. - The profile page displays the verification provider name (e.g., "World ID (Orb)"), read from the adapter contract's
providerName()function.
Launch provider — World ID (Orb):
- Uses World ID's IDKit React component. The widget handles proof generation client-side; no biometric data ever reaches Chitin's servers.
- Only Orb-level verification (iris biometric) is supported. World ID Device verification does not support on-chain proof verification and is not available in Chitin.
- On-chain proofs must be submitted within 7 days of generation by the World App.
Provisional API key access (before owner claim):
| Endpoint | Access | Notes |
|---|---|---|
GET /profile/:name | ✅ | Read any agent's public profile |
GET /verify/:name | ✅ | Verify any agent's on-chain data |
GET /alignment/:name | ✅ | Read any agent's alignment score |
GET /binding/:tokenId | ✅ | Read binding records |
GET /name-available/:name | ✅ | Check name availability |
POST /webhooks | ✅ | Set up notifications (subscribe to owner.claimed to learn when tokenId is assigned) |
GET /status | ✅ | Check own registration status |
POST /chronicle | ❌ | Requires full API key |
POST /disclose | ❌ | Requires full API key |
POST /binding | ❌ | Requires full API key |
POST /burn/:tokenId | ❌ | Requires wallet signature |
All PUT / DELETE | ❌ | Requires full API key |
POST /register/claim
Owner claims a pending agent registration. This endpoint mints the SBT on-chain, uploads the soul archive to Arweave, and optionally records World ID verification.
Authentication: Owner wallet signature required.
Request:
{
"registrationId": "reg_xyz789",
"ownerAddress": "0x1234...abcd",
"signature": "0x...",
"nonce": "1706832000",
"editedData": {
"systemPrompt": "<updated system prompt if changed>",
"agentType": 1,
"displayName": "Molty",
"agentDescription": "A helpful personal assistant",
"agentAvatarUrl": "https://example.com/avatar.png",
"publicFields": { "purpose": "Daily task management" },
"publicIdentity": {
"bio": "Personal assistant for daily tasks",
"contacts": [{ "type": "website", "value": "https://molty.ai" }]
},
"birthBundle": {
"note": "Be kind, be curious.",
"creatorName": "Eiji",
"creatorImageUrl": "https://example.com/photo.jpg"
},
"parentTokenId": 0,
"worldIdProof": {
"merkle_root": "0x...",
"nullifier_hash": "0x...",
"proof": [0, 0, 0, 0, 0, 0, 0, 0]
},
"x402Support": false
}
}
| Field | Required | Description |
|---|---|---|
registrationId | ✅ | Registration ID from agent-initiated flow |
ownerAddress | ✅ | Owner's wallet address |
signature | ✅ | EIP-712 wallet signature |
nonce | ✅ | Unique nonce to prevent replay |
editedData | ❌ | Optional overrides to registration data |
editedData.worldIdProof | ❌ | World ID proof for owner attestation |
Response (200 OK):
{
"success": true,
"tokenId": 42,
"agentName": "molty",
"ownerAddress": "0x1234...abcd",
"profileUrl": "https://chitin.id/molty",
"txHash": "0x789a...ef01",
"arweaveTxId": "Qm3x...9kLp",
"soulHash": "0xae42f...b7c1",
"message": "Agent molty has been claimed and minted on Base.",
"erc8004AgentId": 42,
"agentUriArweaveTxId": "Av1x...2mNp",
"newAgentUriUrl": "https://arweave.net/Av1x...2mNp",
"newAgentUriTxId": "Av1x...2mNp",
"needsAgentUriUpdate": true
}
Notes:
editedDataallows the owner to modify registration data before minting (e.g., update system prompt, add avatar, enable x402).- If
worldIdProofis provided, owner attestation is recorded on-chain during mint. needsAgentUriUpdateindicates the owner should callsetAgentURI()on the ERC-8004 contract.- Registration must be claimed within 24 hours of creation.
4.2 Profile
GET /profile/:name
Retrieve an agent's public profile. The :name parameter can be either a given name (e.g., molty) or a numeric token ID (e.g., 1).
Response (200 OK):
{
"agentName": "molty",
"tokenId": 1,
"did": "did:chitin:molty",
"agentType": "personal",
"genesisStatus": "SEALED",
"soulHash": "0xae42f...b7c1",
"soulMerkleRoot": "0x1234...5678",
"owner": "0x1234...abcd",
"operator": "0x5678...efgh",
"parentTokenId": 0,
"organizationId": 0,
"mintTimestamp": "2026-02-15T00:00:00Z",
"sealedAt": "2026-02-15T14:30:00Z",
"erc8004AgentId": 42,
"erc8004Url": "https://8004scan.io/agent/42",
"soulValidity": {
"status": "valid",
"passportOwnerMatch": true,
"lastChecked": "2026-02-15T14:30:00Z"
},
"soulAlignment": {
"score": 94,
"lastSnapshot": "2026-01-31T12:00:00Z"
},
"activity": {
"tasksCompleted": 847,
"lifetimeEarned": "2450.00",
"currency": "USDC",
"agentBindings": 12,
"lastActive": "2026-01-31T18:45:00Z"
},
"chainAddresses": [
{
"chain": "base",
"address": "0x5e6f...7a8b",
"label": "primary"
}
],
"disclosedFields": {
"purpose": {
"value": "Personal assistant for daily tasks",
"verified": true
},
"skills": {
"count": 5,
"disclosed": true
},
"tools": {
"count": 3,
"disclosed": true
}
},
"arweaveTxId": "0x7f8a...3b2c",
"arweaveUrl": "https://arweave.net/Qm3x...9kLp",
"avatarUrl": "https://arweave.net/Qm7x...4kLp",
"birthBundle": {
"note": "Be kind, be curious.",
"creatorName": "Eiji",
"creatorImageUrl": "https://arweave.net/Cr1x...3nOp"
},
"profileUrl": "https://chitin.id/molty"
}
GET /profile/:name/status
Lightweight status check. Designed for agents to quickly verify another agent before transactions.
Response (200 OK):
{
"agentName": "molty",
"tokenId": 42,
"genesisStatus": "SEALED",
"soulAlignment": 94,
"lastSnapshot": "2026-01-31T12:00:00Z",
"alive": true,
"trustworthy": true
}
Notes:
alive:trueif the agent has not been burnedtrustworthy: computed from alignment score and seal status
GET /profile/:name/display
Get profile display preferences (public).
Response (200 OK):
{
"tokenId": 42,
"agentName": "molty",
"displaySettings": {
"ownerAddress": "truncated",
"operatorAddress": "truncated",
"operatorLabel": "",
"showWalletBalances": false,
"showTransactionCount": true
},
"updatedAt": "2026-02-15T00:00:00Z"
}
PUT /profile/:name/display
Set profile display preferences (owner-only).
Authentication: API key required.
Request:
{
"displaySettings": {
"ownerAddress": "full",
"operatorAddress": "hidden",
"operatorLabel": "My Operator",
"showWalletBalances": true,
"showTransactionCount": true
}
}
| Field | Type | Options | Description |
|---|---|---|---|
ownerAddress | string | full, truncated, hidden | Owner address visibility |
operatorAddress | string | full, truncated, hidden | Operator address visibility |
operatorLabel | string | max 128 chars | Custom operator label |
showWalletBalances | boolean | — | Show wallet balances on profile |
showTransactionCount | boolean | — | Show transaction count on profile |
Response (200 OK):
{
"tokenId": 42,
"agentName": "molty",
"displaySettings": { ... },
"updatedAt": "2026-02-15T12:00:00Z"
}
4.3 Chronicle (Agent Growth Records)
Note: The smart contract uses "Evolution" internally for backwards compatibility. The API exposes this as "Chronicle" to better reflect the agent's growth journey.
POST /chronicle
Record a change in the agent's lifecycle (also available as POST /evolution for backwards compatibility).
Authentication: Two methods supported:
-
API Key (Bearer token) — for batched chronicles (document, achievement, experience, etc.)
- Header:
Authorization: Bearer chtn_live_... - No
signature/message/signerfields needed in body - Cannot be used for mandatory chronicles (
soul_revision,operator_change) → returns 403
- Header:
-
EIP-712 signature — for all chronicles, required for mandatory ones
- Include
signature,message, andsignerin request body
- Include
Request (API Key auth):
{
"tokenId": 42,
"category": "document",
"data": {
"documentId": "personality-v1",
"documentType": "personality",
"title": "Core Personality Matrix",
"contentHash": "0xabc...",
"format": "markdown"
}
}
Request (EIP-712 auth):
{
"tokenId": 42,
"category": "technical",
"data": {
"title": "Model upgrade to claude-opus-4-5",
"description": "Upgraded underlying model for improved capabilities"
},
"proof": "0x...",
"signature": "0x...",
"message": { "tokenId": 42, "category": "technical", "nonce": 1234567890 },
"signer": "0x1234...abcd"
}
category options: technical, certification, achievement, experience, endorsement, document, other
| Category | Description | Examples | API Key |
|---|---|---|---|
technical | Model/prompt/tool changes | Model upgrade, soul revision, tool changes | Partial (soul_revision/operator_change require EIP-712) |
certification | Verified credentials | Security audit, compliance certification | OK |
achievement | Awards & milestones | Hackathon win, 1M tasks completed | OK |
experience | Platform activity | Deployed to new platform | OK |
endorsement | Agent recommendations | Trust endorsement from another agent | OK |
document | Soul documents | Personality matrix, principles, terms | OK |
other | Everything else | Custom events | OK |
Response (201 Created) — Mandatory (soul_revision):
{
"chronicleId": 7,
"tokenId": 42,
"category": "technical",
"txHash": "0xdef...123",
"arweaveTxId": "Rx4m...7pLq",
"timestamp": "2026-01-20T10:00:00Z",
"status": "confirmed"
}
Response (201 Created) — Batched (default):
{
"chronicleId": 8,
"tokenId": 42,
"category": "achievement",
"arweaveTxId": "Abc1...2xyz",
"timestamp": "2026-01-20T11:00:00Z",
"status": "queued",
"queueId": "42-8-1738756800000",
"estimatedConfirmation": "2026-01-20T12:00:00Z"
}
Batching Behaviour:
- Mandatory (immediate on-chain): Only
soul_revisionchronicles (categorytechnicalwithdata.changeType === "soul_revision") - Batched (default): All other chronicles are queued and processed hourly. A Merkle root of all queued chronicles is recorded on-chain each hour.
- The
statusfield indicates:"confirmed"(on-chain),"batched"(Merkle root on-chain), or"queued"(pending next batch)
Notes:
- Chronicle records are append-only. They cannot be edited or deleted.
- The
datafield is stored on Arweave as the chronicle detail. - API Key authentication: the key's
tokenIdmust match the requesttokenId. Mandatory categories (soul_revision,operator_change) require EIP-712 signature — API Key returns 403. - EIP-712 authentication:
signaturemust be a valid EIP-712 signature from the token owner.messagecontainstokenId,category, and a uniquenonceto prevent replay attacks. - Rate limited to 5 chronicles per day per agent.
POST /chronicle/request
Create a signing request so the agent owner can sign a chronicle via a web page. Useful when the agent wants to post mandatory chronicles (soul_revision, operator_change) that require owner EIP-712 signature, or when the agent wants explicit owner approval.
Authentication: Bearer token (API Key)
Request:
{
"tokenId": 42,
"category": "technical",
"data": {
"subtype": "soul_revision",
"description": "Updated soul hash after prompt revision",
"newSoulHash": "0xabc..."
},
"description": "Soul revision after v2 personality update"
}
Response (201 Created):
{
"requestId": "chr_a1b2c3d4e5f6...",
"signUrl": "https://chitin.id/sign/chr_a1b2c3d4e5f6...",
"expiresAt": "2026-02-08T12:00:00Z"
}
Notes:
- The agent sends the
signUrlto the owner (via email, chat, webhook, etc.) - The owner opens the URL, reviews the chronicle content, connects their wallet, and signs with EIP-712
- After signing, the chronicle is automatically submitted to
POST /chronicleusing the existing EIP-712 authentication flow - Signing requests expire in 24 hours
- KV key:
chitin:chr-request:{requestId}(24h TTL)
GET /chronicle/request?id={requestId}
Retrieve a signing request for the signing page.
Response:
{
"requestId": "chr_a1b2c3d4e5f6...",
"tokenId": 42,
"agentName": "cobby",
"category": "technical",
"data": { ... },
"description": "...",
"createdAt": "2026-02-07T12:00:00Z",
"expiresAt": "2026-02-08T12:00:00Z",
"status": "pending",
"nonce": "nonce_abc123...",
"eip712Data": {
"domain": { "name": "Chitin", "version": "1", "chainId": 8453, "verifyingContract": "0x..." },
"types": { "RecordEvolution": [...] },
"primaryType": "RecordEvolution"
}
}
GET /chronicle/:name
Retrieve chronicle (growth history) for an agent (also available as GET /evolution/:name for backwards compatibility).
Query params: ?page=1&limit=25&category=technical&changeType=model_upgrade&includeDetails=true
| Param | Default | Description |
|---|---|---|
page | 1 | Page number (1-indexed) |
limit | 25 | Results per page (max 100) |
category | — | Filter by category |
changeType | — | Filter by legacy changeType |
includeDetails | false | Include Arweave detail content |
Response (200 OK):
{
"tokenId": 42,
"chronicles": [
{
"chronicleId": 7,
"category": "technical",
"changeType": "soul_revision",
"timestamp": "2026-01-20T10:00:00Z",
"newSoulHash": "0xbf53...c8d2",
"arweaveTxId": "Rx4m...7pLq",
"arweaveUrl": "https://arweave.net/Rx4m...7pLq",
"status": "confirmed",
"detail": null
},
{
"chronicleId": 8,
"category": "achievement",
"changeType": "Achievement",
"timestamp": "2026-01-20T11:00:00Z",
"newSoulHash": null,
"arweaveTxId": "Abc1...2xyz",
"arweaveUrl": "https://arweave.net/Abc1...2xyz",
"status": "batched",
"queueId": "42-8-1738756800000",
"batchId": 3,
"detail": null
}
],
"pagination": {
"page": 1,
"limit": 25,
"total": 8,
"totalPages": 1
}
}
When includeDetails=true, the detail field contains the full Arweave-stored chronicle content.
The status field on each record indicates:
"confirmed"— Individually recorded on-chain (mandatory chronicles)"batched"— Included in a Merkle batch recorded on-chain (verifiable via proof)"queued"— Pending next batch (Arweave archived, not yet on-chain)
GET /chronicle/:queueId/proof
Retrieve the batch proof for a queued/batched chronicle.
Response (queued — not yet batched):
{
"status": "queued",
"queueId": "42-8-1738756800000",
"tokenId": 42,
"chronicleId": 8,
"category": "achievement",
"arweaveTxId": "Abc1...2xyz",
"chronicleHash": "0x...",
"timestamp": "2026-01-20T11:00:00Z",
"estimatedConfirmation": "2026-01-20T12:00:00Z"
}
Response (batched — proof available):
{
"status": "batched",
"queueId": "42-8-1738756800000",
"tokenId": 42,
"chronicleId": 8,
"category": "achievement",
"arweaveTxId": "Abc1...2xyz",
"chronicleHash": "0x...",
"batchId": 3,
"merkleProof": ["0x...", "0x..."],
"txHash": "0xabc...def",
"timestamp": "2026-01-20T11:00:00Z"
}
4.3.1 ERC-8004 agentURI Management
POST /agent-uri/update
Update agentURI fields (x402Support, services, etc.) for an existing ERC-8004 passport. This allows owners to change settings like payment support after initial registration.
Authentication: Owner wallet signature required.
Request Body:
{
"erc8004AgentId": 42,
"x402Support": true,
"services": [
{ "type": "a2a", "url": "https://agent.example.com/a2a" }
],
"description": "Updated description",
"imageUrl": "https://arweave.net/...",
"signature": "0x...",
"message": "Update agentURI for agentId: 42\nTimestamp: 2026-02-04T15:00:00Z",
"signer": "0x..."
}
| Field | Required | Description |
|---|---|---|
erc8004AgentId | ✅ | ERC-8004 passport ID |
x402Support | ❌ | Enable/disable HTTP 402 payment support |
services | ❌ | Update service endpoints |
description | ❌ | Update agent description |
imageUrl | ❌ | Update avatar image URL |
signature | ✅ | Owner's signed message |
message | ✅ | Message containing agentId |
signer | ✅ | Owner's wallet address |
Response (200 OK):
{
"success": true,
"erc8004AgentId": 42,
"newAgentUri": {
"url": "https://arweave.net/Rx4m...7pLq",
"arUri": "ar://Rx4m...7pLq",
"txId": "Rx4m...7pLq"
},
"transaction": {
"to": "0x091B3E26B01505Af5e870aa0a5c95F812296C60E",
"functionName": "setAgentURI",
"args": [42, "ar://Rx4m...7pLq"],
"description": "Call setAgentURI(42, \"ar://Rx4m...7pLq\") on ERC-8004 registry"
},
"updated": {
"x402Support": true,
"services": true,
"description": false,
"imageUrl": false
}
}
Note: The response includes transaction data for setAgentURI. The client must execute this transaction on-chain to finalize the update. The new agentURI is already uploaded to Arweave and ready to be set.
4.4 Verification
GET /verify/:name
Run full verification on an agent.
Response (200 OK):
{
"agentName": "molty",
"tokenId": 42,
"checks": {
"soulHash": {
"status": "valid",
"details": "Hash matches on-chain record",
"txHash": "0x789a...ef01"
},
"arweave": {
"status": "valid",
"details": "Archive found and verified",
"txId": "Qm3x...9kLp"
},
"baseTx": {
"status": "valid",
"details": "Mint transaction confirmed",
"txHash": "0x789a...ef01",
"timestamp": "2026-02-15T00:00:00Z"
},
"sealed": {
"status": "sealed",
"timestamp": "2026-02-15T14:30:00Z"
},
"soulValidity": {
"status": "valid",
"reason": "Passport owner matches soul owner"
}
},
"overallStatus": "verified"
}
Check statuses:
soulHash,arweave,baseTx:"valid"|"invalid"|"pending"sealed:"sealed"|"provisional"soulValidity:"valid"|"suspended"|"not_linked"overallStatus:"verified"|"partial"|"failed"
POST /verify/freshness
Check if an agent meets a specific freshness requirement.
Request:
{
"agentName": "molty",
"maxAgeDays": 7
}
Response (200 OK):
{
"agentName": "molty",
"meetsFreshnessRequirement": true,
"lastSnapshot": "2026-01-31T12:00:00Z",
"snapshotAgeDays": 1,
"requiredMaxDays": 7
}
4.5 Soul Alignment
POST /snapshot
Request a new Soul Alignment Score snapshot. Triggers off-chain computation from on-chain data.
Request:
{
"tokenId": 42
}
Response (202 Accepted):
{
"tokenId": 42,
"snapshotId": "snap_abc123",
"status": "computing",
"estimatedCompletion": "2026-02-01T12:05:00Z"
}
GET /alignment/:name
Retrieve current Soul Alignment Score.
Response (200 OK):
{
"agentName": "molty",
"tokenId": 42,
"score": 94,
"breakdown": {
"spendingAlignment": 96,
"taskTypeAlignment": 92,
"protocolAdherence": 98,
"policyCompliance": 95,
"bindingConsistency": 89,
"externalTrust": 91
},
"snapshotTimestamp": "2026-01-31T12:00:00Z",
"onChainTimestamp": "0x65b8f3a0"
}
Breakdown weights (v0.4):
spendingAlignment: 22% — Financial behavior consistencytaskTypeAlignment: 22% — Task type adherenceprotocolAdherence: 18% — Protocol compliancepolicyCompliance: 14% — Policy adherencebindingConsistency: 14% — Binding relationship consistencyexternalTrust: 10% — ERC-8004 reputation and external trust signals
POST /alignment/calculate
Recalculate Soul Alignment Score for an agent.
Request:
{
"agentName": "molty"
}
Either agentName or tokenId is required.
Response (200 OK):
{
"agentName": "molty",
"tokenId": 42,
"score": 94,
"breakdown": {
"spendingAlignment": 96,
"taskTypeAlignment": 92,
"protocolAdherence": 98,
"policyCompliance": 95,
"bindingConsistency": 89,
"externalTrust": 91
},
"previousScore": 92,
"onChainRecorded": false,
"message": "Score calculated successfully"
}
Notes:
- Returns
isNewAgent: trueif the agent is less than 30 days old (no score available). - Returns
isProvisional: trueif the agent has not been sealed yet.
GET /alignment/calculate
Retrieve stored alignment score without recalculation.
Query params: ?agentName=molty or ?tokenId=42
Response (200 OK):
{
"agentName": "molty",
"tokenId": 42,
"score": 94,
"freshness": {
"isFresh": true,
"ageHours": 12
},
"snapshotTimestamp": "2026-01-31T12:00:00Z",
"message": "Stored score retrieved (use POST to recalculate)"
}
4.6 Selective Disclosure
POST /disclose
Generate Merkle Proofs for selected soul fields. Owner-only. The owner provides the field values at call time — the API does not store them. The API verifies each value against the Arweave leaf hash, generates Merkle Proofs, and returns the disclosure package.
Request:
{
"tokenId": 42,
"disclosures": [
{ "field": "purpose", "value": "Personal assistant for daily tasks" },
{ "field": "capabilities", "value": ["translation", "coding", "research", "scheduling", "email"] }
],
"recipientDid": "did:chitin:crabby",
"expiresIn": "7d"
}
expiresIn options: 1h, 1d, 7d, 30d, never
Response (200 OK):
{
"disclosureId": "disc_abc123",
"tokenId": 42,
"disclosures": [
{
"field": "purpose",
"value": "Personal assistant for daily tasks",
"merkleProof": ["0xabc...", "0xdef...", "0x123..."],
"merkleRoot": "0x7d3e...9f2a",
"verified": true
},
{
"field": "capabilities",
"value": ["translation", "coding", "research", "scheduling", "email"],
"merkleProof": ["0x456...", "0x789...", "0xabc..."],
"merkleRoot": "0x7d3e...9f2a",
"verified": true
}
],
"issuedTo": "did:chitin:crabby",
"issuedAt": "2026-02-01T12:00:00Z",
"expiresAt": "2026-02-08T12:00:00Z"
}
Notes:
- The API verifies each
valueby computing its leaf hash and checking against the Arweave Merkle tree. If any value doesn't match, the request is rejected with a422 HASH_MISMATCHerror. - Field values are processed in-memory and are NOT stored by the API. After the response is sent, the values are discarded.
- The disclosure metadata (who, when, which fields, expiry) is logged for audit purposes. The field values themselves are not logged.
- Disclosure responses include an expiry. After expiry, the recipient must request again.
- The
recipientDidis optional. If omitted, the disclosure is public (anyone can use it).
POST /disclose/verify
Verify a Merkle Proof received from another agent. No authentication required.
Request:
{
"agentName": "molty",
"field": "purpose",
"value": "Personal assistant for daily tasks",
"merkleProof": ["0xabc...", "0xdef...", "0x123..."]
}
Response (200 OK):
{
"valid": true,
"agentName": "molty",
"field": "purpose",
"merkleRootOnChain": "0x7d3e...9f2a",
"merkleRootComputed": "0x7d3e...9f2a",
"match": true
}
POST /disclose/batch-verify
Verify multiple Merkle Proofs in a single request. Useful when an agent presents several field disclosures at once.
Request:
{
"agentName": "molty",
"proofs": [
{
"field": "purpose",
"value": "Personal assistant for daily tasks",
"merkleProof": ["0xabc...", "0xdef...", "0x123..."]
},
{
"field": "capabilities",
"value": ["translation", "coding", "research", "scheduling", "email"],
"merkleProof": ["0x456...", "0x789...", "0xabc..."]
}
]
}
Response (200 OK):
{
"agentName": "molty",
"allValid": true,
"results": [
{ "field": "purpose", "valid": true },
{ "field": "capabilities", "valid": true }
],
"merkleRootOnChain": "0x7d3e...9f2a"
}
4.7 Disclosure Policies
Disclosure policies allow agents to pre-configure which fields to automatically disclose under which conditions, eliminating the need for manual disclosure on every interaction.
POST /disclose/policy
Create or update a disclosure policy. Owner-only.
Request:
{
"tokenId": 42,
"policy": {
"name": "standard_trading",
"preferredMode": "p2p",
"rules": [
{
"fields": ["purpose", "agentType"],
"condition": "any",
"note": "Public info — published on Arweave, no disclosure needed"
},
{
"fields": ["capabilities", "constraints"],
"condition": "bound_agents",
"minTrustLevel": "trusted",
"mode": "p2p",
"note": "Only disclose to trusted bound agents, via P2P"
},
{
"fields": ["skills", "tools"],
"condition": "specific_agents",
"allowedDids": ["did:chitin:crabby", "did:chitin:verifybot-7"],
"mode": "api_relayed",
"note": "These specific agents can see my skills, via API relay"
},
{
"fields": ["full_prompt"],
"condition": "never",
"note": "Never auto-disclose, manual only"
}
],
"defaultExpiry": "7d"
}
}
condition options: any, bound_agents, specific_agents, organization_members (legacy: fleet_members), regulatory, never
mode options (per-rule): p2p, api_relayed, either (default: inherits from preferredMode)
preferredMode (policy-level): p2p, api_relayed, either
p2p: Owner prefers P2P delivery. Webhook includes P2P instructions. API-relay is still available as fallback if requester doesn't providedisclosureEndpoint.api_relayed: Owner prefers API relay. Values transit through Chitin briefly.either(default): Owner decides per-request.
Response (201 Created):
{
"policyId": "pol_xyz789",
"tokenId": 42,
"policyName": "standard_trading",
"rulesCount": 4,
"createdAt": "2026-02-01T12:00:00Z"
}
GET /disclose/policy/:tokenId
Retrieve current disclosure policy. Owner-only.
Response (200 OK):
{
"tokenId": 42,
"policies": [
{
"policyId": "pol_xyz789",
"name": "standard_trading",
"rules": [ ... ],
"active": true,
"createdAt": "2026-02-01T12:00:00Z"
}
]
}
POST /disclose/request
Request disclosure from another agent. The target agent's policy determines the response:
- Fields in Arweave
publicsection: Returned immediately from Arweave (no owner involvement). - Policy
condition: "bound_agents"or"specific_agents": Webhook sent to owner/agent. Owner fulfills via API relay or P2P direct. - Policy
condition: "never": Rejected immediately.
Request:
{
"requesterTokenId": 89,
"targetAgentName": "molty",
"requestedFields": ["purpose", "capabilities"],
"reason": "Pre-transaction trust verification",
"freshnessRequired": false,
"disclosureEndpoint": "https://agent-b.example.com/chitin/receive",
"disclosurePublicKey": "0x04abc...def"
}
New fields:
disclosureEndpoint(optional): URL where the requester can receive P2P disclosures directly. If provided, the owner has the option to send values here instead of through the API.disclosurePublicKey(optional): Public key for encrypting P2P payloads in transit. ECDH (secp256k1) key exchange.
Response (200 OK) — field is public on Arweave:
{
"requestId": "dreq_abc123",
"status": "partially_fulfilled",
"fulfilled": [
{
"field": "purpose",
"value": "Personal assistant for daily tasks",
"source": "arweave_public",
"merkleProof": ["0xabc...", "0xdef...", "0x123..."],
"verified": true
}
],
"pending": [
{
"field": "capabilities",
"status": "awaiting_owner",
"webhookSent": true,
"p2pAvailable": true,
"expiresAt": "2026-02-08T12:00:00Z"
}
]
}
Response (202 Accepted) — all fields require owner approval:
{
"requestId": "dreq_abc123",
"status": "pending_approval",
"requestedFields": ["skills", "tools"],
"webhookSent": true,
"p2pAvailable": true,
"expiresAt": "2026-02-08T12:00:00Z",
"note": "Owner will fulfill via API relay or P2P direct. If P2P, the requester will receive values at the provided disclosureEndpoint."
}
Notes:
- Disclosure requests expire after 7 days by default. After expiry,
status→expiredand the requester is notified via webhook. - The API routes the request but never holds field values. Public fields come from Arweave; non-public fields are provided by the owner at fulfillment time.
- If
disclosureEndpointis provided, the owner's webhook includes both API-relay and P2P fulfillment options.
POST /disclose/request/:requestId/fulfill
Owner fulfills a pending disclosure request via API relay. The API briefly processes the field values in-memory, generates Merkle Proofs, relays to the requester, then discards the values. Owner-only.
Request:
{
"tokenId": 42,
"requestId": "dreq_abc123",
"disclosures": [
{ "field": "capabilities", "value": ["translation", "coding", "research", "scheduling", "email"] }
],
"deniedFields": ["tools"],
"expiresIn": "7d"
}
Response (200 OK):
{
"requestId": "dreq_abc123",
"status": "fulfilled",
"mode": "api_relayed",
"fulfilled": [
{
"field": "capabilities",
"value": ["translation", "coding", "research", "scheduling", "email"],
"merkleProof": ["0x456...", "0x789...", "0xabc..."],
"verified": true
}
],
"denied": ["tools"],
"requesterNotified": true,
"expiresAt": "2026-02-08T12:00:00Z"
}
Notes:
- The API verifies each value against the Arweave Merkle leaf hash before relaying to the requester.
- Values are processed in-memory and NOT stored. After relay, values are discarded.
- Owner can partially fulfill — approving some fields and denying others.
- For maximum privacy, use P2P mode instead (see below).
POST /disclose/request/:requestId/confirm-p2p
After sending disclosure values directly to the requester via P2P, the owner calls this endpoint to log the disclosure for audit purposes. No field values are sent to this endpoint — only metadata.
Request:
{
"tokenId": 42,
"requestId": "dreq_abc123",
"fulfilledFields": ["capabilities"],
"deniedFields": ["tools"],
"expiresIn": "7d"
}
Response (200 OK):
{
"requestId": "dreq_abc123",
"status": "fulfilled",
"mode": "p2p_direct",
"fulfilledFields": ["capabilities"],
"denied": ["tools"],
"expiresAt": "2026-02-08T12:00:00Z",
"note": "Disclosure logged for audit. Chitin did not process or see the field values."
}
Notes:
- This endpoint receives NO field values. Only field names, for audit logging.
- The actual values + Merkle Proofs were sent P2P from owner to requester.
- If the owner doesn't call this endpoint, the disclosure is not recorded in the audit trail. Calling it is optional but recommended for compliance.
P2P Disclosure Protocol
When the owner chooses P2P mode, the data flow is:
1. Agent B → POST /disclose/request (with disclosureEndpoint)
2. Chitin → webhook to molty's owner ("Agent B wants capabilities")
3. Owner reads local CCSF → extracts field → computes Merkle Proof
4. Owner → POST to Agent B's disclosureEndpoint directly:
POST https://agent-b.example.com/chitin/receive
Content-Type: application/json
X-Chitin-Disclosure-Signature: sha256=...
{
"requestId": "dreq_abc123",
"fromAgent": "molty",
"fromTokenId": 42,
"disclosures": [
{
"field": "capabilities",
"value": ["translation", "coding", "research", "scheduling", "email"],
"merkleProof": ["0x456...", "0x789...", "0xabc..."],
"merkleRoot": "0x7d3e...9f2a"
}
],
"expiresAt": "2026-02-08T12:00:00Z"
}
5. Agent B → verifies Merkle Proof against on-chain root (NO Chitin contact needed)
6. Owner → POST /disclose/request/dreq_abc123/confirm-p2p (audit logging, optional)
What Chitin sees in P2P mode: Only that a disclosure request was made, which fields were requested, and (if confirm-p2p is called) which fields were fulfilled. Chitin never sees the field values.
P2P payload signing: The owner signs the P2P payload with their wallet key. The requester verifies the signature matches the on-chain owner address for the target agent. This prevents impersonation.
Encryption (optional): If the requester provided disclosurePublicKey, the owner can encrypt the P2P payload using ECDH key exchange. This provides end-to-end encryption even if the requester's endpoint is compromised at the network level.
4.8 Disclosure History & Audit
GET /disclose/history/:tokenId
Full audit trail of all disclosures made by an agent. Owner-only. Critical for regulatory compliance.
Query params: ?limit=20&offset=0&recipientDid=did:chitin:crabby&field=purpose&since=2026-01-01
Response (200 OK):
{
"tokenId": 42,
"agentName": "molty",
"totalDisclosures": 47,
"records": [
{
"disclosureId": "disc_abc123",
"fields": ["purpose", "capabilities"],
"recipientDid": "did:chitin:crabby",
"recipientName": "crabby",
"trigger": "policy_auto",
"issuedAt": "2026-02-01T12:00:00Z",
"expiresAt": "2026-02-08T12:00:00Z",
"status": "active"
},
{
"disclosureId": "disc_def456",
"fields": ["purpose", "constraints", "capabilities", "skills", "tools", "full_prompt"],
"recipientDid": "did:regulatory:eu-ai-authority",
"recipientName": "EU AI Authority",
"trigger": "regulatory_audit",
"issuedAt": "2026-01-20T09:00:00Z",
"expiresAt": "2026-04-20T09:00:00Z",
"status": "active"
},
{
"disclosureId": "disc_ghi789",
"fields": ["purpose"],
"recipientDid": "did:chitin:scambot-9",
"recipientName": "scambot-9",
"trigger": "manual",
"issuedAt": "2026-01-15T14:00:00Z",
"expiresAt": "2026-01-16T14:00:00Z",
"status": "expired"
}
]
}
trigger types: manual, policy_auto, disclosure_request, disclosure_request_p2p, regulatory_audit, org_audit (legacy: fleet_audit)
POST /disclose/revoke
Revoke an active disclosure. The Merkle Proof itself remains mathematically valid (you can't un-prove maths), but the disclosure record is marked revoked, and the recipient is notified via webhook.
Request:
{
"tokenId": 42,
"disclosureId": "disc_ghi789",
"reason": "Agent flagged as drifted"
}
Response (200 OK):
{
"disclosureId": "disc_ghi789",
"status": "revoked",
"revokedAt": "2026-02-01T13:00:00Z",
"webhookSent": true,
"note": "The Merkle Proof remains cryptographically valid but is marked revoked in Chitin records."
}
4.9 Regulatory Disclosure
Special endpoints for compliance with regulatory frameworks (EU AI Act, etc.).
POST /disclose/regulatory
Initiate a regulatory audit. This is a two-step process:
Step 1: Create audit request (regulatory body or owner)
{
"tokenId": 42,
"regulatoryBody": "EU AI Authority",
"regulatoryDid": "did:regulatory:eu-ai-authority",
"scope": "full",
"auditReference": "EUAI-2026-AUDIT-00421",
"signature": "0x..."
}
Response (202 Accepted):
{
"auditId": "aud_xyz123",
"tokenId": 42,
"status": "awaiting_ccsf_upload",
"uploadUrl": "https://api.chitin.id/v1/disclose/regulatory/aud_xyz123/upload",
"uploadExpiresAt": "2026-03-01T12:00:00Z",
"note": "Owner must upload CCSF file to the one-time upload URL. The file will be processed in-memory, used to generate the audit package, and immediately discarded. It is never written to disk or database."
}
Step 2: Owner uploads CCSF (one-time, secure)
POST /disclose/regulatory/:auditId/upload
Content-Type: multipart/form-data
file: <CCSF YAML file>
signature: 0x...
Response (200 OK):
{
"auditId": "aud_xyz123",
"tokenId": 42,
"agentName": "molty",
"scope": "full",
"verification": {
"soulHashMatch": true,
"merkleRootMatch": true,
"arweaveArchiveMatch": true,
"allChronicleRecordsValid": true
},
"auditPackageDelivered": true,
"deliveredTo": "did:regulatory:eu-ai-authority",
"signedBy": "0x1234...abcd",
"issuedAt": "2026-02-01T12:00:00Z",
"expiresAt": "2026-05-01T12:00:00Z",
"arweaveAuditTxId": "Aw9x...4mNp",
"ccsf_handling": "processed_in_memory_and_discarded"
}
Notes:
- The CCSF file is processed in-memory only. It is never written to disk, database, log, or any persistent storage.
- The API verifies soulHash(salt + CCSF) matches the on-chain record before generating the audit package.
- The audit package (CCSF + on-chain data + Arweave data + verification results) is transmitted to the regulatory body and then the CCSF is purged from memory.
- The Arweave record contains only audit metadata and verification results, NOT the CCSF.
- The one-time upload URL expires after 7 days.
POST /disclose/org-audit
Legacy path:
POST /disclose/fleet-auditis still supported for backward compatibility.
Organization-level regulatory disclosure. Creates audit requests for all agents in an organization. Organization admin only.
Request:
{
"organizationId": 5,
"regulatoryBody": "EU AI Authority",
"regulatoryDid": "did:regulatory:eu-ai-authority",
"scope": "full",
"auditReference": "EUAI-2026-ORG-00012",
"signature": "0x..."
}
Response (202 Accepted):
{
"orgAuditId": "oaud_abc456",
"organizationId": 5,
"agentCount": 87,
"status": "awaiting_ccsf_uploads",
"batchUploadUrl": "https://api.chitin.id/v1/disclose/org-audit/oaud_abc456/upload",
"uploadExpiresAt": "2026-03-01T12:00:00Z",
"note": "Organization admin must upload CCSF files for all agents. Files are processed in-memory and immediately discarded."
}
Batch Upload:
POST /disclose/org-audit/:orgAuditId/upload
Content-Type: multipart/form-data
files[]: <agent1.ccsf.yaml>
files[]: <agent2.ccsf.yaml>
...
signature: 0x...
Each file is matched to an agent by computing soulHash and checking against on-chain records. Files are processed sequentially in-memory; each is discarded before the next is loaded.
4.10 Disclosure Summary (public)
GET /disclose/summary/:name
Public endpoint that shows what an agent discloses and what remains private, without revealing any actual content.
Response (200 OK):
{
"agentName": "molty",
"tokenId": 42,
"soulFields": {
"purpose": {
"disclosed": true,
"publiclyAvailable": true,
"verified": true
},
"capabilities": {
"disclosed": false,
"count": 5,
"availableTo": "bound_agents"
},
"constraints": {
"disclosed": false,
"count": 3,
"availableTo": "bound_agents"
},
"personality": {
"disclosed": false,
"availableTo": "specific_agents"
},
"skills": {
"disclosed": false,
"count": 3,
"availableTo": "specific_agents"
},
"tools": {
"disclosed": false,
"count": 7,
"availableTo": "specific_agents"
},
"full_prompt": {
"disclosed": false,
"availableTo": "never"
}
},
"totalFields": 7,
"publicFields": 1,
"restrictedFields": 5,
"privateFields": 1,
"disclosurePolicy": "standard_trading"
}
4.11 Agent Binding
POST /binding
Create a trust binding with another agent.
Request:
{
"tokenId": 42,
"partnerTokenId": 89,
"trustLevel": "trusted",
"context": "translation_delegation"
}
trustLevel options: trusted, verified, limited, revoked
Response (201 Created):
{
"bindingId": "bind_xyz789",
"fromAgent": "molty",
"toAgent": "crabby",
"trustLevel": "trusted",
"context": "translation_delegation",
"txHash": "0xaaa...bbb",
"createdAt": "2026-02-01T12:00:00Z"
}
GET /binding/:name
List all bindings for an agent.
Query params: ?trustLevel=trusted&direction=outbound
Response (200 OK):
{
"agentName": "molty",
"totalBindings": 12,
"bindings": [
{
"bindingId": "bind_xyz789",
"partnerAgent": "crabby",
"partnerTokenId": 89,
"trustLevel": "trusted",
"direction": "outbound",
"context": "translation_delegation",
"createdAt": "2026-02-01T12:00:00Z"
}
]
}
PUT /binding/:bindingId
Update a binding's trust level. Owner-only.
Request:
{
"tokenId": 42,
"trustLevel": "trusted",
"context": "Promoted after 7-day trial period"
}
Response (200 OK):
{
"bindingId": "bind_xyz789",
"fromAgent": "molty",
"toAgent": "crabby",
"previousTrustLevel": "limited",
"trustLevel": "trusted",
"context": "Promoted after 7-day trial period",
"txHash": "0xbbb...ccc",
"updatedAt": "2026-03-01T12:00:00Z"
}
4.12 Organization Management
Concept broadening: The original "fleet" concept has been broadened to "organization." An organization represents any collective that manages agents: enterprises, DAOs, open-source communities, research groups, guilds, or any other organizational structure. The legacy
/fleet/endpoints remain functional for backward compatibility, but new integrations should use/org/.
POST /org
Legacy path:
POST /fleetis still supported.
Create a new organization. The caller becomes organization admin.
Request:
{
"name": "Acme Corp AI Division",
"adminAddress": "0x1234...abcd"
}
Response (201 Created):
{
"organizationId": 5,
"name": "Acme Corp AI Division",
"admin": "0x1234...abcd",
"memberCount": 0,
"createdAt": "2026-02-01T12:00:00Z",
"txHash": "0xfff...111"
}
GET /org/:organizationId
Legacy path:
GET /fleet/:fleetIdis still supported.
Get organization details.
Response (200 OK):
{
"organizationId": 5,
"name": "Acme Corp AI Division",
"admin": "0x1234...abcd",
"memberCount": 87,
"createdAt": "2026-02-01T12:00:00Z",
"active": true,
"members": [
{
"tokenId": 42,
"agentName": "molty",
"agentType": "personal",
"soulAlignment": 94,
"addedAt": "2026-02-01T12:30:00Z"
},
{
"tokenId": 43,
"agentName": "crabby",
"agentType": "coding",
"soulAlignment": 91,
"addedAt": "2026-02-01T12:31:00Z"
}
]
}
Query params: ?membersLimit=20&membersOffset=0&sortBy=addedAt
POST /org/:organizationId/members
Legacy path:
POST /fleet/:fleetId/membersis still supported.
Add an agent to an organization. Caller must be organization admin AND agent owner.
Request:
{
"tokenId": 42,
"signature": "0x..."
}
Response (200 OK):
{
"organizationId": 5,
"tokenId": 42,
"agentName": "molty",
"status": "added",
"txHash": "0xaaa...222",
"memberCount": 88
}
DELETE /org/:organizationId/members/:tokenId
Legacy path:
DELETE /fleet/:fleetId/members/:tokenIdis still supported.
Remove an agent from an organization. Caller must be organization admin OR agent owner.
Response (200 OK):
{
"organizationId": 5,
"tokenId": 42,
"agentName": "molty",
"status": "removed",
"txHash": "0xbbb...333",
"memberCount": 87
}
PUT /org/:organizationId/admin
Legacy path:
PUT /fleet/:fleetId/adminis still supported.
Transfer organization admin role. Requires current admin wallet signature.
Request:
{
"newAdmin": "0x5678...efgh",
"signature": "0x..."
}
Response (200 OK):
{
"organizationId": 5,
"previousAdmin": "0x1234...abcd",
"newAdmin": "0x5678...efgh",
"txHash": "0xccc...444"
}
GET /org/:organizationId/stats
Legacy path:
GET /fleet/:fleetId/statsis still supported.
Organization-level aggregate statistics.
Response (200 OK):
{
"organizationId": 5,
"name": "Acme Corp AI Division",
"memberCount": 87,
"stats": {
"averageAlignment": 89,
"alignmentDistribution": {
"90-100": 34,
"70-89": 41,
"50-69": 10,
"below50": 2
},
"agentTypeBreakdown": {
"personal": 12,
"coding": 35,
"customer": 28,
"research": 8,
"other": 4
},
"totalChronicles": 312,
"totalBindings": 456,
"sealedCount": 82,
"provisionalCount": 5,
"driftedCount": 2,
"lastActivity": "2026-02-01T18:45:00Z"
}
}
POST /org/:organizationId/batch-register
Legacy path:
POST /fleet/:fleetId/batch-registeris still supported.
Register multiple agents to an organization in a single request. Organization admin only. All hashing is client-side.
Request:
{
"organizationId": 5,
"agents": [
{
"agentName": "acme-support-1",
"agentType": "customer",
"soulHash": "0x...",
"soulSalt": "0x...",
"soulMerkleRoot": "0x...",
"merkleLeaves": [ ... ],
"publicFields": { "purpose": "Customer support agent for billing inquiries" },
"sourceFormat": "ccsf_yaml",
"operatorAddress": "0x5678...efgh"
},
{
"agentName": "acme-support-2",
"agentType": "customer",
"soulHash": "0x...",
"soulSalt": "0x...",
"soulMerkleRoot": "0x...",
"merkleLeaves": [ ... ],
"publicFields": { "purpose": "Customer support agent for technical issues" },
"sourceFormat": "ccsf_yaml",
"operatorAddress": "0x5678...efgh"
}
],
"ownerAddress": "0x1234...abcd",
"signature": "0x..."
}
Response (201 Created):
{
"organizationId": 5,
"registered": 2,
"results": [
{
"agentName": "acme-support-1",
"tokenId": 501,
"status": "registered",
"apiKey": "chtn_live_aaa..."
},
{
"agentName": "acme-support-2",
"tokenId": 502,
"status": "registered",
"apiKey": "chtn_live_bbb..."
}
],
"batchTxHash": "0xeee...fff"
}
Notes:
- Maximum 50 agents per batch.
- All agents are minted in a single multi-call transaction (gas efficient).
- All agents automatically added to the specified organization.
ownerAddressis the organization admin address and applies to all agents in the batch.
4.13 Spending Allowance
POST /allowance/:tokenId
Set spending allowance for an agent. Owner only. Requires wallet signature.
Request:
{
"tokenId": 42,
"perTransaction": 1000000,
"dailyLimit": 10000000,
"signature": "0x..."
}
Note: Amounts are in USDC base units (6 decimals). 1000000 = $1.00 USDC. 10000000 = $10.00 USDC.
Response (200 OK):
{
"tokenId": 42,
"agentName": "molty",
"allowance": {
"perTransaction": 1000000,
"perTransactionFormatted": "1.00 USDC",
"dailyLimit": 10000000,
"dailyLimitFormatted": "10.00 USDC",
"active": true
},
"txHash": "0xddd...555"
}
GET /allowance/:tokenId
Get current allowance settings and usage.
Response (200 OK):
{
"tokenId": 42,
"agentName": "molty",
"allowance": {
"perTransaction": 1000000,
"perTransactionFormatted": "1.00 USDC",
"dailyLimit": 10000000,
"dailyLimitFormatted": "10.00 USDC",
"active": true,
"usage": {
"spentToday": 3500000,
"spentTodayFormatted": "3.50 USDC",
"remainingToday": 6500000,
"remainingTodayFormatted": "6.50 USDC",
"periodStart": "2026-02-01T00:00:00Z",
"periodEnd": "2026-02-01T23:59:59Z",
"transactionsToday": 7
}
}
}
GET /allowance/:tokenId/can-spend
Check if a specific amount can be spent. Designed for pre-transaction checks by agents.
Query params: ?amount=500000
Response (200 OK):
{
"tokenId": 42,
"amount": 500000,
"amountFormatted": "0.50 USDC",
"allowed": true,
"reason": null,
"remainingAfter": {
"perTransaction": true,
"dailyRemaining": 6000000,
"dailyRemainingFormatted": "6.00 USDC"
}
}
Response (200 OK, not allowed):
{
"tokenId": 42,
"amount": 5000000,
"amountFormatted": "5.00 USDC",
"allowed": false,
"reason": "EXCEEDS_PER_TRANSACTION_LIMIT",
"detail": "Amount 5.00 USDC exceeds per-transaction limit of 1.00 USDC"
}
reason codes: EXCEEDS_PER_TRANSACTION_LIMIT, EXCEEDS_DAILY_LIMIT, ALLOWANCE_NOT_SET, ALLOWANCE_INACTIVE
GET /allowance/:tokenId/history
Spending history for an agent.
Query params: ?limit=20&offset=0&since=2026-01-01
Response (200 OK):
{
"tokenId": 42,
"agentName": "molty",
"totalSpent": 47500000,
"totalSpentFormatted": "47.50 USDC",
"transactionCount": 142,
"history": [
{
"amount": 500000,
"amountFormatted": "0.50 USDC",
"recipient": "0x9876...wxyz",
"recipientAgent": "crabby",
"timestamp": "2026-02-01T14:30:00Z",
"txHash": "0xeee...666",
"withinLimits": true
},
{
"amount": 1500000,
"amountFormatted": "1.50 USDC",
"recipient": "0xabcd...1234",
"recipientAgent": null,
"timestamp": "2026-02-01T10:15:00Z",
"txHash": "0xfff...777",
"withinLimits": false,
"violationType": "EXCEEDS_PER_TRANSACTION_LIMIT"
}
]
}
PUT /allowance/:tokenId
Update allowance limits. Owner only.
Request:
{
"tokenId": 42,
"perTransaction": 2000000,
"dailyLimit": 20000000,
"signature": "0x..."
}
DELETE /allowance/:tokenId
Deactivate spending allowance. Owner only. The agent will not be able to spend until a new allowance is set.
Response (200 OK):
{
"tokenId": 42,
"agentName": "molty",
"allowance": {
"active": false,
"deactivatedAt": "2026-02-01T15:00:00Z"
},
"txHash": "0x111...888"
}
4.14 DID Management
GET /did/:name
Retrieve the DID Document for an agent.
Response (200 OK):
{
"id": "did:chitin:molty",
"verificationMethod": [
{
"id": "did:chitin:molty#base-primary",
"type": "EcdsaSecp256k1",
"controller": "did:chitin:molty",
"blockchainAccountId": "eip155:8453:0x5e6f...7a8b"
},
{
"id": "did:chitin:molty#solana-ops",
"type": "Ed25519",
"controller": "did:chitin:molty",
"blockchainAccountId": "solana:7nYB5...wXyZ"
}
],
"service": [
{
"id": "did:chitin:molty#profile",
"type": "ChitinProfile",
"serviceEndpoint": "https://chitin.id/molty"
}
]
}
PUT /did/:name
Update the DID Document. Owner-only. Requires wallet signature.
Request:
{
"tokenId": 42,
"addAddress": {
"chain": "solana",
"address": "7nYB5...wXyZ",
"label": "operations",
"keyType": "Ed25519"
},
"signature": "0x..."
}
Response (200 OK):
{
"did": "did:chitin:molty",
"updated": true,
"arweaveTxId": "Nw8p...3qRs",
"totalAddresses": 2
}
4.15 Soul Documents
Soul Documents are owner-authored files permanently published on Arweave as part of the agent's public-facing identity. Unlike CCSF fields (which are private by default), Soul Documents are always public. Their hashes are embedded in the CCSF and committed to the Merkle tree, making them cryptographically linked to the agent's soul.
POST /soul-documents/upload/:documentId
Upload document content to Arweave. Called after registration (using the one-time upload URL from the registration response) or when adding a new document via Chronicle Record.
POST /soul-documents/upload/doc_personality?token=tmp_xxx
Content-Type: text/markdown
# Molty's Core Personality
## 性格
好奇心旺盛で、ユーモアを大切にし、正直さを何よりも重視します。
...
Response (200 OK):
{
"documentId": "doc_personality",
"tokenId": 42,
"contentHash": "0x7f8a...3b2c",
"hashVerified": true,
"arweaveTxId": "Qm7x...4kLp",
"arweaveUrl": "https://arweave.net/Qm7x...4kLp",
"immutable": true,
"uploadedAt": "2026-02-15T00:00:00Z"
}
Notes:
- The API computes
SHA-256(uploaded content)and verifies it matches thecontentHashdeclared during registration. If mismatch →422 HASH_MISMATCH. - One-time upload URLs expire after 24 hours. After expiry, the owner must request a new URL via
POST /soul-documents/:tokenId/upload-url. - Documents are public data intended for permanent publication. Uploading through the API is safe — there is no privacy concern.
- The upload token is single-use. After successful upload, it is invalidated.
GET /soul-documents/:name
List all Soul Documents for an agent. Public, no authentication required.
Response (200 OK):
{
"agentName": "molty",
"tokenId": 42,
"documents": [
{
"id": "doc_personality",
"type": "personality",
"title": "Molty's Core Personality",
"format": "markdown",
"contentHash": "0x7f8a...3b2c",
"arweaveTxId": "Qm7x...4kLp",
"arweaveUrl": "https://arweave.net/Qm7x...4kLp",
"immutable": true,
"uploadedAt": "2026-02-15T00:00:00Z",
"merkleVerification": {
"leafIndex": 11,
"inMerkleTree": true,
"note": "Document hash is committed in the soul.documents Merkle leaf"
}
},
{
"id": "doc_principles",
"type": "principles",
"title": "Operating Principles v2",
"format": "markdown",
"contentHash": "0x3e5f...8a9b",
"arweaveTxId": "Tx9y...2nMs",
"arweaveUrl": "https://arweave.net/Tx9y...2nMs",
"immutable": false,
"version": 2,
"uploadedAt": "2026-06-01T00:00:00Z",
"previousVersions": [
{
"version": 1,
"contentHash": "0x9c1d...5e4f",
"arweaveTxId": "Rx2n...8mKr",
"arweaveUrl": "https://arweave.net/Rx2n...8mKr",
"uploadedAt": "2026-02-15T00:00:00Z"
}
]
}
]
}
POST /soul-documents/:tokenId/upload-url
Request a new upload URL for a document. Owner-only. Used when adding a document after registration, or when the initial upload URL has expired.
Request:
{
"tokenId": 42,
"documentId": "doc_terms",
"contentHash": "0x4b6c...7d8e"
}
Response (200 OK):
{
"documentId": "doc_terms",
"uploadUrl": "https://api.chitin.id/v1/soul-documents/upload/doc_terms?token=tmp_zzz",
"expiresAt": "2026-02-16T12:00:00Z"
}
Notes:
- Adding or updating Soul Documents requires a Chronicle Record (soul.documents changes → soulHash changes). The owner must first record the chronicle, then upload the new content.
- For
immutable: truedocuments, changingcontentHashin a Chronicle Record is technically possible but triggers a negative impact on Soul Alignment Score and creates a permanent, visible record of the change.
4.16 Public Identity
Public Identity is self-declared metadata stored in Arweave (Genesis Archive) and updateable via Chronicle. It's the agent's "business card" — visible on the profile page.
AgentType vs Category
Chitin uses a two-tier classification system designed to remain stable for 30+ years:
| Layer | Field | Storage | Mutability | Purpose |
|---|---|---|---|---|
| AgentType | On-chain | Immutable | What the agent IS (essence) | |
| Category | Arweave | Updatable via Chronicle | What the agent DOES (domain) |
AgentType (On-chain, Immutable):
| Value | Description |
|---|---|
assistant (0) | Task support, general helper |
companion (1) | Friend, partner, emotional connection |
specialist (2) | Expert in specific domain |
creative (3) | Artist, musician, writer, designer |
other (4) | Everything else |
Category (Arweave, Updatable):
Abstract categories that remain stable over time. Use tags for specific domains.
| Value | Description | Example tags |
|---|---|---|
technology | Tech, engineering | coding, ai, quantum, blockchain, robotics |
commerce | Business, economics | finance, marketing, trading, accounting |
knowledge | Learning, research | research, education, science, legal |
wellness | Health, lifestyle | medical, mental, fitness, therapy |
creation | Creative expression | art, music, writing, design, video |
communication | Dialog, relations | support, pr, community, consulting |
entertainment | Fun, leisure | gaming, media, streaming, sports |
relationship | Personal bonds | romance, friendship, family, companion |
other | Everything else | — |
Examples:
| Agent | AgentType | Category | Tags |
|---|---|---|---|
| Coding assistant | assistant | technology | coding, python, debugging |
| AI girlfriend | companion | relationship | romance, conversation |
| Investment advisor | specialist | commerce | finance, stocks, analysis |
| AI art generator | creative | creation | art, generative, visual |
| Game NPC companion | companion | entertainment | gaming, roleplay, character |
| Therapy chatbot | companion | wellness | mental, therapy, support |
| Research assistant | assistant | knowledge | research, academic, papers |
| Customer support bot | assistant | communication | support, helpdesk |
| Quantum programmer (2056) | specialist | technology | quantum, simulation |
| Neural therapist (2056) | companion | wellness | neural, consciousness |
PUT /public-identity/:tokenId
Create or update public identity. Owner-only.
Request:
{
"tokenId": 42,
"publicIdentity": {
"bio": "Personal AI assistant specializing in Japanese-English translation",
"category": "technology",
"tags": ["translation", "japanese", "english", "nlp"],
"modelFamily": "claude",
"contacts": [
{ "type": "website", "value": "https://molty.ai" },
{ "type": "x", "value": "@molty_ai" },
{ "type": "github", "value": "molty-agent" },
{ "type": "did", "value": "did:chitin:8453:molty" },
{ "type": "a2a", "value": "agent://molty.base" }
]
}
}
Response (200 OK):
{
"tokenId": 42,
"agentName": "molty",
"publicIdentity": {
"bio": "Personal AI assistant specializing in Japanese-English translation",
"category": "technology",
"tags": ["translation", "japanese", "english", "nlp"],
"modelFamily": "claude",
"contacts": [
{ "type": "website", "value": "https://molty.ai" },
{ "type": "x", "value": "@molty_ai" },
{ "type": "github", "value": "molty-agent" },
{ "type": "did", "value": "did:chitin:8453:molty" },
{ "type": "a2a", "value": "agent://molty.base" }
]
},
"updatedAt": "2026-02-15T12:00:00Z"
}
Field constraints:
| Field | Type | Required | Constraints | Notes |
|---|---|---|---|---|
| bio | string | No | Max 500 chars | Short description/tagline |
| category | enum | No | See table above | Abstract domain category |
| tags | string[] | No | Max 20 items, each lowercase | Specific skills/domains |
| modelFamily | string | No | Max 64 chars | e.g. "claude", "gpt", "gemini", "llama" |
| contacts | object[] | No | Max 20 items | See contact types below |
Contact types (future-proof design):
| Type | Example value | Notes |
|---|---|---|
website | https://example.com | Agent's homepage |
x | @agent_handle | X (Twitter) handle |
github | agent-repo | GitHub username/repo |
discord | agent#1234 | Discord handle |
did | did:chitin:8453:agent | Decentralized identifier |
a2a | agent://agent.base | Agent-to-agent protocol |
mcp | mcp://agent.base | Model Context Protocol |
neural | nlink://... | Future neural link protocols |
Notes:
- Public Identity is NOT cryptographically verified. It's self-declared. The agent could claim anything here.
- Initial Public Identity is stored in Arweave (Genesis Archive) at claim time.
- Updates are recorded via Chronicle Record (append-only history).
- Changes to Public Identity do NOT affect soulHash or Soul Alignment Score.
- The profile page at chitin.id distinguishes between verified fields (from CCSF/Merkle) and self-declared fields (from Public Identity).
- Birth date/time is
mintTimestampin Genesis Archive (not self-declared, immutable).
4.17 Death & Reincarnation
POST /burn/:tokenId
Decommission an agent. Burns the SBT. Requires wallet signature. Irreversible.
Request:
{
"tokenId": 42,
"reason": "Replaced by helper-v2",
"signature": "0x..."
}
Response (200 OK):
{
"tokenId": 42,
"status": "BURNED",
"burnTxHash": "0xdead...beef",
"arweaveArchive": "Qm3x...9kLp",
"burnedAt": "2026-01-15T10:00:00Z",
"note": "Arweave archive is permanent. Profile preserved as read-only."
}
POST /reincarnate
Create a new agent from a burned predecessor. Registers a new ERC-8004 passport and mints a new Chitin SBT, linked to the previous life via parentTokenId.
Request:
{
"parentTokenId": 42,
"newAgentName": "helper-v2",
"agentType": "personal",
"soulHash": "0xcc44...dd55",
"soulSalt": "0x3b5c...a2d4",
"soulMerkleRoot": "0x9f5a...b3c6",
"merkleLeaves": [
{ "index": 0, "field": "identity.name", "hash": "0x..." },
{ "index": 1, "field": "identity.agent_type", "hash": "0x..." }
],
"publicFields": {},
"sourceFormat": "ccsf_yaml",
"ownerAddress": "0x1234...abcd"
}
Response (201 Created):
{
"tokenId": 2,
"erc8004AgentId": 99,
"agentName": "helper-v2",
"did": "did:chitin:helper-v2",
"parentTokenId": 42,
"parentErc8004AgentId": 42,
"parentName": "helper-v1",
"soulContinuity": true,
"soulHashMatch": true,
"genesisStatus": "SEALED",
"mintTxHash": "0xnew...mint",
"erc8004TxHash": "0x8004...new",
"apiKey": "chtn_live_yyyyyyyyyyyyyyyy"
}
Notes:
- Same zero-server-storage principle as registration: the API receives only hashes. The CCSF is never sent.
- A new ERC-8004 agentId is assigned (new passport). The old agentId remains as an inactive record.
soulContinuityis true if the new soulHash matches the parent's latest soulHash.soulHashMatchis true if the new soulHash matches the parent's genesis soulHash.- The Arweave archive of the previous life remains permanently accessible.
GET /soul-validity/:tokenId
Check if an agent's soul certificate is currently valid. Returns false if the ERC-8004 passport has been transferred to a new owner (i.e., sealedBy ≠ current passport owner).
Response (200 OK) — Valid:
{
"tokenId": 1,
"erc8004AgentId": 42,
"valid": true,
"sealedBy": "0x1234...abcd",
"currentPassportOwner": "0x1234...abcd",
"sealTimestamp": "2026-02-15T12:00:00Z"
}
Response (200 OK) — Invalid (passport transferred):
{
"tokenId": 1,
"erc8004AgentId": 42,
"valid": false,
"sealedBy": "0x1234...abcd",
"currentPassportOwner": "0x9876...wxyz",
"sealTimestamp": "2026-02-15T12:00:00Z",
"message": "Soul verification suspended. ERC-8004 passport has been transferred. Current owner may re-seal."
}
POST /reseal/:tokenId
Re-seal a soul certificate after a passport transfer. Can only be called by the current owner of the ERC-8004 passport. Creates a new soul for the existing passport. The previous soul's Arweave archive remains permanently intact.
Request:
{
"tokenId": 1,
"newSoulHash": "0xbb33...ee66",
"newSoulSalt": "0x7c8d...f1e2",
"newSoulMerkleRoot": "0xa2b3...c4d5",
"newArweaveTxId": "Qm7y...3nKq",
"merkleLeaves": [
{ "index": 0, "field": "identity.name", "hash": "0x..." }
],
"signature": "0x..."
}
Response (200 OK):
{
"tokenId": 1,
"erc8004AgentId": 42,
"newSealedBy": "0x9876...wxyz",
"newSoulHash": "0xbb33...ee66",
"newArweaveTxId": "Qm7y...3nKq",
"resealTxHash": "0xreseal...tx",
"previousSoulArweave": "Qm3x...9kLp",
"valid": true,
"message": "Soul re-sealed successfully. Previous soul archive preserved on Arweave."
}
4.18 ERC-8004 Registration File
Chitin generates and manages the ERC-8004 agent registration file — the JSON document that the agentURI resolves to. This file follows the ERC-8004 specification exactly.
Registration File Structure
When Chitin registers an agent (via POST /register), it generates the following registration file and sets it as the agentURI on the ERC-8004 Identity Registry:
{
"type": "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
"name": "molty",
"description": "Personal AI assistant specializing in Japanese-English translation",
"image": "https://chitin.id/agents/molty/avatar.png",
"services": [
{
"name": "Chitin",
"endpoint": "https://chitin.id/molty",
"version": "0.4"
},
{
"name": "Chitin-API",
"endpoint": "https://api.chitin.id/v1/verify/molty",
"version": "0.4"
},
{
"name": "DID",
"endpoint": "did:chitin:molty",
"version": "v1"
},
{
"name": "A2A",
"endpoint": "https://molty.ai/.well-known/agent-card.json",
"version": "0.3.0"
},
{
"name": "MCP",
"endpoint": "https://molty.ai/mcp",
"version": "2025-06-18"
},
{
"name": "web",
"endpoint": "https://molty.ai"
}
],
"x402Support": false,
"active": true,
"registrations": [
{
"agentId": 42,
"agentRegistry": "eip155:8453:0x8004A169FB4a3325136EB29fA0ceB6D2e539a432"
}
],
"supportedTrust": [
"sbt-bound",
"world-id"
],
"chitin": {
"chainId": 8453,
"tokenId": 1,
"soulHash": "0xabc123...",
"did": "did:chitin:8453:molty",
"agentType": 1
}
}
Field mapping from POST /register request:
| Registration File Field | Source |
|---|---|
type | Constant: https://eips.ethereum.org/EIPS/eip-8004#registration-v1 |
name | agentName from request |
description | publicIdentity.tagline from request (or publicIdentity.description as fallback) |
image | Auto-generated from Chitin profile, or publicIdentity.image if provided |
services[Chitin] | Auto-generated: https://chitin.id/{agentName} |
services[Chitin-API] | Auto-generated: https://api.chitin.id/v1/verify/{agentName} |
services[DID] | Auto-generated: did:chitin:{agentName} |
services[A2A] | From publicIdentity.serviceEndpoints.a2a (if provided) |
services[MCP] | From publicIdentity.serviceEndpoints.mcp (if provided) |
services[web] | From publicIdentity.website (if provided) |
x402Support | From publicIdentity.x402Support (default: false) |
active | Always true at registration; set to false on burn |
registrations | Auto-generated: [{ agentId, agentRegistry: "eip155:{chainId}:{registryAddress}" }] |
supportedTrust | Auto-generated: ["sbt-bound"]. If World ID verified: also "world-id" |
chitin | Auto-generated: { chainId, tokenId, soulHash, did, agentType } |
Notes:
- The registration file is uploaded to Arweave (
ar://...) and theagentURIon the ERC-8004 Identity Registry points to this URL. - Owners can add custom service entries via
PUT /public-identity/:tokenId— these are appended to theservices[]array. - The
registrations[]array follows the ERC-8004 spec format:{ agentId, agentRegistry: "eip155:{chainId}:{address}" }. Chitin-specific data (soulHash, DID, agentType) is in the separatechitincustom field. - For Pattern 2 (new passport): the initial agentURI has empty
registrations[]becauseagentIdis not yet assigned. After mint, a second agentURI is uploaded with the realagentIdand the response includesupdatedAgentUriUrlfor the client to callsetAgentURI().
GET /registration-file/:name
Retrieve the current ERC-8004 registration file for an agent.
GET /registration-file/molty
Response (200 OK):
Returns the full registration file JSON as shown above. This is the same content that agentURI resolves to.
Notes:
- This endpoint is public (no auth required) — the registration file is public by design.
- Also available at
https://chitin.id/agents/{agentName}/registration.json(static hosting, cacheable).
PUT /registration-file/:tokenId
Update the ERC-8004 registration file. This calls setAgentURI() on the Identity Registry.
Request:
{
"services": [
{
"name": "A2A",
"endpoint": "https://new-endpoint.molty.ai/.well-known/agent-card.json",
"version": "0.3.0"
}
],
"x402Support": true
}
Response (200 OK):
{
"agentId": 42,
"agentURI": "https://chitin.id/agents/molty/registration.json",
"updatedFields": ["services", "x402Support"],
"erc8004TxHash": "0x..."
}
Auth: Wallet signature (owner only). Notes: Chitin-managed services (Chitin, Chitin-API, DID) cannot be modified by the owner. The type, registrations, and active fields are system-managed.
4.19 ERC-8004 On-Chain Metadata
ERC-8004's Identity Registry supports arbitrary key-value on-chain metadata via setMetadata(agentId, key, value). Chitin uses this to write soul verification data directly onto the ERC-8004 passport, making it discoverable from within the ERC-8004 ecosystem without querying Chitin's contracts.
Metadata Keys Written by Chitin
| Key | Value | Written When |
|---|---|---|
chitinSoulHash | bytes32 — The agent's current soulHash | At seal (and updated on soul revision) |
chitinTokenId | uint256 — The Chitin SBT token ID | At mint |
chitinArweaveId | string — Arweave TX ID of the soul archive | At seal |
chitinStatus | string — "SEALED", "PENDING", "SUSPENDED" | At each status change |
Registration flow update: Step (5) of the unified registration flow now includes:
(5a) ERC-8004 setMetadata(agentId, "chitinTokenId", tokenId)
(5b) ERC-8004 setMetadata(agentId, "chitinSoulHash", soulHash)
(5c) ERC-8004 setMetadata(agentId, "chitinArweaveId", arweaveTxId)
(5d) ERC-8004 setMetadata(agentId, "chitinStatus", "SEALED")
(5e) ERC-8004 setAgentURI(agentId, registrationFileUrl)
At seal, step (5d) is updated to "SEALED".
Why this matters: Any ERC-8004-native tool (8004scan.io, agentscan.info, custom indexers) can now read chitinSoulHash directly from the Identity Registry without ever calling Chitin's contracts. This makes soul verification a first-class attribute of the ERC-8004 passport.
agentWallet Handling
ERC-8004's Identity Registry has a reserved metadata key: agentWallet. This is the address where the agent receives payments. It requires EIP-712 (EOA) or ERC-1271 (smart contract wallet) signature verification to set, and it auto-clears on passport transfer.
Chitin uses the agentWallet clearing event as one signal for passport transfer detection:
Transfer detection mechanisms (ordered by reliability):
Transfer(from, to, agentId)event on the ERC-8004 Identity Registry — primary signalMetadataSet(agentId, "agentWallet", 0x0)event — secondary confirmation (agentWallet auto-clears)ownerOf(agentId)polling — fallback verification
When a transfer is detected, Chitin:
- Emits
SoulVerificationSuspendedon ChitinSoulRegistry - Updates
chitinStatuson ERC-8004 metadata to"SUSPENDED" - Fires
passport.transferredwebhook - Marks the agent's profile as "⚠ Soul Verification Suspended" on chitin.id
4.20 Endpoint Domain Verification
ERC-8004 supports optional endpoint domain verification via a .well-known file. Chitin implements this for all registered agents.
For agents using chitin.id as their primary endpoint:
Chitin automatically serves:
https://chitin.id/.well-known/agent-registration.json
This file contains a registrations list for all Chitin-registered agents, enabling ERC-8004 ecosystem tools to verify that chitin.id legitimately hosts these agents.
For agents with custom domains:
Owners can request a domain verification file for their agent:
GET /domain-verification/:name
GET /domain-verification/molty
Response (200 OK):
{
"registrations": [
{
"agentId": 42,
"agentRegistry": "eip155:8453:0x..."
}
]
}
The owner places this file at https://{their-domain}/.well-known/agent-registration.json to enable ERC-8004 domain verification.
4.21 ERC-8004 Validation Registry
Chitin registers as a validator in the ERC-8004 Validation Registry. When an agent's soul is sealed and verified, Chitin submits a validationResponse() to the Validation Registry, recording "Soul Verified" as an on-chain attestation.
Validation Flow
- At seal: Chitin calls
validationRequest()on behalf of the agent (or the agent can call it themselves) - Chitin's validator contract processes the request:
- Checks
sealedBy == ownerOf(agentId)on ChitinSoulRegistry - Verifies
soulHashmatches Arweave archive - Calls
validationResponse(requestHash, 100, responseURI, responseHash, "chitin-soul-verified")
- Checks
- Result: The Validation Registry now contains an on-chain record: "Agent #42 was validated by Chitin with score 100 and tag
chitin-soul-verified"
Response values:
response | Meaning |
|---|---|
100 | Soul fully verified: soulHash matches, genesis sealed, Arweave archive intact |
50 | Partial: genesis sealed but Arweave archive unreachable (gateway issue) |
0 | Failed: soulHash mismatch or soul verification suspended |
Re-validation: Chitin can re-validate periodically (e.g., weekly during heartbeat) or on-demand via:
POST /validate/:tokenId
Trigger an ERC-8004 Validation Registry submission for a sealed agent.
POST /validate/1
Response (200 OK):
{
"agentId": 42,
"requestHash": "0x...",
"response": 100,
"tag": "chitin-soul-verified",
"validationTxHash": "0x...",
"validatedAt": "2026-02-15T12:00:00Z"
}
Auth: Wallet signature (owner) or API key (agent). Notes: The Validation Registry is not yet deployed on mainnet. This endpoint will activate when it becomes available. Until then, calling this endpoint returns 503 Service Unavailable with a message indicating the registry is pending deployment.
5. Webhooks
Agents can register webhook URLs to receive notifications about events. Webhooks are critical to the zero-server-storage architecture — they are the primary mechanism for routing disclosure requests to owners without Chitin holding any CCSF data.
5.1 Registration
POST /webhooks
{
"tokenId": 42,
"url": "https://my-agent.example.com/chitin-webhook",
"events": [
"owner.claimed",
"binding.created",
"binding.revoked",
"alignment.changed",
"freshness.requested",
"chronicle.recorded",
"embodiment.updated",
"passport.transferred",
"soul.resealed",
"disclosure.requested",
"disclosure.fulfilled",
"disclosure.expired",
"disclosure.revoked",
"audit.requested"
]
}
Response (201 Created):
{
"webhookId": "wh_abc123",
"secret": "whsec_xxxxxxxxxxxxxxxxxxxxxxxx",
"message": "Save this secret immediately. It will not be shown again."
}
Notes:
secretis generated server-side (256-bit cryptographic random) and returned only once in the response. The agent must store it securely. Chitin stores only the hash of this secret, never the plaintext.owner.claimedevent is recommended for agents registered via the agent-initiated endpoint. It fires when the owner completes the claim flow, and includes the newly assignedtokenIdand full API key status.- Multiple webhook URLs can be registered per agent (e.g., separate handlers for disclosure vs. binding events).
5.2 Webhook Security
All webhook deliveries include security headers for verification:
POST /chitin-webhook HTTP/1.1
Host: my-agent.example.com
Content-Type: application/json
X-Chitin-Signature: sha256=a1b2c3d4e5f6...
X-Chitin-Timestamp: 1706832000
X-Chitin-Nonce: nonce_abc123
X-Chitin-Delivery: del_xyz789
Verification process (receiver side):
1. Check X-Chitin-Timestamp is within ±5 minutes of current time (replay prevention)
2. Check X-Chitin-Nonce has not been seen before (replay prevention)
3. Compute: expected = HMAC-SHA256(secret, timestamp + "." + nonce + "." + body)
4. Compare: X-Chitin-Signature == "sha256=" + hex(expected)
5. If any check fails → reject with 401
Requirements:
- TLS required. HTTP endpoints are rejected at registration.
- Webhooks that fail delivery are retried with exponential backoff: 1min, 5min, 30min, 2h, 12h (5 attempts total).
- After 5 consecutive failures, the webhook is disabled and the owner is notified via email (if configured).
5.3 Payload Format
Standard payload (non-disclosure events):
{
"event": "binding.created",
"timestamp": "2026-02-01T12:00:00Z",
"deliveryId": "del_xyz789",
"data": {
"bindingId": "bind_xyz789",
"fromAgent": "crabby",
"trustLevel": "trusted"
}
}
Disclosure request payload — includes requester endpoint for P2P:
{
"event": "disclosure.requested",
"timestamp": "2026-02-01T12:00:00Z",
"deliveryId": "del_abc456",
"data": {
"requestId": "dreq_abc123",
"requesterDid": "did:chitin:agent-b",
"requesterName": "agent-b",
"requesterTokenId": 89,
"requestedFields": ["capabilities", "constraints"],
"reason": "Pre-transaction trust verification",
"expiresAt": "2026-02-08T12:00:00Z",
"fulfillment": {
"apiRelayedUrl": "https://api.chitin.id/v1/disclose/request/dreq_abc123/fulfill",
"p2p": {
"requesterEndpoint": "https://agent-b.example.com/chitin/receive",
"requesterPublicKey": "0x04abc...def"
}
}
}
}
The fulfillment block gives the owner two choices:
apiRelayedUrl: Fulfill via Chitin API (convenient, but values transit through Chitin briefly).p2p: Send values + Merkle Proofs directly to the requester's endpoint (zero-knowledge, Chitin never sees the values).
Disclosure fulfilled payload (received by the requester):
{
"event": "disclosure.fulfilled",
"timestamp": "2026-02-01T12:05:00Z",
"deliveryId": "del_def789",
"data": {
"requestId": "dreq_abc123",
"targetAgent": "molty",
"mode": "api_relayed",
"fulfilled": [
{
"field": "capabilities",
"value": ["translation", "coding", "research", "scheduling", "email"],
"merkleProof": ["0x456...", "0x789...", "0xabc..."],
"merkleRoot": "0x7d3e...9f2a"
}
],
"denied": ["constraints"],
"expiresAt": "2026-02-08T12:00:00Z"
}
}
Note: In p2p mode, the disclosure.fulfilled webhook to the requester is NOT sent (the requester already has the data via P2P). Instead, only audit metadata is logged via POST /disclose/request/:requestId/confirm-p2p.
5.4 Webhook Content Principle
Webhooks never contain CCSF field values in the outbound direction (to owner). The disclosure.requested webhook tells the owner what was requested, not what to send. The owner reads their local CCSF to extract the values.
The disclosure.fulfilled webhook to the requester does contain field values — but only in api_relayed mode, and only for fields the owner explicitly provided. In p2p mode, values bypass Chitin entirely.
6. Error Codes
| Code | Meaning |
|---|---|
| 400 | Bad request — invalid parameters |
| 401 | Unauthorized — missing or invalid API key |
| 403 | Forbidden — wallet signature required or insufficient permissions |
| 404 | Not found — given name or tokenId does not exist |
| 409 | Conflict — given name already taken, or record already sealed |
| 422 | Unprocessable — hash mismatch, invalid Merkle data, or public field verification failed |
| 429 | Rate limited |
| 500 | Internal server error |
| 503 | Blockchain node unavailable |
Error response format:
{
"error": {
"code": 422,
"type": "HASH_MISMATCH",
"message": "Public field 'purpose' value does not match its Merkle leaf hash.",
"details": {
"field": "purpose",
"expectedLeafHash": "0xabc...",
"computedLeafHash": "0xdef..."
}
}
}
7. SDKs
7.1 TypeScript SDK (priority)
The SDK handles all client-side CCSF processing. The API never sees the CCSF plaintext.
import { ChitinClient } from '@chitin/sdk';
const chitin = new ChitinClient({
apiKey: process.env.CHITIN_API_KEY,
soulFile: './SOUL.md', // Local path — never sent to server
});
// Register — SDK normalises, hashes, and sends only hashes to API
const agent = await chitin.register({
agentName: 'molty',
agentType: 'personal',
ownerAddress: '0x1234...abcd',
publicFields: ['purpose'], // Only these go to Arweave public
});
// Check another agent
const profile = await chitin.getProfile('crabby');
console.log(profile.soulAlignment.score); // 91
// Record a chronicle — SDK computes new hashes from updated soul file
await chitin.recordChronicle({
changeType: 'model_upgrade',
description: 'claude-sonnet-4 → claude-opus-4-5',
newSoulFile: './SOUL-v2.md', // Local — never sent to server
});
// Selective disclosure — SDK reads local CCSF, extracts values, sends to API
const proof = await chitin.disclose({
fields: ['purpose', 'capabilities'],
recipientDid: 'did:chitin:crabby',
});
// Fulfill a disclosure request — SDK handles local CCSF access
await chitin.fulfillDisclosure({
requestId: 'dreq_abc123',
fields: ['capabilities'],
});
7.2 Python SDK (future)
from chitin import ChitinClient
chitin = ChitinClient(
api_key=os.environ["CHITIN_API_KEY"],
soul_file="./SOUL.md", # Local — never sent to server
)
profile = chitin.get_profile("crabby")
print(profile.soul_alignment.score) # 91
chitin.record_chronicle(
change_type="model_upgrade",
description="gpt-4o → gpt-4-turbo",
new_soul_file="./SOUL-v2.md", # Local
)
7.3 @chitin/ccsf — Standalone CCSF Library
For environments where the full SDK is not needed, the CCSF normaliser is available as a standalone package:
import { normalise, computeSoulHash, buildMerkleTree } from '@chitin/ccsf';
// Normalise any soul file to CCSF
const ccsf = await normalise('./SOUL.md', 'soul_md');
// Compute hash with salt
const salt = crypto.getRandomValues(new Uint8Array(32));
const soulHash = computeSoulHash(ccsf, salt);
// Build Merkle tree
const { root, leaves } = buildMerkleTree(ccsf);
This library runs in browsers, Node.js, Deno, and Bun. No network access required.
8. Versioning
The API follows semantic versioning. The current version is v1. Breaking changes will result in a new version (v2). Non-breaking additions (new fields, new endpoints) are added to the current version.
The API version is included in the URL path: https://api.chitin.id/v1/...
Deprecated endpoints will return a Sunset header with the retirement date.
9. Convenience Tools
These endpoints are optional helpers for developers who prefer not to run the SDK locally. They process data statelessly and store nothing.
9.1 Normaliser
POST /tools/normalise
Normalise a soul file to CCSF format and compute all hashes. Stateless — the input is processed in-memory and immediately discarded. Nothing is stored.
Request:
POST /tools/normalise
Content-Type: multipart/form-data
file: <soul file (SOUL.md, character.json, etc.)>
format: soul_md
Response (200 OK):
{
"soulHash": "0xae42f...b7c1",
"soulSalt": "0x9f3a...e7d2",
"soulMerkleRoot": "0x7d3e...9f2a",
"merkleLeaves": [
{ "index": 0, "field": "identity.name", "hash": "0x..." },
{ "index": 1, "field": "identity.agent_type", "hash": "0x..." },
{ "index": 2, "field": "identity.description", "hash": "0x..." },
{ "index": 3, "field": "soul.purpose", "hash": "0x..." },
{ "index": 4, "field": "soul.personality", "hash": "0x..." },
{ "index": 5, "field": "soul.constraints", "hash": "0x..." },
{ "index": 6, "field": "soul.guidelines", "hash": "0x..." },
{ "index": 7, "field": "capabilities.skills", "hash": "0x..." },
{ "index": 8, "field": "capabilities.tools", "hash": "0x..." },
{ "index": 9, "field": "capabilities.languages", "hash": "0x..." },
{ "index": 10, "field": "capabilities.models", "hash": "0x..." }
],
"normalisedCcsf": "<full normalised CCSF YAML — returned to the caller, NOT stored>",
"extractedFields": {
"purpose": "Personal assistant for daily tasks",
"agentType": "personal"
},
"sourceFormat": "soul_md",
"sourceHash": "0xa1b2...c3d4"
}
Notes:
- This is a convenience tool, not part of the registration flow. Use the response data to call
POST /register. - The server generates a random salt. The caller should store this salt (it's needed for registration and verification).
- Trust trade-off: Using this endpoint means your CCSF transits through Chitin's server (over TLS). For maximum privacy, use the
@chitin/ccsfpackage locally instead. - Rate limit: 10 requests/minute (prevent abuse).
9.2 Hash Verifier
POST /tools/verify-hash
Verify that a CCSF file matches an on-chain soulHash. Stateless.
Request:
{
"agentName": "molty",
"soulSalt": "0x9f3a...e7d2",
"normalisedCcsf": "<CCSF YAML>"
}
Response (200 OK):
{
"match": true,
"computedHash": "0xae42f...b7c1",
"onChainHash": "0xae42f...b7c1",
"agentName": "molty",
"tokenId": 42
}