---
title: Portal logins
description: The agent signs in to portals such as Sankhya with the login of whoever uses it, without the username and password passing through the conversation.
sidebar:
  order: 4
---

Some work starts in a portal that asks for a username and password, such as an ERP or a bank's site. The agent stores that login in Siglata once and, when it needs it, a script on your machine reads it and signs in. The username and password never appear in the conversation, and nobody has to keep them in a `.env` file.

Each person stores their own login. Two people can each have their own `sankhya`, so a company skill signs in to the portal as whoever runs it.

## Store a login [#request]

Ask the agent, for example: "store the Sankhya login". It calls `credential_request` with a name and the login's fields:

```js
return await credential_request({
  name: "sankhya",
  fields: ["username", "password"],
});
```

The answer carries a link. Open it and type the values on the page; if Siglata asks you to sign in, sign in and open the link again. The link works once, for an hour, and only for the person who asked. To change the password, ask again with the same name: the new values replace the old ones.

`credential_list` shows the names, the fields, whose each login is and whether it has values, never the values. `isSelected: true` marks the login `credential_use` uses for you.

## Skills that sign in to portals [#skills]

A company skill declares the logins it needs in its `SKILL.md` frontmatter:

```yaml
---
name: extrair-pedidos-sankhya
description: Extracts the day's orders from Sankhya.
credentials: [sankhya]
---
```

`skill_find` returns that list as `credentials`. Before running the skill, the agent checks each name with `credential_list` and, for each missing login, sends the person a `credential_request` link right away so they type their own. A `credentials` entry that is not a list of login names makes the `file_write` of the `SKILL.md` fail, naming the field.

## Use a login [#use]

When a script needs the login, the agent calls `credential_use` and hands the URL to the script. The token follows the `#` and never appears in a request URL: the script sends `{ "token" }` in a `POST` to the URL without the `#`:

```js
return await credential_use({ name: "sankhya" });
```

`name` leads to your own login with that name; if you have none, to the company login shared with you. If neither exists, `credential_use` fails with `not_found` and the agent asks for yours with `credential_request`.

The URL works once, for about 60 seconds, and answers with `{ "name": "sankhya", "values": { "username": "…", "password": "…" } }`. Every use is recorded with the login, the person and the time. The agent never opens the URL or shows what it returns.

## Who can use it [#access]

The login `credential_request` creates is personal: only the person who created it can use it or change its values. Owners and administrators see that it exists (its name, owner, fields and whether it has values) and can delete it when someone leaves the organization, with `credential_delete({ credentialId })`, but they can't use it or read its values. A personal login can't be shared.

For a login several people use, an owner or administrator creates the company login with `visibility: "org"`:

```js
return await credential_request({
  name: "sankhya",
  fields: ["username", "password"],
  visibility: "org",
});
```

It serves every member who has no login of their own with that name. To let specific members or teams change its values, grant write with `grant_set` and the `credentialId` that `credential_list` shows. `credential_delete` removes the values, the open links and the grants.

## How the values are kept [#storage]

The values are encrypted with AES-GCM before they reach the database, under a key of their own, which is itself encrypted under the server's vault key. The organization is part of the encryption, so a value copied into another organization does not open. The database holds only the ciphertext.
