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

# Business applications

> Connect Salesforce, NetSuite, ServiceNow, Monday, PostHog and Priority ERP so the agent can query them as tables

These connectors reach SaaS applications through their own APIs and present them to the agent as **tables**. The agent writes queries in each platform's native language — SOQL for Salesforce, SuiteQL for NetSuite, a Table API query spec for ServiceNow, a board query spec for monday.com, HogQL for PostHog — and Bag of words handles discovery, paging and row limits.

| Connector | Query surface | Auth | Edition |
| :- | :- | :- | :- |
| [Salesforce](#salesforce) | SOQL over the REST API | Connected App (JWT bearer), or username/password | |
| [NetSuite](#netsuite) | SuiteQL over the REST API | Token-Based Authentication | |
| [ServiceNow](#servicenow) | Table API query specs | Username/password, or per-user OAuth | Beta |
| [Monday data](#monday-data) | Board query specs over the GraphQL API | API token, or per-user OAuth | Beta |
| [PostHog](#posthog) | HogQL | Personal API key | Beta |
| [Priority ERP](#priority-erp) | OData v4 over the REST API | Personal Access Token, API user, or per-user OAuth (on-prem) | Beta |

***

## Salesforce

Bag of words enumerates the org's queryable objects — standard and custom `__c` — filtering out system and plumbing objects (sharing rows, field history, change events, Chatter feeds). Reference fields become foreign keys. Discovery is capped at 500 objects, with common CRM objects (Account, Contact, Lead, Opportunity, Case, and so on) prioritized so they always survive the cap. A single query returns at most 10,000 rows.

### Before you start (JWT bearer)

The recommended path is a **Connected App** using the OAuth 2.0 JWT bearer flow: a certificate signs a short-lived assertion that is exchanged for an access token. No interactive login, no stored password.

<Steps>
  <Step title="Generate a key pair">
    Create an RSA private key and a matching X.509 certificate. Keep the private key (PEM) — you will paste it into Bag of words.
  </Step>

  <Step title="Create the Connected App">
    In Salesforce **Setup → App Manager → New Connected App**, enable OAuth settings, tick **Use digital signatures**, and upload the certificate.
  </Step>

  <Step title="Copy the Consumer Key">
    From the Connected App's detail page, copy the **Consumer Key** — this is the OAuth `client_id`.
  </Step>

  <Step title="Pre-authorize the service account user">
    Set the Connected App's OAuth policy to **Admin approved users are pre-authorized**, then grant the profile or permission set of the Salesforce user Bag of words will authenticate as.

    <Warning>
      The JWT flow fails if the app is not admin pre-authorized for that specific user. This is the most common cause of an authentication error on first connect.
    </Warning>
  </Step>
</Steps>

### Fields

| Field | Required | Default | Notes |
| :- | :- | :- | :- |
| `sandbox` | No | `false` | Authenticate against `test.salesforce.com`. |
| `domain` | No | `login` | Login domain: `login` (production), `test` (sandbox), or a My Domain subdomain. |
| `objects` | No | — | Comma-separated objects to index, e.g. `Account,Contact,MyObj__c`. Blank auto-discovers. |

### Authentication modes

Both modes are **system**-scope — one shared credential for the whole connection.

**Connected App (JWT Bearer) — `jwt`** (the default)

| Field | Required | Notes |
| :- | :- | :- |
| `consumer_key` | Yes | The Connected App's Consumer Key (OAuth `client_id`). |
| `private_key` | Yes | RSA private key in PEM form, matching the certificate uploaded to the Connected App. |
| `username` | Yes | The Salesforce username to authenticate as — the JWT subject. Must be pre-authorized for the app. |

**Username / Password — `userpass`** (legacy SOAP login)

| Field | Required | Notes |
| :- | :- | :- |
| `username` | Yes | Salesforce username. |
| `password` | Yes | Salesforce password. |
| `security_token` | Yes | The user's security token, appended to the password by the login API. Reset it from **Settings → My Personal Information → Reset My Security Token**. |

***

## NetSuite

Bag of words queries NetSuite with **SuiteQL** over the REST API (`https://<account>.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql`), signing each request with OAuth 1.0a HMAC-SHA256 — NetSuite's Token-Based Authentication (TBA). Results are paged automatically.

### Before you start

<Steps>
  <Step title="Enable the features">
    In NetSuite **Setup → Company → Enable Features → SuiteCloud**, enable **REST Web Services** and **Token-Based Authentication**.
  </Step>

  <Step title="Create an integration record">
    **Setup → Integration → Manage Integrations → New**. Enable **Token-Based Authentication**. On save, NetSuite shows the **Consumer Key** and **Consumer Secret** once — copy both.
  </Step>

  <Step title="Create an access token">
    **Setup → Users/Roles → Access Tokens → New**, choosing the integration record, the user, and a role with permission to run SuiteQL against the records you want. NetSuite shows the **Token ID** and **Token Secret** once — copy both.
  </Step>
</Steps>

<Warning>
  The consumer secret and token secret are displayed exactly once. If you lose them you must create a new integration record or access token.
</Warning>

### Fields

| Field | Required | Default | Notes |
| :- | :- | :- | :- |
| `table_filter` | No | — | Comma-separated table names to include in schema discovery. Blank discovers all tables. |

### Authentication

**Token-Based Auth — `token`** (system scope, the only mode)

| Field | Required | Notes |
| :- | :- | :- |
| `account_id` | Yes | NetSuite account id, e.g. `1234567` or `1234567_SB1`. Used for both the REST host and the OAuth realm. |
| `consumer_key` | Yes | From the integration record. |
| `consumer_secret` | Yes | From the integration record. |
| `token_id` | Yes | From the access token. |
| `token_secret` | Yes | From the access token. |

***

## ServiceNow

<Note>
  The ServiceNow connector is in **beta**.
</Note>

Bag of words queries the ServiceNow **Table API** (`/api/now/table/{table}`). Schema discovery reads ServiceNow's own metadata tables in bulk — `sys_db_object` for tables and inheritance, `sys_dictionary` for fields — so a full snapshot takes a handful of requests rather than one per table. Reference fields become foreign keys.

### Fields

| Field | Required | Default | Notes |
| :- | :- | :- | :- |
| `instance_url` | Yes | — | Your instance URL, e.g. `https://acme.service-now.com`. |
| `tables` | No | — | Comma-separated tables to expose. Blank uses a curated set of common ITSM tables (incident, change\_request, problem, task, and so on). |
| `discover_all` | No | `false` | Discover all business tables, including custom `u_`/`x_` tables, instead of the curated set. |
| `display_values` | No | `true` | Return human-readable display values for reference and choice fields. |

### Authentication modes

**Username / Password — `userpass`** (scopes: system and user)

| Field | Required | Notes |
| :- | :- | :- |
| `username` | Yes | A ServiceNow user with read access to the target tables. |
| `password` | Yes | That user's password. |
| `oauth_client_id` | No | Client ID of an OAuth app registered in the instance (**System OAuth → Application Registry**). Setting it enables per-user sign-in. |
| `oauth_client_secret` | No | Client secret of that OAuth app. Leave blank if the app is registered as a public client — it then authenticates with PKCE only. |

**Sign in with ServiceNow — `oauth`** (scope: user, no fields). Each user signs in against the instance's own endpoints (`/oauth_auth.do`, `/oauth_token.do`) with the `useraccount` scope, so queries run with that person's own ServiceNow permissions. This requires the `oauth_client_id` above to be configured on the connection.

<Note>
  Per-user authentication on a database-style connection requires an Enterprise license. See [Authentication and access](/data-sources/authentication).
</Note>

Register the OAuth app's redirect URI in ServiceNow as:

```text theme={null}
https://<your-bow-host>/api/connections/oauth/callback
```

***

## Monday data

<Note>
  The Monday data connector is in **beta**.
</Note>

Bag of words queries the monday.com **GraphQL API** and presents each **board as a table**: board columns become schema columns (named by their titles, with status and dropdown labels surfaced to the agent), items become rows, and connect-boards columns become foreign keys to the linked board. Duplicate board names are disambiguated with the board id.

The agent sends a JSON query spec — board, columns, filter rules, ordering, limit — which Bag of words translates into `items_page` calls with cursor pagination. A single query returns at most 10,000 rows. Filters on status and dropdown columns accept the human-readable label (for example `"Done"`); the connector translates it to the label index that monday's API actually matches on. Values come back typed: numbers as floats, checkboxes as booleans, ratings as integers, everything else as display text. Every result also carries three built-in columns — `item_id`, `name` (the item name) and `group` — regardless of which columns the query selects.

<Note>
  This is separate from the **Monday MCP preset**, which exposes monday.com as callable tools (create an item, post an update). Use this connector when you want boards indexed and queryable as data; the two can coexist.
</Note>

### Fields

| Field | Required | Default | Notes |
| :- | :- | :- | :- |
| `workspaces` | No | — | Comma-separated workspace names or ids to index. Blank indexes all workspaces the token can see. The account subdomain (`acme` in `acme.monday.com`) is **not** a workspace name — use the workspace names from monday's left sidebar, or leave blank. A wrong value here filters out every board, so discovery reports 0 tables. |
| `boards` | No | — | Comma-separated board names or ids to index. Blank indexes every visible board. |

### Authentication modes

**API Token — `api_token`** (scopes: system and user)

| Field | Required | Notes |
| :- | :- | :- |
| `api_token` | Yes | A monday.com API token (avatar → **Developers** → **My access tokens**). Use a dedicated service user's token so shared queries don't depend on one person's account. |
| `oauth_client_id` | No | Client ID of an app created in monday.com's **Developer Center**. Setting it enables per-user sign-in. |
| `oauth_client_secret` | No | Client secret of that app. |

**Sign in with monday.com — `oauth`** (scope: user, no fields). Each user signs in against `auth.monday.com` with read-only scopes (`me:read boards:read workspaces:read users:read account:read`) — enable exactly these on the app in monday's Developer Center, since monday rejects the authorize request if any requested scope is missing. Queries run with that person's own board permissions. This requires the `oauth_client_id` above to be configured on the connection. monday access tokens do not expire and no refresh token is issued.

<Note>
  Per-user authentication on a database-style connection requires an Enterprise license. See [Authentication and access](/data-sources/authentication).
</Note>

Register the OAuth app's redirect URL in monday.com's Developer Center as:

```text theme={null}
https://<your-bow-host>/api/connections/oauth/callback
```

<Warning>
  Trial and free monday.com accounts have small API rate and daily budgets. Indexing very large accounts or running heavy backfills can exhaust the daily budget, after which the API returns 429 for every request until it resets. The connector absorbs transient throttling automatically.
</Warning>

***

## PostHog

<Note>
  The PostHog connector is in **beta**.
</Note>

Bag of words queries PostHog with **HogQL** against a fixed catalog that mirrors PostHog's data model — `events`, `persons`, sessions, and related tables — rather than introspecting the instance.

### Fields

| Field | Required | Default | Notes |
| :- | :- | :- | :- |
| `host` | No | `https://us.posthog.com` | Instance URL: `https://us.posthog.com`, `https://eu.posthog.com`, or your self-hosted address. |
| `project_id` | Yes | — | PostHog Project ID, found in the project's settings. |

### Authentication

**Personal API Key — `api_key`** (scopes: system and user)

| Field | Required | Notes |
| :- | :- | :- |
| `api_key` | Yes | A PostHog **Personal API Key** with the `query:read` and `project:read` scopes. Create it under your PostHog user settings. |

Because this mode is available at user scope as well as system scope, each user can supply their own personal API key instead of sharing the admin's. Per-user credentials on a database-style connection require an Enterprise license — see [Authentication and access](/data-sources/authentication).

***

## Priority ERP

<Note>
  The Priority ERP connector is in **beta**.
</Note>

Bag of words queries Priority Software's **OData v4** REST API, and works against both **Priority Cloud** and **on-premise** installations — the OData surface ships with the on-prem application server, so only the service root and the TLS posture differ.

**Priority forms are tables.** Each form is an OData entity set and each form column a property, so `ORDERS`, `CUSTOMERS` and `PART` become queryable tables. The whole tenant schema comes back from one `$metadata` request rather than a per-object crawl.

Column names alone would be unreadable, so the catalog uses Priority's own business vocabulary: the human label of each column comes from its `Priority.OData.Description` annotation. Those titles follow the environment language — a Hebrew tenant returns Hebrew titles — so form **names** are always the identifiers and titles are only ever descriptions.

### Fields

| Field | Required | Default | Notes |
| :- | :- | :- | :- |
| `service_root` | Yes | — | Full Priority OData URL, e.g. `https://priority.example.com/odata/Priority/tabula.ini/mycompany`. The host, `tabula.ini` file and company all vary per installation. |
| `forms` | No | — | Comma-separated forms to expose, e.g. `ORDERS, CUSTOMERS, PART`. Blank uses a curated set of common ERP forms. |
| `discover_all` | No | `false` | Index every form in `$metadata`, including customer-specific ones, instead of the curated set. |
| `verify_ssl` | No | `true` | Verify the server's TLS certificate. Disable only for self-signed certs on internal hosts. |
| `max_calls_per_minute` | No | `100` | Client-side throttle. Priority Cloud allows 100 calls/minute per user and 15 concurrent requests; on-premise has no documented ceiling, so raise it there, or set `0` to disable throttling. |

<Note>
  Custom fields appear only once REST metadata has been rebuilt for that form inside Priority. If a column you expect is missing, rebuild the form's metadata in Priority and re-index the connection.
</Note>

### Authentication modes

**Personal Access Token — `pat`** (the default; scopes: system and user)

| Field | Required | Notes |
| :- | :- | :- |
| `pat` | Yes | A token from **System Management → System Maintenance → Users → REST Interface Access Tokens**. Sent as the Basic-auth username with the literal password `PAT`. |
| `oauth_client_id` | No | On-premise only. The **Application ID** of an app registered under **Manage IDs Externally → External Applications**. Setting it enables per-user sign-in. |

This is Priority's own recommendation for server-to-server clients, and the only mode available in **both** Cloud and on-premise. At user scope it is bring-your-own-token: in Priority Cloud there is no OAuth, so it is the only per-user path.

**API Username / Password — `basic`** (scopes: system and user)

| Field | Required | Notes |
| :- | :- | :- |
| `username` | Yes | The value of the **API User Name** field in the user's Personnel File — not their normal Priority login. |
| `password` | Yes | That API user's password. |

<Warning>
  Priority rejects Basic authentication entirely while External ID access is enabled on the installation. Use the PAT mode there.
</Warning>

**Sign in with Priority (on-prem) — `oauth`** (scope: user, no fields). Per-user delegated OAuth, **on-premise only** — Priority scopes its OAuth 2.0 guide to non-SaaS installations, and it requires the paid External ID module plus an external identity provider. Endpoints are derived per-tenant from the service root. This requires the `oauth_client_id` above to be configured on the connection.

<Note>
  Per-user authentication on a database-style connection requires an Enterprise license. See [Authentication and access](/data-sources/authentication).
</Note>

Register the OAuth app's redirect URI in Priority as:

```text theme={null}
https://<your-bow-host>/api/connections/oauth/callback
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.