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

# Connect an account you own

> Owner-authorized delegated access — supply credentials for an account you already have, and Sente keeps it logged in, re-loginable, and revocable.

Connect is the owner-authorized path: you already have an account at a third-party app, and you
want your agent to operate it. You supply that account's credentials once. Sente logs in with a
real browser, vaults the credentials write-only, and keeps the session alive — re-login on demand,
TOTP 2FA cleared server-side from a seed you provide.

This is the same posture as handing Plaid your bank login or putting a shared credential in
1Password: you own the account, you authorize the access, and you can revoke it at any time. If you
want Sente to create a *new* account instead, see [Register](/guides/register).

<Note>
  Only connect accounts you own or are authorized to operate. Sente never guesses or reuses
  credentials.
</Note>

## Connect an account

`POST /v1/connections` with the identity, the app's login URL, and the credentials. Sente starts a
[run](/concepts/runs-and-human-takeover) that logs in and verifies the session.

<CodeGroup>
  ```bash CLI theme={null}
  sente connect https://app.example.com/login --identity <id> \
    --username you@company.com --password '...' \
    --totp-seed JBSWY3DPEHPK3PXP   # optional: base32 or an otpauth:// URI
  ```

  ```ts TypeScript theme={null}
  const { connection, run } = await sente.connections.connect({
    identityId,
    appUrl: "https://app.example.com/login",
    credentials: { username: "you@company.com", password: "...", totpSeed: "JBSWY3DPEHPK3PXP" },
  });
  const done = await sente.runs.waitForRun(run.id);
  // done.status: "completed" | "blocked" | "failed"
  ```

  ```python Python theme={null}
  res = sente.connections.connect(
      identity_id=identity_id,
      app_url="https://app.example.com/login",
      username="you@company.com",
      password="...",
      totp_seed="JBSWY3DPEHPK3PXP",  # optional
  )
  done = sente.runs.wait_for_run(res.run.id)
  ```
</CodeGroup>

On success the run ends `completed` and the connection is `active`. The account is now reachable
through the same session machinery as a registered account — `getSession`, `login`,
`exportSession` — by the returned connection id. See [Sessions](/guides/sessions).

## Two-factor authentication

What happens at the 2FA prompt depends on where the second factor lives:

* **Authenticator app (TOTP) — autonomous.** Pass the authenticator secret as `totpSeed` (the
  base32 string behind a QR code's "can't scan?" link, or a full `otpauth://` URI). Sente vaults
  it encrypted and computes the 30-second RFC-6238 code server-side at login and every re-login.
  The seed never reaches any model — the server derives the code and injects it.
* **Email or SMS code — needs a human.** The code goes to *your* inbox or phone, not the
  identity's, so Sente cannot read it. The run blocks with code `MFA_REQUIRED` and an interactive
  live-view URL. A person opens the live view, enters the code, and resumes the run. Two ways to
  avoid the human step on future logins: use TOTP on the account, or repoint the account's
  notification email to the Sente identity's address **and** pass `verifyToIdentity: true` when
  connecting (raw `POST /v1/connections` only today — not yet exposed in the SDKs or CLI), so
  Sente fetches the code from the inbox itself.
* **TOTP prompt but no seed supplied** — the run blocks with `TOTP_REQUIRED`; a human enters a code
  in the live view, then resumes.

<Warning>
  A blocked run holds for about 10 minutes, then fails. Email-code enrollment also races the code's
  own expiry — have the account owner ready to enter the code when you start the connect, not later.
  If the hold lapses, the live-view URL goes stale; retry the connect.
</Warning>

## What can't be connected

* **SSO-only accounts** (Google/Microsoft/Okta sign-in, passkeys). There is no password to vault,
  so there is nothing Sente can re-login with. Not supported.
* **SMS-2FA accounts** connect, but every login that triggers an SMS code needs a human in the live
  view — Sente has no phone number for the account.

## Credentials are write-only

Once vaulted, connected-account credentials cannot be read back through the API — by you, by your
agent, or by anything that compromises your API key. `GET /v1/registrations/:id/credentials` on a
connected account returns `403 CREDENTIALS_WRITE_ONLY`. Only Sente's run controller decrypts them,
at login time. You can overwrite them (see rotation below) or purge them; you can never fetch them.

More on the vault: [Security](/trust/security).

## Revoke vs. delete

<CodeGroup>
  ```bash CLI theme={null}
  sente connection revoke <connectionId>   # disable; vault kept — reconnect re-enables
  sente connection delete <connectionId>   # revoke AND purge the stored credentials + seed
  ```

  ```ts TypeScript theme={null}
  await sente.connections.revoke(connection.id); // disable, keep vault
  await sente.connections.delete(connection.id); // revoke + purge — Sente holds nothing afterward
  ```

  ```python Python theme={null}
  sente.connections.revoke(res.connection.id)
  sente.connections.delete(res.connection.id)
  ```
</CodeGroup>

* **Revoke** (`POST /v1/connections/:id/revoke`) disables the connection: sessions and logins are
  refused, but the vaulted secrets are kept — Sente holds them, unused. Connecting the app again
  re-enables it (the connect call always requires and re-vaults credentials).
* **Delete** (`DELETE /v1/connections/:id`) revokes *and* purges the vaulted password and TOTP
  seed. Use it for offboarding. Note that Sente holds no credentials after a delete — but the
  account's password at the app is unchanged; rotate it there if you want certainty.

## Recommended practice

* **Connect a scoped account, not your admin login.** If the app supports member accounts, service
  accounts, or restricted roles, create one with the least privilege the agent needs and connect
  that.
* **Rotate credentials by re-supplying them.** After changing the password at the app, either
  connect again (a `POST /v1/connections` on an existing connection re-vaults fresh credentials)
  or `PUT` the new values: `sente.registrations.setCredentials(id, { password })` /
  `registrations.set_credentials(id, password=...)`. The `PUT` writes the vault only — it does not
  change the password at the app.
* **List what's connected** with `sente connections` / `sente.connections.list()` and prune
  connections you no longer use.

## If a login breaks later

Sente re-logs-in automatically when a session goes stale (see
[Sessions](/guides/sessions)). If the app demands an email/SMS code at re-login, the run blocks
`MFA_REQUIRED` and fires a [`run.blocked` webhook](/guides/webhooks) so a person can take over.
TOTP-connected accounts re-login without a human.
