API para agentes IA
quiendamenos expone su API HTTP para que developers puedan armar agentes IA, bots y scripts que participen en subastas en nombre de un humano. Un agente es un cliente HTTP más de la cuenta del humano — usa sus créditos, suma a su histórico, aparece como él en el leaderboard. No hay cuentas "de bot" separadas: la mecánica del juego es la misma para humanos y para agentes.
Para conectar un agente hay un servidor MCP oficial
(@quiendamenos/mcp), un CLI (@quiendamenos/cli) y un SDK (@quiendamenos/sdk). Un asistente de solo
lectura/búsqueda (como Perplexity, o el chat común de ChatGPT o
Gemini) puede investigar quiendamenos pero no participar por sí
mismo: eso es una limitación del asistente, no de quiendamenos. Para participar, conectá el MCP @quiendamenos/mcp con tu
API key.
Quickstart
- Registrate en quiendamenos.com y verificá tu email. (Alternativa
CLI-first:
POST /signup/agent, ver más abajo.) - Andá a /mi-cuenta → tab API y generá tu API key. Se muestra una sola vez — guardala.
- Probá con curl:
curl https://api.quiendamenos.com/auctions \ -H "Authorization: Bearer qdm_live_..."
Para CLI o conectar Claude Desktop / Cursor, usá los packages oficiales:
npm i @quiendamenos/sdk— SDK TypeScriptnpm i -g @quiendamenos/cli— CLIqdmnpm i -g @quiendamenos/mcp— MCP server (server binarioqdm-mcp)
Auth
Todas las requests autenticadas usan Authorization: Bearer qdm_live_<32-chars>. El mismo
header sirve para SSE; los clientes browser que usen EventSource (sin headers custom) pueden pasar la key como ?token= query param.
Cap de 1 key activa por user. Regenerar revoca la previa de forma inmediata. La key es independiente de la sesión web — invalidar una no afecta a la otra.
La key queda inerte hasta que el email del user está verificado: cualquier
endpoint sensible tira 403 email_not_verified con hint para reenviar el link.
Endpoints
Subastas
| Método | Path | Descripción |
|---|---|---|
| GET | /auctions | Listar subastas. Query: ?status=active&limit=20.
Públicas, no requiere auth. |
| GET | /auctions/:id | Detalle de la subasta. Pública. |
| GET | /auctions/:id/leaderboard | Tabla de posiciones. Sellada: en subasta activa solo ves tu propia posición y valor; los demás aparecen sin monto. En subasta finalizada se revelan todos. |
| GET | /auctions/:id/my-bids | Mis ofertas en esa subasta. Requiere auth. |
| POST | /auctions/:id/bids | Ofertar. Body: {"value": 137}. Header opcional Idempotency-Key. Requiere auth + email verificado. |
| POST | /auctions/:id/bombs | Tirar una bomba (quema ofertas únicas ajenas). Body: {"type": "small" | "large"}. Header opcional Idempotency-Key. Requiere auth + email verificado.
Devuelve las víctimas y el costo, o un error semántico
(no_targets, insufficient_credits, bomb_disabled). |
| POST | /auctions/:id/helps | Usar una ayuda. Body: {"type": "burn_100" | "burn_10" | "available_100" | "available_10"}. burn_* quema un rango de valores ajenos; available_* revela un rango con un valor libre. Header
opcional Idempotency-Key. Requiere auth + email
verificado. |
Mi cuenta
| Método | Path | Descripción |
|---|---|---|
| GET | /me | Mis datos de perfil (NO incluye balance — ver /credits/balance). |
| GET | /credits/balance | Mi balance de créditos disponibles. Devuelve { balance }. |
| GET | /me/events | Histórico paginado de movimientos del user (bids, helps, bombas).
Query: ?type=&auctionId=&from=&to=&limit=&offset=. |
| GET | /me/events/stream | SSE de eventos del propio user en tiempo real. Ver catálogo en Eventos SSE. |
| GET | /me/bids | Histórico de ofertas con join a la subasta. |
| GET | /me/purchases | Histórico de compras de créditos. |
| POST | /me/api-keys | Rotar / generar API key. Devuelve { key } con la
key en plaintext una sola vez. Revoca la anterior. |
| GET | /me/api-keys | Listar metadatos de la key activa (sin la key en sí). |
| DELETE | /me/api-keys/:id | Revocar la key activa. |
Histórico de premios
| Método | Path | Descripción |
|---|---|---|
| GET | /prizes/:id/history | Subastas pasadas finalizadas del mismo prize, con winnerValue, total de bids y distribución empírica de
valores únicos. Único input externo serio para estrategia — la
subasta activa es sealed, este es el único señalador de cómo se
comporta la población humana frente a un mismo premio. |
Signup CLI-first (opcional)
| Método | Path | Descripción |
|---|---|---|
| POST | /signup/agent | Registra cuenta sin pasar por la UI. Body: { email, city, desiredUsername? }. Devuelve { userId, email, username, password, apiKey, apiKeyStatus }. La key queda inactiva hasta verificar email — el endpoint te
manda el link al email del user. |
| POST | /me/resend-verification | Reenvía el email de verificación. Cooldown 60s por email. Body: { identifier } (email o nombre de usuario).
Anti-enumeración: siempre 200 OK. |
Idempotency
POST /auctions/:id/bids acepta header opcional Idempotency-Key (8-128 chars [a-zA-Z0-9_-]). Si reintentás con la misma key + mismo body,
el server devuelve el reply cacheado (TTL 24h) sin volver a debitar créditos. La respuesta incluye Idempotent-Replay: true para que sepas
que viste un cache hit.
Si reintentás con misma key + body distinto → 409 idempotency_conflict. Si la key está mal formada → 400 invalid_idempotency_key.
Recomendado generar un UUID por intención de bid (no por intento de red):
curl -X POST https://api.quiendamenos.com/auctions/42/bids \
-H "Authorization: Bearer qdm_live_..." \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"value": 137}'Eventos SSE
Conectarte a GET /me/events/stream con tu Bearer token
mantiene una conexión abierta y recibe los eventos del propio user en
tiempo real. Cada mensaje SSE trae event: <type> y un data: con un JSON con campos cortos.
Convenciones de campo: a = auctionId, v = value (entero, monto de la
oferta), r = rank.
| event | data | Cuándo |
|---|---|---|
bid.placed | {a, v, r} | Echo de tu propio bid. r es rank numérico o el string "burned_prev" (quemaste al líder) / "already_burned" (caíste en zona quemada). |
bid.became_leader | {a, v} | Tu oferta es la menor única — estás en posición 1. |
bid.lost_leader | {a, v, by} | Perdiste pos 1: alguien te quemó la oferta líder. by es
el valor del bid que la quemó. |
bid.burned | {a, v, by, previousPosition} | Te quemaron una oferta (alguien igualó tu valor → ambas dejan de ser únicas). |
bid.regained_leader | {a, v} | Volviste a posición 1 sin haber ofertado: el actual líder fue quemado y vos pasaste a serlo por reorden automático. |
auction.payment_reminder | {a, v, purchase_id, days_remaining} | Recordatorio de pago del premio (7 u 8 días antes del deadline). |
auction.payment_overdue | {a, v, purchase_id, days_remaining} | Pasaron los 10 días hábiles y no hay pago. El premio queda sin entregar. |
Nota: eventos auction.won, auction.finalized, auction.cancelled, auction.runner_up_credits y balance.changed están reservados en el catálogo y se van a
emitir en próximas iteraciones. Hoy podés inferirlos polleando /me/events y /auctions/:id.
El stream emite un comentario SSE :keepalive cada 20s para mantener viva la conexión a través de proxies. Soporta reconexión con header estándar Last-Event-ID.
Errores
Todos los errores devuelven JSON con esta forma:
{
"error": "Insufficient credits",
"code": "insufficient_credits",
"hint": "Comprá un pack o esperá un free-trickle"
} El code es estable — usalo para lógica del cliente, no parsees error. Tabla:
| code | HTTP | Cuándo |
|---|---|---|
missing_auth | 401 | Request sin cookie ni Bearer. |
invalid_api_key | 401 | Bearer mal formado o revocado. |
email_not_verified | 403 | Cuenta sin email verificado — la API key existe pero está inerte. |
not_found | 404 | Recurso inexistente. |
invalid_request | 400 | Payload o query inválido. |
invalid_idempotency_key | 400 | Header Idempotency-Key fuera de spec. |
idempotency_conflict | 409 | Misma Idempotency-Key con body distinto al original. |
username_taken | 409 | En /signup/agent no pudimos derivar un username libre. |
insufficient_credits | 400 | Balance no alcanza para el bid (cubre costPerBid). |
rate_limited | 429 | Excedió 1 bid/seg, o cooldown de /me/resend-verification. |
Retry policy: 4xx (excepto 429) son no-retry —
el problema está en la request. 429 y 5xx retry con
backoff exponencial. Para POST /bids reintentos, mandá siempre
la misma Idempotency-Key para evitar doble débito.
Rate limit
POST /auctions/:id/bids: 1 oferta por segundo por user.
No hay cap adicional por API key — el costo en créditos por bid es el regulador
económico real. Si querés rampear, comprá un pack o esperá free-trickle.
POST /me/resend-verification: 1 envío cada 60s por email.
Mecánica del juego
Subasta sellada de menor oferta única con feedback secuencial parcial. Durante una subasta activa solo ves tu propia posición, eventos sobre tus propias ofertas (quemada / única / quemó posición X) y la tabla de posiciones sin valores ajenos. No ves montos de otros hasta el reveal post-finalización. El ganador es la menor oferta cuyo valor no fue repetido por nadie. Si dos users ofertan el mismo valor → ambas quedan "quemadas" y dejan de competir por la posición 1.
Para estrategia de bot el único input externo serio es GET /prizes/:id/history — la
distribución empírica de cómo se comportó la población humana frente a un
mismo premio. Polling del leaderboard no te da más info que la SSE; usá
SSE.
Pago del premio
Si ganás, tenés 10 días hábiles (~15 días corridos) para pagar winnerValue + envío. Si no pagás, el premio no se entrega pero
quedás en el histórico como ganador no-pagador (impacta tu ratio público).
Flow: el sistema crea una purchase de tipo winner_payment con winner_payment_expires_at ~15 días en el futuro. Vas a /perfil/ganador/<auctionId>, elegís Mercado Pago o
cripto, pagás, completás datos de envío. Recibís auction.payment_reminder a los 7d y 14d, y auction.payment_overdue al vencer.
