1. ¿Qué es la sandbox y por qué tu widget está en una jaula?
Cuando ChatGPT muestra tu widget, lo renderiza no como un <iframe src="https://tu-sitio"> normal. El widget se ejecuta en una «sandbox» gestionada: un iframe aislado con un origin separado y ajustes de seguridad estrictos.
Técnicamente se ve más o menos así:
flowchart TD
User["Usuario en ChatGPT"]
Chat["ChatGPT UI + modelo"]
Iframe["Tu widget
iframe en sandbox"]
MCP["Tu MCP / backend"]
User --> Chat
Chat -->|llamada a tool| MCP
MCP -->|structuredContent + _meta| Chat
Chat -->|window.openai.*| Iframe
Iframe -->|callTool / follow-up| Chat
Chat --> MCP
Tu código se ejecuta solo dentro de ese iframe, y el acceso al resto del mundo va a través de un API estrechamente controlada que proporciona el host (ChatGPT). El widget no debe:
- romper el propio ChatGPT (DOM, estilos, rendimiento);
- violar la privacidad del usuario;
- salir a la red sin control.
De aquí se derivan las limitaciones clave de la sandbox.
Aislamiento de DOM y origin
El widget vive en un dominio especial de sandbox (por ejemplo, https://sandbox-apps.oaiusercontent.com), con el atributo sandbox en el iframe. Esto significa que:
- no puedes acceder a window.parent ni al document de ChatGPT: obtendrás SecurityError;
- las cosas entre dominios como postMessage están controladas por el host;
- cualquier intento de «arreglar la interfaz de ChatGPT con tu CSS» está condenado al fracaso.
Red y restricciones de CSP
El navegador y la política CSP del host limitan el acceso a la red para tu widget:
- el método fetch tiene acceso solo a dominios de una lista permitida que debe pasar revisión;
- qué dominios puede tocar el widget los declaras explícitamente mediante openai/widgetCSP en las respuestas de MCP; en caso contrario, las peticiones simplemente no pasarán;
- la vía recomendada para todo lo serio es no salir a la red desde el widget, sino ir al backend mediante herramientas MCP y callTool (lo veremos con detalle en el Módulo 4).
En la práctica: piensa en el widget como en una capa de UI fina. Habla con ChatGPT y con tu servidor por canales estrictamente definidos, no como un SPA normal que vive libremente en internet.
Almacenamientos y recursos
Los almacenamientos locales (localStorage, sessionStorage) están disponibles, pero las cookies no. Tenlo en cuenta al desarrollar la aplicación. La memoria y la CPU están limitadas: si decides recalcular todos los números primos hasta mil millones dentro del widget, el host tiene pleno derecho a matar tu iframe.
De aquí una conclusión importante: nada de cálculos pesados ni «cachés» de larga vida en el widget. La lógica compleja —en el servidor, no en el componente de React.
2. window.openai: el puente entre el widget y ChatGPT
Para que el widget pueda saber algo (resultados de herramientas, modo de visualización, locale, estado), ChatGPT al inicializar incrusta en la ventana del iframe un único objeto global: window.openai.
No es tu código ni un paquete de npm, sino un host object que proporciona la propia plataforma de IA. Por debajo está basado en eventos y mensajes entre el host y el iframe, pero casi no necesitas pensar en eso. Es importante recordar varias cosas.
Quién y cuándo crea window.openai
window.openai aparece solo:
- dentro del iframe que ChatGPT crea para tu widget;
- cuando la plantilla HTML se entrega con el mimeType correcto (text/html+skybridge) y ha pasado todas las comprobaciones.
Ya has visto este tipo en el módulo sobre HelloWorld App: es exactamente lo que devuelve la página del widget en lugar de un text/html normal.
Si abres la página del widget directamente en el navegador, entonces:
console.log(window.openai); // undefined
y eso es normal. Por eso, en el código del widget, siempre conviene comprobar que el objeto existe si cuentas con un modo «standalone» para desarrollo local o Storybook.
Ejemplo primitivo (no final, solo ilustración):
if (typeof window !== "undefined" && (window as any).openai) {
console.log("We are inside ChatGPT sandbox!");
}
Asincronía de la inicialización
Por debajo, ChatGPT va actualizando window.openai a medida que llegan nuevos datos (nuevo toolOutput, cambio de displayMode, etc.), usando el evento interno openai:set_globals.
Es decir, sus «valores» no son estáticos: el modelo de IA puede llamar a una herramienta MCP, el backend devuelve un nuevo structuredContent, y window.openai.toolOutput cambia justo debajo de tu componente de React.
De aquí dos recomendaciones:
- No hagas snapshots «ciegos» del tipo const toolOutput = window.openai.toolOutput una vez al inicio pensando que serán eternos. Un mismo widget puede ser reutilizado por ChatGPT.
- Usa la capa de hooks (más abajo), que sabe suscribirse a los cambios.
3. Anatomía de window.openai: datos, API y contexto
La documentación oficial ofrece una tabla bastante compacta de campos y métodos de window.openai. Aquí la convertimos en algo más «humano».
Campos y métodos principales
window.openai = {
// State & data
toolInput, // JSON: parámetros que la IA pasó a tu herramienta MCP
toolOutput, // JSON: parámetros que tu herramienta MCP devolvió a la IA
toolResponseMetadata, // Respuesta del MCP-tool: parte de _meta: {...}
widgetState, // Puedes leer el estado guardado del widget
setWidgetState, // Puedes guardar el estado de tu widget aquí
// Runtime APIs
callTool, // Puedes invocar un MCP-tool
sendFollowUpMessage, // Enviar de forma discreta un mensaje para la IA en el chat: empezará a responder
requestDisplayMode, // Cambiar el widget a otro modo: fullscreen, pip, inline
requestModal, // Convertir el widget en una ventana modal
requestClose, // Cierra el widget. Si es modal, lo cierra y vuelve a ser widget
requestCheckout, // Abre una ventana modal de pago. El servidor debe implementar ACP
notifyIntrinsicHeight, // Notificación de cambio de altura del widget
openExternal, // Abrir un enlace en una nueva ventana
// Context
theme, // Tema: claro u oscuro
displayMode, // Modo de visualización actual del widget; puede diferir de requestDisplayMode
maxHeight, // Altura máxima permitida del widget
safeArea, // «Área segura de renderizado» — relevante para móviles con «notch»
view,
userAgent, // userAgent del navegador
locale // locale del navegador
}
Lo mismo en una tabla:
| Categoría | Propiedad / método | Para qué sirve |
|---|---|---|
| State & data | |
Argumentos con los que se llamó a la herramienta. Solo lectura. |
| State & data | |
Tu structuredContent de la respuesta MCP. Lo que ve el widget y el modelo. |
| State & data | |
_meta de la respuesta. Solo visible para el widget; el modelo no lo lee. |
| State & data | |
Instantánea del estado de UI que ChatGPT guarda entre renderizados del widget. |
| State & data | |
Guardar una nueva instantánea de widgetState de forma síncrona. |
| Function | |
Llamar a una herramienta MCP desde el widget. |
| Function | |
Pedir a ChatGPT que envíe un mensaje al chat en nombre del widget. Empezará a responder. |
| Function | |
Pedir al host inline / fullscreen / pip. |
| Function | |
Solicitar apertura de una ventana modal. |
| Function | |
Informar de que la altura del contenido ha cambiado. |
| Function | |
Abre un diálogo de pago según el protocolo ACP. |
| Function | |
Abrir un enlace externo en el navegador del usuario. |
| Context | |
Señales del entorno: tema, modo, altura disponible, locale, etc. |
No es necesario memorizar todo de esta tabla de golpe: tómatela como un «mapa del territorio». Ahora lo veremos no como «referencia», sino como lo haría una persona normal.
toolInput y toolOutput: de dónde vienen los datos
Cuando el modelo decide llamar a tu herramienta, forma argumentos JSON. Esos argumentos:
- llegan al servidor MCP como input al manejador;
- al mismo tiempo aparecen en window.openai.toolInput en el widget.
Tras ejecutar la herramienta, el servidor devuelve:
- structuredContent: datos estructurados para la UI;
- _meta: datos privados solo para el widget;
- content: texto para el propio modelo, para que pueda «explicar» al usuario qué ha pasado.
structuredContent pasa a ser window.openai.toolOutput, y _meta pasa a ser window.openai.toolResponseMetadata.
Mini-ejemplo (JS vainilla, sin React):
const root = document.getElementById("root");
// Se puede usar con seguridad el operador nullish
const gifts = window.openai.toolOutput?.gifts ?? [];
root.textContent = `Regalos encontrados: ${gifts.length}`;
widgetState y setWidgetState: la memoria del widget
widgetState es lo que la plataforma está dispuesta a recordar sobre tu UI entre renderizados e incluso entre turnos separados del diálogo.
Ejemplos naturales para widgetState:
- el regalo seleccionado;
- la ordenación actual (por precio / por popularidad);
- el número de página en una lista.
No naturales:
- respuesta en bruto de una API externa;
- una imagen en base64;
- tokens secretos.
Recuerda dos cosas:
- widgetState se almacena y se pasa al modelo junto con el contexto; por tanto, no pongas ahí nada sensible.
- El tamaño es limitado (aprox. 4 000 tokens), así que no lo conviertas en una mini base de datos.
Ejemplo sencillísimo (directo, sin hooks, en JS vainilla):
const current = window.openai.widgetState ?? { selectedGiftId: null };
function selectGift(id) {
window.openai.setWidgetState({ ...current, selectedGiftId: id });
}
En código real lo envolveremos con hooks de React.
Runtime API: callTool, sendFollowUpMessage y análogos
Estos métodos permiten que el widget no solo «se dibuje», sino también interactúe con el diálogo y el servidor.
Varios escenarios típicos:
- callTool("search_gifts", { budget: 50 }) — el usuario pulsa «Cambiar presupuesto», llamas al servidor y actualizas la UI;
- sendFollowUpMessage({ prompt: "Muestra ideas más caras" }) — en lugar de pedir al usuario que escriba texto, añades un botón de follow-up que crea un nuevo mensaje en el chat;
- requestDisplayMode({ mode: "fullscreen" }) — si el modo inline se queda pequeño, el widget puede pedir amablemente a ChatGPT ocupar toda la pantalla;
- openExternal({ href: "https://myshop.com/checkout?giftId=123" }) — enviar al usuario a un sitio externo (checkout, perfil, etc.) por un canal verificado.
Todo va «por el cable» a través de ChatGPT, no directamente a internet.
Contexto del entorno: tema, modo, altura, locale
Campos como theme, displayMode, maxHeight, locale te dan una idea del entorno en el que vive el widget.
Por ejemplo:
const theme = window.openai.theme; // "light" o "dark"
const mode = window.openai.displayMode; // "inline" | "fullscreen" | "pip"
const maxH = window.openai.maxHeight; // altura disponible
const locale = window.openai.locale; // "en-US", "de-DE", ...
Con estas señales puedes:
- ajustar colores y márgenes según el tema;
- cambiar el layout en función del modo (inline vs fullscreen);
- localizar las etiquetas de la UI al idioma del usuario (habrá todo un módulo sobre esto).
La plataforma te da señales sobre cuánto espacio hay, qué tema y qué locale. Es sensato usarlas mediante useOpenAIGlobal, useDisplayMode, useMaxHeight y otros hooks, para que el widget se vea «nativo» en ChatGPT.
4. Hooks sobre window.openai: no tocar el objeto global a mano
Acceder directamente a window.openai es cómodo para un prototipo, pero rápido convierte el código en un caos: suscripciones a eventos, comprobaciones de undefined, envoltorios repetidos. Por eso, en la plantilla de Next.js para el Apps SDK hay un conjunto listo de hooks de React que esconden detalles y lo hacen todo reactivo.
Un índice típico de hooks se ve así:
// app/hooks/openai/index.ts
export { useCallTool } from "./use-call-tool";
export { useSendMessage } from "./use-send-message";
export { useOpenExternal } from "./use-open-external";
export { useRequestDisplayMode, useRequestModal, useRequestClose } from "./use-request-display-mode";
export { useRequestCheckout } from "./use-request-checkout";
// State hooks
export { useDisplayMode } from "./use-display-mode";
export { useWidgetProps } from "./use-widget-props";
export { useWidgetState } from "./use-widget-state";
export { useOpenAIGlobal } from "./use-openai-global";
export { useMaxHeight } from "./use-max-height";
export { useIsChatGptApp } from "./use-is-chatgpt-app";
Los nombres y la ruta exacta pueden variar un poco en tu plantilla, pero la idea es la misma: en lugar de window.openai.* usas hooks. Veamos los clave.
useWidgetProps: entrada y salida de la herramienta
useWidgetProps suele devolver un objeto con los datos que necesita el widget: toolInput, toolOutput, toolResponseMetadata y a veces flags adicionales como isLoading.
Ejemplo:
import { useWidgetProps } from "../hooks/openai";
type Gift = { id: string; title: string; price: number };
export function GiftList() {
const { toolOutput } = useWidgetProps<{ gifts: Gift[] }>();
const gifts = toolOutput?.gifts ?? [];
if (!gifts.length) {
return <div>Por ahora no hay opciones de regalos.</div>;
}
return (
<ul>
{gifts.map((g) => (
<li key={g.id}>{g.title} — ${g.price}</li>
))}
</ul>
);
}
Nada de window.openai en el componente — y eso es bueno.
useWidgetState: «envoltura reactiva» sobre widgetState
useWidgetState permite trabajar con widgetState como con un estado de React normal: obtienes [state, setState], y el hook por debajo lo sincroniza con window.openai.widgetState y setWidgetState.
Ejemplo:
import { useWidgetState } from "../hooks/openai";
type UiState = { selectedGiftId: string | null };
export function SelectedGiftIndicator() {
const [uiState, setUiState] = useWidgetState<UiState>(() => ({
selectedGiftId: null,
}));
if (!uiState?.selectedGiftId) {
return <div>Aún no se ha seleccionado un regalo.</div>;
}
return (
<div>
Has seleccionado un regalo con id={uiState.selectedGiftId}
<button onClick={() => setUiState({ selectedGiftId: null })}>
Restablecer
</button>
</div>
);
}
Tras el clic, setUiState no solo actualizará el estado de React, sino que también guardará el nuevo estado en ChatGPT.
useOpenAIGlobal: acceso a cualquier campo de window.openai
Si necesitas acceder a un único campo global (por ejemplo, tema o modo), existe el hook universal useOpenAIGlobal(key). Se suscribe al evento openai:set_globals y devuelve el valor siempre actualizado.
Ejemplo:
import { useOpenAIGlobal } from "../hooks/openai";
export function ThemeAwareBlock() {
const theme = useOpenAIGlobal<"light" | "dark">("theme");
const background = theme === "dark" ? "#222" : "#fff";
const color = theme === "dark" ? "#fff" : "#000";
return <div style={{ background, color }}>Respeto el tema de ChatGPT</div>;
}
useCallTool, useSendMessage, useOpenExternal y otros
- useCallTool(name): devuelve una función que llama a la herramienta MCP con ese nombre. Es un envoltorio sobre callTool.
- useSendMessage(): envuelve sendFollowUpMessage para que el widget pueda iniciar mensajes.
- useOpenExternal(): ayudante cómodo sobre openExternal({ href }).
- useRequestDisplayMode() y useRequestModal(): envoltorios para pedir cambio de modo / apertura de modal.
Ejemplo básico del mini‑widget GiftGenius, que usa casi todo a la vez:
import {
useWidgetProps,
useWidgetState,
useCallTool,
useSendMessage,
useOpenExternal,
} from "../hooks/openai";
type Gift = { id: string; title: string; url: string; price: number };
export function GiftWidget() {
const { toolOutput } = useWidgetProps<{ gifts: Gift[] }>();
const gifts = toolOutput?.gifts ?? [];
const [ui, setUi] = useWidgetState<{ selectedId: string | null }>(() => ({
selectedId: null,
}));
const callSearch = useCallTool("search_gifts");
const sendMessage = useSendMessage();
const openExternal = useOpenExternal();
if (!gifts.length) {
return <div>No hay ideas por ahora. Intenta pedir a GPT que actualice los resultados.</div>;
}
return (
<div>
{gifts.map((g) => (
<button
key={g.id}
style={{
display: "block",
fontWeight: ui?.selectedId === g.id ? "bold" : "normal",
}}
onClick={() => setUi({ selectedId: g.id })}
>
{g.title} — ${g.price}
</button>
))}
<div style={{ marginTop: 12 }}>
<button
onClick={() =>
sendMessage({ prompt: "Muestra regalos más caros que los actuales." })
}
>
Pedir más ideas
</button>
<button
onClick={async () => {
await callSearch({ budget: 200 });
}}
>
Actualizar con un presupuesto de $200
</button>
{ui?.selectedId && (
<button
onClick={() =>
openExternal({
href: `https://giftgenius.example.com/checkout?id=${ui.selectedId}`,
})
}
>
Ir a la compra
</button>
)}
</div>
</div>
);
}
Esta página aún está verde (en los siguientes módulos mejoraremos UX, manejo de errores, etc.), pero ya ilustra el enfoque: nada de accesos directos a window.openai, solo hooks.
5. Práctica: explorando la sandbox y window.openai
Para sentir qué significa «un widget que no es un sitio típico», es útil hacer un par de ejercicios.
Ejercicio: «Explora el entorno»
Toma tu actual app/page.tsx en el widget y añade al primer render un efecto simple:
import { useEffect } from "react";
import { useIsChatGptApp } from "../hooks/openai";
export default function Root() {
const isChatGpt = useIsChatGptApp();
useEffect(() => {
if (typeof window !== "undefined") {
console.log("window.origin =", window.origin);
console.log("window.openai =", (window as any).openai);
}
}, []);
return (
<main>
<h1>GiftGenius widget</h1>
<p>Ejecutado dentro de ChatGPT: {String(isChatGpt)}</p>
</main>
);
}
Abre DevTools: o bien directamente en la ventana de ChatGPT (mediante el visor del túnel, si lo permite), o en tu navegador local abriendo la página directamente. En ambos casos compara:
- al ejecutarlo en un navegador normal, isChatGptApp será false, y window.openai probablemente undefined;
- al ejecutarlo a través de ChatGPT verás un objeto con campos como toolInput, toolOutput, theme, etc.
Es una buena sensación intuitiva: el mismo código de React se comporta de forma distinta según el entorno, y justo para eso existen los hooks.
Ejercicio: «Muestra todo lo que ofrece la plataforma»
Añade un componente temporal para depuración:
import { useWidgetProps, useOpenAIGlobal } from "../hooks/openai";
export function DebugPanel() {
const { toolInput, toolOutput, toolResponseMetadata } = useWidgetProps();
const theme = useOpenAIGlobal("theme");
const displayMode = useOpenAIGlobal("displayMode");
return (
<pre style={{ fontSize: 10, maxHeight: 200, overflow: "auto" }}>
{JSON.stringify(
{ toolInput, toolOutput, toolResponseMetadata, theme, displayMode },
null,
2
)}
</pre>
);
}
E introduce temporalmente <DebugPanel /> bajo la UI principal. Así verás claramente:
- qué campos llegan exactamente desde MCP en toolOutput;
- qué vive en _meta (por ejemplo, locale, userLocation, etc.);
- cómo cambia displayMode cuando expandes el widget.
Luego puedes quitar este componente o dejarlo activable mediante algún flag como DEBUG_WIDGET.
6. Relaciones: ChatGPT ↔ widget ↔ MCP/servidor
Para no tratar al widget como «el actor principal» del sistema, conviene fijar de nuevo los roles.
- El usuario escribe: «Elige un regalo para mi novia, presupuesto 50$».
- El modelo de ChatGPT decide llamar a tu herramienta MCP search_gifts con los argumentos { recipient: "girlfriend", budget: 50 }.
- El servidor MCP ejecuta la lógica de negocio y devuelve:
- content con una breve descripción para el modelo;
- structuredContent con el array de regalos;
- _meta con detalles técnicos (por ejemplo, source y moneda).
- ChatGPT:
- muestra al usuario un mensaje de texto («He encontrado varias opciones…»);
- crea el iframe del widget y le pasa structuredContent y _meta mediante window.openai.toolOutput y toolResponseMetadata.
- Tu widget:
- renderiza la UI según toolOutput;
- ante interacciones llama a callTool o envía un follow-up;
- El modelo decide a continuación qué hacer con los resultados de estas acciones.
Todo esto conduce a una idea importante: el widget nunca es el único dueño del proceso. Es una capa de UI que vive en el ecosistema formado por el modelo y el servidor MCP. Las cosas complejas (autorización, acceso a datos privados, lógica seria de negocio) deben quedarse del lado del servidor. El widget se encarga de una interfaz cómoda y de comunicarse con el usuario con cuidado.
7. Políticas y reglas del juego en la sandbox
Toda esta construcción con un iframe aislado y window.openai existe por requisitos de seguridad y privacidad. Las guías oficiales de OpenAI subrayan varios principios.
Primero, minimización de datos. No debes intentar extraer del usuario tanta PII (información personalmente identificable) como puedas a través del widget y llevarla a tu lado. Todo lo realmente necesario debe estar descrito con claridad en las herramientas, y tanto el modelo como la capa de seguridad mirarán de cerca esas llamadas.
Segundo, prohibición de tracking oculto y fingerprinting. No se puede construir un sistema para «espiar» el dispositivo del usuario, recolectar huellas del navegador ni buscar formas de saltarse las limitaciones. Parámetros como userAgent, userLocation, etc., son pistas para el UX, no para autorización o identificación.
Tercero, todo lo que metas en structuredContent, _meta, widgetState, de algún modo lo verá el usuario o puede verlo un revisor del Store. Por tanto:
- no pongas allí claves de API, tokens, contraseñas ni secretos de administración;
- el estado del widget debe diseñarse de forma que el usuario no se sorprenda al verlo en logs o depuración.
Cuarto, llamadas de red. Las peticiones directas desde el widget a APIs de terceros solo están permitidas a una lista estrictamente limitada de dominios y en escenarios no sensibles. En cuanto hay dinero, cuentas o datos privados — todo debe pasar por MCP/backend.
8. Errores típicos al trabajar en la sandbox y con window.openai
Error n.º 1: pensar que el widget es «un sitio normal en un iframe».
Los principiantes intentan por costumbre acceder a window.parent, cambiar estilos de ChatGPT o usar localStorage como siempre. En la sandbox esto o no funciona, o funciona de forma inestable: el origin es distinto, el storage está aislado, y el acceso al DOM está bloqueado. Hay que asumir que vives en un entorno gestionado y te comunicas con el host solo a través de window.openai y los hooks.
Error n.º 2: toquetear window.openai directamente por todas partes.
Código del tipo window.openai.toolOutput en diez componentes es el camino a una app difícil de depurar. Además, tú mismo tendrás que gestionar eventos, asincronía y comprobaciones de undefined. Es mucho más fiable usar desde el principio useWidgetProps, useWidgetState, useOpenAIGlobal y otros hooks, que ya envuelven openai:set_globals y sincronizan el estado.
Error n.º 3: guardar en widgetState cualquier cosa (y especialmente secretos).
A veces apetece «por si acaso» meter ahí un objeto enorme con los resultados de una API o incluso un token de acceso. Como resultado, crece el contexto, empeora el trabajo del modelo y rompes requisitos básicos de seguridad. widgetState debe ser pequeño, contener solo señales de UI y nunca datos confidenciales.
Error n.º 4: intentar salir a internet directamente desde el widget.
Llamadas fetch("https://api.superbank.com/...") desde la sandbox casi seguro chocarán con CORS, y aunque lo ajustes perfecto, será inseguro y difícil de gestionar. Todo lo relacionado con cuentas reales, dinero y datos personales debe implementarse como herramientas MCP y llamarse mediante callTool o desde el servidor.
Error n.º 5: confiar en la estabilidad de window.openai fuera de ChatGPT.
A veces se intenta ejecutar el widget como un SPA independiente y no se añaden comprobaciones de que window.openai puede ser undefined. En dev esto termina con un crash «Cannot read properties of undefined». Usa useIsChatGptApp, comprobaciones de typeof window !== "undefined" y un UI de fallback para los casos en que no haya widget como tal.
Error n.º 6: ignorar el contexto del entorno (theme, displayMode, maxHeight, locale).
Puedes, claro, fijar una altura de 2000px, tema oscuro siempre y maquetar para escritorio — pero la experiencia será rara. La plataforma te da señales sobre cuánto espacio hay, qué tema y qué locale — úsales con useOpenAIGlobal, useDisplayMode, useMaxHeight, etc., para que el widget parezca «nativo» en ChatGPT.
Error n.º 7: intentar «saltarse» la política con scripts de terceros.
A veces surge la tentación de incluir un tracker, un bundle JS de terceros o ejecutar código desde otro dominio «a escondidas». La sandbox y las políticas CSP existen precisamente para impedirlo: los scripts de terceros son bloqueados, y los intentos de saltarse el sistema conducen directamente a que tu App sea rechazada en el Store.
GO TO FULL VERSION