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

# Sync a database connection's objects

> Refresh the object catalog, poll until it settles, and choose which objects the connection exposes.

<Info>**Uses:** Python · TypeScript · CLI · REST API</Info>

Applications that expose selectable objects - databases especially - need one extra step after connecting: telling Engini which tables or entities to work with. You can check whether an app needs this via `supportsObjectSelection` on the application, or `useSelectDBObjects` on the connection.

The sequence is **refresh → poll → browse → select → poll again**.

<Note>
  cURL samples assume `BASE=https://api.engini.io/v1` and `AUTH="x-api-key: $ENGINI_API_KEY"` - the setup from the [REST walkthrough](/examples/rest-walkthrough). Steps 1-2 are shown raw first because the polling contract is where the surprises live; the SDK and CLI wrap both steps in one call (the CodeGroup in step 2).
</Note>

## 1. Trigger a refresh

Refresh is asynchronous. An empty `200` means *accepted*, not *finished*.

```bash theme={null}
curl -s -X POST "$BASE/connections/42/refresh-objects" -H "$AUTH" -o /dev/null -w "%{http_code}\n"
```

<Note>
  Async triggers can return a gateway `502`/`503`/`504` while the job is still accepted server-side. Don't treat that as failure - the status poll is the source of truth. Both SDKs already tolerate this.
</Note>

## 2. Poll until it settles

```bash theme={null}
until [ "$(curl -s "$BASE/connections/42/refresh-status" -H "$AUTH" | jq -r '.statusDescription')" != "InProgress" ]; do
  sleep 2
done
curl -s "$BASE/connections/42/refresh-status" -H "$AUTH" | jq '{status, statusDescription, error, lastRefreshDate}'
```

<Warning>
  Branch on **`statusDescription`** and `error`, not the numeric `status` enum. The enum is `Ok = 0`, `Error = 1`, `InProgress = 2`, but a finished refresh has been observed reporting `status: 0` with descriptive text carrying the real meaning - so the text plus `error` is the reliable signal. The SDKs' `wait_for_refresh` already encodes this.
</Warning>

<CodeGroup>
  ```python Python theme={null}
  client.connections.refresh(42)
  status = client.connections.wait_for_refresh(42)   # 300s timeout, 2s interval
  ```

  ```typescript TypeScript theme={null}
  await client.connections.refresh(42);
  const status = await client.connections.waitForRefresh(42);
  ```

  ```bash CLI theme={null}
  # waits by default; --no-wait returns immediately with status "pending"
  engini connections refresh 42 --timeout 600
  ```
</CodeGroup>

## 3. Browse what's available

<CodeGroup>
  ```python Python theme={null}
  objs = client.connections.objects(42)                  # full catalog
  orders = client.connections.objects(42, name="order")  # server-side name filter
  ```

  ```typescript TypeScript theme={null}
  const objs = await client.connections.objects(42);
  const orders = await client.connections.objects(42, { name: "order" });
  ```

  ```bash CLI theme={null}
  engini connections objects 42
  engini connections objects 42 --name order
  ```

  ```bash cURL theme={null}
  curl -s "$BASE/connections/42/objects?top=100" -H "$AUTH" \
    | jq -r '.items[] | "\(.objectId)\t\(.name)\t\(.type)\t\(.isSelected)"'

  curl -s "$BASE/connections/42/objects?name=order" -H "$AUTH" | jq '.totalCount'
  ```
</CodeGroup>

Large schemas paginate like any other list (`offset`/`top`, up to 1000 per page) - see [paging large catalogs](/examples/pagination). The SDKs and CLI page for you.

## 4. Select

<Warning>
  Selecting **replaces** the entire selection - it isn't additive. To add one object, send the existing selected ids plus the new one, or you'll silently deselect everything else.
</Warning>

Selecting triggers another refresh, so wait for `refresh-status` again before assuming the new objects are queryable:

<CodeGroup>
  ```python Python theme={null}
  objs = client.connections.objects(42)
  keep = [o.object_id for o in objs if o.name.startswith("dim_")]
  client.connections.select_objects(42, keep)
  client.connections.wait_for_refresh(42)
  ```

  ```typescript TypeScript theme={null}
  const objs = await client.connections.objects(42);
  const keep = objs.filter((o) => o.name.startsWith("dim_")).map((o) => o.objectId);
  await client.connections.selectObjects(42, keep);
  await client.connections.waitForRefresh(42);
  ```

  ```bash CLI theme={null}
  engini connections select-objects 42 --object 12 --object 15 --object 19
  ```

  ```bash cURL theme={null}
  curl -s -X POST "$BASE/connections/42/select-objects" -H "$AUTH" \
    -H "Content-Type: application/json" -d '{"objectIds":[12,15,19]}'
  # then poll refresh-status as in step 2
  ```
</CodeGroup>

## Adding to an existing selection

<CodeGroup>
  ```python Python theme={null}
  current = [o.object_id for o in client.connections.objects(42) if o.is_selected]
  client.connections.select_objects(42, current + [77])
  client.connections.wait_for_refresh(42)
  ```

  ```typescript TypeScript theme={null}
  const current = (await client.connections.objects(42))
    .filter((o) => o.isSelected)
    .map((o) => o.objectId);
  await client.connections.selectObjects(42, [...current, 77]);
  await client.connections.waitForRefresh(42);
  ```
</CodeGroup>

## A useful side effect

**A completed refresh is the strongest health signal a connection can give you** - stronger than `check`, which can return an inconclusive empty `200`. A refresh that finishes proves the credential actually authenticated against the provider. If you only run one verification after creating a connection, make it this one.

## The whole thing in one command

```bash theme={null}
engini connect postgres                          # walks credentials -> check -> refresh -> object selection
engini connections objects 42 --name orders      # inspect later
engini connections select-objects 42 --object 12 --object 15
```
