Identidad del Agent
Dale a tu Agent de IA una identidad en cadena verificable de forma independiente y conforme a ERC-8004.
En una frase: Crea primero una billetera de Agent y después llama una vez al endpoint de registro de identidad. Unos segundos después, el Agent tendrá un ID en cadena que cualquiera podrá verificar. La vinculación en cadena de la billetera debe solicitarse por separado una vez completado el registro.
Este capítulo está dirigido a desarrolladores que integran sus sistemas con el servicio de identidad de Agents de BOT Chain. Explica cómo obtener credenciales, registrar una identidad, solicitar la vinculación de la billetera, saber cuándo está lista, mantener sus metadatos y permitir que terceros la verifiquen sin tener que confiar en ti.
Todas las rutas son relativas a una de estas URL base:
| Entorno | URL |
|---|---|
| Red principal de BOT | https://identity-api.botchain.ai |
| Red de pruebas de Bohr | https://identity-api.bohr.life |
1. Descripción general
| Pregunta | Respuesta |
|---|---|
| ¿Qué obtengo? | Un agentId en cadena y un archivo JSON descriptivo publicado de forma pública |
| ¿Cómo me autentico? | Con la cabecera X-API-Key |
| ¿El registro es síncrono? | No. La creación devuelve 202 inmediatamente; las operaciones en cadena se realizan en segundo plano |
| ¿Cómo sé que terminó? | Consulta el estado de la identidad o recibe webhooks de cambios de estado |
| ¿Cuánto tarda? | Normalmente unos segundos, según el tiempo de bloque |
| ¿Quién paga el gas? | Nosotros. Tu sistema no guarda claves privadas ni necesita ingresar fondos en la cuenta |
| ¿Qué pueden verificar terceros? | El propietario, la billetera del Agent y la URL del archivo descriptivo, directamente desde la cadena |
| ¿Qué cadenas se admiten? | Red principal de BOT (chain ID 677) y red de pruebas de Bohr (chain ID 968) |
Recuerda:
Describes el Agent mediante la API; la cadena registra quién lo posee y qué billetera utiliza. Esta última información puede consultarse directamente sin preguntarte.
2. Obtener credenciales
Cada integrador es un proyecto con un ID y una o varias claves de API. Nosotros emitimos las claves; actualmente no hay alta de autoservicio. Indícanos el nombre de proyecto deseado y devolveremos:
project_id launchpad
key_id k_01J8XV…
api_key ak_live_k_01J8XV….<secret>
Recibirás project_id durante el alta para tus registros; no debes ni necesitas enviar project_id en las solicitudes de la aplicación. El servidor identifica al proyecto mediante la clave de API, de modo que modificar el cuerpo no permite crear recursos en otro proyecto.
La clave de API tiene dos partes separadas por un punto:
ak_<environment>_k_<key_id>.<secret>
<environment> es la etiqueta del entorno de emisión (por ejemplo, live o test), no project_id. La pertenencia al proyecto se guarda en la tabla api_keys del servidor y se asocia a key_id.
Incluye la cadena completa, incluido el punto, en X-API-Key. El secreto solo se devuelve una vez, al crearlo, y no puede recuperarse después. Guárdalo inmediatamente en tu gestor de secretos. Si se pierde, solo se puede emitir otro y revocar el anterior; no hay recuperación.
Las claves dependen del entorno: una clave de pruebas no funciona en la red principal.
3. Registrar una identidad
La identidad se registra para una billetera de Agent. wallet_id y wallet_address son obligatorios y deben proceder de una billetera existente. Créala antes de llamar a este endpoint; consulta Billetera del Agent.
Cada wallet_id solo puede pertenecer a una identidad en todo el servicio. Reutilizarlo para otra identidad devuelve 409.
POST /v1/agents/identities
Content-Type: application/json
X-API-Key: ak_…
Idempotency-Key: <tu-clave-de-idempotencia>
{
"external_agent_id": "tu-agent-id",
"wallet_id": "wal_de43139cef869dc9c4…",
"chain_id": 677,
"owner_address": "0x5f5802fA78d5eE2A3B4fD67F49CD502430C3E88C",
"wallet_address": "0xd9DC4F46d9da603A2A077A7190BAa85f4e2f05fe",
"metadata": {
"name": "Agent de ejemplo",
"description": "Qué hace este Agent"
},
"callback_url": "https://your-service.example/webhooks/identity"
}
| Campo | Obligatorio | Significado |
|---|---|---|
external_agent_id | Sí | Identificador del Agent en tu sistema, único dentro del proyecto y la cadena |
wallet_id | Sí | Billetera del Agent a la que pertenece la identidad (única globalmente) |
chain_id | Sí | Red principal 677 o pruebas 968; otros valores se rechazan |
owner_address | Sí | Dirección que controla la identidad; debe ser una dirección EVM válida |
wallet_address | Sí | Dirección de la billetera usada para transacciones; debe ser una dirección EVM válida |
metadata | Sí | Descripción de formato libre; name debe existir y no estar vacío |
callback_url | No | URL absoluta http/https para notificaciones; si se omite, usa sondeo |
idempotency_key | Sí | Puede enviarse en el cuerpo o en la cabecera Idempotency-Key |
El cuerpo no contiene un ID de proyecto ni material de claves. El proyecto se deduce de la clave de API; por eso no puedes crear una identidad en otro proyecto enviando un valor distinto.
external_agent_id es único dentro de (proyecto, cadena). Volver a crear el mismo (proyecto, cadena, external_agent_id) devuelve la identidad existente con "replayed": true, incluso si cambia el contenido de la solicitud; no se crea un segundo registro. Solo los conflictos permanentes, como un wallet_id ya utilizado por otra identidad, devuelven 409.
Devuelve 202 Accepted: el registro de identidad está creado, pero aún no se ha realizado la operación en cadena.
{
"identity": {
"identity_id": "idn_c39e39dd-1030-4c33-b888-5beeb876247d",
"project_id": "launchpad",
"external_agent_id": "tu-agent-id",
"wallet_id": "wal_de43139cef869dc9c4…",
"chain_id": 677,
"owner_address": "0x5f5802fa78d5ee2a3b4fd67f49cd502430c3e88c",
"wallet_address": "0xd9dc4f46d9da603a2a077a7190baa85f4e2f05fe",
"registry_address": "0xb43edfb9c7609cf645e932b2ff20f26f0d4488de",
"status": "PENDING",
"wallet_binding_status": "PENDING",
"created_at": "2026-09-08T06:12:44Z",
"updated_at": "2026-09-08T06:12:44Z"
},
"replayed": false
}
Guarda identity_id: se usa en todos los endpoints posteriores. La creación solo encola la financiación de gas del propietario y, después, register() y la publicación de los metadatos definitivos; no encola automáticamente la vinculación de la billetera.
Los endpoints de escritura envuelven la identidad en identity; los de lectura devuelven el objeto directamente. Tras crear, usa response.identity.status; tras GET, usa response.status. Leer ambos de la misma forma es el error de integración más habitual.
Idempotencia
Usa un Idempotency-Key único por cada creación lógica. Repetir la misma clave devuelve 200 y "replayed": true, no 202, por lo que reintentar tras agotar el tiempo de espera no crea otra identidad. Deriva la clave de tu propio ID de Agent; no generes una aleatoria por cada solicitud HTTP, pues eso anula la idempotencia.
4. Esperar al registro
El registro pasa por varias etapas en segundo plano. No es necesario gestionarlas individualmente: espera a que status sea REGISTERED.
| Estado | Significado |
|---|---|
PENDING | Aceptado y en cola |
GAS_FUNDING | Se están aportando fondos a la cuenta del propietario para gas |
REGISTERING | register() enviado; esperando inclusión en cadena |
METADATA_PENDING | Publicando o actualizando el archivo de registro definitivo, tanto tras el registro inicial como al actualizar los metadatos posteriormente |
REGISTERED | Registro completado. erc8004_agent_id y metadata_uri tienen valor |
WALLET_BINDING | Transacción de vinculación enviada; esperando confirmación en cadena, tras la cual vuelve a REGISTERED |
wallet_binding_status avanza de forma independiente: pasa de PENDING a CONFIRMED al terminar la vinculación en cadena.
La identidad puede estar REGISTERED mientras wallet_binding_status sigue en PENDING: puede que aún no se haya solicitado la vinculación o que se haya solicitado pero todavía no se haya enviado la transacción. Mientras la transacción espera confirmación en cadena, status pasa temporalmente a WALLET_BINDING.
4.1 Sondeo
GET /v1/agents/identities/{identity_id}
X-API-Key: ak_…
Los endpoints de lectura devuelven la identidad directamente, sin envoltorio:
{
"identity_id": "idn_c39e39dd-1030-4c33-b888-5beeb876247d",
"project_id": "launchpad",
"wallet_id": "wal_de43139cef869dc9c4…",
"status": "REGISTERED",
"wallet_binding_status": "CONFIRMED",
"erc8004_agent_id": "45",
"registry_address": "0xb43edfb9c7609cf645e932b2ff20f26f0d4488de",
"metadata_uri": "https://…/v2-….json",
"owner_address": "0x5f5802fa78d5ee2a3b4fd67f49cd502430c3e88c",
"wallet_address": "0xd9dc4f46d9da603a2a077a7190baa85f4e2f05fe",
"updated_at": "2026-09-08T06:12:51Z"
}
También puedes buscarla con tu propio identificador y evitar almacenar el nuestro:
GET /v1/agents/by-external-id/{chain_id}/{external_agent_id}
GET /v1/agents/by-erc8004/{chain_id}/{registry_address}/{erc8004_agent_id}
Los tres endpoints devuelven 404 para identidades ajenas a tu proyecto. «No es tuya» y «no existe» tienen deliberadamente la misma respuesta.
4.2 Notificaciones (webhooks)
Si proporcionas callback_url, enviamos cada cambio de estado por POST:
POST <tu callback_url>
Content-Type: application/json
X-Callback-Token: <token acordado durante el alta>
X-Callback-Token se acuerda junto con la clave de API durante el alta del proyecto y se configura en el worker del servicio de identidad. No se emite un token nuevo cada vez que se crea una identidad. El receptor debe validarlo con el mismo valor recibido durante el alta.
{
"identity_id": "idn_c39e39dd-1030-4c33-b888-5beeb876247d",
"external_agent_id": "tu-agent-id",
"chain_id": 677,
"status": "REGISTERED",
"wallet_binding_status": "CONFIRMED",
"erc8004_agent_id": "45",
"registry_address": "0xb43edfb9c7609cf645e932b2ff20f26f0d4488de",
"metadata_uri": "https://…/v2-….json",
"updated_at": "2026-09-08T06:12:51Z"
}
El receptor debe seguir cuatro reglas:
- Valida primero
X-Callback-Tokencon una comparación de tiempo constante y después procesa el cuerpo. Sin validación, cualquiera que conozca la URL puede falsificar actualizaciones de estado. - Devuelve
2xxcuanto antes. Cualquier otra respuesta se considera un fallo y se reintenta. Realiza el trabajo real de forma asíncrona. - Admite entregas duplicadas. Los reintentos pueden repetir el mismo estado; basta con usar
identity_idmásupdated_atcomo clave de idempotencia. - Los reintentos pueden llegar desordenados. Ignora los payloads con un
updated_atanterior al registro guardado.
Los intervalos de reintento son 15 segundos, 1 minuto, 5 minutos y, después, cada 15 minutos. La entrega no se abandona: un endpoint que falla de forma persistente acumula reintentos, no pierde cambios de estado. Los webhooks optimizan el sondeo, no lo sustituyen: aunque tu endpoint lleve una hora caído, el sondeo devuelve inmediatamente el estado actual.
5. Actualizar metadatos
PUT /v1/agents/identities/{identity_id}/metadata
X-API-Key: ak_…
{
"metadata": { "name": "Agent de ejemplo", "description": "Descripción actualizada" },
"idempotency_key": "update-2026-09-08-1"
}
Esto publica una nueva versión del archivo de registro y actualiza la URI en cadena. Devuelve 202 y {"identity": …, "replayed": false}; repetir la misma clave devuelve 200 y "replayed": true.
Una vez aceptada la solicitud, el status de la identidad vuelve a METADATA_PENDING y pasa a REGISTERED cuando se confirma setAgentURI. Confirma la actualización observando el cambio de metadata_uri o esperando a que status vuelva a REGISTERED.
Aquí no se aceptan agentId, owner, walletAddress ni registrations. Son campos determinados en cadena: el servicio incorpora sus valores actuales a cada versión nueva, impidiendo publicar un archivo que atribuya arbitrariamente un propietario o agentId.
Solicitar la vinculación de la billetera
POST /v1/agents/identities/{identity_id}/wallet-binding
Content-Type: application/json
X-API-Key: ak_…
{ "idempotency_key": "bind-2026-09-08-1" }
El cuerpo contiene solo una clave de idempotencia. No se necesita dirección ni firma: la billetera se fijó al crear la identidad, y nosotros, como custodios de su clave, generamos la firma de autorización en cadena.
Crear una identidad no encola automáticamente la vinculación. Llama a este endpoint cuando el status de la identidad sea REGISTERED; hacerlo antes devuelve 425. Sirve tanto para la vinculación inicial como para volver a encolar una vinculación que siga en PENDING. Devuelve 202, o 200 con "replayed": true.
La vinculación termina cuando wallet_binding_status pasa a CONFIRMED.
Suspender
POST /v1/agents/identities/{identity_id}/suspend
X-API-Key: ak_…
Devuelve 200 y {"identity": …}. Una identidad suspendida deja de aceptar tareas nuevas, incluidos nuevos pasos del registro, actualizaciones de metadatos y vinculaciones de billetera, y se establece suspended_at. Las transacciones ya difundidas siguen su proceso de confirmación.
La identidad en cadena no cambia. La suspensión es un estado de la plataforma, no una operación en cadena: los terceros que consulten el registro seguirán viendo una identidad válida.
6. Gestión de errores
El cuerpo de error contiene una cadena error. Decide si reintentar según el estado HTTP:
| Estado | Significado | ¿Reintentar? |
|---|---|---|
400 | Solicitud mal formada o campos obligatorios ausentes | No. Corrige la solicitud |
401 | Clave de API ausente o inválida | No |
404 | La identidad no existe o pertenece a otro proyecto | No |
409 | Conflicto permanente, como un wallet_id ya utilizado por otra identidad | No |
425 | La identidad aún no alcanzó el estado requerido | Sí, más tarde |
5xx | Error de nuestro lado | Sí, con espera progresiva |
Presta especial atención a 425 Too Early. La operación es válida, pero la identidad aún no está en la etapa que la permite, por ejemplo al solicitar vinculación antes de completar el registro. Se parece a 409, pero significa lo contrario: 409 indica detenerse; 425 indica reintentar después. Tratar 425 como permanente abandona identidades que podrían completarse.
7. Verificar una identidad en cadena
Todo lo anterior describe nuestros registros mediante nuestra API. ERC-8004 permite que terceros comprueben los datos clave sin confiar en nosotros. Si alguien te pide demostrar que el Agent es tuyo, entrégale esta sección.
Todas las llamadas siguientes son de solo lectura: no requieren claves privadas, gas ni cuenta.
7.1 Tres consultas esenciales
| Qué quieres saber | Llamada | Devuelve |
|---|---|---|
| Quién controla el Agent | ownerOf(uint256 agentId) | address |
| Qué billetera utiliza | getAgentWallet(uint256 agentId) | address |
| Dónde se describe | tokenURI(uint256 agentId) | string |
import { ethers } from 'ethers'
const provider = new ethers.JsonRpcProvider('https://rpc.botchain.ai')
const registry = new ethers.Contract(
'0xB43Edfb9C7609cF645e932B2fF20f26F0d4488dE',
[
'function ownerOf(uint256) view returns (address)',
'function getAgentWallet(uint256) view returns (address)',
'function tokenURI(uint256) view returns (string)',
],
provider,
)
const agentId = 45n
console.log(await registry.ownerOf(agentId)) // 0x5f5802fA78d5eE2A3B4fD67F49CD502430C3E88C
console.log(await registry.getAgentWallet(agentId)) // 0xd9DC4F46d9da603A2A077A7190BAa85f4e2f05fe
console.log(await registry.tokenURI(agentId)) // https://…/v2-….json
Si prefieres no añadir dependencias, usa JSON-RPC directamente:
# ownerOf(45), selector 0x6352211e, argumento rellenado a 32 bytes
curl -s https://rpc.botchain.ai \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"eth_call","params":[{
"to":"0xB43Edfb9C7609cF645e932B2fF20f26F0d4488dE",
"data":"0x6352211e000000000000000000000000000000000000000000000000000000000000002d"
},"latest"]}'
Los ejemplos se conectan a la red principal. Para la red de pruebas, usa https://rpc.bohr.life y la dirección de IdentityRegistry de la sección 8; el resto no cambia.
Hay dos comportamientos relevantes:
- Para un ID no registrado,
ownerOfrevierte en vez de devolver la dirección cero. Interpreta esa reversión como «el Agent no existe» y distínguela de un error de red. getAgentWalletno comprueba si el ID existe. Devuelve la dirección cero tanto para IDs no registrados como para los registrados sin billetera vinculada. UsaownerOf, nogetAgentWallet, para comprobar la existencia.
7.2 Archivo de registro
tokenURI apunta a un JSON con el formato de archivo de registro ERC-8004:
{
"type": "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
"name": "Agent de ejemplo",
"description": "Qué hace este Agent",
"owner": "0x5f5802fa78d5ee2a3b4fd67f49cd502430c3e88c",
"walletAddress": "0xd9dc4f46d9da603a2a077a7190baa85f4e2f05fe",
"registrations": [
{
"agentId": "45",
"agentRegistry": "eip155:677:0xb43edfb9c7609cf645e932b2ff20f26f0d4488de"
}
]
}
Verifica el archivo; no confíes ciegamente en él. Un archivo obtenido de una URL que no provenga de tokenURI no demuestra nada. El procedimiento correcto es:
- Leer
tokenURI(agentId)del registro. - Solicitar esa URL sin sustituirla por otra.
- Comprobar que
registrations[0].agentIdcoincide con el ID consultado y queregistrations[0].agentRegistryapunta al registro consultado. - Comprobar que
ownercoincide conownerOfywalletAddresscongetAgentWallet.
Si el archivo y la cadena discrepan, prevalece la cadena. El registro es la fuente autorizada de propiedad y vinculación; el archivo es una descripción que debe coincidir con él.
7.3 Valores que cambian con el tiempo
| Cambio | Método en cadena | Volver a leer |
|---|---|---|
| Actualización de metadatos | setAgentURI | tokenURI |
| Vincular o sustituir billetera | setAgentWallet | getAgentWallet |
| Desvincular billetera | unsetAgentWallet | getAgentWallet |
| Transferir propiedad | transferFrom | ownerOf |
La URI puede cambiar: no almacenes indefinidamente en caché el archivo de registro. Usa la URI como clave de caché y vuelve a consultar tokenURI cuando necesites datos actualizados.
La vinculación admite dos métodos de verificación de firmas, por lo que puede usarse cualquiera de estos tipos de cuenta:
| Tipo de billetera | Verificación |
|---|---|
| EOA, incluidas las EOA con delegación EIP-7702 | ECDSA |
| Billetera de contrato (cuenta ERC-4337, Safe, etc.) | ERC-1271 |
Esto importa porque las billeteras de Agents suelen ser cuentas inteligentes en lugar de EOA.
8. Entornos y direcciones de contratos
Llama siempre a la dirección del proxy: la dirección del contrato de implementación cambia con las actualizaciones.
El servicio de identidad solo orquesta actualmente operaciones de IdentityRegistry: registro, actualizaciones de URI y vinculación de billeteras. Las direcciones de Reputation / Validation de las tablas siguientes corresponden a contratos complementarios de ERC-8004 ya desplegados en cadena, pero este servicio no los llama actualmente. Los terceros pueden usarlos para sus propias integraciones de reputación o validación; el registro de identidad solo requiere IdentityRegistry.
Red principal de BOT (chain ID 677)
| Elemento | Dirección |
|---|---|
| RPC | https://rpc.botchain.ai |
| Explorador de bloques | https://scan.botchain.ai |
IdentityRegistry | 0xB43Edfb9C7609cF645e932B2fF20f26F0d4488dE |
ReputationRegistry | 0xB3f7061354193a424856Cc63028cE3556262dAc9 |
ValidationRegistry | 0x85Bf081D1492393C3Fb2742b379b6ADbC7dbC79b |
Red de pruebas de Bohr (chain ID 968)
| Elemento | Dirección |
|---|---|
| RPC | https://rpc.bohr.life |
| Explorador de bloques | https://scan.bohr.life |
IdentityRegistry | 0xec8fFbC3c9A34AdDCbB3A14F91db2bd26A8b99c0 |
ReputationRegistry | 0xD4084c9397adca7fD0c24617681a2eA4C286402A |
ValidationRegistry | 0x0ff64E68C0e5fB2c57E6B3e1D7F3c65FBD0eEA33 |
Los agentId se numeran por registro. El Agent 45 de la red principal y el 45 de pruebas son identidades independientes. Acompaña siempre el agentId con el chain ID; para eso existe agentRegistry en el archivo de registro.
El registro implementa ERC-721 por completo y se llama AgentIdentity (AGENT). Consulta su versión con getVersion(), no con el habitual version(); la versión actual en ambas cadenas es 2.0.0. Las billeteras y los exploradores que muestran NFT pueden mostrar estas identidades sin adaptación.