VitrinaAPI

Probar tu integración sin tocar datos reales

Qué es un espacio de prueba, cómo conseguir una clave sk_test_ y qué queda capturado.

Cada workspace puede tener su propio espacio de prueba. Es una copia sintética, aparte de tus datos reales. Nada de lo que hay adentro representa a una persona real.

Las respuestas de esta página salen de una corrida contra un espacio de prueba. Los identificadores serán otros; la forma es la misma.

Trampa

Una clave sk_test_ jamás lee tu workspace real, y una real jamás lee el de prueba

Una clave sk_test_ solo sirve en tu espacio de prueba, y una clave real solo en tu workspace real. Usarla del otro lado responde 401. No hay una URL, un parámetro ni un header para cruzar: lo decide la clave que usas.

Conseguir una clave sk_test_

POST /api-keys con livemode: false emite una clave de prueba. El tenant_id que trae de vuelta es el de tu espacio de prueba, no el de tu workspace. La clave vive ahí, aunque la pediste desde el real.

Sin un espacio de prueba todavía, la respuesta es esta:

{
  "error": {
    "code": "SANDBOX_NOT_PROVISIONED",
    "message": "Este espacio de trabajo todavía no tiene un entorno de prueba. Créalo primero y luego genera la clave de prueba.",
    "details": {
      "provision": ["POST /api/v1/sandbox/automotive", "POST /api/v1/sandbox/clinic"]
    },
    "requestId": "5e2b1b14-e657-4d6e-9ade-61bdec0112e2"
  }
}

El propio error dice qué llamar. El espacio de prueba no se crea solo al pedir una clave.

POST /sandbox/automotive crea una automotora sintética. Trae vehículos con marcas y modelos reales, compradores, conversaciones, cotizaciones y reservas. Entrega además una clave de prueba lista para usar:

curl -X POST https://api.vitrinadev.com/api/v1/sandbox/automotive \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "seed": "mi-demo" }'
{
  "data": {
    "created": true,
    "tenant": {
      "id": "34d213e0-5cac-4c86-a57c-4db28d59b9ec",
      "name": "Autos Alameda (demostración)",
      "slug": "autos-alameda-demo-3"
    },
    "vertical": "automotive",
    "seed": "mi-demo",
    "stats": {
      "vehicles": 40,
      "contacts": 30,
      "leads": 30,
      "quotes": 10,
      "reservations": 5,
      "…": "…"
    },
    "test_key": {
      "secret": "sk_test_gZCK…",
      "prefix": "sk_test_gZCK",
      "scopes": ["marketplace:read", "marketplace:write", "leads:read", "leads:write", "contacts:read", "contacts:write", "conversations:read", "conversations:write", "messages:send", "quotes:read", "quotes:write", "reservations:read", "reservations:write", "sandbox:read"],
      "livemode": false
    }
  }
}

seed es lo único que decide el contenido. La misma semilla vuelve a armar el mismo lote, con los mismos compradores. Repetir la llamada responde 200 con created: false y sin clave nueva. Hay un espacio de prueba por workspace, de cualquiera de las dos verticales.

Con el espacio ya aprovisionado, POST /api-keys con livemode: false emite cuantas claves de prueba quieras. Elige los scopes que necesites:

curl -X POST https://api.vitrinadev.com/api/v1/api-keys \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "integración — pruebas", "scopes": ["tenant:read"], "livemode": false }'
{
  "data": {
    "id": "11773f0e-f653-4e2f-8f9d-5c67d0a3eb88",
    "tenant_id": "34d213e0-5cac-4c86-a57c-4db28d59b9ec",
    "name": "integración — pruebas",
    "prefix": "sk_test_x_-L",
    "scopes": ["tenant:read"],
    "revoked_at": null,
    "livemode": false,
    "secret": "sk_test_x_-L…"
  }
}

GET /api-keys lista las dos familias juntas. livemode las distingue, así que las administras desde una sola pantalla:

{
  "data": [
    { "id": "11773f0e-…", "name": "integración — pruebas", "prefix": "sk_test_x_-L", "livemode": false, "…": "…" },
    { "id": "8cd24c44-…", "name": "token de la persona dueña del workspace", "prefix": "sk_vyJfH", "livemode": true, "…": "…" }
  ],
  "meta": { "total": 2 }
}

Qué se captura y qué se entrega

Un espacio de prueba nunca abre un canal real. La mensajería, el correo, los portales de publicación y la emisión electrónica pasan por un proveedor simulado. Quedan capturados, nunca salen.

Los webhooks son la única excepción. Se entregan de verdad a tu URL, con "livemode": false en el cuerpo y el header Vitrina-Livemode: false. El mecanismo completo está en Webhooks.

Un envío intentado desde una conversación del espacio de prueba pasa por la misma política de envío que un workspace real:

{
  "error": {
    "code": "OUTBOUND_WARNING",
    "message": "This message was not sent yet: the outbound policy warns about quiet_hours, conversation_human_owned. Resend the same request with \"acknowledge\": [\"quiet_hours\",\"conversation_human_owned\"] to send it anyway; the acknowledgment is recorded.",
    "details": {
      "reasons": [
        { "code": "quiet_hours", "kind": "advertencia", "detail": { "windowStartHour": 9, "windowEndHour": 20 } },
        { "code": "conversation_human_owned", "kind": "advertencia", "detail": { "conversationId": "f6506b7f-a342-48d6-8590-011f263f6b13" } }
      ]
    },
    "requestId": "d5ab5a84-3580-44ec-a11d-3ada6953f651"
  }
}

Reenviada con acknowledge, la misma llamada responde 201 con el mensaje persistido. En vez de salir por WhatsApp, queda anotada. GET /sandbox/outbound lista lo capturado, con un resumen por canal:

{
  "data": [
    {
      "id": "0aab045e-1441-41f3-9258-8a780c501b1a",
      "tenant_id": "34d213e0-5cac-4c86-a57c-4db28d59b9ec",
      "rail": "whatsapp",
      "provider": "whatsapp_cloud",
      "destination": "56908450000",
      "body": "Hola, te confirmo tu cita para el jueves a las 10.",
      "origin": "provider.sendTextMessage",
      "idempotency_key": "74ec9181-edf1-48dc-aab3-4883c7eb2e2e",
      "captured_at": "2026-09-23T05:28:53.464Z"
    }
  ],
  "meta": {
    "summary": [{ "rail": "whatsapp", "count": 1 }],
    "rails": ["whatsapp", "email", "instagram", "messenger", "webchat", "voice", "tax_document", "portal", "other"]
  }
}

GET /sandbox/outbound/{id} devuelve una sola fila. Trae el payload entero que el canal intentado recibió. Sirve para comparar bit a bit contra lo que tu integración cree haber mandado.

Lo que el modo de prueba no simula

  • El bloqueo y la advertencia de envío corren igual. El 409 OUTBOUND_WARNING de arriba sale de un espacio de prueba. La política se evalúa antes de capturar. Así tu integración aprende a manejar un 422 OUTBOUND_BLOCKED o un 409 OUTBOUND_WARNING con datos sintéticos, antes de llegar a producción.
  • La idempotencia también. Reenviar el mismo idempotency_key devuelve la captura original. No escribe una segunda: la fila de arriba lleva el mismo id que el mensaje que la generó.
  • El techo de llamadas es el mismo limitador, por clave, sin distinguir prueba de real. Autenticación tiene el detalle.

Lo que no tiene forma de reproducirse es el límite propio de un proveedor externo. El throttling por calidad que aplica un canal de mensajería no existe en modo de prueba, y no hay un valor sintético que enseñe cuánto tarda en aparecer.

Los huecos permanentes

Voz es la única superficie sin una captura equivalente. Una llamada saliente deja fila y conversación en el espacio de prueba. El intento queda anotado, pero no se marca ningún número.

El panel de Chileautos no se puede conectar en un espacio de prueba. Ni siquiera se puede usar para capturar sus publicaciones:

{
  "error": {
    "code": "SANDBOX_CHANNEL_FORBIDDEN",
    "message": "Este es un espacio de demostración con datos sintéticos: no se pueden conectar canales reales. Los envíos quedan registrados en «Envíos capturados» en lugar de salir.",
    "details": {
      "attempted": "chileautos_panel",
      "reason": "El panel de Chileautos se opera con un navegador real y una sesión de proxy: no se puede simular en un espacio de demostración. Usa la integración Chileautos (API), cuyas publicaciones quedan capturadas."
    },
    "requestId": "42b11cd8-09d3-4155-ac75-380ad413563e"
  }
}

La integración REST de Chileautos sí funciona en modo de prueba, y sus publicaciones aparecen en el rail portal.

Reiniciar el espacio de prueba

POST /sandbox/reset borra los datos sintéticos y cada captura, y genera un lote nuevo desde la misma semilla:

{
  "data": {
    "tenant_id": "34d213e0-5cac-4c86-a57c-4db28d59b9ec",
    "vertical": "automotive",
    "seed": "mi-demo",
    "counts": { "vehicle": 40, "contact": 30, "lead": 30, "…": "…" },
    "deleted": { "vehicle": 40, "contact": 30, "sandbox_outbound_capture": 1, "…": "…" }
  }
}

deleted.sandbox_outbound_capture es la prueba: cada envío que habías capturado se va con el resto. Lo que sobrevive es el workspace, sus miembros y sus claves. Las sk_test_ siguen sirviendo después del reinicio, y también sus suscripciones a webhooks y su plan gratuito. Las fechas se re-anclan al día del reinicio y los identificadores visibles (L-1, C-1…) empiezan de nuevo. Solo se puede llamar desde el workspace real que lo aprovisionó, o desde una clave del propio espacio de prueba.

En esta página