> ## Documentation Index
> Fetch the complete documentation index at: https://docs.inagent.inconcertcx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Credenciales

> Uso de credenciales seguras desde herramientas de código en Inagent.

export const ControllerIntellisense = () => {
  const items = [{
    name: "channel",
    kind: "property",
    type: "string",
    desc: "Canal asociado al controller actual."
  }, {
    name: "configuration",
    kind: "property",
    type: "Config",
    desc: "Configuración disponible para la ejecución de la herramienta."
  }, {
    name: "credentials",
    kind: "property",
    type: "Credentials",
    desc: "Módulo para recuperar credenciales seguras desde una herramienta de código."
  }, {
    name: "send",
    kind: "method",
    type: "(data) => void",
    desc: "Envía datos desde la herramienta."
  }, {
    name: "sendPush",
    kind: "method",
    type: "(payload) => Promise<void>",
    desc: "Envía una notificación push."
  }, {
    name: "session",
    kind: "property",
    type: "Session",
    desc: "Sesión activa asociada al controller actual."
  }];
  const [selectedIndex, setSelectedIndex] = useState(2);
  const [isDark, setIsDark] = useState(false);
  const selected = items[selectedIndex];
  const theme = getIntellisenseTheme(isDark);
  useEffect(() => {
    const root = document.documentElement;
    const updateTheme = () => setIsDark(root.classList.contains("dark"));
    updateTheme();
    const observer = new MutationObserver(updateTheme);
    observer.observe(root, {
      attributes: true,
      attributeFilter: ["class"]
    });
    return () => observer.disconnect();
  }, []);
  return <div className="not-prose" style={{
    background: theme.shellBg,
    border: `1px solid ${theme.border}`,
    borderRadius: 8,
    padding: 20,
    fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
    fontSize: 15,
    overflowX: "auto"
  }}>
      <div style={{
    color: theme.text,
    marginBottom: 10
  }}>
        <span style={{
    color: theme.accent
  }}>controller</span>.
      </div>
      <div style={{
    width: "min(100%, 440px)",
    background: theme.popupBg,
    border: `1px solid ${theme.border}`,
    borderRadius: 6,
    boxShadow: theme.shadow,
    overflow: "hidden"
  }}>
        {items.map((item, index) => <button key={item.name} type="button" onClick={() => setSelectedIndex(index)} style={{
    display: "flex",
    alignItems: "center",
    gap: 10,
    width: "100%",
    border: 0,
    padding: "7px 12px",
    background: index === selectedIndex ? theme.activeRow : "transparent",
    cursor: "pointer",
    font: "inherit",
    textAlign: "left"
  }}>
            <span style={{
    width: 8,
    height: 8,
    borderRadius: "50%",
    background: item.kind === "method" ? theme.methodDot : theme.propertyDot,
    flexShrink: 0
  }} />
            <span style={{
    color: theme.text,
    flex: 1
  }}>{item.name}</span>
            <span style={{
    color: theme.muted,
    fontSize: 13
  }}>{item.kind}</span>
          </button>)}
        <div style={{
    borderTop: `1px solid ${theme.border}`,
    padding: "12px 14px",
    fontFamily: "var(--font-sans, Inter, sans-serif)"
  }}>
          <div style={{
    color: theme.accent,
    fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
    fontSize: 14,
    marginBottom: 4
  }}>{selected.type}</div>
          <p style={{
    color: theme.description,
    fontSize: 14,
    lineHeight: 1.5,
    margin: 0
  }}>{selected.desc}</p>
        </div>
      </div>
    </div>;
};

export const CredentialsIntellisense = () => {
  const items = [{
    name: "getByName",
    params: "name: string",
    returns: "Promise<ICredential>",
    desc: "Recupera una credencial por su nombre. Es el método habitual cuando el código debe usar una credencial conocida por su identificador funcional."
  }, {
    name: "getById",
    params: "id: string",
    returns: "Promise<ICredential>",
    desc: "Recupera una credencial por su identificador único."
  }];
  const [selectedIndex, setSelectedIndex] = useState(0);
  const [isDark, setIsDark] = useState(false);
  const selected = items[selectedIndex];
  const theme = getIntellisenseTheme(isDark);
  useEffect(() => {
    const root = document.documentElement;
    const updateTheme = () => setIsDark(root.classList.contains("dark"));
    updateTheme();
    const observer = new MutationObserver(updateTheme);
    observer.observe(root, {
      attributes: true,
      attributeFilter: ["class"]
    });
    return () => observer.disconnect();
  }, []);
  return <div className="not-prose" style={{
    background: theme.shellBg,
    border: `1px solid ${theme.border}`,
    borderRadius: 8,
    padding: 20,
    fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
    fontSize: 15,
    overflowX: "auto"
  }}>
      <div style={{
    color: theme.text,
    marginBottom: 10
  }}>
        <span style={{
    color: theme.accent
  }}>controller.credentials</span>.
      </div>
      <div style={{
    width: "min(100%, 440px)",
    background: theme.popupBg,
    border: `1px solid ${theme.border}`,
    borderRadius: 6,
    boxShadow: theme.shadow,
    overflow: "hidden"
  }}>
        {items.map((item, index) => <button key={item.name} type="button" onClick={() => setSelectedIndex(index)} style={{
    display: "flex",
    alignItems: "center",
    gap: 10,
    width: "100%",
    border: 0,
    padding: "7px 12px",
    background: index === selectedIndex ? theme.activeRow : "transparent",
    cursor: "pointer",
    font: "inherit",
    textAlign: "left"
  }}>
            <span style={{
    width: 8,
    height: 8,
    borderRadius: "50%",
    background: theme.methodDot,
    flexShrink: 0
  }} />
            <span style={{
    color: theme.text,
    flex: 1
  }}>{item.name}</span>
            <span style={{
    color: theme.muted,
    fontSize: 13
  }}>method</span>
          </button>)}
        <div style={{
    borderTop: `1px solid ${theme.border}`,
    padding: "12px 14px",
    fontFamily: "var(--font-sans, Inter, sans-serif)"
  }}>
          <div style={{
    color: theme.accent,
    fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
    fontSize: 14,
    marginBottom: 4
  }}>
            {selected.name}({selected.params}): {selected.returns}
          </div>
          <p style={{
    color: theme.description,
    fontSize: 14,
    lineHeight: 1.5,
    margin: 0
  }}>{selected.desc}</p>
        </div>
      </div>
    </div>;
};

export const CredentialPropertiesIntellisense = () => {
  const items = [{
    name: "id",
    type: "string",
    kind: "property",
    desc: "Identificador único de la credencial."
  }, {
    name: "name",
    type: "string",
    kind: "property",
    desc: "Nombre de la credencial."
  }, {
    name: "description",
    type: "string | null",
    kind: "property",
    desc: "Descripción opcional."
  }, {
    name: "type",
    type: "string",
    kind: "property",
    desc: "Tipo de credencial. Determina el esquema de atributos."
  }, {
    name: "active",
    type: "boolean",
    kind: "property",
    desc: "Indica si la credencial está activa."
  }, {
    name: "TenantId",
    type: "string",
    kind: "property",
    desc: "Tenant al que pertenece la credencial."
  }, {
    name: "createdAt",
    type: "Date",
    kind: "property",
    desc: "Fecha de creación."
  }, {
    name: "createdBy",
    type: "string",
    kind: "property",
    desc: "Usuario que creó la credencial."
  }, {
    name: "publicAttributes",
    type: "Record<string, unknown>",
    kind: "property",
    desc: "Atributos no sensibles, como el nombre de un header o una URL base."
  }, {
    name: "publicAttributesSchema",
    type: "JSONSchema",
    kind: "property",
    desc: "Esquema JSON de los atributos públicos."
  }, {
    name: "privateAttributes",
    type: "Record<string, unknown>",
    kind: "private",
    desc: "Atributos sensibles descifrados, como una API key, token o contraseña."
  }, {
    name: "privateAttributesSchema",
    type: "JSONSchema",
    kind: "private",
    desc: "Esquema JSON de los atributos privados."
  }];
  const [selectedIndex, setSelectedIndex] = useState(0);
  const [isDark, setIsDark] = useState(false);
  const selected = items[selectedIndex];
  const theme = getIntellisenseTheme(isDark);
  useEffect(() => {
    const root = document.documentElement;
    const updateTheme = () => setIsDark(root.classList.contains("dark"));
    updateTheme();
    const observer = new MutationObserver(updateTheme);
    observer.observe(root, {
      attributes: true,
      attributeFilter: ["class"]
    });
    return () => observer.disconnect();
  }, []);
  return <div className="not-prose" style={{
    background: theme.shellBg,
    border: `1px solid ${theme.border}`,
    borderRadius: 8,
    padding: 20,
    fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
    fontSize: 15,
    overflowX: "auto"
  }}>
      <div style={{
    color: theme.comment,
    marginBottom: 4,
    fontSize: 14
  }}>{"// crea la variable a partir del controller"}</div>
      <div style={{
    color: theme.text,
    marginBottom: 10
  }}>
        <span style={{
    color: theme.keyword
  }}>const</span> credencial = <span style={{
    color: theme.keyword
  }}>await</span> <span style={{
    color: theme.accent
  }}>controller</span>.credentials.<span style={{
    color: theme.fn
  }}>getByName</span>(<span style={{
    color: theme.string
  }}>"TEST"</span>);
      </div>
      <div style={{
    color: theme.text,
    marginBottom: 10
  }}>
        <span style={{
    color: theme.accent
  }}>credencial</span>.
      </div>
      <div style={{
    width: "min(100%, 440px)",
    background: theme.popupBg,
    border: `1px solid ${theme.border}`,
    borderRadius: 6,
    boxShadow: theme.shadow,
    overflow: "hidden"
  }}>
        <div style={{
    maxHeight: 260,
    overflowY: "auto"
  }}>
          {items.map((item, index) => <button key={item.name} type="button" onClick={() => setSelectedIndex(index)} style={{
    display: "flex",
    alignItems: "center",
    gap: 10,
    width: "100%",
    border: 0,
    padding: "7px 12px",
    background: index === selectedIndex ? theme.activeRow : "transparent",
    cursor: "pointer",
    font: "inherit",
    textAlign: "left"
  }}>
              <span style={{
    width: 8,
    height: 8,
    borderRadius: "50%",
    background: item.kind === "private" ? theme.privateDot : theme.propertyDot,
    flexShrink: 0
  }} />
              <span style={{
    color: theme.text,
    flex: 1
  }}>{item.name}</span>
              <span style={{
    color: theme.muted,
    fontSize: 13
  }}>{item.kind}</span>
            </button>)}
        </div>
        <div style={{
    borderTop: `1px solid ${theme.border}`,
    padding: "12px 14px",
    fontFamily: "var(--font-sans, Inter, sans-serif)"
  }}>
          <div style={{
    color: theme.accent,
    fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
    fontSize: 14,
    marginBottom: 4
  }}>{selected.type}</div>
          <p style={{
    color: theme.description,
    fontSize: 14,
    lineHeight: 1.5,
    margin: 0
  }}>{selected.desc}</p>
        </div>
      </div>
    </div>;
};

export const getIntellisenseTheme = isDark => isDark ? {
  shellBg: "#1e1e1e",
  popupBg: "#252526",
  text: "#d4d4d4",
  description: "#b3b3b3",
  muted: "#9ca3af",
  border: "#454545",
  activeRow: "#04395e",
  accent: "#9cdcfe",
  keyword: "#569cd6",
  fn: "#dcdcaa",
  string: "#ce9178",
  comment: "#6a9955",
  propertyDot: "#75beff",
  methodDot: "#b180d7",
  privateDot: "#ce9178",
  shadow: "0 8px 24px rgba(0,0,0,0.35)"
} : {
  shellBg: "#f3f3f3",
  popupBg: "#ffffff",
  text: "#1e1e1e",
  description: "#444444",
  muted: "#6e6e6e",
  border: "#d0d0d0",
  activeRow: "#d6e8fb",
  accent: "#0b5fb0",
  keyword: "#0000ff",
  fn: "#795e26",
  string: "#a31515",
  comment: "#008000",
  propertyDot: "#0b5fb0",
  methodDot: "#7a3fa0",
  privateDot: "#a31515",
  shadow: "0 8px 24px rgba(0,0,0,0.12)"
};

## Propósito

El módulo de credenciales permite almacenar datos sensibles, como API keys, tokens o contraseñas, y recuperarlos desde el código de una herramienta en tiempo de ejecución.

<Callout type="info">
  Usa credenciales para evitar hardcodear secretos directamente en el código de una herramienta. Las credenciales se recuperan ya descifradas y listas para usar.
</Callout>

## Acceso desde código

El módulo está disponible a través de `controller.credentials` dentro del runtime de la herramienta de código. También aparece en el autocompletado del editor junto al resto de propiedades de `controller`.

<ControllerIntellisense />

## Métodos disponibles

`controller.credentials` expone dos métodos para recuperar credenciales:

<CredentialsIntellisense />

### `getByName(name: string)`

Recupera una credencial por su nombre. Es el método habitual cuando el código debe usar una credencial conocida por su identificador funcional.

```javascript theme={null}
const credential = await controller.credentials.getByName("nombre_credencial");
```

### `getById(id: string)`

Recupera una credencial por su identificador único.

```javascript theme={null}
const credential = await controller.credentials.getById("id_credencial");
```

## Estructura de una credencial

Una vez recuperada, la credencial expone las siguientes propiedades:

<CredentialPropertiesIntellisense />

| Propiedad                 | Descripción                                                            |
| ------------------------- | ---------------------------------------------------------------------- |
| `id`                      | Identificador único de la credencial.                                  |
| `name`                    | Nombre de la credencial.                                               |
| `description`             | Descripción opcional.                                                  |
| `type`                    | Tipo de credencial. Determina el esquema de atributos.                 |
| `active`                  | Indica si la credencial está activa.                                   |
| `TenantId`                | Tenant al que pertenece la credencial.                                 |
| `createdAt`               | Fecha de creación.                                                     |
| `createdBy`               | Usuario que creó la credencial.                                        |
| `publicAttributes`        | Atributos no sensibles, como el nombre de un header o una URL base.    |
| `publicAttributesSchema`  | Esquema JSON de los atributos públicos.                                |
| `privateAttributes`       | Atributos sensibles descifrados, como una API key, token o contraseña. |
| `privateAttributesSchema` | Esquema JSON de los atributos privados.                                |

### `publicAttributes` vs `privateAttributes`

* **`publicAttributes`**: datos no sensibles asociados a la credencial. Por ejemplo, en una credencial de tipo API key, aquí puede guardarse el nombre del header HTTP donde debe enviarse la clave, como `X-Api-Key` o `Authorization`.
* **`privateAttributes`**: datos sensibles almacenados cifrados y entregados ya descifrados en tiempo de ejecución. Aquí puede guardarse la API key, el token o la contraseña.

## Ejemplo de uso

```javascript theme={null}
// Recuperar la credencial por nombre
const credential = await controller.credentials.getByName("mi_api_externa");

// Acceder a los atributos
const headerName = credential.publicAttributes.header_name;
const apiKey = credential.privateAttributes.api_key;

// Usar en una llamada HTTP
const response = await fetch("https://api.ejemplo.com/endpoint", {
  method: "GET",
  headers: {
    [headerName]: apiKey
  }
});

const data = await response.json();
return data;
```

De esta forma, ni el nombre del header ni la API key quedan escritos directamente como valores fijos en el código de la herramienta.

## Buenas prácticas

* Recupera credenciales por nombre cuando el código deba ser legible y estable.
* Evita registrar valores de `privateAttributes` en logs o respuestas.
* Mantén en `publicAttributes` solo datos que no sean sensibles.
* Prueba la herramienta en Playground antes de publicarla.

## Próximos pasos

<Card title="Usar una herramienta de código" icon="code" href="./code">
  Revisa cómo ejecutar JavaScript dentro del flujo conversacional de Inagent.
</Card>
