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

# Providers

> Query any connected tool at providers.{connector}.{table} and join connectors in SQL.

The **providers** pool is the data from every tool you connected. Each
connector is a schema. Each [tap](/connectors/taps) is a table. You
query them with ordinary SQL, over the same Flight SQL connection, and
you can join any of those tables to each other.

Google Workspace directory users are `providers.google.users`. GitHub
pull requests are `providers.github.pull_requests`. Linear issues are
`providers.linear.issues`. A person who exists in more than one of those
tools is joinable on email, or through `providers.identity.*` when the
emails do not match.

A tap lands as one or more tables in that connector's schema. Nested
object arrays become their own tables. Column names are the tap schema
names, lowercased (`primaryEmail` -> `primaryemail`). Nested objects
that flatten use `__` between segments (`assignee.email` ->
`assignee__email`). See
[How taps project into tables](/connectors/tap-projection)
for dedup keys, ordering keys, and identity flags.

<Note>
  A table appears only if that connector is configured in your workspace
  and the tap has synced. The examples below use common connectors.
  Swap slugs and column names for what
  [the catalog](/data#the-catalog) lists for you.
</Note>

## Address a table

```
providers.{connector}.{table}
```

`{connector}` is the connector slug (`google`, `github`, `linear`,
`slack`, `okta`, `workday`, `salesforce`). `{table}` is the tap name,
lowercased. Child tables from exploded arrays are `{tap}__{path}`:
GitHub issue labels are `providers.github.issues__labels`.

References are fully qualified. There is no `USE` or `search_path`.

Which column is unique, which version of a row wins, and which fields
identify a person come from flags on the tap schema. You inspect those
flags; you do not set them.

## See what you have

List tables, columns, and row counts from the
[catalog](/data#the-catalog) before you assume a table
has rows. A table can be registered before the first sync writes data.

Connected tools and enabled taps are also live in the
[workspace](/data/workspace) pool:

```sql theme={null}
SELECT *
FROM workspace.providers
```

```sql theme={null}
SELECT *
FROM workspace.provider_connections
```

Language-specific Flight SQL clients are on
[Querying the lake](/data#querying-the-lake).

## Query one connector

Active Google Workspace users, with org unit and last login:

```sql theme={null}
SELECT
  id,
  primaryemail,
  orgunitpath,
  isadmin,
  lastlogintime
FROM providers.google.users
WHERE suspended = false
LIMIT 10
```

Open Linear issues, most recently updated first:

```sql theme={null}
SELECT
  identifier,
  title,
  priority,
  team__key,
  assignee__email,
  state__name,
  updatedat
FROM providers.linear.issues
WHERE completedat IS NULL
  AND canceledat IS NULL
ORDER BY updatedat DESC
LIMIT 20
```

Open GitHub pull requests in one repo. `full_name` is copied onto the
PR so you do not have to join `repositories`:

```sql theme={null}
SELECT
  number,
  title,
  state,
  user__login,
  draft,
  created_at,
  html_url
FROM providers.github.pull_requests
WHERE full_name = 'your-org/your-repo'
  AND state = 'open'
ORDER BY created_at DESC
```

## Combine connectors on email

Person-email columns (`x-transformPersonEmail` on the tap) are safe to
join to each other and to `workspace.users.email`. Linear `email` and
Google `primaryemail` are the same kind of value:

```sql theme={null}
SELECT
  lu.email,
  lu.name AS linear_name,
  lu.active AS linear_active,
  gu.orgunitpath,
  gu.isadmin,
  gu.lastlogintime
FROM providers.linear.users AS lu
JOIN providers.google.users AS gu
  ON gu.primaryemail = lu.email
```

The same join against workspace members:

```sql theme={null}
SELECT
  u.email,
  u.name,
  gu.orgunitpath,
  gu.isadmin
FROM workspace.users AS u
JOIN providers.google.users AS gu
  ON gu.primaryemail = u.email
```

People who left in Workday but still have a Google account:

```sql theme={null}
SELECT
  w.email,
  w.businesstitle,
  w.terminationdate,
  gu.id AS google_id,
  gu.suspended,
  gu.isadmin,
  gu.orgunitpath
FROM providers.workday.workers AS w
JOIN providers.google.users AS gu
  ON gu.primaryemail = w.email
WHERE w.isactive = false
```

Open Salesforce opportunities whose owner is suspended in Google:

```sql theme={null}
SELECT
  o.name AS opportunity,
  o.amount,
  o.stagename,
  o.closedate,
  su.name AS owner_name,
  su.email AS owner_email,
  gu.suspended,
  gu.orgunitpath
FROM providers.salesforce.opportunities AS o
JOIN providers.salesforce.users AS su
  ON su.id = o.ownerid
JOIN providers.google.users AS gu
  ON gu.primaryemail = su.email
WHERE o.isclosed = false
  AND gu.suspended = true
```

## Combine connectors when emails differ

GitHub org members have a `login` and an `id`, not an email. Slack and
Okta store email on a nested profile that may not flatten. Do not join
two providers' `id` columns to each other and expect a person match.

Taps with `identityDirectory: true` (Google `users`, Linear `users`,
GitHub `members`, Slack `users`, Workday `workers`, and others) feed
`providers.identity.*`. That family appears after at least one
directory tap has synced.

| Table                                     | Grain                                                                                             |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `providers.identity.account`              | One row per resolved person (`id`, `name`).                                                       |
| `providers.identity.account_associations` | One row per `(connector, tap, account_id)`. Maps a provider-native id onto `identity_account_id`. |
| `providers.identity.account_merges`       | Survivor / merged id + `occurred_at`.                                                             |

`account_id` is the provider-native principal (Google `users.id`, GitHub
`members.id`, Linear `users.id`). `account_id_type` is `id`, `uuid`, or
`email`. `match_method` records how the link was made (`canonical`,
`email_exact`, `email_local_part`, `email_fuzzy`, `name_fuzzy`).
Identity is best-effort: service accounts and shared mailboxes get
identities too.

Every account linked to a resolved person:

```sql theme={null}
SELECT
  a.id AS identity_id,
  a.name,
  assoc.account_id,
  assoc.account_id_type,
  assoc.match_method,
  assoc.match_confidence
FROM providers.identity.account AS a
JOIN providers.identity.account_associations AS assoc
  ON assoc.identity_account_id = a.id
ORDER BY a.name, assoc.match_method
```

Google directory row plus every other account for that person, including
GitHub members that have no email:

```sql theme={null}
SELECT
  a.name AS identity_name,
  gu.primaryemail,
  gu.orgunitpath,
  assoc.account_id,
  assoc.account_id_type,
  assoc.match_method
FROM providers.google.users AS gu
JOIN providers.identity.account_associations AS g_assoc
  ON g_assoc.account_id = gu.id
JOIN providers.identity.account AS a
  ON a.id = g_assoc.identity_account_id
JOIN providers.identity.account_associations AS assoc
  ON assoc.identity_account_id = a.id
```

People linked to more than one account. Start here when you want to see
whether identity coverage is dense enough to join two specific tools:

```sql theme={null}
SELECT
  a.id,
  a.name,
  COUNT(*) AS linked_accounts
FROM providers.identity.account AS a
JOIN providers.identity.account_associations AS assoc
  ON assoc.identity_account_id = a.id
GROUP BY a.id, a.name
HAVING COUNT(*) > 1
ORDER BY linked_accounts DESC
```

Full column notes for the identity family are on
[How taps project into tables](/connectors/tap-projection).

## Join work items, not just people

Linear issues carry `branchname`. GitHub pull requests carry the head
branch on the flattened `head` object. That join does not need email or
identity:

```sql theme={null}
SELECT
  i.identifier,
  i.title AS issue_title,
  i.state__name AS issue_state,
  i.assignee__email,
  pr.full_name,
  pr.number AS pr_number,
  pr.state AS pr_state,
  pr.html_url
FROM providers.linear.issues AS i
JOIN providers.github.pull_requests AS pr
  ON pr.head__ref = i.branchname
WHERE i.branchname IS NOT NULL
```

Google group members who are also directory users, with org unit and
admin flag:

```sql theme={null}
SELECT
  gm.email,
  gm.role,
  gm.status,
  gu.orgunitpath,
  gu.isadmin,
  gu.suspended
FROM providers.google.group_members AS gm
JOIN providers.google.users AS gu
  ON gu.primaryemail = gm.email
WHERE gm.role = 'OWNER'
```

`providers.google.groups` is the group list (`name`, `email`,
`directmemberscount`). Confirm in the catalog how members link back to
a group before you join those two tables; the member tap's documented
columns are the member fields, not the parent group id.

## Child tables

Nested object arrays explode to a second table, `{tap}__{path}`, with
the parent id repeated. GitHub issue labels:

```sql theme={null}
SELECT *
FROM providers.github.issues__labels
LIMIT 10
```

Google `users` does the same for phones, emails, and external ids
(`providers.google.users__phones`, and similar). Child tables have their
own dedup keys. Do not `SELECT DISTINCT id` on a child table to "clean
it up"; the parent id is supposed to repeat. Details are on
[How taps project into tables](/connectors/tap-projection).

## Quality and other pools

SPC, Gate 2 checks, and table stats for provider tables live in
[Quality](/data/quality)
(`providers_quality.google.users__checks`, and the
`providers_quality.summary.*` tables). Identity tables do not get
quality sidecars.

A Plot can read providers and write to
[parables](/data/parables). Published uploads in
[artifacts](/data/artifacts) join the same way as any
other table -- typically on a person-email column.
