> This page is for Inicio.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://signalcore.ai/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://signalcore.ai/docs/_mcp/server.

# Automatizaciones en código

El builder escribe las automatizaciones por ti. Esta guía es para leerlas, cambiarlas o
escribirlas tú.

## Una automatización

Es un archivo en la carpeta del agente, `automations/<nombre>.py`: una función bajo un
disparador, con un título y un resumen con tus palabras, y sus pasos con nombre.

**`automations/recordatorio.py`**

```python title="automations/recordatorio.py"
from datetime import date, timedelta

import sc


@sc.cron(
    "0 9 * * *",
    tz="Europe/Madrid",
    title="Recordatorio de citas",
    summary="Avisa por WhatsApp a quien tiene cita mañana",
    table="citas",
    group_by="aviso",
)
def recordatorio(event: sc.Event) -> str:
    manana = (date.today() + timedelta(days=1)).isoformat()
    avisos = 0
    with sc.step("Avisar a quien tiene cita mañana"):
        for cita in sc.data.find("citas", where={"dia": manana, "aviso": "pendiente"}):
            texto = sc.ai(f"Recordatorio breve y cálido de esta cita: {cita['values']}")
            sc.send(cita["values"]["telefono"], texto, via="whatsapp", key=f"aviso:{cita['id']}")
            sc.data.update("citas", cita["id"], {"aviso": "enviado"})
            avisos += 1
    return f"{avisos} avisos"
```

* Lo que devuelve es la línea de la ejecución: "12 avisos".
* `table` y `group_by` hacen que la página de la automatización muestre la tabla por estados
  ("Pendientes 8, Enviados 34").
* `key=` hace que cada aviso se envíe una sola vez, aunque la ejecución vuelva a empezar.
* El agente necesita acceso a la tabla `citas` y un canal de WhatsApp. Pasadas 24 horas desde
  el último mensaje del cliente, WhatsApp solo deja enviar plantillas: elige una en los ajustes
  del canal o pásala con `template={"name": ..., "values": [...]}`.

## Disparadores

Uno por automatización:

| Disparador                                           | Empieza                                                                      |
| ---------------------------------------------------- | ---------------------------------------------------------------------------- |
| `@sc.cron("0 9 * * 1-5", tz="Europe/Madrid", ...)`   | A una hora, en cron de cinco campos. Sin `tz`, en UTC.                       |
| `@sc.webhook(...)`                                   | Cuando otro sistema llama a su dirección. Lo que manda está en `event.data`. |
| `@sc.event("gmail", "GMAIL_NEW_GMAIL_MESSAGE", ...)` | Cuando pasa algo en una app conectada.                                       |
| `@sc.on("message.received", decides=True, ...)`      | Cuando llega un mensaje a un canal, con una regla tuya.                      |

Todos llevan `title` y `summary`. Una ejecución dura como mucho 5 minutos; `timeout_s=` la
alarga hasta 900 segundos.

Una regla sobre lo que llega decide cada mensaje con `sc.answer()`, `sc.hold(motivo)` (a una
persona) o `sc.ignore(motivo)`:

**`automations/proveedores.py`**

```python title="automations/proveedores.py"
import sc


@sc.on(
    "message.received",
    decides=True,
    title="Proveedores",
    summary="Pasa a una persona lo que traen los proveedores",
)
def proveedores(event: sc.Event) -> None:
    with sc.step("Mirar si es un proveedor"):
        if "albarán" in event.text.lower():
            sc.hold("Es un proveedor")
```

Si una regla falla o tarda, el agente responde: un error nunca deja a un cliente sin respuesta.

## Lo que puede llamar

El código se ejecuta aislado, sin red salvo a SignalCore. Todo lo que hace pasa por `sc`:

| Llamada                                                       | Hace                                                                                                                                                                                                                                                                                                                                       |
| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `sc.data.find(tabla, where=, words=)` · `add` · `update`      | Lee y escribe en las tablas que el agente tiene.                                                                                                                                                                                                                                                                                           |
| `sc.ai(prompt, model=, system=, schema=, files=, **opciones)` | Una llamada a un modelo; lo que hace el modelo decide qué devuelve. Uno que escribe da texto (con `schema`, JSON); uno que dibuja, habla, hace música o vídeo deja un archivo del agente y da su ruta; uno que escucha un audio (`files=[ruta]`) da lo que dice. Las opciones son las del modelo (`voice=`, `seconds=`, `aspect_ratio=`…). |
| `sc.send(contacto, texto, via=, template=, files=, key=)`     | Un mensaje a un contacto por uno de los canales del agente; `files`, rutas de archivos suyos (lo que hizo `sc.ai`), van como imagen, audio, vídeo o documento.                                                                                                                                                                             |
| `sc.tool(app, accion, **datos)`                               | Una acción de una app que el agente tiene.                                                                                                                                                                                                                                                                                                 |
| `sc.http.get(url)` · `post` · `put`                           | Una llamada a otra web; `key=` pone una de sus claves sin que el código la vea.                                                                                                                                                                                                                                                            |
| `sc.files.read(ruta)` · `write` · `list` · `search`           | Sus archivos y los de la organización.                                                                                                                                                                                                                                                                                                     |
| `sc.notify(texto)`                                            | Una línea en tu bandeja.                                                                                                                                                                                                                                                                                                                   |
| `sc.agent.start(texto)`                                       | Abre una conversación del agente, para lo que pide criterio de principio a fin.                                                                                                                                                                                                                                                            |
| `sc.step(nombre)`                                             | Nombra un paso en el registro y en la página de la automatización.                                                                                                                                                                                                                                                                         |

¿Necesita una librería? Decláralas en `agent.yaml`, fijadas, y se instalan antes de la primera
ejecución:

**`agent.yaml`**

```yaml title="agent.yaml"
packages:
  pip: [openpyxl==3.1.5]
  apt: [poppler-utils]
```

## Probar y encender

Con el builder: pídele el cambio, la prueba en seco y te enseña la tarjeta para encenderla.

Con la [API](/guias/api), con una clave con permiso `write`:

```bash
# 1. Escribir el archivo. Si algo está mal, la respuesta dice qué.
curl -X PUT https://signalcore.ai/v1/agents/$AGENT/folder/automations/recordatorio.py \
  -H "Authorization: Bearer $SIGNALCORE_KEY" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile c recordatorio.py '{content: $c}')"

# 2. Probarla en seco: lee de verdad, no envía ni cambia nada.
curl -X POST https://signalcore.ai/v1/agents/$AGENT/automations/recordatorio/test \
  -H "Authorization: Bearer $SIGNALCORE_KEY"

# 3. Encenderla. Una automatización nueva empieza en pausa.
curl -X PATCH https://signalcore.ai/v1/agents/$AGENT/automations/recordatorio \
  -H "Authorization: Bearer $SIGNALCORE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"on": true}'
```

Sus ejecuciones, con cada paso: `GET /v1/runs?agent=$AGENT&automation=recordatorio` y
`GET /v1/runs/{run_id}`.

## Una acción propia

Lo que el agente hace **mientras habla** (no solo) es una acción: un archivo en `actions/`, una
función que el modelo llama como herramienta.

**`actions/precio.py`**

```python title="actions/precio.py"
import sc

PRECIOS = {"limpieza": 45.0, "blanqueamiento": 180.0}


@sc.action
def precio(servicio: str, con_iva: bool = True) -> float:
    """El precio de un servicio en euros. Llámala cuando pregunten cuánto cuesta un tratamiento."""
    base = PRECIOS[servicio.lower()]
    return round(base * 1.21, 2) if con_iva else base
```

El docstring le dice al modelo cuándo llamarla. `@sc.action(destructive=True)` marca una que
cambia algo fuera: una persona dice que sí antes de que se ejecute.