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

# Authenticate to the Workspace API

> Send a Workspace API token on every request, then verify it and list Provider connections.

Every Workspace API request sends a bearer token in the `Authorization`
header, and Workspace-scoped requests name the Workspace in the `X-Tenant`
header. The token is a long-lived `pat_` credential issued for one person
that acts with its owner's current permissions on every request.

```bash theme={null}
curl "$PARABLE_API_URL/api/users/me" \
  -H "Authorization: Bearer pat_..." \
  -H "X-Tenant: acme-corp"
```

This page is for Workspace integrators.

## Availability

Parable gives you the Workspace API address. Examples on these pages write
it as `$PARABLE_API_URL`. `$PARABLE_WORKSPACE` is your Workspace's slug, the
name in its address.

The address `https://api.parable.work` serves the Parable app, not the
Workspace API. Requests to it redirect to sign-in or return an error.

The one exception is the Parable Pulse Mac app. Pulse calls a few Workspace
API operations on the Parable app address, to sign in and to enroll the Mac.
Those operations are for Pulse only. They are not a general API.

SQL over Flight has its own address: `sql.parable.work` in production and
`sql.staging.parable.work` in staging, on port 443 with TLS. See
[Connect a SQL tool](#connect-a-sql-tool).

Some tasks have no public operation yet:

* Connect a Provider that signs in with OAuth from **Providers** in the
  Workspace app.
* Contact your Parable representative to change which data streams a
  connection syncs.

## API tokens

Tokens start with `pat_`. There are three kinds.

| Kind | Who creates it | Reaches | Works on |
| - | - | - | - |
| SQL token | A Workspace member whose role can query the Provider Pools (`tenant.data.query.providers`), in **Protection → Access Tokens** | The Workspace it was created in | [Flight SQL](/protocols/sql/flight-sql.md) only |
| MCP token | The same member, on the same page | The Workspace it was created in | The Workspace API's read-only operations only |
| Parable token | Parable, for a person | Every Workspace its owner can reach when the request is made | Every API its owner can call: the Workspace API and Flight SQL |

A member chooses SQL or MCP when creating a token. A SQL token is for a SQL
tool. An MCP token is for an MCP host such as Claude Code: it reads the
Workspace and changes nothing.

The kind is the token's audience, which the Workspace API reads on every
request:

| Audience | Kind | Accepted by |
| - | - | - |
| `flight_sql` | SQL token | Flight SQL |
| `mcp` | MCP token | The Workspace API's read-only operations, and through them the [MCP endpoint](/protocols/mcp.md#streamable-http) |
| `all` | Parable token | The Workspace API, Flight SQL, and for Parable staff the Admin API |

The MCP endpoint takes an MCP token and nothing else. The Workspace API
reports the audience of the token that authenticated a request on
`auth/me`, and the endpoint refuses every other credential with `403`.

Every token is:

* **Issued per user.** Actions run as that person, with the roles they hold
  when each request is made. Removing their role or membership, or
  deactivating them, refuses the token's next request.
* **No sessions.** A token cannot create or join a Workspace, start a desktop
  sign-in, or create another token. Each would leave a
  credential that outlives the token's revocation.
* **Shown once.** Parable stores a SHA-256 hash. A lost token cannot be
  recovered; issue a replacement.
* **Revocable.** Revoking one token does not sign the person out of the app.

A Parable token also:

* **Acts as its owner.** It stores no Workspaces and no permissions. On every
  request it gets exactly what its owner's signed-in session gets: name a
  Workspace in the `X-Tenant` header and it holds the owner's roles there;
  name none and it reaches what the owner reaches outside a Workspace, such as
  `GET /api/auth/me`. A change to a person's role changes what every token
  they own can do.

A SQL token works on SQL only: every Workspace API route refuses it with
`403`.

An MCP token works on read-only Workspace API operations only:

* An operation that changes something refuses it with `403` and the code
  `WA-PAT-005`, whatever its owner's roles allow. That includes creating or
  revoking a token and signing out.
* Flight SQL refuses it with `PERMISSION_DENIED`. Use a SQL token there.
* Each request it makes is recorded against its owner, with the client's
  name when the request sends one in `X-Parable-Client`.
* **The Workspace must allow external agents.** An MCP token is for an agent
  outside Parable, and such an agent sends what it reads to its own model
  provider, which the Workspace's AI processor settings do not cover. So the
  Workspace decides: **Allow external agents**, in **Protection → Access
  Tokens**, is off until someone who manages sign-in (`tenant.auth.manage`)
  turns it on. While it is off, nobody can create an MCP token, and one that
  exists is refused on its next request with `403`, the code `WA-PAT-006` and
  a message that names the setting. Turning it on again lets unexpired,
  unrevoked tokens work. SQL tokens are not affected.

A SQL token and an MCP token also:

* **Expire.** Each lasts 90 days unless its creator chooses 1 to 365 days.
* **Follow their owner's access.** Each request runs with the owner's
  Workspace roles at that moment, under the [Provider Policies](/protection/provider-policies.md)
  those roles fall under. A staff role never counts. After the owner
  loses a role, the Workspace API refuses the token on the next request that
  role allowed. After the owner loses the membership or is deactivated, it
  refuses every request. Flight SQL caches a successful check for 30
  seconds, so a SQL token can run queries for up to 30 seconds more.
* **Stop when revoked, within the same bound.** The Workspace API refuses a
  revoked token on its next request, and Flight SQL within 30 seconds:
  measured on a local stack, a revoked SQL token ran its last query 30.0 to
  30.2 seconds after its last uncached check. Through the MCP endpoint a
  revoked MCP token's next tool call is refused; the list of tools the
  endpoint offers can be up to 5 seconds old.
* **Are visible to administrators.** People who manage the Workspace's members
  (`tenant.user.manage`) see and revoke every member's token.
* **Can be turned off.** A token does not pass through the Workspace's
  sign-in method, so people who manage sign-in (`tenant.auth.manage`) can turn
  tokens off in **Protection → Access Tokens**. The Workspace API then
  refuses every MCP token for the Workspace, Flight SQL refuses every token
  for it, Parable tokens included, and nobody can create one. This is
  deliberate: turning tokens off is the Workspace's own security control, so
  it holds even for Parable staff whose signed-in session still reaches the
  Workspace. Tokens are allowed until someone turns them off.

Signing in to the app creates a short-lived browser session. Use a token for
scripts, SDKs, and BI tools. Use the app for interactive work.

Store tokens in a secret manager. Issue one token per integration. Revoke a
token when a teammate leaves, an integration retires, or the value leaks.

| Status | Meaning |
| - | - |
| `401 Unauthorized` | Missing, malformed, revoked, or expired token, its owner was deactivated, or an MCP token has no Workspace scope. |
| `403 Forbidden` | The token is valid, but an MCP token request names no Workspace or one outside its scope, the person's roles do not cover the operation, the operation would start a session or create a token, it is a SQL token on a Workspace API route, it is an MCP token on an operation that changes something, the Workspace has turned tokens off, or it is an MCP token and the Workspace has not allowed external agents. |

Language clients send the same header. See the [TypeScript](/protocols/sdk/typescript.md),
[Python](/protocols/sdk/python.md), [Go](/protocols/sdk/go.md), and
[Rust](/protocols/sdk/rust.md) primers.

## Sign in from an MCP host

An MCP host such as Claude Code, the [claude.ai](https://claude.ai) and
Claude Desktop connectors can sign in to the [MCP endpoint](/protocols/mcp.md)
with OAuth instead of a pasted token. The Workspace API is the OAuth 2.1
authorization server; the MCP endpoint's protected-resource metadata names
it, so a host that follows the MCP authorization specification finds it on
its own.

| What | Value |
| - | - |
| Issuer | `https://mcp.parable.work`, the host of the MCP endpoint |
| Metadata | `/.well-known/oauth-authorization-server` at the issuer |
| Endpoints | `/oauth/authorize`, `/oauth/token`, `/oauth/revoke`, `/oauth/register`, `/oauth/jwks` at the issuer |
| Client registration | A Client ID Metadata Document (an HTTPS URL as `client_id`, which Claude Code uses), or dynamic registration of a native public client. A registration without `application_type`, as the Python MCP SDK sends it, counts as native when every redirect URI is an `http` loopback one; with an HTTPS redirect URI it must say `native`, and `web` is refused. `client_name` is optional for a registration, which the Python MCP SDK also leaves out: the consent page then says it gave no name. The consent page names every client by where the code goes: the redirect host, or an application on this computer for a loopback redirect, with the client's name only as its own claim, and it warns when the redirect is not on this computer. A metadata document must be served as `application/json` (or a `+json` type) and must name itself. Control and invisible format characters, such as a zero width space, are removed from `client_name` before it is shown or stored |
| Authorization | Code with PKCE `S256` only; `resource` required, naming the MCP endpoint; `state` returned with `iss`. A `state` over 512 characters is refused in the browser with `invalid_request`, because an error redirect must carry it back |
| Redirect URIs | Matched exactly against the client's registration, except that a registered `http://127.0.0.1/…`, `http://[::1]/…` or `http://localhost/…`, with or without a port, matches any port on the same host, path (as written, so a percent-encoded path differs) and query (OAuth 2.1 section 8.4.2, RFC 8252 section 7.3): a native client takes an ephemeral port at request time. The specifications name only the IP literals, but Claude Code registers `http://localhost/callback` and signs in from `http://localhost:{port}/callback`. HTTPS URIs stay exact |
| Scopes | `data:read` |
| Authorization code | A single-use secret (`pac_` and 32 random bytes), five minutes; the server keeps its hash, and a code presented twice revokes the grant |
| Access token | One hour |
| Refresh token | Rotated on every refresh; a token used twice revokes the grant; unused for 30 days, it expires |

The flow, from the person's side:

1. The host opens the browser at the authorization endpoint. The Workspace
   app asks the person to sign in, when they are not, and shows a consent
   page naming the client and where it redirects, with a choice of Workspace:
   only Workspaces the person belongs to that **Allow external agents**
   are offered.
2. The person allows, and the host receives a code it exchanges for tokens.
3. Every call the host makes runs as the person, in that Workspace, with
   the person's roles at that moment, on the operations the `data:read`
   scope covers: the same reads an MCP token reaches.
4. Clearing the host's authentication revokes the grant: in Claude Code,
   `claude mcp logout parable`. Revocation, losing the membership or the
   role, and turning **Allow external agents** off are each refused on the
   next tool call. For up to 5 seconds the MCP server can still admit the
   access token and list the tools it offered before.

On a machine without a browser, such as an SSH session,
`claude mcp login parable --no-browser` prints the authorization URL instead
of opening it. Open it in a browser, allow, and paste the address the browser
was sent to back into Claude Code when it asks.

Turning **Allow external agents** off refuses every OAuth sign-in to the
Workspace but revokes no grant. While it is off, each delegated request is
refused with `403` and `WA-PAT-006`, a refresh with `invalid_grant`, and the
consent page does not offer the Workspace. A host that tries a refresh while
the setting is off discards its tokens on the `invalid_grant`; an access
token lasts an hour, so a short off period may never cause a refresh. Once
the setting is back on, an unrevoked grant works again. Claude Code shows
the server as **Needs authentication** while the setting is off, and
afterwards `claude -p` keeps skipping it until the person runs
`claude mcp login parable` or reconnects from `/mcp`. The
setting is the Workspace's only control over OAuth sign-ins; the app lists
no grants and cannot revoke one, so a person ends their own from the host.

Refusals follow the OAuth specifications: `invalid_client` for a client the
server cannot verify, `invalid_request` for a redirect URI the client did not
register (the loopback rule above aside), which is answered in the browser
and never redirected,
`invalid_target` for a `resource` that is not the MCP endpoint,
`invalid_scope` for a scope this server does not issue, and `invalid_grant`
for a code or refresh token that is unknown, spent, expired or bound to
another client, and for a refresh once the person is no longer an active
member or the Workspace has turned external agents off. A consent request expires after ten minutes and is answered
once: after Allow or Deny, a second answer, from a stale tab or a double
submit, is refused. Start again from the host.

An MCP host never forwards its token to the Workspace API. The MCP endpoint
validates the token itself and calls the Workspace API as the person under
its own delegation, which the Workspace API checks against the grant on
every request. The audit trail records each call as the person, with the
host's name.

## Connect a SQL tool

A SQL token, or a Parable token, queries the Workspace's pools over
[Flight SQL](/protocols/sql/flight-sql.md).
[Query Provider Pools with SQL](/protocols/sql/query-provider-pools.md) walks
through creating a SQL token and connecting DBeaver or Python.

| Setting | Value |
| - | - |
| Host | `sql.parable.work` (production), `sql.staging.parable.work` (staging) |
| Port and transport | `443`, TLS: `grpc+tls://sql.parable.work:443` for ADBC, `jdbc:arrow-flight-sql://sql.parable.work:443` for JDBC |
| Token | `authorization: Bearer pat_...` metadata; ADBC option `adbc.flight.sql.authorization_header` |
| Workspace | `x-tenant: <workspace-slug>` metadata on every call; ADBC option `adbc.flight.sql.rpc.call_header.x-tenant` |

Flight SQL answers a refused token with a gRPC status, not an HTTP one. The
[Flight SQL](/protocols/sql/flight-sql.md#tokens) page lists them.

## First request

These three calls confirm a Parable token and show the Workspace. All three
are read-only, so an MCP token passes each one its owner's roles cover. A SQL
token is refused on them with `403`; check one by connecting a SQL tool
instead.
Protocol paths keep `tenant` in the identifier.

**1. Check who you are**

`GET /api/users/me` returns the person the token belongs to.

```bash theme={null}
curl "$PARABLE_API_URL/api/users/me" \
  -H "Authorization: Bearer $PARABLE_API_TOKEN" \
  -H "X-Tenant: $PARABLE_WORKSPACE"
```

A `401` means the token is wrong or revoked. A `200` with your own
record means authentication is working. Operation:
[Get the current member](/protocols/workspace/http.md).

**2. List Provider connections**

`GET /api/vendors/tenant-connector-instances` lists each configured
Provider connection and its sync status.

```bash theme={null}
curl "$PARABLE_API_URL/api/vendors/tenant-connector-instances" \
  -H "Authorization: Bearer $PARABLE_API_TOKEN" \
  -H "X-Tenant: $PARABLE_WORKSPACE"
```

This is enough to build a sync-health check. Browse and configure
connections on [Provider Directory](/protocols/workspace/providers/directory.md).

**3. See the catalog**

`GET /api/v2/catalog` returns the Workspace's available data catalog.

```bash theme={null}
curl "$PARABLE_API_URL/api/v2/catalog" \
  -H "Authorization: Bearer $PARABLE_API_TOKEN" \
  -H "X-Tenant: $PARABLE_WORKSPACE"
```

Pool addresses are on the [SQL primer](/protocols/sql).

## Response envelope

Every Workspace API endpoint wraps the payload the same way.

| Field | Purpose |
| - | - |
| `data` | The resource or list you asked for. |
| `meta.requestId` | Unique id for the request. Include it when contacting support. |

The envelope has no total count and no paging links. A list that pages does
it through its own parameters, such as `limit` and `offset`, or a cursor.
Each operation's reference page lists its parameters.

Field meaning uses namespaced scalars such as `Identity.UUID` and
`Contact.Email`. See [Field types](/protocols/workspace/field-types).


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