---
author: OOMOL
author_url: https://oomol.com/about/
title: Call Provider APIs with the SaaS Proxy
description: Use a Project API key and a connected end-user account to call
  provider APIs through the SaaS proxy.
lang: en
canonical_url: https://oomol.com/docs/saas-proxy/
markdown_url: https://oomol.com/docs/saas-proxy.md
---

# Call Provider APIs with the SaaS Proxy

Use the SaaS proxy when you need a provider endpoint beyond the packaged Connector actions. Your backend supplies the endpoint, method and parameters; Connector selects the end user’s connected account and injects its provider credentials. The provider must support proxy calls, and the account’s existing permissions still apply.

## Prepare the account

Complete the [Connector for SaaS guide](/docs/connector-saas/) first. Store the Project API key on your backend and save the verified user-to-account binding. Use the Project key shaped like oo_proj_…, not a personal OOMOL key or a provider token. Derive the user and account from your authenticated product session; do not trust arbitrary account IDs supplied by a browser.

## Call with HTTP

```http
POST https://connector.oomol.com/v1/saas/proxy/{service}
Authorization: Bearer <project-api-key>
Content-Type: application/json
```

This read-only Gmail example gets the connected account’s profile. Replace the provider config and connected account IDs with the values saved by your backend. The Gmail account needs permission to read its profile. The outer HTTP method is always POST; the upstream method is inside request.

```bash
export PROJECT_API_KEY='oo_proj_...'
export PROVIDER_CONFIG_ID='<gmail-provider-config-id>'
export CONNECTED_ACCOUNT_ID='<connected-account-id>'

curl --silent --show-error \
  'https://connector.oomol.com/v1/saas/proxy/gmail' \
  -H "Authorization: Bearer $PROJECT_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'x-request-id: req-saas-proxy-1' \
  --data @- <<JSON
{
  "providerConfigId": "$PROVIDER_CONFIG_ID",
  "userId": "customer-1",
  "connectedAccountId": "$CONNECTED_ACCOUNT_ID",
  "request": {
    "endpoint": "/users/me/profile",
    "method": "GET"
  }
}
JSON
```

## Request fields

| Field | Usage |
| --- | --- |
| `userId` | Required: your product’s end-user ID, matching the connected account. |
| `providerConfigId` | Optional, recommended: the provider config ID. Its service must match the URL. If omitted, the project must have exactly one active config for that service. |
| `connectedAccountId` / `alias` | Optional: supply at most one. If both are omitted, the latest active account for this user and provider config is selected. |
| `request.endpoint` | Required: a path beginning with /, appended to the provider base URL, or an absolute HTTPS URL on an allowed origin. Gmail’s base URL is https://gmail.googleapis.com/gmail/v1. |
| `request.method` | Required: GET, POST, PUT, PATCH or DELETE. |
| `request.query` | Optional: an object of string, number, boolean or null values. Null entries are skipped; arrays and nested objects are not accepted. |
| `request.headers` | Optional: non-authentication headers. Connector injects credentials; do not supply Authorization, Cookie, Host or X-API-Key. |
| `request.body` | Optional: JSON or text for an upstream write request. GET does not send a body. |

## Call with ProjectConnector

Use a release of @oomol-lab/connector that includes ProjectConnector.proxy. If your installed version does not expose this method, use the HTTP example. The SDK uses externalUserId and connectionName; it serializes them as userId and alias. connectedAccountId takes precedence over connectionName. The generic type describes the expected response; it does not validate provider data at runtime.

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

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

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

const result = await project.proxy<{ emailAddress: string }>(
  "customer-1",
  "gmail",
  { endpoint: "/users/me/profile", method: "GET" },
  {
    providerConfigId: process.env.PROVIDER_CONFIG_ID!,
    connectedAccountId: process.env.CONNECTED_ACCOUNT_ID!,
    retries: 0,
  },
);

if (result.status < 200 || result.status >= 300) {
  throw new Error(`Provider returned HTTP ${result.status}`);
}
console.log(result.data.emailAddress);
```

For several calls by the same user, bind the user once:

```ts
const user = project.forUser("customer-1");
const profile = await user.proxy<{ emailAddress: string }>(
  "gmail",
  { endpoint: "/users/me/profile", method: "GET" },
  {
    providerConfigId: process.env.PROVIDER_CONFIG_ID!,
    connectionName: "work",
    retries: 0,
  },
);
if (profile.status < 200 || profile.status >= 300) {
  throw new Error(`Provider returned HTTP ${profile.status}`);
}
console.log(profile.data.emailAddress);
```

## Read responses and handle errors

The following HTTP envelope is illustrative. The SDK returns its data object directly: status, headers and data. HTTP callers can store meta.executionId for troubleshooting.

```json
{
  "success": true,
  "message": "OK",
  "data": {
    "status": 200,
    "headers": { "content-type": "application/json" },
    "data": {
      "emailAddress": "user@example.com",
      "messagesTotal": 120,
      "threadsTotal": 80,
      "historyId": "12345"
    }
  },
  "meta": {
    "executionId": "exec-example",
    "service": "gmail",
    "appId": "app-example"
  }
}
```

Check both layers. A successful Connector envelope can contain a non-2xx upstream status in data.status; with the SDK, check result.status. Inspect the provider’s response body as well. Connector failures throw ConnectorError in the SDK. An invalid Project API key can return HTTP 401 with plain text Unauthorized, so HTTP callers must not assume every error body is JSON.

## Limits and troubleshooting

- The proxy returns parsed JSON or text, not a streaming or binary transfer interface. The default upstream timeout is 30 seconds and the default response limit is 4 MiB; providers can override these limits.

- Proxy does not add OAuth permissions. If a provider rejects a call for missing scopes, review the provider config and have the user authorize the required permissions again. Only provider-approved origins are accepted; this is not a proxy for arbitrary URLs.

- Use provider_config_not_found or multiple_provider_configs_found to diagnose config selection, and proxy_not_supported to identify providers without proxy support. For proxy_upstream_timeout or proxy_response_too_large, reduce the request or response size where possible.

- A timeout does not prove that an upstream write failed. Disable automatic retries for operations that may create duplicate effects; the SDK examples set retries to zero. Record the request ID and HTTP meta.executionId when present, and check the project’s Execution logs in OOMOL Console.

Continue with [ProjectConnector account integration](/docs/project-connector/).
