> ## Documentation Index
> Fetch the complete documentation index at: https://wondeya.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Errores

> La envoltura de error, los códigos estables y sobre qué ramificar

Cada error que devuelve la API de wondeya es un documento de problema con la misma forma, y todos llevan un `code` estable y legible por máquina. El código cliente ramifica sobre `code`. Nunca debe ramificar sobre `detail`, que es texto para humanos, localizado y libre de cambiar.

## La envoltura

```json theme={null}
{
  "type": "https://wondeya.com/docs/errors/wondeya.page.path_taken",
  "title": "Page address already taken",
  "status": 409,
  "detail": "Ya existe otra page con esa dirección en este workspace.",
  "instance": "/workspaces/6620…/pages",
  "code": "wondeya.page.path_taken",
  "requestId": "01JR3…"
}
```

| Campo | Qué es |
| - | - |
| `type` | El URI de este tipo de problema. Redirige a esta página (no tiene anclas por código); trata `code` como el identificador real sobre el que ramificar. |
| `title` | Resumen breve, estable y en inglés del *tipo*. Nunca localizado, nunca por ocurrencia. |
| `status` | El estado HTTP, repetido en el cuerpo. |
| `detail` | Explicación para humanos de *esta* ocurrencia, localizada. **No es un contrato.** |
| `instance` | La ruta donde ocurrió. |
| `code` | El identificador estable, con espacio de nombres por puntos. **Este es el contrato.** |
| `requestId` | Coincide con el encabezado `x-request-id` y con los registros del servidor. Es lo que pide soporte. |
| `errors` | Presente en los fallos de validación: una entrada por campo, cada una con la ruta del campo y un motivo. |

Las superficies pública y MCP envían este cuerpo como `application/problem+json`; las rutas de la consola envían el cuerpo idéntico como `application/json`.

Todo lo que no es un error esperado se convierte en un `500` genérico con el código `wondeya.internal.error`. Los stack traces, los mensajes de base de datos, los prompts y los errores de proveedores externos nunca aparecen en una respuesta; van a los registros, indexados por el mismo `requestId`.

## Códigos por área

### Solicitud y transporte

| Código | Estado | Significado |
| - | - | - |
| `wondeya.request.invalid` | 400 | La solicitud estaba malformada. |
| `wondeya.request.validation_failed` | 400 | Falló la validación a nivel de campo; mira `errors`. |
| `wondeya.request.payload_too_large` | 413 | Supera el límite de tamaño de esa ruta. |
| `wondeya.request.unprocessable` | 422 | Bien formada pero no aceptable. |
| `wondeya.rate_limit.exceeded` | 429 | Demasiadas solicitudes. En `/v1` el presupuesto es por clave, compartido entre todas las rutas. |
| `wondeya.idempotency.conflict` | 422 / 409 | Se reutilizó un `Idempotency-Key` con un cuerpo distinto (422), o mientras la primera solicitud aún se ejecutaba (409). |
| `wondeya.service.unavailable` | 503 | Una dependencia está caída, o la superficie está deshabilitada. |
| `wondeya.internal.error` | 500 | Fallo inesperado. Reporta el `requestId`. |

### Autenticación y sesión

| Código | Estado | Significado |
| - | - | - |
| `wondeya.auth.unauthorized` | 401 | Sin sesión válida. |
| `wondeya.auth.forbidden` | 403 | Autenticado, no permitido. |
| `wondeya.auth.invalid_credentials` | 401 | Correo o contraseña incorrectos, deliberadamente la misma respuesta para ambos. |
| `wondeya.auth.email_taken` | 409 | Esa dirección ya tiene una cuenta. |
| `wondeya.auth.weak_password` | 422 | Contraseña rechazada. |
| `wondeya.auth.email_not_verified` | 403 | Verifica la dirección primero. |
| `wondeya.auth.token_invalid` | 400 | Un enlace de verificación, restablecimiento o invitación es inválido o expiró. |
| `wondeya.auth.account_locked` | 429 | Demasiados intentos fallidos. |
| `wondeya.auth.csrf_failed` | 403 | Una mutación sin el encabezado `x-csrf-token`, o con el valor incorrecto. |
| `wondeya.auth.oauth_failed` | 400 | Una sola respuesta para todo fallo de inicio de sesión social, deliberadamente opaca. |
| `wondeya.auth.provider_unavailable` | 503 | El proveedor de identidad es inalcanzable. |

### Credenciales

| Código | Estado | Significado |
| - | - | - |
| `wondeya.api_key.invalid` | 401 | Clave desconocida, revocada o expirada: una sola respuesta para las tres, a propósito. |
| `wondeya.api_key.scope_missing` | 403 | A la clave le falta el permiso que esa llamada necesita. |
| `wondeya.api_key.limit_reached` | 409 | Ya existen dos claves activas. |
| `wondeya.site_key.invalid` | 401 | El site key no resuelve. |
| `wondeya.site_key.origin_not_allowed` | 403 | El origen que llama no está registrado en ese site key. |
| `wondeya.site_key.origin_invalid` | 422 | Un origen registrado nunca podría funcionar como ancestro de frame. El valor infractor se nombra en `errors`. |
| `wondeya.oauth.unauthorized` | 401 / 403 | Superficie MCP: sin token OAuth válido (401), o una llamada de consentimiento o revocación que la sesión no puede hacer (403). |
| `wondeya.oauth.insufficient_scope` | 403 | Superficie MCP: a la conexión no se le otorgó ninguno de los permisos que esa herramienta acepta. Vuelve a conectarte y otórgalos. |

### Espacios de trabajo y membresía

| Código | Estado | Significado |
| - | - | - |
| `wondeya.tenant.not_found` | 404 | No existe tal espacio de trabajo; también la respuesta cuando no eres miembro de él. |
| `wondeya.tenant.slug_taken` | 409 | Esa dirección de espacio de trabajo está tomada. |
| `wondeya.tenant.not_member` | 404 | La misma respuesta que la de arriba, por diseño. |
| `wondeya.tenant.insufficient_role` | 403 | Miembro donde se requiere administrador o propietario. |
| `wondeya.tenant.last_owner` | 409 | El último propietario no puede salir ni ser removido. |
| `wondeya.tenant.member_not_found` | 404 | No existe tal miembro en este espacio de trabajo. |
| `wondeya.tenant.already_member` | 409 | Ya es miembro. |
| `wondeya.invitation.invalid` | 400 | Invitación ausente, usada o expirada. |
| `wondeya.invitation.email_mismatch` | 403 | La invitación pertenece a otra dirección. |
| `wondeya.tenant.context_missing` / `wondeya.tenant.scope_violation` | 500 | Guardas internas de aislamiento que fallan de forma cerrada. Nada que arreglar de tu lado; reporta el `requestId`. |

### Agentes

| Código | Estado | Significado |
| - | - | - |
| `wondeya.agent.not_found` | 404 | No existe tal agente en este espacio de trabajo. En la resolución del embed y en el chat con site key, esta es también la única respuesta para un site key desconocido o revocado, un agente ausente y un espacio de trabajo suspendido. |
| `wondeya.agent.invalid` | 400 | Payload inválido; `errors` nombra el campo. |
| `wondeya.agent.in_use` | 409 | Una página todavía responde con este agente; `errors` nombra las páginas. Vuelve a apuntarlas o elimínalas primero. |

### Proyectos (sitios) y sus direcciones

| Código | Estado | Significado |
| - | - | - |
| `wondeya.project.not_found` | 404 | No existe tal sitio en este espacio de trabajo; también la respuesta para un sitio que no es tuyo. |
| `wondeya.project.invalid` | 400 | Payload inválido; `errors` nombra el campo. |
| `wondeya.project.slug_taken` | 409 | La dirección es un espacio de nombres **global** y alguien la tiene; `errors` lleva un vecino disponible. Una dirección retirada sigue rechazándose aquí hasta que su propietario la libere. |
| `wondeya.project.slug_reserved` | 409 | La plataforma es dueña de ese nombre; nunca se puede otorgar. |
| `wondeya.host.taken` | 409 | El dominio ya está conectado en otro lugar. |
| `wondeya.host.invalid` | 400 | No es un nombre conectable. |
| `wondeya.host.site_has_no_home` | 409 | Un dominio sirve un sitio en su raíz; un sitio sin página en `/` todavía no puede tomar uno. |
| `wondeya.host.edge_unavailable` | 503 | No se pudo alcanzar la CDN; no se cambió nada. Reintenta. |

### Páginas y tema

| Código | Estado | Significado |
| - | - | - |
| `wondeya.page.not_found` | 404 | No existe tal página en este espacio de trabajo. |
| `wondeya.page.invalid` | 400 | Payload inválido; `errors` nombra el campo. |
| `wondeya.page.path_taken` | 409 | Otra página del mismo sitio tiene esa ruta. |
| `wondeya.page.path_reserved` | 409 | La plataforma reserva esa ruta. Un problema distinto del de arriba, con una solución distinta. |
| `wondeya.page.root_required` | 409 | La página en `/` no se puede despublicar, mover ni eliminar mientras otras páginas del sitio estén en vivo. |
| `wondeya.page.og_format_unsupported` | 415 | Una imagen de tarjeta social que no es PNG, JPEG o WebP con un tamaño legible. |
| `wondeya.theme.invalid` | 400 | Un token fuera del conjunto cerrado. `errors` nombra el campo. |
| `wondeya.theme.retired` | 410 | Se eliminaron las anulaciones de tema por agente; la apariencia pertenece a la marca del proyecto. |
| `wondeya.host.not_found` | 404 | Host desconocido, dominio no verificado, página no publicada o espacio de trabajo suspendido: una sola respuesta para todos. |

### Chat

| Código | Estado | Significado |
| - | - | - |
| `wondeya.chat.agent_scope_conflict` | 400 | `agentId` en `POST /chat` pertenece a la Vista previa de la consola y solo es válido en un turno de sesión. Combinado con un site key o una dirección pública se rechaza, porque el localizador ya nombra quién responde. |

### Planes y facturación

| Código | Estado | Significado |
| - | - | - |
| `wondeya.billing.limit_reached` | 402 | El tope del plan para algo contable; `errors[0].field` nombra cuál (`projects`, `agents`, `pages`, `knowledgeDocuments`, `customDomains`, `seats`). La solución es una mejora de plan, no un reintento. |
| `wondeya.billing.not_configured` | 503 | Este despliegue no puede vender ese plan. |
| `wondeya.billing.no_subscription` | 409 | No hay suscripción que cancelar o cambiar. |
| `wondeya.billing.provider_unavailable` | 502 | No se pudo alcanzar al proveedor de pagos. |
| `wondeya.billing.plan_invalid` | 400 | El plan nombrado no existe. |
| `wondeya.billing.signature_invalid` | 400 | Un webhook de pago cuya firma falló. Se responde al proveedor; nunca dice qué verificación falló. |
| `wondeya.usage.quota_exhausted` | 402 | El espacio de trabajo gastó sus mensajes de AI, así que el turno generativo se rechaza. A diferencia de `limit_reached`, este es el 402 propio del chat; la solución es una mejora de plan o un paquete de mensajes. |
| `wondeya.usage.budget_exceeded` | 429 | El tope diario de actividad del espacio de trabajo. A diferencia del 429 de límite de tasa, no lleva `Retry-After`. |

### Onboarding

| Código | Estado | Significado |
| - | - | - |
| `wondeya.onboarding.invalid` | 400 | Solicitud de onboarding inválida: el nombre del negocio, la descripción o el archivo de audio no pasaron la validación. `errors` nombra el campo. |

### Conocimiento

| Código | Estado | Significado |
| - | - | - |
| `wondeya.knowledge.source_not_found` | 404 | No existe tal fuente en este espacio de trabajo. |
| `wondeya.knowledge.source_invalid` | 400 | Payload inválido, o una subida sin archivo. |
| `wondeya.knowledge.format_unsupported` | 415 | Un formato de documento que el pipeline no puede leer. |
| `wondeya.knowledge.url_not_allowed` | 400 | Una dirección privada, un esquema prohibido o un puerto prohibido. Deliberadamente vago. |
| `wondeya.knowledge.topic_not_found` | 404 | No existe tal tema en este alcance. |
| `wondeya.knowledge.topic_invalid` | 400 | Una operación de tema inválida, como fusionar un tema consigo mismo. |

Una fuente que falla **después** de haber sido aceptada no lanza un error: pasa a `status: "failed"` y lleva un motivo legible por máquina como `unsupported_document`, `ingest_failed`, `fetch_timeout`, `fetch_dns_failed`, `fetch_response_too_large` o `fetch_blocked_address:link_local`. Ramifica sobre esos de la misma forma, y muéstrale a una persona la versión traducida, nunca el código en crudo.

### Recursos

| Código | Estado | Significado |
| - | - | - |
| `wondeya.asset.not_found` | 404 | No existe tal recurso en este espacio de trabajo. |
| `wondeya.asset.invalid` | 400 | Payload inválido. |
| `wondeya.asset.format_unsupported` | 415 | Los **bytes** no son una imagen ni un video legible, diga lo que diga el nombre del archivo. |
| `wondeya.asset.id_taken` | 409 | Otro recurso en esa página ya usa la clave. |
| `wondeya.asset.video_too_long` | 422 | El video dura más del tope de 60 segundos. |

### Marca

| Código | Estado | Significado |
| - | - | - |
| `wondeya.brand.not_found` | 404 | No hay archivo de marca en ese slot. |
| `wondeya.brand.invalid` | 400 | Payload inválido; `errors` nombra el campo. |
| `wondeya.brand.format_unsupported` | 415 | Un formato que ese slot no acepta; `errors` nombra el archivo. |
| `wondeya.brand.svg_rejected` | 400 | El SVG llevaba script, manejadores o referencias externas; `errors` nombra cada motivo. |

### Recursos genéricos

| Código | Estado | Significado |
| - | - | - |
| `wondeya.resource.not_found` | 404 | Caso general para una ruta o recurso que no existe. |
| `wondeya.resource.conflict` | 409 | Conflicto general. |

## Dos hábitos que vale la pena tener

**Trata los 404 idénticos como idénticos.** Varios códigos colapsan a propósito situaciones distintas en una sola respuesta: «no es miembro» se lee igual que «no existe tal espacio de trabajo», y un host desconocido se lee igual que una página no publicada. Distinguirlos convertiría el endpoint en una sonda. No construyas una UI que adivine cuál fue en realidad.

**Guarda el `requestId`.** Es el único asidero que ata un fallo que viste a los registros del lado del servidor de la misma solicitud.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.