> 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.

# La API

La API pública está en `https://signalcore.ai/v1`: JSON de entrada y salida, y un WebSocket por
conversación para lo que pasa en directo. La app usa la misma API en `/api`, con su sesión; tu
código usa siempre `/v1`. Cada endpoint está en la [referencia](/referencia).

Cada llamada lleva `Authorization: Bearer` con una clave o un token.

Desde Python o TypeScript, los SDK hacen lo mismo con cada llamada tipada:
[Python](/guias/sdk-python) y [TypeScript](/guias/sdk-typescript).

## Claves

| Clave                 | Para                                           | Puede                                                   |
| --------------------- | ---------------------------------------------- | ------------------------------------------------------- |
| **Secreta** `sk_…`    | Tu servidor. Nunca en una página.              | Lo que digan sus permisos (abajo).                      |
| **Publicable** `pk_…` | Una página, como el [widget](/guias/chat-web). | Hablar con un agente, solo desde las webs que indiques. |

Los permisos de una clave secreta:

| Permiso | Deja                                                                                                                                                         |
| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `read`  | Leer todo (cada `GET`).                                                                                                                                      |
| `write` | Cambiar la organización: equipos, agentes, su código, archivos, tablas, claves, automatizaciones, canales, apps, contactos, quién responde una conversación. |
| `talk`  | Hablar: empezar conversaciones, escribir en ellas, llamar, subir archivos, crear tokens de persona.                                                          |
| `admin` | Crear las organizaciones de tus clientes y los webhooks.                                                                                                     |

### Crear una clave

Las claves las crea el dueño de la cuenta, con su sesión. Mientras llega su pantalla en la app,
créala desde la consola del navegador con [signalcore.ai/app](https://signalcore.ai/app) abierta
y la sesión iniciada:

**`Consola del navegador, en signalcore.ai/app`**

```js title="Consola del navegador, en signalcore.ai/app"
const answer = await fetch("/api/keys", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ kind: "secret", name: "Mi servidor", scopes: ["read", "write", "talk"] }),
});
console.log((await answer.json()).key); // sk_…: solo se muestra esta vez
```

Una clave publicable nombra su agente y sus webs:
`{ kind: "publishable", name: "Web", agent_id: "agt_…", origins: ["https://tuweb.com"] }`.
`GET /api/keys` las lista y `DELETE /api/keys/{id}` revoca una, y con ella los tokens que creó.

## Hablar con un agente

Busca el id del agente en sus equipos, empieza una conversación y escríbele. Con `wait: true`
la respuesta llega cuando el agente termina (hasta 60 segundos).

```bash
export SIGNALCORE_KEY=sk_...

# Los equipos, con sus agentes
curl https://signalcore.ai/v1/squads -H "Authorization: Bearer $SIGNALCORE_KEY"

# Una conversación nueva
curl -X POST https://signalcore.ai/v1/agents/agt_.../conversations \
  -H "Authorization: Bearer $SIGNALCORE_KEY"

# Escribir y esperar la respuesta
curl -X POST https://signalcore.ai/v1/conversations/cnv_.../messages \
  -H "Authorization: Bearer $SIGNALCORE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text": "¿Abrís los sábados?", "wait": true}'
```

**`Respuesta`**

```json title="Respuesta"
{
  "conversation_id": "cnv_...",
  "message": { "id": "msg_...", "role": "user", "text": "¿Abrís los sábados?", "...": "..." },
  "status": "done",
  "replies": [
    { "id": "msg_...", "role": "assistant", "text": "Sí, de 10:00 a 14:00. ¿Te reservo una cita?", "...": "..." }
  ],
  "waiting": [],
  "error": null
}
```

`status` es `done`, `waiting` (espera algo: una respuesta a una pregunta, un sí; está en
`waiting`), `failed` (con `error`) o `running`: aún no ha terminado, lee la conversación después
con `GET /v1/conversations/{id}`.

El builder también es un agente: con una clave con `talk` puedes hablarle igual.

## En directo

Cada conversación tiene un WebSocket con todo lo que pasa: la respuesta mientras se escribe, lo
que hace el agente, lo que enseña.

```
wss://signalcore.ai/v1/conversations/{id}/live?token=<clave o token>
```

Desde un servidor también vale la cabecera `Authorization`. Cada mensaje es JSON con un `type`. Envías `{"type": "say", "text": "..."}` y recibes, entre
otros, `message` (un mensaje guardado), `text` (un trozo de la respuesta), `step` (algo que hace
el agente), `done` (terminó) y `error`.

## Organizaciones de tus clientes

Si eres una agencia o un software que da agentes a sus clientes, crea una organización por
cliente (permiso `admin`). Cada una nace con su equipo General y su builder, y no comparte nada
con las demás.

```bash
curl -X POST https://signalcore.ai/v1/orgs \
  -H "Authorization: Bearer $SIGNALCORE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Clínica Sol"}'
```

Para actuar en ella, añade su id en cada llamada: `SignalCore-Org: org_…`.

## Tokens de persona

Para que tus usuarios hablen con un agente desde tu producto, tu servidor crea un token para
cada uno (permiso `talk`). Dura una hora y solo deja hablar con los agentes que nombres y ver
sus propias conversaciones.

```bash
curl -X POST https://signalcore.ai/v1/tokens \
  -H "Authorization: Bearer $SIGNALCORE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"agents": ["agt_..."], "external_id": "user_42", "name": "Lucía Pérez"}'

# {"token": "eyJ...", "expires_at": "..."}
```

Con una clave publicable, la página manda también `SignalCore-Visitor: <id>`: un id del
visitante que la página guarda (16 a 128 caracteres de `A-Za-z0-9_-`). Los componentes de
[chat web](/guias/chat-web) lo hacen por ti.

## Webhooks

Para enterarte de lo que pasa sin preguntar, registra una dirección tuya (permiso `admin`):

```bash
curl -X POST https://signalcore.ai/v1/webhooks \
  -H "Authorization: Bearer $SIGNALCORE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://tuapp.com/signalcore", "events": ["message.created", "run.failed"]}'

# {"secret": "whsec_...", ...}: solo se muestra esta vez
```

Eventos: `message.created`, `conversation.holder_changed`, `inbox.item_created`,
`run.finished`, `run.failed`, `call.ended`, `channel.status_changed`, `approval.requested`.

Cada evento llega como `POST` con `{id, type, created_at, org_id, data}` y la cabecera
`SignalCore-Signature: t=<segundos>,v1=<firma>`. Compruébala sobre el cuerpo tal como llega:

```python
import hashlib
import hmac
import time


def verified(body: bytes, signature: str, secret: str) -> bool:
    parts = dict(part.split("=", 1) for part in signature.split(","))
    if abs(time.time() - int(parts["t"])) > 300:
        return False
    expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + body, hashlib.sha256)
    return hmac.compare_digest(expected.hexdigest(), parts["v1"])
```

Responde con un `2xx` en 10 segundos. Si no, se reintenta (a los 15 s, 30, 60 y 120) y, tras el
quinto intento, se da por perdido. El `id` es el mismo en cada intento.

## Escrituras sin repetir

Un `POST`, `PUT`, `PATCH` o `DELETE` puede llevar `Idempotency-Key: <hasta 255 caracteres>`. Si
lo mandas otra vez con la misma clave en las siguientes 24 horas, recibes la primera respuesta
(con `Idempotent-Replayed: true`) y no se hace dos veces.

## Límites

600 llamadas por minuto por clave (o por persona de una clave). Pasado el límite, `429` con
`Retry-After`.

## Errores

Los errores son `{code, detail}`: `code` para tu código, `detail` en palabras.

```json
{ "code": "unauthorized", "detail": "This key is not valid, or was revoked" }
```

| Estado | `code`                         | Quiere decir                                                                                           |
| ------ | ------------------------------ | ------------------------------------------------------------------------------------------------------ |
| 401    | `unauthorized`                 | Falta la clave, o no es válida.                                                                        |
| 403    | `forbidden`                    | La clave no tiene el permiso.                                                                          |
| 404    | `not_found`                    | No existe, o no es tuyo.                                                                               |
| 409    | `conflict`                     | Choca con algo que ya existe.                                                                          |
| 422    | `invalid`, o el de la negativa | La petición no vale: `busy` (el agente aún responde), `pending` (espera una respuesta antes), `files`. |
| 429    | `rate_limited`                 | Demasiadas llamadas: espera lo que diga `Retry-After`.                                                 |

Si el cuerpo no tiene la forma esperada, el `422` es `{"detail": [{"loc": [...], "msg": "..."}]}`.