Saltar al contenido
AIM CODEXESEN
Cómo funcionaPreciosPruébaloComprobar un documentoVerificar con IAConfianzaEntrarESEN
EntrarEmpezar gratis

Séllalo desde tu programa

Si tu despacho ya trabaja con un gestor, no hace falta cambiar de sitio: estos seis pasos bastan para sellar un documento y mandarlo como enlace verificable desde donde ya estás.

Tu clave

Está en Ajustes → API. Va en la cabecera X-API-Key de cada llamada. No la pongas en código que viaje al navegador.

1. Crear el documento

curl -X POST https://www.aim-codex.ai/api/v1/documents/write \
  -H "X-API-Key: TU_CLAVE" -H "Content-Type: application/json" \
  -d '{"title":"Presupuesto reforma","markdown":"Total: 6.000 EUR"}'

2. Desde una plantilla: crear el borrador y sellarlo

# /documents/write ya lo deja sellado (status sealed): este paso es solo para plantillas
curl -X POST https://www.aim-codex.ai/api/v1/documents \
  -H "X-API-Key: TU_CLAVE" -H "Content-Type: application/json" \
  -d '{"template_id":"presupuesto","data":{"emisor":"Reformas Paco","cliente":"Ana Ruiz"}}'
curl -X POST https://www.aim-codex.ai/api/v1/documents/DOC_ID/seal \
  -H "X-API-Key: TU_CLAVE" -H "Content-Type: application/json" -d '{}'

3. Crear el enlace para el cliente

curl -X POST https://www.aim-codex.ai/api/v1/documents/DOC_ID/share \
  -H "X-API-Key: TU_CLAVE" -H "Content-Type: application/json" \
  -d '{"label":"Construcciones Lopez","allow_accept":true}'

4. Ver si lo han abierto o aceptado

curl https://www.aim-codex.ai/api/v1/documents/DOC_ID/opens -H "X-API-Key: TU_CLAVE"
curl https://www.aim-codex.ai/api/v1/acceptances -H "X-API-Key: TU_CLAVE"

5. Descargar el expediente probatorio

curl https://www.aim-codex.ai/api/v1/documents/DOC_ID/dossier \
  -H "X-API-Key: TU_CLAVE" -o expediente.pdf

6. Comprobar un documento sin clave

curl https://www.aim-codex.ai/api/v1/verify/HUELLA_SHA256

Eso es todo. El enlace que devuelve url se abre sin cuenta, en el idioma del navegador de tu cliente, y se puede comprobar contra Bitcoin sin pasar por nosotros.

Si una llamada se corta y no sabes si llegó, repítela con la misma cabecera Idempotency-Key (cualquier texto único tuyo, hasta 200 caracteres): durante 24 horas devolvemos la respuesta de la primera vez y no se envía nada dos veces. La lista de documentos admite ?limit= (hasta 500) y &offset=, del más reciente al más antiguo. Los errores llegan en JSON, con error y code.

Que te avise a ti

En Ajustes → API pones una dirección tuya y CODEX te llama cuando pasa algo. Sin sondear nada: tu gestor se entera en el acto de que el cliente ha firmado.

POST https://tu-gestor.example/codex
{
  "event": "document.accepted",
  "doc_id": "doc-XXXXXXXX",
  "content_hash": "6ab026dc...",
  "name": "Antonio Lopez",
  "at": "2026-09-04T09:52:00Z"
}

Eventos que se avisan:

Cada llamada va firmada con tu secreto, para que sepas que viene de nosotros. Y como es una petición HTTP normal, sirve igual para Zapier, Make o cualquier cosa que sepa recibir un webhook.

Comprobar que el aviso es nuestro

Cada llamada lleva X-Codex-Event, X-Codex-Event-Id (el mismo en los reintentos: úsalo para no procesar dos veces), X-Codex-Timestamp y la firma X-Codex-Signature-256: t=…,v1=…: HMAC-SHA256 con tu secreto sobre t + "." + cuerpo. Rechaza las que tengan más de cinco minutos. (X-Codex-Signature firma solo el cuerpo y se mantiene por compatibilidad.) Si tu servidor responde 5xx o no responde, se reintenta dos veces más; las redirecciones no se siguen. El secreto está en Ajustes → API, y ahí mismo se puede cambiar.

import hmac, hashlib, time

def es_de_codex(cuerpo: bytes, cabecera: str, secreto: str) -> bool:
    partes = dict(p.split('=', 1) for p in cabecera.split(','))
    if abs(time.time() - int(partes['t'])) > 300:
        return False
    esperada = hmac.new(secreto.encode(), partes['t'].encode() + b'.' + cuerpo,
                        hashlib.sha256).hexdigest()
    return hmac.compare_digest(esperada, partes['v1'])

Límites

Si superas un límite recibes 429 con Retry-After: espera esos segundos y repite. La verificación pública admite 120 consultas por minuto y por IP; el servidor MCP, otras 120. Las respuestas de error llevan error (texto) y code (estable, para tu programa: mira la tabla de errores); con X-Lang: en el texto llega en inglés.

Errores

Todo error 4xx llega en JSON con error (texto para una persona) y code (estable, para tu programa). Compara siempre code, nunca el texto. Antes de crear o sellar se comprueban tipos y longitudes: lo sellado no se puede corregir, así que un dato mal formado se rechaza con 400 y no se sella nada.

400bad_jsonel cuerpo no es JSON válido (cortado o mal formado), o no es un objeto
400bad_typesun campo con el tipo equivocado (una lista donde va un texto); viene con field
400too_longun campo pasa de su máximo; viene con field y max_length (title: 200 caracteres)
400no_title · no_content · no_templatefalta el título, el contenido o la plantilla
400invalid_data · no_signers · no_recipientsdatos de plantilla no válidos, sin firmantes, sin destinatarios
400bad_limit · bad_offset · webhook_not_publicpaginación fuera de rango; webhook a una dirección interna
401unauthorizedfalta la clave o no es válida
402planla función es de un plan superior (min_plan dice cuál)
403forbidden · needs_logintu rol no lo permite; o hace falta una persona (contraseña del propietario)
404not_foundno existe, o no es de tu cuenta
409conflictel estado no lo permite (p. ej. pedir acuse de un borrador)
413too_largela petición pasa del tamaño máximo
429ratedemasiadas peticiones: espera lo que diga Retry-After

Leer la verificación: valid, no authentic

GET /api/v1/verify/HUELLA devuelve tres cosas distintas. authentic: los bytes son los que se sellaron (sigue siendo true en un documento revocado o caducado). state: valid, revoked, expired, superseded, hash_only, received, flagged o unknown. valid: true solo si state es valid. Si tu programa mira un solo campo, que sea valid. La verificación por fichero (/verify-file/SHA256_DEL_FICHERO) da también state: conflict cuando cuentas distintas sellaron el mismo fichero; ahí valid es false. El servidor MCP usa el mismo criterio con su campo vigente.

{"authentic": true, "revoked": true, "state": "revoked", "valid": false, ...}

Más rutas

POST /api/v1/documents                     {template_id, data}        -> borrador / draft
POST /api/v1/documents/DOC_ID/seal         {}                         -> sellado / sealed (idempotente)
POST /api/v1/seal-hash                     {hash, label}              -> solo la huella / fingerprint only
POST /api/v1/documents/DOC_ID/signature    {signers:[{name,email,tipo}]}  -> firma / signature flow
     tipo: signer | approver | cc          (sin tipo: signer / default: signer)
POST /d/SHARE_ID/delegate                  {email,to_name,to_email,reason[,code]}  -> delegar / delegate
POST /api/v1/documents/DOC_ID/bulk-share   {recipients:[{name,email}]}
POST /api/v1/acuses                        {doc_id, recipients:[{name,email}], deadline}
GET  /api/v1/documents?limit=1..500&offset=0  -> documents, count, total, limit, offset, next_offset

Papeles en un flujo de firma

Cada elemento de signers admite tipo (también type o kind): signer (por defecto: firma en su turno), approver (aprueba o rechaza con motivo en su turno, sin firmar; su justificante dice «Aprobado por») o cc (copia de solo lectura: recibe el enlace al crear el flujo y la copia final; no firma ni bloquea). role sigue siendo el papel libre («Arrendador»); si vale approver o cc, se toma como tipo. Un tipo desconocido da 400 bad_role; un flujo solo de copias, 400 no_signers. Un firmante o un aprobador puede pasar su turno desde su enlace (POST /d/SHARE_ID/delegate, con su correo y, si el servidor envía correo, el código que recibe): el delegado tiene su propio enlace y su propio código. GET /api/v1/documents/DOC_ID/signatures devuelve tipo, delegated_to y delegated_from (quién, cuándo y por qué); los estados nuevos son approved y delegated. Webhook: document.signature_delegated. Las copias y quien delegó no cuentan en signed/total.

{"mode": "sequential", "signers": [
  {"name": "Gestor", "email": "gestor@empresa.es", "tipo": "cc"},
  {"name": "Socia", "email": "socia@empresa.es", "tipo": "approver"},
  {"name": "Cliente", "email": "cliente@empresa.es", "role": "Arrendatario"}]}

El webhook desde tu programa

POST /api/v1/tenant/settings con {"webhook_url": "https://…"}. Adónde van los avisos lo decide una persona: con solo la clave, añade "password" (la del propietario) o recibirás 403 needs_login. El secreto se enseña una sola vez, en la respuesta que lo crea o lo rota ("rotate_webhook_secret": true, también con password); después sale como ••••••. Guárdalo entonces. Una dirección interna (localhost, red privada, metadatos de la nube) se rechaza con 400 webhook_not_public.

El MCP por dentro

JSON-RPC 2.0 sobre HTTP (Streamable HTTP) en https://www.aim-codex.ai/mcp. Herramientas: verificar_documento, search y fetch. Un argumento mal formado devuelve un resultado con isError: true; el exceso de peticiones, HTTP 429 con un error JSON-RPC -32000 y Retry-After.

curl -X POST https://www.aim-codex.ai/mcp -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search","arguments":{"query":"HUELLA_SHA256"}}}'

¿Y con tu gestor: Holded, a3, Sage?

No hace falta que construyamos un conector para cada uno. Pon en el campo de webhook una dirección de Zapier o Make, y desde ahí conecta con lo que uses: Holded, hojas de cálculo, tu CRM o el correo del equipo. Diez minutos y sin programar.

Lo decimos así porque es lo que funciona hoy, no lo que nos gustaría anunciar. Si tu gestor no está en ninguno de los dos, la API de arriba sirve igual.

Diplomas en formato estándar

Si emites diplomas, cada uno se puede leer también como Verifiable Credential (W3C): el formato de datos del estándar. Público y sin clave, como toda nuestra verificación. Todavía no va firmado con la clave del emisor, que es lo que exigen las carteras: su prueba es el anclaje en Bitcoin y su estado se consulta aquí.

Cómo se calcula el sello, paso a paso →

curl https://www.aim-codex.ai/v/HUELLA_SHA256/w3c.json

¿Prefieres que lo haga tu asistente?

Conecta https://www.aim-codex.ai/mcp como servidor MCP en Claude o ChatGPT y pídele que compruebe un documento. Sin cuenta y sin coste.

BIOMAG S.L.U. · NIF B-01037241 · Vitoria-Gasteiz