---
title: Connect end-user accounts with ProjectConnector
description: Use ProjectConnector in a TypeScript backend to connect accounts
  for SaaS users and execute actions on their behalf.
lang: en
canonical_url: https://oomol.com/en/docs/project-connector/
markdown_url: https://oomol.com/en/docs/project-connector.md
---

# Connect end-user accounts with ProjectConnector

Use `ProjectConnector` when every user of your product needs to connect their own provider account. Your backend identifies each user with `externalUserId`, creates authorization links, stores connected account IDs, and executes actions for that user.

This path uses a project API key shaped like `oo_proj_…`. It is separate from the personal `api_…` key used by `Connector`.

## Prepare the project

Before writing the runtime flow, create these resources in OOMOL Console:

1. A Connector project.
2. A provider config for each service users can connect.
3. A project API key stored in your backend secret manager.

The [Connector for SaaS guide](/en/docs/connector-saas/) covers Console setup and the matching REST requests.

## Install and initialize

```sh
npm install @oomol-lab/connector
```

```ts
import { ProjectConnector } from "@oomol-lab/connector";

const project = new ProjectConnector({
  apiKey: process.env.OOMOL_PROJECT_API_KEY!,
});
```

## Create an OAuth authorization request

Use a stable ID from your own user database as `externalUserId`:

```ts
const request = await project.connect.oauth("user_42", {
  service: "gmail",
  connectionName: "work",
  returnUri: "https://app.example.com/connected",
});

redirectUserTo(request.authorizationUrl);
```

After the user authorizes, wait for the request to reach a final state:

```ts
const connected = await project.waitForConnection(request);

if (connected.status === "connected") {
  saveConnectedAccountId(connected.connectedAccountId);
}
```

`project.connect.oauth` returns an authorization request that waits for the user. Only after authorization succeeds does `waitForConnection` return a `connectedAccountId` in the request result.

For API-key and custom-credential providers, use `connect.apiKey` or `connect.customCredential`. These methods validate the credential and return a connected account synchronously.

## Execute for one user

```ts
const result = await project.execute(
  "user_42",
  "gmail.search_threads",
  { query: "is:unread" },
  { connectedAccountId: "ca-1" },
);
```

Pass `connectedAccountId` when you have it. It selects one specific account and avoids relying on the latest active account. `connectionName` is available when your product uses stable aliases instead.

Bind the user once with `forUser` when several operations belong to the same request or job:

```ts
const user = project.forUser("user_42");
await user.execute("slack.post_message", {
  channel: "#general",
  text: "shipped",
});
```

## Keep product boundaries explicit

Your product authenticates its own users and controls which providers and actions they can use. Keep the project API key on the backend, pass a consistent `externalUserId`, and store the returned account selector beside the corresponding product user.

See the [TypeScript SDK reference](/en/docs/connector-sdk/) for authorization-request and connected-account lifecycle fields, precise action types, errors, retries, waiting options, and the complete `ProjectConnector` API.
