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

# OAuth for apps

> How an approved app or an AI assistant connects to Usertick for a person, with the authorization code grant, PKCE, rotating refresh tokens and revocation.

Apps that act in Usertick for other people, such as the [Usertick app for Zapier](https://docs.usertick.com/integrations/zapier.md), and the AI assistants that connect to the [MCP server](https://docs.usertick.com/developers/mcp.md) sign in with OAuth 2. The person approves the app in Usertick and selects the organization. An app such as Zapier receives an access token for the [REST API](https://docs.usertick.com/developers/api/overview.md), and an assistant receives one for the MCP server only.

> [!NOTE]
>
> Apps such as Zapier are registered by Usertick. AI assistants register themselves when they connect to the MCP server (see [AI assistants](#ai-assistants)). Your own code that calls the REST API uses an [API key](https://docs.usertick.com/developers/api/authentication.md) instead.

## Endpoints

| Step | URL |
| - | - |
| Authorization server metadata ([RFC 8414](https://www.rfc-editor.org/rfc/rfc8414)) | `https://app.usertick.com/.well-known/oauth-authorization-server` |
| Consent page | `https://app.usertick.com/oauth/authorize` |
| Token | `POST https://app.usertick.com/api/oauth/token` |
| Revoke | `POST https://app.usertick.com/api/oauth/revoke` |
| Client registration ([RFC 7591](https://www.rfc-editor.org/rfc/rfc7591)), for assistants | `POST https://app.usertick.com/api/oauth/register` |

The issuer is `https://app.usertick.com`. Every answer the consent page sends back carries it as `iss` ([RFC 9207](https://www.rfc-editor.org/rfc/rfc9207)), the approval and the refusal alike.

## Scopes

| Scope | What the consent page says |
| - | - |
| `surveys:read` | See your surveys and their questions |
| `surveys:write` | Pause and resume your surveys |
| `responses:read` | See your responses and their AI analysis |
| `responses:write` | Add comments and change status, type and urgency |
| `responses:delete` | Delete responses, including everything one person sent |
| `analytics:read` | See your metrics: counts, scores and breakdowns |
| `webhooks:read` | See the webhooks the app sets up and their deliveries |
| `webhooks:write` | Send new responses to addresses the app sets up |

Each app can ask only for the scopes that Usertick registered for it. The [Usertick app for Zapier](https://docs.usertick.com/integrations/zapier.md) can ask for `surveys:read`, `responses:read`, `responses:write` and `webhooks:write`. If an app asks for any other scope, Usertick refuses the approval with `400` and code `OAU-002-001`.

[Authentication](https://docs.usertick.com/developers/api/authentication.md#scopes) describes what each scope allows. An access token works with the permissions of the person who approved the app, within the approved scopes. Changes made with it appear in the activity log under that person's name, marked as made through the app.

If the person approves the app again in the same organization, the app keeps its connection. The new scopes apply when the app redeems the new code, and until then the app's tokens keep the scopes they had.

## Connect an app

The flow is the authorization code grant ([RFC 6749](https://www.rfc-editor.org/rfc/rfc6749)) with PKCE ([RFC 7636](https://www.rfc-editor.org/rfc/rfc7636)).

**1. Send the person to the consent page**

```text theme={null}
https://app.usertick.com/oauth/authorize
  ?client_id=YOUR_CLIENT_ID
  &redirect_uri=https://your-app.example.com/oauth/callback
  &response_type=code
  &scope=surveys:read responses:read responses:write webhooks:write
  &state=RANDOM_STATE
  &code_challenge=BASE64URL_SHA256_OF_THE_VERIFIER
  &code_challenge_method=S256
```

The redirect URI must exactly match one registered for the app. An empty `scope` asks for every scope the app may request. PKCE is optional, but when the app sends it, the method must be `S256` because Usertick refuses `plain`.

**2. The person approves**

After signing in if needed, the person selects the organization and then **Allow access**.

Only an owner, or a member whose role has the **Manage integrations** permission, can connect an app. Otherwise, Usertick refuses the approval with `403` and code `OAU-002-003`. The organization must be on the Starter plan or above. On the Free plan, Usertick refuses the approval with `403` and code `OAU-002-006`, and the consent page asks for a plan upgrade. When the plan later no longer includes apps, the app's API calls return `403` with `APK-004-008` until an upgrade.

Usertick then sends the browser to `redirect_uri?code=...&state=...&iss=https://app.usertick.com`. The code works once and expires after 10 minutes. If the person selects **Cancel**, the browser goes to `redirect_uri?error=access_denied&state=...&iss=https://app.usertick.com` instead.

**3. Exchange the code for tokens**

Your server posts a form to the token endpoint. The client credentials go either in the form, as below, or in HTTP Basic authentication, but not in both.

```bash theme={null}
curl -X POST "https://app.usertick.com/api/oauth/token" \
  -d grant_type=authorization_code \
  -d code=THE_CODE \
  -d redirect_uri=https://your-app.example.com/oauth/callback \
  -d code_verifier=THE_VERIFIER \
  -d client_id=YOUR_CLIENT_ID \
  -d client_secret=YOUR_CLIENT_SECRET
```

```json theme={null}
{
  "access_token": "uto_...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "utr_...",
  "scope": "surveys:read responses:read responses:write webhooks:write"
}
```

**4. Call the API**

The app sends `Authorization: Bearer uto_...`, and when a call returns `401`, it refreshes the token and tries again.

## AI assistants

An assistant that connects to the MCP server signs in the way the [MCP authorization spec](https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization) describes. The [MCP server page](https://docs.usertick.com/developers/mcp.md) shows how each assistant connects; this section is for building one.

* **Discovery.** A request to `https://mcp.usertick.com/mcp` without a token returns `401` with `WWW-Authenticate: Bearer resource_metadata="https://mcp.usertick.com/.well-known/oauth-protected-resource/mcp"`. That document ([RFC 9728](https://www.rfc-editor.org/rfc/rfc9728)) names `https://mcp.usertick.com/mcp` as the resource and `https://app.usertick.com` as the authorization server, whose metadata lists the endpoints above.
* **Client identity.** An assistant uses an https URL that serves its Client ID Metadata Document as its `client_id` (`client_id_metadata_document_supported` is `true`), or registers at the registration endpoint. Either way it is a public client: no secret, `token_endpoint_auth_method` `none`. A registration takes 1 to 10 redirect URIs: https URLs, http URLs on `localhost` or `127.0.0.1` (matched on any port), or an app's own scheme. A registration that nobody approves within a day is deleted.
* **PKCE is required.** An assistant's authorization without an `S256` code challenge returns `400` with `OAU-002-010`.
* **The resource is the MCP server.** An assistant sends `resource=https://mcp.usertick.com/mcp` on the authorization and the token request ([RFC 8707](https://www.rfc-editor.org/rfc/rfc8707)). Any other value returns `400`, `OAU-002-009` on the consent page and `invalid_target` at the token endpoint. Its tokens work only through the MCP server; the REST API refuses them with `401` and `APK-004-007`.
* **Who may connect one.** Any member may connect an assistant, with no permission needed, and picks the scopes on the consent page. The assistant then works with that member's own role. A member whose role reaches only some surveys can't connect one in that organization (`403`, `OAU-002-011`), because the assistant reads every survey.
* **The plan.** The organization must be on the Pro plan or above. On a lower plan, Usertick refuses the approval with `403` and code `OAU-002-012`.
* **Unrecognized assistants.** The consent page names an assistant Usertick doesn't recognize with the name it gave itself, marks it as unrecognized and says where the person goes after answering.

## Refresh tokens

* An access token lasts 1 hour.
* A refresh token lasts 90 days from when it was issued. Every refresh issues a new one, so a connection that keeps refreshing doesn't expire.
* Every refresh returns a new access token **and** a new refresh token, so the app has to store the new refresh token each time.
* A used refresh token presented again within 30 seconds gets its own new pair, so two parallel refreshes don't break the connection. After those 30 seconds, a reused refresh token revokes every token issued in its chain, and the person has to approve the app again. The connection itself remains, so the webhook subscriptions the app made keep working.
* Usertick stores only hashes of codes and tokens.

```bash theme={null}
curl -X POST "https://app.usertick.com/api/oauth/token" \
  -d grant_type=refresh_token \
  -d refresh_token=utr_... \
  -d client_id=YOUR_CLIENT_ID \
  -d client_secret=YOUR_CLIENT_SECRET
```

A refresh always issues tokens with every scope the person approved, even when the request sends a narrower `scope`, and the `scope` in the token response lists them. If the request's `scope` names a scope that wasn't approved, Usertick returns `invalid_scope`.

## Revoke a token

```bash theme={null}
curl -X POST "https://app.usertick.com/api/oauth/revoke" \
  -d token=utr_... \
  -d token_type_hint=refresh_token \
  -d client_id=YOUR_CLIENT_ID \
  -d client_secret=YOUR_CLIENT_SECRET
```

The endpoint returns `200` with an empty body, including for a token Usertick doesn't know ([RFC 7009](https://www.rfc-editor.org/rfc/rfc7009)). Revoking a token stops it and the tokens issued in its chain. When the app has no other working token for that connection, Usertick revokes the whole connection.

## When a connection stops

A connection stops working, and its tokens return `401`, when:

* a member whose role has the **Manage integrations** or **Manage developer settings** permission selects **Revoke access** on its line under **Connections** in **Settings → Integrations → Zapier**, or for an assistant under **Settings → Developers → API keys**,
* the person who connected it selects **Revoke access** under **Settings → Security**,
* the person who connected it leaves the organization or has their account deactivated or scheduled for deletion; for an app such as Zapier also when they lose the **Manage integrations** permission, and for an assistant when their role stops reaching every survey,
* the organization becomes inactive.

When the person leaves the organization, Usertick revokes the connection permanently. Usertick also revokes a connection automatically when the app doesn't exchange the approval's code for tokens within 1 day, and when the connection has no refresh token that still works and hasn't been used for 90 days. Revoking a connection also turns off every webhook subscription the app made.

## Token endpoint errors

The token and revoke endpoints return errors in the OAuth format, `{ "error": "...", "error_description": "..." }`.

| HTTP | `error` | When |
| - | - | - |
| 401 | `invalid_client` | Wrong client id or secret, or a secret sent for an assistant (a public client) |
| 400 | `invalid_request` | A parameter is missing or repeated, or the client credentials came both ways |
| 400 | `unsupported_grant_type` | A grant other than `authorization_code` or `refresh_token` |
| 400 | `invalid_grant` | The code or refresh token is unknown, expired, used or revoked, the redirect URI or code verifier does not match, or the connection was revoked |
| 400 | `invalid_scope` | A refresh asked for more than was approved |
| 400 | `unauthorized_client` | Revoking a token that belongs to another app |
| 400 | `invalid_target` | An assistant's `resource` is not the one its authorization was for |

The registration endpoint returns `201` with the new `client_id`, or `400` with `invalid_redirect_uri` (a redirect URI a public client can't use) or `invalid_client_metadata` (anything but a public client, or no MCP server set up). It allows 30 registrations per minute per IP address.

The token and revoke endpoints allow 3,000 requests per minute per IP address. A request over that limit returns `429` with a `Retry-After` header and `GEN-429-001` in the REST API's [error format](https://docs.usertick.com/developers/api/errors.md) instead of the OAuth format. A request body over 16 KB returns `413`.


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