Tutorial para crear la automatización que da acceso al curso y entrega el link, el usuario y una contraseña ya lista a quien compra
Seguí los pasos en orden. Cada uno te dice qué necesitás tener y qué tenés que crear. Las perlitas de oro 💎 son los detalles que evitan errores.
👆 Hacé clic en cada paso para ver el desarrollo completo
🔐
Quien compra recibe link, usuario y contraseña y entra al curso al instante
⚙️ El orden de armado
Preparar→Supabase y función→Disparador→Dar acceso→Poner contraseña→Enviar accesos→Limpiar
🎯
Antes de empezar
Para qué sirve esta automatización
+
🎯 Para qué sirve
Para darle acceso al curso a quien compra sin que tenga que crear su contraseña. Normalmente el sistema le manda un correo para que la cree él mismo, y ese correo muchas veces cae en spam. Con esta automatización la contraseña ya está hecha y le llega por WhatsApp y por email.
📦 Lo que le llega
El link donde ingresa al curso.
Su usuario: el email que dejó al comprar.
Su contraseña, ya creada, por ejemplo Bosque4821!.
👆 Lo único que hace la persona
Hace clic en el link.
Pone su usuario y la contraseña que le llegó.
Entra al curso de manera inmediata.
💎 Perlita de oro
La persona no tiene que crear nada ni buscar correos: le llega todo listo y entra en un clic.
🎓
Paso 1 · Preparación
Tener el curso y su oferta
+
🤔 Para qué
La automatización no le da acceso al curso directamente: le da acceso a una oferta, y la oferta es la que tiene adentro el curso.
⚙️ Qué tenés que tener
Entrá a Memberships → Cursos y fijate que el curso esté creado y con sus clases.
En Ofertas, creá una oferta que incluya ese curso, con un nombre claro, por ejemplo Nombre de tu curso.
Copiá el link de ingreso al área de cursos, algo como https://programas.tudominio.com/login.
💎 Perlita de oro
Una oferta puede tener varios cursos adentro: así se arma un pack con un solo acceso.
🗄️
Paso 2 · Preparación
Lo necesario para crear esta automatización
+
📋 Lo que tenés que tener
El curso con su oferta y el link de ingreso (paso 1).
Una cuenta en Supabase, configurada como se explica acá abajo.
El campo personalizado "Clave curso" (paso 3).
Una integración privada de la subcuenta, con su token (paso 4).
La función que pone la contraseña, con su lista de palabras (paso 5).
Dos etiquetas: una que dispara el acceso y otra que queda fija (paso 6).
Un número conectado a WhatsApp en la subcuenta (paso 9).
🗄️ Para qué Supabase
Es donde vive la función que pone la contraseña (paso 5) y donde se guardan, en privado, los datos que usa. GeniuStack no puede hacer esa parte solo.
🆕 Crear la cuenta y el proyecto
Si ya tenés una cuenta y un proyecto en Supabase, podés usar ese y saltear esta parte.
Entrá a supabase.com y tocá Start your project.
Registrate con tu email y una contraseña (o con tu cuenta de GitHub). Si es con email, abrí el correo que te llega y tocá Confirm your mail.
Te pide crear una organización (es como la carpeta donde van tus proyectos):
Name: tu nombre o el de tu marca.
Type: Personal.
Plan: Free (gratis).
Tocá Create organization.
Después te pide crear el proyecto (tu base de datos):
Project name: un nombre claro, por ejemplo accesos-cursos.
Database Password: tocá Generate a password y guardala en un lugar seguro. Para este tutorial no la vas a necesitar, pero no se puede volver a ver.
Region: elegí la más cercana a tus alumnos. Para Latinoamérica, South America (São Paulo).
Tocá Create new project.
Esperá uno o dos minutos mientras se arma. Cuando termina, ves el panel del proyecto con el menú a la izquierda.
⚙️ Cargar el código
En el menú de la izquierda, entrá a SQL Editor (el ícono con el símbolo >_).
Tocá New query (o el +) para abrir una hoja en blanco.
Tocá el botón Copiar el código de acá abajo y pegalo en esa hoja.
En la PARTE 1 del código, cambiá lo que está entre comillas:
token → el token de la integración privada (paso 4).
location → el ID de la subcuenta (paso 4).
secreto → una palabra secreta que inventes. La vas a poner también en el webhook (paso 8).
portal → la dirección de tu área de cursos, sin el /login (paso 1).
campo → la clave del campo del paso 3, sin el contact. (si lo llamaste "Clave curso", dejalo como está).
En la PARTE 4, cambiá las palabras por las del tema de tu curso (paso 5).
Apretá Run, abajo a la derecha (o Ctrl + Enter). Abajo tiene que aparecer Success. No rows returned.
Para armar la URL del webhook (paso 8) necesitás dos datos. Los encontrás en Project Settings (el engranaje ⚙️, abajo en el menú de la izquierda):
Project URL: en Data API. Es algo como https://abcdxyz.supabase.co.
anon key: en API Keys, la que dice anon · public (si no la ves, está en la pestaña Legacy API Keys). Es un texto largo que empieza con eyJ.
Lo que está en amarillo es lo que tenés que cambiar. Todo lo demás queda como está.
-- 🔐 Acceso al curso con contraseña lista
-- Copiá todo, pegalo en el SQL Editor de Supabase, cambiá los datos de la PARTE 1 y apretá Run.
-- ═══ PARTE 1 · TUS DATOS (cambiá solo lo que está entre comillas) ═══
create table if not exists acceso_config (clave text primary key, valor text);
alter table acceso_config enable row level security; -- sin permisos: nadie la puede leer desde afuera
insert into acceso_config (clave, valor) values
('token', 'PEGÁ-ACÁ-EL-TOKEN-DE-LA-INTEGRACIÓN'),
('location', 'PEGÁ-ACÁ-EL-ID-DE-LA-SUBCUENTA'),
('secreto', 'INVENTÁ-UNA-PALABRA-SECRETA'),
('portal', 'https://programas.tudominio.com'),
('campo', 'clave_curso')
on conflict (clave) do update set valor = excluded.valor;
-- ═══ PARTE 2 · La extensión que deja hablar con GeniuStack ═══
create extension if not exists http with schema extensions;
-- ═══ PARTE 3 · El registro: qué pasó con cada alumno ═══
create table if not exists acceso_log (
id bigserial primary key,
creado timestamptz default now(),
email text,
resultado text,
datos jsonb
);
alter table acceso_log enable row level security; -- solo se ve desde el SQL Editor
-- ═══ PARTE 4 · La lista de palabras de la contraseña (cambialas por las de tu curso) ═══
-- Sin tildes ni ñ, con la primera letra en mayúscula. La contraseña queda así: Palabra + 4 números + !
create or replace function acceso_clave_nueva() returns text language sql volatile as $$
with p as (select array[
'Sol','Luna','Rio','Bosque','Monte','Nube','Mar','Arbol','Flor','Estrella','Lago','Selva','Volcan','Cascada','Arena','Brisa','Lluvia','Hoja','Roca','Valle'
] as lista)
select lista[1 + floor(random() * array_length(lista, 1))::int] || (1000 + floor(random() * 9000)::int)::text || '!' from p
$$;
-- ═══ PARTE 5 · La función que recibe el webhook (no hace falta tocar nada) ═══
create or replace function acceso_curso(json) returns json language plpgsql security definer set search_path = public, extensions as $$
declare
d jsonb := $1::jsonb;
extra jsonb := coalesce(d->'customData', '{}'::jsonb);
v_loc text := (select valor from acceso_config where clave = 'location');
v_tok text := (select valor from acceso_config where clave = 'token');
v_portal text := rtrim((select valor from acceso_config where clave = 'portal'), '/');
v_nombre_campo text := (select valor from acceso_config where clave = 'campo');
v_contacto text := coalesce(d->>'contact_id', d->>'contactId', extra->>'contact_id');
v_email text := lower(trim(coalesce(d->>'email', extra->>'email', '')));
v_link text := coalesce(extra->>'link', d->>'link', '');
v_token text; v_clave text; r http_response; v_campo text; v_res text; cab http_header[];
begin
if coalesce(extra->>'secreto', d->>'secreto', '') is distinct from (select valor from acceso_config where clave = 'secreto') then
insert into acceso_log (email, resultado, datos) values (v_email, 'palabra secreta incorrecta', d);
return json_build_object('ok', false, 'error', 'palabra secreta incorrecta');
end if;
v_token := replace((regexp_match(v_link, 'token=([^&]+)'))[1], '%2E', '.');
if v_token is null then
insert into acceso_log (email, resultado, datos) values (v_email, 'no vino el link mágico', d);
return json_build_object('ok', false, 'error', 'no vino el link mágico');
end if;
cab := array[http_header('version', '2023-02-21'), http_header('source', 'PORTAL_USER'), http_header('channel', 'APP'),
http_header('origin', v_portal), http_header('referer', v_portal || '/')];
-- ¿Ya tenía contraseña?
r := http(('POST', 'https://services.leadconnectorhq.com/clientportal-middleware/clientclub/v2/' || v_loc || '/users/check-password-set', cab, 'application/json',
json_build_object('token', v_token, 'locationId', v_loc)::text)::http_request);
if r.status >= 300 then
insert into acceso_log (email, resultado, datos) values (v_email, 'no se pudo revisar el acceso ' || r.status || ' ' || left(r.content, 200), d);
return json_build_object('ok', false, 'error', 'revisar ' || r.status);
end if;
v_res := case when (r.content::jsonb->>'isPasswordSet')::boolean then 'contraseña cambiada (ya tenía)' else 'contraseña puesta' end;
v_email := coalesce(r.content::jsonb->>'email', v_email);
-- Siempre una contraseña nueva y lista
v_clave := acceso_clave_nueva();
r := http(('POST', 'https://services.leadconnectorhq.com/clientportal-middleware/clientclub/v2/' || v_loc || '/users/set-new-password', cab, 'application/json',
json_build_object('emailId', v_email, 'newPassword', v_clave, 'confirmPassword', v_clave, 'token', v_token)::text)::http_request);
if r.status >= 300 then
insert into acceso_log (email, resultado, datos) values (v_email, 'no se pudo poner la contraseña ' || r.status || ' ' || left(r.content, 200), d);
return json_build_object('ok', false, 'error', 'contraseña ' || r.status);
end if;
-- Guardarla en el campo del contacto
r := http(('GET', 'https://services.leadconnectorhq.com/locations/' || v_loc || '/customFields?model=contact',
array[http_header('Authorization', 'Bearer ' || v_tok), http_header('Version', '2021-07-28')], null, null)::http_request);
select f->>'id' into v_campo from jsonb_array_elements(r.content::jsonb->'customFields') f
where f->>'fieldKey' = 'contact.' || v_nombre_campo or lower(f->>'name') = lower(replace(v_nombre_campo, '_', ' ')) limit 1;
if v_campo is null then
insert into acceso_log (email, resultado, datos) values (v_email, v_res || ', pero no encontré el campo ' || v_nombre_campo, d);
return json_build_object('ok', false, 'error', 'falta el campo ' || v_nombre_campo);
end if;
r := http(('PUT', 'https://services.leadconnectorhq.com/contacts/' || v_contacto,
array[http_header('Authorization', 'Bearer ' || v_tok), http_header('Version', '2021-07-28')], 'application/json',
json_build_object('customFields', json_build_array(json_build_object('id', v_campo, 'field_value', v_clave)))::text)::http_request);
if r.status >= 300 then
insert into acceso_log (email, resultado, datos) values (v_email, v_res || ', pero no se pudo guardar en el contacto ' || r.status, d);
return json_build_object('ok', false, 'error', 'guardar ' || r.status);
end if;
insert into acceso_log (email, resultado, datos) values (v_email, v_res, d);
return json_build_object('ok', true, 'resultado', v_res);
end $$;
revoke all on function acceso_curso(json) from public;
grant execute on function acceso_curso(json) to anon;
🔎 Para ver qué pasó con cada alumno
Pegá esto en el SQL Editor y apretá Run: te muestra lo último que hizo la función, con el email y el resultado.
select creado, email, resultado from acceso_log order by id desc limit 20;
Supabase pausa los proyectos gratis que pasan 7 días sin ninguna actividad, y una base pausada no responde. Cada contraseña que se entrega cuenta como actividad: si vendés al menos una vez por semana, se mantiene despierta sola. Pero si pasa una semana sin ventas, al próximo comprador no le llega la contraseña. Para estar tranquila, entrá a tu proyecto cada pocos días o dejá programado algo que lo despierte.
🔒 Cuidado
La anon key va en la URL del webhook y no da acceso a tus datos. El token, en cambio, se guarda solo en la tabla privada.
💎 Perlita de oro
Una sola cuenta y un solo proyecto sirven para todos tus cursos: cada subcuenta suma su token y su campo.
🔑
Paso 3 · Preparación
Crear el campo personalizado "Clave curso"
+
🤔 Para qué
Es el lugar de la ficha del contacto donde va a quedar guardada su contraseña. De ahí la toman el WhatsApp y el email.
⚙️ Qué tenés que crear
Entrá a Configuración → Campos personalizados.
Creá un campo nuevo de contacto, de tipo texto de una línea.
Ponele de nombre Clave curso.
Guardá y copiá su clave, algo como {{contact.clave_curso}}. La vas a usar en los mensajes.
⚠️ Ojo
Tiene que ser un campo personalizado del contacto, no un valor personalizado. Son cosas distintas y el valor no sirve para esto.
💎 Perlita de oro
Primero se prepara dónde se guarda el dato. Después se arman los pasos que lo usan.
🔌
Paso 4 · Preparación
Crear la integración privada
+
🤔 Para qué
Es el permiso que le das a la función del paso 5 para que pueda escribir la contraseña en el campo "Clave curso" del contacto.
⚙️ Qué tenés que crear
Entrá a Configuración → Integraciones privadas y creá una nueva.
Ponele un nombre que la identifique, por ejemplo Acceso cursos.
Dale estos permisos:
Contactos: ver y editar.
Campos personalizados: ver.
Copiá el token que te da y guardalo en un lugar seguro. No se pega en ninguna parte de GeniuStack: va adentro de la función del paso 5.
Anotá también el ID de la subcuenta (Location ID), que está en Configuración → Perfil de la empresa.
🔒 Cuidado
El token da acceso a tus contactos. No lo compartas ni lo pegues en lugares públicos: solo va adentro de la función.
💎 Perlita de oro
El campo es el cajón; la integración es la llave para poner la contraseña adentro.
⚙️
Paso 5 · Preparación
La función que pone la contraseña
+
🤔 Para qué
Es la pieza que GeniuStack no trae: le pone la contraseña al alumno en el área de cursos, como si la hubiera creado él, y la guarda en su ficha. Vive afuera de GeniuStack y se arma una sola vez por subcuenta.
🔄 Qué hace cada vez que la llaman
Revisa que venga la palabra secreta (así nadie más la puede usar).
Toma el link mágico del alumno que le manda la automatización.
Con ese link, le pone una contraseña nueva en el área de cursos.
La escribe en el campo "Clave curso" del contacto.
Deja anotado cada paso, por si algo falla.
📋 Qué necesita
El token y el ID de la subcuenta del paso 4.
El nombre del campo del paso 3.
Una palabra secreta inventada por vos.
📍 Dónde vive
En la base de Supabase del paso 2. No hace falta crear una nueva para cada curso.
Ahí, en una tabla privada que solo lee la función, se guardan el token, el ID de la subcuenta y la palabra secreta.
Una misma base sirve para todos tus cursos: cada subcuenta suma su token y su campo.
🎲 La lista de palabras de la contraseña
Cada contraseña se arma con una palabra al azar de una lista + 4 números + !. La lista la elegís vos, según el tema del curso:
Armá entre 20 y 40 palabras del tema. Por ejemplo, de naturaleza: SolLunaBosqueCascadaEstrella…
Escribilas sin tildes y sin ñ (Arbol, no Árbol), con la primera letra en mayúscula.
La lista va en la PARTE 4 del código del paso 2. Podés poner las que quieras: las cuenta sola. Si después la querés cambiar, editás las palabras y volvés a correr el código.
Cada curso puede tener su propia lista.
⚙️ Qué te queda para el paso siguiente
La URL de la función: la vas a pegar en el webhook (paso 8).
La palabra secreta: también va en el webhook.
⚠️ Las reglas de la contraseña
El área de cursos pide al menos 8 caracteres, con mayúscula, minúscula, número y un signo. Por eso el formato es Palabra + 4 números + !, por ejemplo Bosque4821!. Una como bosque-4821 la rechaza por débil.
💎 Perlita de oro
Con palabras del tema del curso la contraseña es fácil de recordar y suena a tu marca.
🏷️
Paso 6 · Disparador
Crear la automatización y el trigger
+
📋 Qué necesitás
Dos etiquetas creadas:
La que dispara el acceso, por ejemplo curso-acceso.
La que queda fija al final, por ejemplo alumnos curso.
⚙️ Qué tenés que crear
Entrá a Automatización y creá una automatización nueva desde cero.
Ponele un nombre claro, por ejemplo Acceso curso X.
Agregá el disparador Etiqueta de contacto.
En el filtro, elegí Etiqueta agregada y seleccioná curso-acceso.
⚠️ Ojo
Usá una etiqueta distinta para cada curso. Si repetís la misma, todos reciben el acceso al mismo curso.
💎 Perlita de oro
La etiqueta es el interruptor: se prende cuando la persona compra y todo lo demás pasa solo.
🎟️
Paso 7
Course Grant Offer: dar el acceso al curso
+
🤔 Para qué
Le da a la persona el acceso a la oferta del curso y hace que el sistema le prepare su link mágico, que la función necesita en el paso siguiente.
⚙️ Qué tenés que configurar
Agregá la acción Course Grant Offer debajo del disparador.
Elegí la oferta del paso 1.
Debajo, agregá una Espera (Wait) de 1 minuto.
⚠️ Ojo
Esta acción manda también su propio correo de bienvenida, que suele caer en spam. No pasa nada: los accesos importantes van por WhatsApp y por el email del paso 10.
💎 Perlita de oro
La espera le da tiempo al sistema a terminar de crear el acceso antes de ponerle la contraseña.
📡
Paso 8
Webhook: pedir la contraseña
+
🤔 Para qué
Le avisa a la función del paso 5 que tiene que ponerle la contraseña a este alumno y guardarla en su ficha.
📋 Qué necesitás
La URL de la función y la palabra secreta del paso 5.
⚙️ Qué tenés que configurar
Agregá la acción Webhook después de la espera.
Método: POST.
Pegá la URL de la función.
En Custom Data, agregá dos datos:
secreto → la palabra secreta.
link → {{membership_contact.login_url}}
Debajo, agregá otra Espera de 1 minuto.
⚠️ Por qué va antes de los mensajes
Los mensajes usan la contraseña. Si el webhook no está antes, o el Custom Data quedó vacío, el mensaje sale con la contraseña en blanco.
💎 Perlita de oro
El link mágico es la llave del alumno: la función lo usa para ponerle la contraseña. Sin ese dato no puede hacer nada.
💬
Paso 9
WhatsApp: los accesos al celular
+
🤔 Para qué
Es el mensaje que sí o sí ve: le llegan el link, su usuario y su contraseña en el momento.
📋 Qué necesitás
El número conectado a WhatsApp en la subcuenta y la clave del campo del paso 3.
⚙️ Qué tenés que escribir
Agregá la acción de WhatsApp (o SMS, si tu número va por un proveedor no oficial).
Saludá con su nombre: {{contact.first_name}}.
Link para entrar: el link fijo de login del paso 1.
Usuario: {{contact.email}}
Contraseña: {{contact.clave_curso}}
📝 El mensaje listo para copiar
Pegalo en la acción de WhatsApp (o SMS).
Lo que está en amarillo cambialo por el link de tu área de cursos (paso 1). Lo que está entre {{ }} lo completa GeniuStack solo.
¡Hola {{contact.first_name}}! 🎉
Ya tenés tu acceso al curso.
👉 Entrá acá: https://programas.tudominio.com/login
📧 Usuario: {{contact.email}}
🔑 Contraseña: {{contact.clave_curso}}
Guardá este mensaje para tenerlo siempre a mano.
¡Que lo disfrutes!
⚠️ Ojo con el link
En el mensaje va el link fijo de login, no el link mágico. El mágico es solo para la función.
💎 Perlita de oro
El WhatsApp se lee en minutos. Es el "ya está, podés entrar" que tranquiliza.
✉️
Paso 10
Correo electrónico con los accesos
+
🤔 Para qué
Que los datos le queden guardados también en su correo, para cuando los necesite otro día.
⚙️ Qué tenés que escribir
Agregá la acción Correo electrónico (Send Email) después del WhatsApp.
Asunto: 🎉 Tus accesos al curso
En el cuerpo del correo pegá el mismo mensaje del WhatsApp: así la persona recibe lo mismo por los dos lados.
Si querés, el link lo podés poner como botón que diga Entrar al curso.
📝 El mismo mensaje, para el correo
Lo que está en amarillo cambialo por el link de tu área de cursos (paso 1). Lo que está entre {{ }} lo completa GeniuStack solo.
¡Hola {{contact.first_name}}! 🎉
Ya tenés tu acceso al curso.
👉 Entrá acá: https://programas.tudominio.com/login
📧 Usuario: {{contact.email}}
🔑 Contraseña: {{contact.clave_curso}}
Guardá este mensaje para tenerlo siempre a mano.
¡Que lo disfrutes!
⚠️ Ojo
Pedile en el WhatsApp que revise también la carpeta de spam.
💎 Perlita de oro
WhatsApp para el momento, email para que quede guardado: entre los dos, nadie se queda sin entrar.
🧹
Paso 11
Remove Tag y Add Tag: limpiar y marcar
+
🤔 Para qué
Sacar la etiqueta que puso en marcha la automatización y dejarle la etiqueta fija de alumno.
⚙️ Qué tenés que configurar
Agregá Remove Tag y elegí curso-acceso.
Agregá Add Tag y elegí alumnos curso.
✅ Qué ganás
El contacto queda limpio, marcado como alumno.
Si alguna vez necesitás reenviarle los accesos, le volvés a poner curso-acceso y le llega todo de nuevo, con una contraseña nueva.
⚠️ Ojo
La etiqueta fija no puede ser la misma del disparador, o la automatización se vuelve a activar.
💎 Perlita de oro
La etiqueta del disparador se usa para entrar y se borra al salir. Así queda lista para volver a usarse.
🛒
Paso 12
Cómo le llega la etiqueta a quien compra
+
💳 Si paga en la página de pago
Creá otra automatización, aparte, por ejemplo Pago curso X.
Disparador: pago recibido del producto.
Una sola acción: Add Tagcurso-acceso.
🏦 Si paga por transferencia
Le ponés la etiqueta curso-acceso a mano en su contacto, o con un etiquetador solo con su teléfono, y arranca la misma automatización. El etiquetador es opcional: solo hace más rápido este paso.
⚠️ Por qué van separadas
Si ponés los dos disparadores (pago y etiqueta) en la misma automatización, a la persona le llega el mensaje dos veces y con dos contraseñas distintas.
💎 Perlita de oro
Venda por donde venda, todo termina en la misma etiqueta. Así hay una sola automatización de acceso para mantener.
🧪
Paso 13
Probar y publicar
+
✅ La prueba
En la configuración de la automatización, prendé Allow re-entry (permitir volver a entrar).
Creá un contacto de prueba con tu email y tu teléfono.
Agregale la etiqueta curso-acceso.
Esperá unos 2 minutos y revisá que el campo "Clave curso" se haya completado.
Comprobá que te llegaron el WhatsApp y el email.
Fijate que tenga alumnos curso y que se haya borradocurso-acceso.
Entrá al curso con ese usuario y esa contraseña.
Si todo funciona, publicá la automatización.
⚠️ Si algo falla
Mirá el historial de la automatización. Si el mensaje llegó con la contraseña vacía, revisá que el Custom Data del webhook tenga el secreto y el link: es el olvido más común.
💎 Perlita de oro
Probala siempre con vos misma antes que con un alumno real.
Automatización en GeniuStack · Acceso al curso con contraseña lista