Tutorial 2: Assets y automatización
En el Tutorial 1 montaste un robot real que resuelve el RPA Challenge leyendo su trabajo de una cola, y lo lanzaste a mano. Aquí lo llevamos al mundo real con dos cosas que todo proceso de producción necesita:
- Credenciales fuera del código — leerlas cifradas desde assets.
- Disparo automático — que el robot corra solo, por horario (cron) o ante un evento (webhook/API).
Parte 1 — Credenciales con Assets
Sección titulada «Parte 1 — Credenciales con Assets»El problema
Sección titulada «El problema»El RPA Challenge es público, pero tu proceso real no lo será: tendrá un portal con usuario y contraseña, una URL de ERP, un token de API. La regla de oro:
Cómo lo lee el robot
Sección titulada «Cómo lo lee el robot»Dentro de un job gestionado, el robot pide el asset por nombre y NORA se lo entrega
descifrado, sin que el robot sepa de claves de API ni de HTTP. El ejemplo ya lo
hace para la URL del reto: si existe un asset rpa-challenge-url, lo usa; si no, cae
a la URL por defecto.
# rpa_challenge/workflows.py — _resolve_url()def _resolve_url() -> str: asset = nora.asset(URL_ASSET) or {} # nora.asset envuelve sdk.get_asset url = asset.get("value") or RPA_URL # fallback si el asset no existe if url != RPA_URL: nora.log("info", f"URL tomada del asset '{URL_ASSET}'.") return urlnora.asset(name) envuelve la función real del SDK:
from nora_agent import sdk
cred = sdk.get_asset("erp-login") # environment="production" por defectousuario = cred.get("username")secreto = cred["value"] # valor descifrado, solo durante el jobget_asset devuelve un dict con {name, type, environment, value, username?}. No
necesitas X-API-Key dentro del job: el agente inyecta un token de ejecución con
alcance restringido. Detalle en SDK de robots.
Crear el asset
Sección titulada «Crear el asset»Los assets se crean solo desde la consola: Settings → Assets → Nuevo, con rol
admin (la API de escritura POST /api/v1/assets requiere sesión de dashboard admin,
no X-API-Key). Por API key los assets solo se leen (scope assets:read,
GET /api/v1/assets/by-name/{name}).

Para un portal real usarías type: "credential" con username + value. Un asset
credential o secret no se puede volver a leer desde la UI (solo actualizar).
Declarar los assets que el proceso necesita
Sección titulada «Declarar los assets que el proceso necesita»En el Proceso, lista los Assets requeridos (p. ej. rpa-challenge-url). Así el
agente los precarga y los inyecta en el job; get_asset los sirve sin llamada de
red y el token de ejecución solo puede leer esos, no toda la bóveda.
sequenceDiagram
participant Bot as Robot (job)
participant Ag as Agente
participant API as NORA
Note over Ag: el proceso declara required_assets
Ag->>API: precarga los assets del job
API-->>Ag: valores descifrados (scoped)
Bot->>Ag: sdk.get_asset("rpa-challenge-url")
Ag-->>Bot: { value: "https://..." } (sin red)
Parte 2 — Automatizar el disparo
Sección titulada «Parte 2 — Automatizar el disparo»Hasta ahora lanzabas el job con Run. NORA puede dispararlo solo de dos maneras. Ambas terminan creando un job del proceso (ver jobs).
flowchart LR
CRON["Programación (cron)<br/>cada día 7:00"] --> J[Job del proceso]
HOOK["Webhook / API<br/>evento externo"] --> J
J --> M["Máquina Online<br/>ejecuta el robot"]
Opción A — Por horario (cron)
Sección titulada «Opción A — Por horario (cron)»Lo más simple es desde el panel: Programaciones → Nueva; eliges proceso, días y
hora, y el panel arma el cron por ti.
El cron es una expresión de 5 campos
(minuto hora día-del-mes mes día-de-la-semana); por ejemplo 0 7 * * 1-5 = de lunes
a viernes a las 07:00.
Si prefieres la API, el equivalente es:
POST /api/v1/schedules HTTP/1.1Host: nora-api.valisoftconsulting.comContent-Type: application/json
{ "name": "RPA Challenge diario", "process_id": "3f1c…", "cron_expression": "0 7 * * 1-5", "timezone": "America/Lima", "skip_holidays": true}Esto lo corre de lunes a viernes a las 07:00 de Lima. Puntos clave (todos verificados en la guía):
- Zona horaria IANA (
America/Lima,Europe/Madrid,UTC). El disparo se calcula en esa zona y se guarda en UTC. - No solapa: si el job anterior sigue corriendo, ese disparo se omite.
- Sin máquina Online: se omite el ciclo (no hay dónde ejecutar).
- Feriados: con
skip_holidays: truesalta los días que definas en Feriados.
Detalle completo y operaciones (toggle, PUT, DELETE) en
programaciones y triggers.
Opción B — Por evento (webhook / API)
Sección titulada «Opción B — Por evento (webhook / API)»Cuando el disparador es un sistema externo (“llegó una factura”, “se creó un cliente”), tienes dos caminos:
B.1 — Trigger de webhook (token por trigger, firma HMAC). Creas un trigger y NORA
te da una URL pública con un webhook_token y un webhook_secret. El sistema externo
hace POST y NORA crea el job con el payload como input_data:

POST /api/v1/triggers/inbound/AbC123… HTTP/1.1Host: nora-api.valisoftconsulting.comContent-Type: application/jsonX-Webhook-Signature: sha256=<hmac-sha256(secret, cuerpo_crudo)>
{ "lote": "2026-06-26", "origen": "erp" }La firma es obligatoria si el trigger tiene secreto; hay anti-replay y límite de 120 req/min. Ver programaciones y triggers → triggers.
B.2 — Webhook con tu API Key (URL fija por proceso). Más simple si el llamador es
tu propio backend: una X-API-Key y el process_id en la ruta.
curl -X POST \ "https://nora-api.valisoftconsulting.com/api/v1/webhooks/trigger/5e6f7a8b-…" \ -H "X-API-Key: nora_ak_xxxxxxxxxxxxxxxxxxxx" -H "Content-Type: application/json" \ -d '{ "lote": "2026-06-26" }'Valida el payload contra el JSON Schema del proceso y está limitado a 60 req/min por
key. Comparación webhook vs. /jobs/trigger en webhooks.
¿Y la cola?
Sección titulada «¿Y la cola?»Cuando automatizas, recuerda quién llena la cola. Tienes dos patrones:
-
El robot la llena (como en el Tutorial 1):
load_queue()carga si está vacía. Ideal cuando los datos los produce el propio robot o un archivo fijo. -
Un productor externo la llena por API antes (o en vez) de disparar el job, con una key de scope
queues:write:Ventana de terminal curl -X POST \"https://nora-api.valisoftconsulting.com/api/v1/queues/by-name/RPA-Challenge/items/bulk" \-H "X-API-Key: nora_ak_..." -H "Content-Type: application/json" \-d @data/people.jsonAsí el webhook que recibe “llegaron 200 facturas” puede cargar la cola y luego disparar el robot, que las consume una a una. Ver colas vía API.
Resumen
Sección titulada «Resumen»| Necesidad | Solución | Dónde |
|---|---|---|
| Credenciales fuera del código | Assets (get_asset) | assets |
| Correr cada día/hora | Programación (cron) | programaciones |
| Correr ante un evento externo | Webhook/Trigger | webhooks |
| Llenar la cola desde otro sistema | API queues:write | colas vía API |
Siguientes pasos
Sección titulada «Siguientes pasos»- ➡️ Tutorial 3 — Patrones avanzados: revisión humana de items, input atendido, parada limpia, flujos DAG y detección de anomalías.
- Assets y credenciales — tipos, entornos, vaults externos.
- Programaciones y triggers — cron, feriados, HMAC.