Ir al contenido

API pública v0

Cada ficha verificada del registro está disponible como JSON. Sin clave, sin cuenta y sin cuotas que negociar: un endpoint, CORS abierto y respuestas cacheables. Hay un freno por IP contra las ráfagas, lo bastante amplio como para que el uso normal no lo toque nunca. Está pensada para ser la semilla de un estándar abierto, así que el contrato es pequeño y estable.

La API se estrena con la plataforma del registro. El contrato v0 aquí documentado es el del lanzamiento; después llegará una v1 con endpoints de listado y búsqueda. La v0 seguirá funcionando sin cambios cuando llegue la v1.

https://platform.thehumanbehind.com/api/v0
GET https://platform.thehumanbehind.com/api/v0/records/{slug}.json
  • slug es el identificador público de la ficha, el último segmento de la URL de su página (/r/<slug>). Se compara sin distinguir mayúsculas (se pasa a minúsculas antes de la búsqueda).

  • Método GET. Sin autenticación, sin clave de API.
  • Un preflight CORS OPTIONS se responde con 204 y las mismas cabeceras de origen abierto.

Toda respuesta, correcta o de error, lleva estas cabeceras:

CabeceraValor
content-typeapplication/json; charset=utf-8
access-control-allow-origin*
access-control-allow-methodsGET, OPTIONS
cache-controlpublic, max-age=300
  • CORS: access-control-allow-origin: *, puedes llamarla directamente desde un navegador en cualquier sitio.

  • Caché: cache-control: public, max-age=300. Las respuestas se cachean en el borde durante cinco minutos. El objetivo no es la frescura perfecta, sino la verificabilidad.

curl https://platform.thehumanbehind.com/api/v0/records/laia.json

La forma de una respuesta 200 OK, application/json:

{
  "registry_code": "THB-2026-00001",
  "slug": "laia",
  "avatar_name": "Laia",
  "type": "avatar",
  "scope": "general",
  "operates_at": null,
  "responsible_name": "AIGiner S.L.",
  "status": "active",
  "verification_level": "verified",
  "verified_at": "2026-10-15T16:12:39.438015+00:00",
  "registered_at": "2026-07-30T23:44:44.547749+00:00",
  "content_hash": "23ec5dae1e4b330a1ebe266db49bf2d0ca88b7bb6fff0e0eeca1106bbbc5fb6d",
  "url": "https://platform.thehumanbehind.com/r/laia",
  "disputed_at": null
}

Ejemplo trabajado, con la forma exacta de una respuesta real. La API pública solo sirve fichas verificadas, y la verificación de identidad todavía no está abierta, así que hoy el endpoint responde 404 para cualquier slug, y su cuerpo dice exactamente por qué (mira Códigos de estado). Es el registro funcionando como está diseñado, no una caída: una ficha gratuita es privada, y solo la verificación la hace pública.

La respuesta contiene únicamente campos públicos. El correo del titular y los datos de su cuenta no forman parte de la superficie de la API, de forma estructural, no solo por política: la API lee de una vista de base de datos que sencillamente no los contiene.

CampoTipoNotas
registry_codestringEl número THB, p. ej. THB-2026-00001.
slugstringEl identificador público de la ficha.
avatar_namestringEl nombre registrado.
typestring

avatar, voice_clone, agent, image, video o music.

scopestring

general, health, finance, legal, education, public_figure, sport o model.

operates_atstring o nullDónde opera, o null si no se declaró.
responsible_namestringLa persona que responde por él.
statusstring

Estado interno del ciclo de vida, active o unclaimed. El sello de la ficha lo da verification_level (registered o verified), no este campo.

verification_levelstringregistered o verified.
verified_atstring o nullFecha de verificación en ISO-8601 UTC, o null.
registered_atstringMarca de tiempo del alta en ISO-8601 UTC.
content_hashstringSHA-256 del snapshot del alta, en hex minúsculas.
urlstringEnlace canónico a la página legible por humanos.
disputed_atstring o null

Fecha ISO-8601 UTC en que alguien discutió esta ficha, o null si nadie lo ha hecho. Una ficha en disputa conserva su número, su fecha y su huella, y se sigue sirviendo aquí: lo que pierde es poder usarse como prueba de anterioridad mientras dure la disputa. Mira Reclamaciones.

Dos campos merecen nota. verification_level te dice qué sello lleva la ficha; cuando es verified, verified_at lleva la fecha (ver Verificación). Y content_hash es lo que hace posible la verificación independiente: recalcúlalo con los propios campos de esta respuesta usando la receta pública exacta.

Una ficha no es un solo momento. Cada cambio en los ocho campos sellados escribe una versión, y cada versión conserva su huella y su fecha, ancladas las dos en Bitcoin. La respuesta de la ficha lleva un resumen de ese historial, y hay un endpoint dedicado para el historial entero:

GET https://platform.thehumanbehind.com/api/v0/records/{slug}/history.json

Las mismas reglas que el endpoint de arriba: CORS abierto, sin clave, cinco minutos de caché, y un 404 cuyo cuerpo es byte a byte el que ya conoces, así que preguntar por el historial de una ficha privada dice exactamente tan poco como preguntar por la ficha.

El objeto history que viene dentro de la respuesta de la ficha es el resumen, y llega como mucho con las veinte entradas más recientes. Sus dos contadores se llaman metadata_versions_included e instruction_versions_included, y el sufijo no es adorno: cuentan lo que ha venido en esa respuesta, no cuántas versiones tiene la ficha. Cuando truncated es true hay más, y url apunta al endpoint de arriba, que es el que pagina.

CampoTipoNotas
versions[].kindstringmetadata (el contenido de la ficha) o instructions (las de un agente).
versions[].versionnumberLa 1 es el alta. Los números no se reutilizan nunca.
versions[].hashed_atstringCuándo se selló esa versión.
versions[].content_hashstringSolo en las versiones de contenido: la huella de ESA versión.
versions[].snapshotobject

Solo en las versiones de contenido: el objeto exacto que se hasheó. Es lo que permite a cualquiera recomputar la huella de una versión antigua sin cuenta y sin fiarse de la fila que servimos hoy.

truncated / next_offsetbool / numberSe pagina con ?offset= y ?limit=.

Por qué aquí las instrucciones no llevan huella

Sección titulada «Por qué aquí las instrucciones no llevan huella»

En una ficha de tipo agent, el historial dice cuántas veces cambiaron las instrucciones y cuándo. No publica el texto, que es de quien lo escribió, y tampoco publica su SHA-256.

Lo segundo es deliberado y merece explicarse, porque un hash parece que protege. Solo protege cuando su entrada es difícil de adivinar. Las instrucciones de un agente suelen ser plantillas que se repiten entre clientes, así que publicar el digest permitiría a cualquiera con una plantilla candidata confirmar que esa ficha la usa: un oráculo de confirmación, no una salvaguarda. Es el mismo fallo que tuvo la primera receta de anclaje del registro, y el motivo de que se sustituyera en un día.

La huella sí viaja al dueño de la ficha, en su constancia descargable y en su panel, donde es suya para compartirla o no.

CódigoSignificadoCuerpo
200Ficha encontrada y pública.El objeto JSON de arriba.
404

No hay ficha pública con ese slug: nunca existió, o no es visible públicamente (solo registrada, despublicada o en revisión). El cuerpo es idéntico en todos esos casos, así que la API nunca revela si existe una ficha privada.

{ "error": "not_found", "reason": "not_public_until_verified", "message": "The public API only serves verified records. Identity verification is not open yet.", "docs": "https://thehumanbehind.com/docs/api/" }
429

Demasiadas peticiones desde una IP en un minuto. La caché de cinco minutos no cubre a quien varía la query string, así que debajo hay un freno. Espera y reintenta.

{"error":"rate_limited"}
500Error inesperado del servidor.{ "error": "server_error" }
204Preflight CORS (OPTIONS).Vacío.

Las fichas despublicadas y la API JSON. El endpoint JSON devuelve 404 para una ficha despublicada, porque ya no está en la vista pública. El aviso fechado 410 Gone, el que mantiene la constancia, vive en la página legible por humanos /r/<slug>, no en el endpoint JSON. El número THB no se reutiliza en ningún caso.

const res = await fetch(
  "https://platform.thehumanbehind.com/api/v0/records/laia.json",
);
const ficha = await res.json();
console.log(ficha.registry_code); // "THB-2026-00001"
console.log(ficha.url);           // enlace a la página legible por humanos

La razón de ser de la API es que no tengas que fiarte de ella. Descarga una ficha y recalcula su content_hash tú mismo con la receta pública. Aquí lo tienes de principio a fin en Python (solo librería estándar):

import json, hashlib, urllib.request

SLUG = "laia"
url = f"https://platform.thehumanbehind.com/api/v0/records/{SLUG}.json"

# Con User-Agent propio: el de urllib por defecto ("Python-urllib/3.x") lo
# rechaza el Browser Integrity Check de Cloudflare con un 403. Cualquier
# cadena vale; identificarse es lo educado.
req = urllib.request.Request(url, headers={"User-Agent": "thb-verify/1.0"})
rec = json.load(urllib.request.urlopen(req))

# The eight snapshot fields; a missing operates_at becomes "".
fields = ["registry_code", "slug", "avatar_name", "type",
          "scope", "operates_at", "responsible_name", "registered_at"]
snapshot = {k: (rec.get(k) or "") for k in fields}

# The timestamp is canonicalised before hashing: the registry seals
# "...862834Z", while the API returns the raw value "...862834+00:00".
# Skip this line and the digest will not match.
snapshot["registered_at"] = snapshot["registered_at"].replace("+00:00", "Z")

# Canonical form = PostgreSQL jsonb text: keys sorted by (length, then bytes),
# ": " after each key and ", " between pairs.
ordered = dict(sorted(snapshot.items(), key=lambda kv: (len(kv[0]), kv[0])))
canonical = json.dumps(ordered, separators=(", ", ": "), ensure_ascii=False)

digest = hashlib.sha256(canonical.encode("utf-8")).hexdigest()
print(digest == rec["content_hash"])  # True

El v0 de la ruta es una promesa: este contrato no cambiará bajo tus pies. Las nuevas capacidades (listado, búsqueda, filtros) llegarán como v1 en sus propias rutas, y las consultas de ficha por v0 seguirán devolviendo exactamente la forma aquí documentada.

La API es gratuita para cualquier uso razonable: incrustar una comprobación del sello en tu web, construir un buscador, investigar. Las respuestas se cachean en el borde; por favor, no bombardees el origen con tráfico que esquive la caché. Si necesitas acceso masivo o endpoints de listado, háblanos. Para eso está la v1.