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

# Connections & OAuth

> Create connections programmatically: direct credentials, the OAuth flow, object selection, and defaults.

<Tip>The CLI's `engini connect <app>` wraps this whole page in one guided (and agent-resumable) command - see [CLI commands](/cli/commands). Use the SDK when you need the flow inside your own product.</Tip>

## Discover how an application connects

Every application lists its authentication methods - each with an `authentication_id`, a field list, and whether it's OAuth:

<CodeGroup>
  ```python Python theme={null}
  detail = client.applications.get("slack")
  for m in detail.authentication_methods:
      print(m.authentication_id, m.authentication_name, m.requires_o_auth_sign_in)
  ```

  ```typescript TypeScript theme={null}
  const detail = await client.applications.get("slack");
  for (const m of detail.authenticationMethods) {
    console.log(m.authenticationId, m.authenticationName, m.requiresOAuthSignIn);
  }
  ```
</CodeGroup>

## Direct-credential connections (API keys, DB credentials...)

<CodeGroup>
  ```python Python theme={null}
  created = client.connections.create(
      "slack", "Sales workspace", authentication_id=2,
      fields={"token": "xoxb-..."},
  )
  connection_id = created.connection_id
  alive = client.connections.check(connection_id)
  ```

  ```typescript TypeScript theme={null}
  const created = await client.connections.create(
    "slack", "Sales workspace", 2,
    { token: "xoxb-..." },
  );
  const alive = await client.connections.check(created.connectionId);
  ```
</CodeGroup>

## The OAuth flow

Three steps: get a sign-in URL, let the user authorize in a browser, then poll for the captured token and create the connection from it.

<CodeGroup>
  ```python Python theme={null}
  import time, webbrowser

  # 1. Start the sign-in
  resp = client.connections.get_sign_in_url("outlook", authentication_id=0)
  webbrowser.open(resp.sign_in_url)          # or show resp.sign_in_url to your user
  state = resp.state

  # 2. Poll until the user completes the sign-in
  while True:
      token = client.connections.get_access_token(state)
      status = ((token and token.status) or "").lower()
      if status in {"completed", "complete", "success", "succeeded"}:
          break
      if status in {"failed", "error", "cancelled", "canceled", "denied"}:
          raise RuntimeError(token.error_message or "OAuth sign-in failed.")
      time.sleep(2)                           # a null body means "still pending" - keep polling

  # 3. Create the connection from the captured connection_data
  props = token.connection_data.additional_properties or {}
  fields = {k: v if isinstance(v, str) else str(v) for k, v in props.items() if v is not None}
  created = client.connections.create("outlook", "My Outlook", 0, fields)
  ```

  ```typescript TypeScript theme={null}
  // 1. Start the sign-in
  const resp = await client.connections.getSignInUrl("outlook", 0);
  console.log("Authorize here:", resp.signInUrl);
  const state = resp.state;

  // 2. Poll until the user completes the sign-in
  let token;
  for (;;) {
    token = await client.connections.getAccessToken(state);
    const status = (token?.status ?? "").toLowerCase();
    if (["completed", "complete", "success", "succeeded"].includes(status)) break;
    if (["failed", "error", "cancelled", "canceled", "denied"].includes(status))
      throw new Error(token?.errorMessage ?? "OAuth sign-in failed.");
    await new Promise((r) => setTimeout(r, 2000)); // null body = still pending
  }

  // 3. Create the connection from the captured connectionData
  const fields = Object.fromEntries(
    Object.entries(token.connectionData ?? {}).filter(([, v]) => v != null)
      .map(([k, v]) => [k, typeof v === "string" ? v : JSON.stringify(v)]),
  );
  const created = await client.connections.create("outlook", "My Outlook", 0, fields);
  ```
</CodeGroup>

<Note>
  Build the create `fields` from the token's **`connectionData`** only - `tokenData` is the raw OAuth token (access\_token, token\_type...) and the API rejects unknown connector fields. While the sign-in is pending, `get_access_token` returns an empty body - treat that as "keep polling", not an error.
</Note>

## Refresh & object selection

Applications with `supports_object_selection` expose objects (tables, entities) to choose from. Refresh is **asynchronous** - trigger it, then poll:

<CodeGroup>
  ```python Python theme={null}
  client.connections.refresh(connection_id)                       # fire the async job
  status = client.connections.wait_for_refresh(connection_id)     # polls RefreshStatus (300s timeout, 2s interval)

  objs = client.connections.objects(connection_id)
  client.connections.select_objects(connection_id, [o.object_id for o in objs[:5]])
  client.connections.wait_for_refresh(connection_id)              # selection triggers another refresh
  ```

  ```typescript TypeScript theme={null}
  await client.connections.refresh(connectionId);
  const status = await client.connections.waitForRefresh(connectionId); // 300s timeout, 2s interval

  const objs = await client.connections.objects(connectionId);
  await client.connections.selectObjects(connectionId, objs.slice(0, 5).map((o) => o.objectId));
  await client.connections.waitForRefresh(connectionId);
  ```
</CodeGroup>

`select_objects` **replaces** the whole selection set. A completed refresh also proves the connection authenticates - it's a stronger health signal than `check` (which can be inconclusive). A refresh that doesn't finish in time raises `EnginiTimeoutError`.

## Defaults

The default connection is what implicit tool execution resolves to:

<CodeGroup>
  ```python Python theme={null}
  client.connections.set_default(connection_id)
  client.connections.defaults()               # one entry per application
  client.connections.clear_default("slack")
  conn_id = client.connections.resolve("slack", "Sales workspace")   # name -> id
  ```

  ```typescript TypeScript theme={null}
  await client.connections.setDefault(connectionId);
  await client.connections.defaults();
  await client.connections.clearDefault("slack");
  const connId = await client.connections.resolve("slack", "Sales workspace");
  ```
</CodeGroup>
