API para tiendas

Conecta tu punto de venta con La Bóveda

Tu sistema descuenta aquí lo que vendes en mostrador, y le avisamos al momento lo que se vende en línea. Así ninguna carta se vende dos veces.

Conéctala en 3 pasos

Lo que necesita quien programa tu punto de venta, en el orden en que lo va a hacer.

  1. Paso 1

    Genera tu llave

    En Configuración → Conexiones, con un código a tu WhatsApp.

    Configuración, sección Conexiones, con el botón Generar llave y una carta de la tienda entre el mostrador y La Bóveda
    Conexiones, antes de generar la llave.Pantalla real · datos de prueba
  2. Paso 2

    Lee tu inventario

    Para saber cómo se llama cada carta aquí y cuántas hay.

    Ragavan, Nimble Pilferer

    MH2-138-EN-NF-1

    Cantidad · 3
    Disponible · 2
  3. Paso 3

    Descuenta cada venta

    Un movimiento por renglón de tu ticket, y escucha nuestros avisos.

    ticket-8812 · −1
    aplicado
    ticket-8812 · −1
    duplicado
    venta en línea
    venta.creada

Tu llave

Una por tienda. Se muestra una sola vez: si la pierdes, cámbiala y la anterior deja de funcionar.

GET
/yo
cualquiera

Mándala en la cabecera Authorization de cada petición. Este endpoint te dice si funciona.

bash
curl https://cards.the-vault.mx/api/v1/yo \
  -H "Authorization: Bearer vlt_live_TU_LLAVE"

La llave puede

ver tu inventario
mover tu inventario
ver tus ventas
avisar a tu sistema

Nunca puede

tocar tu saldo
cambiar precios
crear subastas

Leer tu inventario

De 500 en 500 con cursor. Filtra por SKU de BinderPOS o por set y número para saber si tienes una carta.

GET
/inventario
inventario:leer
bash
curl "https://cards.the-vault.mx/api/v1/inventario?sku=MH2-138-EN-NF-1" \
  -H "Authorization: Bearer vlt_live_TU_LLAVE"

Así se lee la respuesta

Ragavan, Nimble Pilferer

MH2-138-EN-NF-1

Cantidad · 3
Disponible · 2
En subasta · 1
Precio · $1,450
json
{
  "inventario": [{
    "id": "5eed0004-…",
    "nombre": "Ragavan, Nimble Pilferer",
    "set": "MH2", "numero": "138", "idioma": "en", "foil": false,
    "condicion": "Near Mint",
    "cantidad": 3, "disponible": 2, "apartada_en_subasta": 1,
    "en_custodia": false,
    "precio_actual": 1450
  }],
  "siguiente_cursor": null
}
GET
/inventario/{id}
inventario:leer

Mover tu inventario

Por movimiento, nunca por cantidad final: manda −1 cuando vendes una, no “quedan 2”. Si al mismo tiempo se vende otra en línea, los dos se suman bien.

POST
/movimientos
inventario:mover
bash
curl -X POST https://cards.the-vault.mx/api/v1/movimientos \
  -H "Authorization: Bearer vlt_live_TU_LLAVE" \
  -H "Content-Type: application/json" \
  -d '{
    "movimientos": [{
      "sku": "MH2-138-EN-NF-1",
      "delta": -1,
      "motivo": "venta_mostrador",
      "referencia_externa": "ticket-8812-renglon-1",
      "precio_venta": 1400
    }]
  }'

Motivos

venta_mostrador −
merma −
entrada +
ajuste ±

Cada movimiento responde

aplicado
duplicado
rechazado

referencia_externa es obligatoria

Usa el id de tu ticket o renglón. Si mandas el mismo dos veces —un reintento—, el segundo responde duplicado y no descuenta.

Si se rechaza, trae el motivo

sin_existencia
no_en_tu_inventario
sku_invalido
atributos_incompletos
delta_invalido
motivo_invalido
falta_referencia

Hasta 100 movimientos por petición, cada uno entre −100 y 100. Nombra la carta con inventario_id, con sku o con set + numero + idioma + foil + condicion. Solo cartas que ya tienes: las nuevas se dan de alta con el importador de colección.

GET
/movimientos?desde=…
inventario:leer

Tu historial: lo que mandó tu sistema (origen api) y lo que se hizo en la app (origen app).

Tus ventas en La Bóveda

Lo que se vendió de tu tienda en línea, para descontarlo en tu sistema. Nunca trae datos del comprador.

GET
/ventas?desde=…
ventas:leer

desde filtra por la última actualización: una venta que cambió de estado vuelve a salir. El neto es el mismo que ves en tu Saldo.

Ragavan, Nimble Pilferer

MH2-138-EN-NF-1

Vendida · 1
Neto · $1,305

Estados

por_confirmar
confirmada
recibida
cancelada
faltante
reembolsada
GET
/ventas/{id}
ventas:leer

Avisos a tu sistema

Registra una URL https pública y te avisamos en menos de un minuto cuando se vende o se cancela una venta tuya.

POST
/webhooks
webhooks:administrar
bash
curl -X POST https://cards.the-vault.mx/api/v1/webhooks \
  -H "Authorization: Bearer vlt_live_TU_LLAVE" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://tu-sistema.mx/boveda/avisos" }'

El secreto de firma se muestra una sola vez

Hasta 3 URLs por tienda. Pruébala con POST /webhooks/{id}/probar y apágala con DELETE.

Eventos

venta.creada
venta.cancelada · reasignada
venta.cancelada · rechazada
venta.cancelada · faltante
venta.cancelada · reembolsada
http
POST https://tu-sistema.mx/boveda/avisos
X-Vault-Evento: venta.creada
X-Vault-Entrega: 546d…        ← el mismo id si te llega dos veces
X-Vault-Firma: t=1791500000,v1=9f2c…

{ "evento": "venta.creada",
  "venta": { "id": "964a…", "sku": "MH2-138-EN-NF-1", "nombre": "Ragavan, Nimble Pilferer", "cantidad": 1 } }

Verifica la firma

js
import crypto from 'node:crypto'

// cuerpoCrudo: el body tal cual llegó (string), ANTES de parsearlo.
function avisoValido(cuerpoCrudo, cabeceraFirma, secreto) {
  const { t, v1 } = Object.fromEntries(cabeceraFirma.split(',').map((p) => p.split('=')))
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false // viejo: descártalo
  const esperada = crypto.createHmac('sha256', secreto).update(`${t}.${cuerpoCrudo}`).digest('hex')
  return esperada.length === v1.length &&
    crypto.timingSafeEqual(Buffer.from(esperada), Buffer.from(v1))
}

Si tu URL falla, reintentamos

1 min
5 min
30 min
2 h
12 h
a los 20 fallos se apaga

Responde con cualquier 2xx en menos de 10 segundos; no seguimos redirecciones. Si se perdió un aviso, GET /ventas?desde= siempre tiene la verdad.

Errores y límites

Todo error trae { error: { codigo, mensaje } }. Hasta 120 peticiones por minuto por llave; las listas vienen de 500 en 500.

CódigoQué pasó
401 token_invalido
Falta la llave, no existe, o la cambiaste o desconectaste.
403 solo_tiendas
La cuenta de la llave no es una tienda.
429 cuota_excedida
Pasaste de 120 peticiones por minuto. Espera lo que diga Retry-After.
400 lote_invalido
El cuerpo de /movimientos no trae de 1 a 100 movimientos.
404 no_encontrada
Esa carta o venta no es tuya o no existe.
500 error_interno
Algo falló de nuestro lado. Reintenta con la misma referencia_externa.

¿Listo para conectarla?

Genera tu llave y pégala en tu punto de venta.

Generar mi llave