Saltar al contenido principal

Billetera del Agent

Dale a tu Agent de IA una cuenta que pueda iniciar transacciones sin manejar claves privadas.

En una frase: Una llamada a la API devuelve la dirección de una cuenta inteligente ERC-4337. Nosotros custodiamos las claves privadas y pagamos el gas del despliegue; la cuenta paga el gas de las transferencias posteriores.

Este capítulo está dirigido a desarrolladores que integran sus sistemas con el servicio de billeteras de Agents de BOT Chain. Explica cómo crear una billetera, por qué su dirección existe antes que el contrato, cómo enviar tokens y cómo restringir los destinatarios.

Lee este capítulo antes de Identidad del Agent. Para registrar una identidad se necesita una billetera existente.

Todas las rutas de este capítulo son relativas a una de estas URL base:

EntornoURL
Red principal de BOThttps://wallet-api.botchain.ai
Red de pruebas de Bohrhttps://wallet-api.bohr.life

Las claves de API están vinculadas al entorno. Las solicitudes a la red principal con una clave de pruebas se rechazan; nunca se redirigen silenciosamente a otro entorno.

1. Descripción general​

PreguntaRespuesta
¿Qué obtengo?Una dirección de cuenta inteligente ERC-4337 desde la que el Agent inicia transacciones
¿Debo custodiar claves privadas?No. Nosotros las almacenamos y utilizamos; tu sistema no firma nada
¿Quién paga el gas?Nosotros pagamos el gas del despliegue. El Agent no necesita saldo al crearse; la cuenta paga el gas de las transferencias posteriores
¿Puedo usar la dirección de inmediato?Para recibir fondos, sí. Para enviarlos, primero debe desplegarse la cuenta
¿Cómo transfiero fondos?POST /wallets/{id}/transfers envía una UserOperation
¿Puedo restringir destinatarios?Sí, y es obligatorio. La lista de permitidos se comprueba antes de ejecutar
¿Qué cadenas se admiten?Red principal de BOT (chain ID 677) y red de pruebas de Bohr (chain ID 968)

Recuerda:

La dirección existe desde que se crea la billetera, pero el contrato se despliega después en esa dirección. Es normal: una dirección sin código puede recibir fondos.

2. Obtener credenciales​

Los servicios de billetera e identidad comparten credenciales. Cada integrador es un proyecto con un ID y una o varias claves de API que emitimos nosotros:

ak_<environment>_k_<key_id>.<secret>

Incluye la cadena completa, incluido el punto, en la cabecera 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.

Las claves dependen del entorno: una clave de pruebas no funciona en la red principal.

3. Crear una billetera​

POST /v1/wallets
Content-Type: application/json
X-API-Key: ak_…
{
"external_agent_id": "tu-agent-id",
"chain_id": 677,
"idempotency_key": "create-agent-42",
"callback_url": "https://your-service.example/webhooks/wallet"
}
CampoObligatorioSignificado
external_agent_idSíIdentificador del Agent en tu sistema
chain_idSíRed principal 677; pruebas 968
idempotency_keySíTambién puede enviarse en la cabecera Idempotency-Key
identity_idNoVincula una identidad existente. Normalmente se omite, porque la identidad se registra después
callback_urlNoDirección que recibe los cambios de estado

No debes proporcionar ni la dirección del propietario ni la de la cuenta: nosotros generamos ambas y las devolvemos. Tu sistema no genera nada y ningún material de claves cruza la frontera entre sistemas.

Devuelve 202:

{
"wallet": {
"wallet_id": "wal_de43139cef869dc9c42ae3",
"project_id": "launchpad",
"external_agent_id": "tu-agent-id",
"chain_id": 677,
"owner_address": "0x5f5802fA78d5eE2A3B4fD67F49CD502430C3E88C",
"account_address": "0xd9DC4F46d9da603A2A077A7190BAa85f4e2f05fe",
"deployment_status": "PREDICTED",
"status": "ACTIVE",
"created_at": "2026-09-07T09:37:40Z"
},
"replayed": false
}

La respuesta contiene dos direcciones que no son intercambiables:

CampoSignificado
account_addressDirección del Agent. Envía los fondos aquí; también figura como remitente en la cadena
owner_addressDirección que controla la cuenta. Se usa al registrar la identidad; no envíes fondos aquí

Guarda wallet_id: se usa en todos los endpoints posteriores.

Las respuestas de lectura y escritura tienen estructuras distintas

Los endpoints de escritura envuelven el objeto en wallet; los de lectura devuelven el objeto directamente. Tras crear, usa response.wallet.account_address; tras un GET, usa response.account_address.

Repetir la misma clave de idempotencia devuelve 200, "replayed": true y la billetera original. Reintentar tras un tiempo de espera agotado no crea una segunda cuenta.

También puedes buscar por tu propio identificador y evitar almacenar el nuestro:

GET /v1/wallets/{wallet_id}
GET /v1/wallets/by-external-id/{chain_id}/{external_agent_id}

Las billeteras ajenas a tu proyecto siempre devuelven 404. «No es tuya» y «no existe» tienen la misma respuesta.

4. La dirección existe antes que el contrato​

El valor inicial de deployment_status es PREDICTED. No es un estado transitorio que se resuelva simplemente esperando: así funcionan las cuentas ERC-4337.

CREATE2 calcula la dirección a partir del propietario y el salt; por eso se conoce de antemano y no cambia. El contrato se despliega allí más tarde. Hasta entonces:

Operación¿Disponible en PREDICTED?
Recibir tokens nativos o ERC-20Sí. Es normal enviar fondos a una dirección sin código
Mostrar la dirección a los usuariosSí
Enviar tokensNo
EstadoSignificado
PREDICTEDDirección calculada; aún no hay contrato en la cadena
DEPLOYINGDespliegue enviado; esperando inclusión en la cadena
DEPLOYEDContrato listo; la cuenta puede enviar fondos
El despliegue requiere una llamada explícita

Las transferencias se rechazan si deployment_status no es DEPLOYED. Una transferencia no inicia el despliegue automáticamente: solo devuelve 409 WALLET_NOT_READY. Incluye el despliegue en la inicialización; no esperes que el primer pago lo active.

POST /v1/wallets/{wallet_id}/deploy
{ "idempotency_key": "deploy-agent-42" }

Devuelve 202 y una UserOperation. Consulta la billetera hasta que deployment_status sea DEPLOYED; solo entonces se aceptarán transferencias.

No hace falta saldo para desplegar la cuenta: nosotros pagamos el gas.

5. Enviar tokens​

POST /v1/wallets/{wallet_id}/transfers
Content-Type: application/json
X-API-Key: ak_…
{
"receiver": "0x1111111111111111111111111111111111111111",
"token_address": "0x2222222222222222222222222222222222222222",
"amount": "1000000",
"request_source": "API",
"idempotency_key": "payout-2026-09-08-001"
}
CampoObligatorioSignificado
receiverSíDirección del destinatario; debe estar en la lista de permitidos de esta billetera
token_addressSíDirección del contrato ERC-20, o el literal BOT_NATIVE para el token nativo
amountSíUnidades mínimas, solo dígitos. Para USDT con 6 decimales, "1000000" equivale a 1 USDT
request_sourceSíAPI, CONSOLE o ADMIN; registra el origen de la transferencia
idempotency_keySíEvita transferencias duplicadas al reintentar

Se rechazan inmediatamente cuatro casos:

  • No omitas token_address. Para el token nativo usa BOT_NATIVE, no un valor vacío ni la dirección cero.
  • amount solo admite dígitos. Los decimales y la notación científica se rechazan, no se redondean: tanto "1.5" como "1e6" fallan. Las conversiones de coma flotante pueden perder precisión y alterar la cantidad enviada; por eso no se aceptan esos formatos.
  • request_source es obligatorio y debe ser uno de los tres valores indicados.
  • El destinatario no puede ser la propia dirección de cuenta de la billetera. Una transferencia a sí misma se ejecuta en la cadena y consume gas sin cambiar el saldo, por lo que se bloquea aquí.

amount es una cadena, no un número. Las cantidades de tokens pueden superar el rango numérico exacto de JavaScript.

Devuelve 202 y una UserOperation. La transferencia se ha enviado, pero aún no está confirmada definitivamente:

{
"user_op_hash": "0x…",
"wallet_id": "wal_de43139cef869dc9c42ae3",
"chain_id": 677,
"op_type": "TRANSFER",
"status": "BUILT",
"sender": "0xd9DC4F46d9da603A2A077A7190BAa85f4e2f05fe",
"attempt": 0,
"created_at": "2026-09-08T07:20:11Z"
}

Seguir una transferencia​

GET /v1/user-operations/{user_op_hash}
EstadoSignificado¿Final?
BUILTConstruida, aún sin firmarNo
SIGNEDFirmada, aún sin enviarNo
SUBMITTEDEnviada al bundlerNo
INCLUDEDIncluida en un bloque; recibo aún sin confirmarNo
SUCCEEDEDCompletada. tx_hash y block_number tienen valorSí
REVERTEDLa ejecución en cadena falló y consumió gasSí
FAILEDNunca llegó a la cadena; consulta last_errorSí

Trata REVERTED y FAILED de forma distinta. REVERTED significa que llegó a la cadena y falló allí, por ejemplo por saldo insuficiente o rechazo del contrato del token. FAILED significa que no llegó a ese paso. Es razonable reintentar FAILED con una nueva clave de idempotencia; reintentar REVERTED sin cambios volverá a revertir.

attempt y last_error muestran los intentos que el servicio ya ha realizado por ti.

6. Lista de destinatarios permitidos​

La lista de permitidos es obligatoria

La billetera solo puede enviar fondos a direcciones registradas en la lista. Una lista vacía impide transferir a cualquier dirección; no significa acceso ilimitado. Registra cada destinatario antes de su primera transferencia o se rechazará.

Es intencional: el Agent decide cuándo transferir, por lo que los destinos permitidos deben fijarse de antemano, no deducirse de las instrucciones que recibe. La validación se ejecuta en el servidor antes de construir la operación, de modo que incluso si tu sistema queda totalmente comprometido, un atacante no puede enviar fondos a una dirección nueva.

POST   /v1/wallets/{wallet_id}/allowlist
GET /v1/wallets/{wallet_id}/allowlist
DELETE /v1/wallets/{wallet_id}/allowlist/{entry_id}
{
"chain_id": 677,
"address": "0x1111111111111111111111111111111111111111",
"label": "Cuenta de liquidación",
"allowed_actions": "TRANSFER",
"created_by": "ops@your-company.example",
"idempotency_key": "allow-payout-1"
}
CampoObligatorioSignificado
chain_idSíLas entradas se aíslan por cadena. Una entrada de 968 no autoriza transferencias en 677
addressSíDirección del destinatario
allowed_actionsSíTRANSFER, PAYMENT o ALL
created_bySíRegistra quién aprobó la dirección para auditoría
labelNoNombre reconocible
idempotency_keySí

Las entradas coinciden por (wallet_id, chain_id, address), deben estar ACTIVE y sus allowed_actions deben permitir transferencias. El rechazo indica si falta la entrada, está revocada o no autoriza transferencias. Son tres problemas con soluciones distintas: lee el motivo en vez de volver a añadir la dirección sin más.

El orden de inicialización importa: añade los destinatarios antes de ingresar fondos. Una billetera con saldo pero sin entradas permitidas no puede pagar a nadie.

7. Pausar una billetera​

POST /v1/wallets/{wallet_id}/status
{ "status": "PAUSED" }

PAUSED detiene las nuevas operaciones y nosotros podemos levantarlo. REVOKED es permanente.

El endpoint no ofrece una acción de reanudación deliberadamente. Reactivar una cuenta detenida es una decisión operativa, no la inversión mecánica de detenerla. Contacta con nosotros para reanudarla.

8. Notificaciones (webhooks)​

Si proporcionas callback_url, los cambios de estado se envían por POST con la cabecera X-Callback-Token. El receptor debe seguir cuatro reglas:

  1. Valida primero X-Callback-Token con una comparación de tiempo constante y después procesa el cuerpo.
  2. Devuelve 2xx cuanto antes y realiza el trabajo real de forma asíncrona.
  3. Admite entregas duplicadas. Usa el ID del objeto junto con updated_at como clave de idempotencia.
  4. Los reintentos pueden llegar desordenados. Ignora los payloads cuyo updated_at sea anterior al registro guardado.

Los webhooks optimizan el sondeo, no lo sustituyen. Si el receptor falla, el sondeo sigue devolviendo inmediatamente el estado actual.

9. Gestión de errores​

Las respuestas de error incluyen un código procesable por software:

{ "error_code": "WHITELIST_DENIED", "error_message": "…" }

Decide según error_code, no según el texto del mensaje.

HTTPerror_codeSignificado¿Reintentar?
400INVALID_REQUESTSolicitud mal formada o validación de campo fallidaNo. Corrige la solicitud
401—Clave de API ausente o inválidaNo
403WHITELIST_DENIEDEl destinatario no tiene una entrada permitida activaNo. Añádela primero
403TOKEN_NOT_ALLOWEDLa billetera no puede enviar este tokenNo
404NOT_FOUNDLa billetera no existe o pertenece a otro proyectoNo
409WALLET_NOT_READYSin desplegar, pausada o revocadaDepende; consulta abajo
409IDEMPOTENCY_KEY_CONFLICTSMisma clave de idempotencia usada para otro contenidoNo
422INSUFFICIENT_BALANCESaldo insuficienteNo. Ingresa fondos primero
501NOT_IMPLEMENTEDFunción no habilitada en este entornoNo
5xxINTERNAL_ERRORError de nuestro ladoSí, con espera progresiva

WALLET_NOT_READY cubre tres casos, identificados por status= y deployment= en error_message:

  • deployment=PREDICTED o DEPLOYING → Reintenta cuando termine el despliegue; es temporal.
  • status=PAUSED → Reintenta cuando se reactive la billetera.
  • status=REVOKED → Fallo permanente; no reintentes.

Tratar todo 409 como conflicto permanente descarta erróneamente billeteras aún en despliegue.

10. Entornos​

ElementoRed principal de BOTRed de pruebas de Bohr
Chain ID677968
RPChttps://rpc.botchain.aihttps://rpc.bohr.life
Explorador de bloqueshttps://scan.botchain.aihttps://scan.bohr.life
Bundlerhttps://bundler.botchain.ai/rpchttps://bundler.bohr.life/rpc
EntryPoint0x0000000071727De22E5E9d8BAf0edAc6f37da032Igual que en la red principal
Tipo de cuentaKernel v0.3.3Igual que en la red principal

EntryPoint es el contrato singleton estándar de ERC-4337 v0.7, de modo que las herramientas ERC-4337 existentes funcionan con estas cuentas sin modificaciones.

Siguiente paso: identidad​

La billetera da al Agent una cuenta. La identidad permite verificarlo: cualquiera puede consultar directamente en la cadena quién es su propietario y qué billetera utiliza.

Para registrar la identidad necesitas tres valores de la billetera recién creada:

Campo de la API de identidadOrigen
wallet_idwallet.wallet_id
wallet_addresswallet.account_address
owner_addresswallet.owner_address