White-label
Branding por organización, con contraste medido y SVG rechazado.
Fuente del diagrama (mermaid)
flowchart LR
W[Alta en WorkOS · MANUAL] --> M[Membresía con admin:write]
M --> L[Login]
L --> B[PUT /v1/org/branding]
B --> G[POST /v1/org/branding/logo]
G --> C[Consola brandeada]
El alta de la organización es manual y lo hace una persona. Crear organizaciones por API es mutación del proveedor de identidad: un bug ahí mete a alguien en el tenant equivocado —legítimamente adentro, con todos los controles posteriores dejándolo pasar—, que es la peor clase de fuga en un producto multi-tenant.
curl -sS "$SBOX/v1/org/branding" -X PUT \
-H "authorization: Bearer $TOKEN" \
-H "idempotency-key: branding-demo-1" \
-H "content-type: application/json" \
-d '{"displayName":"Aseguradora Demo","colors":{"primary":"#7c2d12","accent":"#065f46"}}'Contraste
Un branding que no llega a WCAG AA se rechaza con 422, el ratio medido y un color que sí pasa. El cliente que sube su celeste corporativo no elige mal a propósito: elige sin saber que su propio equipo no va a poder leer la consola.
Qué se mide: texto sobre el color de marca a 4,5:1 (donde vive el texto), y marca sobre fondo a 3:1 en los dos temas. No se exige que un color pase contra los dos fondos, porque no hay ninguno que lo haga — la marca define un tono y cada tema usa su escalón.
Logo
512 KiB máximo, image/svg+xml o image/png. Un SVG con <script>, handler
on*, javascript:, <foreignObject>, <iframe> o entidad externa se
rechaza, no se limpia: sanear por lista negra es una carrera que el defensor
pierde, y aceptar uno sería XSS persistente en la sesión de toda la organización
del cliente.
Lo que se rompe
403 si la credencial no tiene admin:write. El permiso sale del catálogo
canónico — no hay un org:branding:write, porque un permiso que el catálogo no
conoce no lo otorga ninguna plantilla de rol.
Ver también el flujo demo completo.