---
title: Connect a client
description: Register a public client, authorize a person with PKCE, exchange the code for a token, and keep it fresh — access tokens last five minutes.
---

Taylorz CRM issues tokens through OAuth 2.0 authorization-code grants with PKCE. Every client is public: it holds no secret, and its token endpoint authentication method is `none`.

A grant is bound to one organization at consent, and a token names exactly one resource. Changing the organization a person is looking at in the web application does not retarget a token that already exists.

## 1. Register the client

Registration needs the session of an **owner or admin of the organization**, and the client is bound to the organization that session has active. Anonymous and dynamic client registration are both disabled, so a client cannot register itself.

There is no first-party screen for this yet. If you are not an administrator of the workspace, ask one to register the client and send you its `client_id`.

What a registration states:

```json
{
  "application_type": "native",
  "client_name": "Your integration",
  "grant_types": ["authorization_code", "refresh_token"],
  "redirect_uris": ["http://localhost:9876/callback"],
  "response_types": ["code"],
  "scope": "offline_access api.access records.read",
  "token_endpoint_auth_method": "none"
}
```

`application_type` matters: a loopback redirect belongs to a native client, and a web client is refused one. Request `offline_access` if you want a refresh token — and you do, for the reason in step 4.

## 2. Send the person to authorize

Generate a verifier, derive its challenge, and send the person to the authorization endpoint. They sign in and consent there.

```
GET /api/auth/oauth2/authorize
  ?client_id=<client_id>
  &response_type=code
  &redirect_uri=<one of the registered uris>
  &resource=<the resource you are connecting to>
  &scope=offline_access+api.access+records.read
  &code_challenge=<BASE64URL(SHA256(verifier))>
  &code_challenge_method=S256
  &state=<your own value>
```

`resource` is required and names what the token will be for: the REST resources, or MCP. A token serves one resource, so a client that needs both authorizes twice.

The response redirects to your `redirect_uri` with `code` and your `state`.

## 3. Exchange the code

```
POST /api/auth/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&client_id=<client_id>
&code=<code>
&code_verifier=<the verifier from step 2>
&redirect_uri=<the same uri>
&resource=<the same resource>
```

You get back an `access_token` and, if you asked for `offline_access`, a `refresh_token`.

Call the API with the access token as a bearer token:

```
GET /api/v1/objects
Authorization: Bearer <access_token>
```

Bearer only. A DPoP-bound token — one carrying a `cnf` claim — is refused.

## 4. Refresh often

**An access token lasts five minutes.** Refreshing is not an edge case to handle later; it is part of a working client. Keep the refresh token, and exchange it before or when a call answers `401`.

```
POST /api/auth/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&client_id=<client_id>
&refresh_token=<refresh_token>
&resource=<the same resource>
```

A refresh needs the connection scope it was granted with, and the organization's module still enabled. If either has gone, the refresh is refused rather than silently downgraded.

## 5. Revoke when you are done

```
POST /api/auth/oauth2/revoke
```

Revoking a token revokes the grant behind it, so a token already issued stops working at once rather than lasting out its five minutes.

## Discovery

The provider publishes its metadata at `/.well-known/oauth-authorization-server/api/auth`. Read the endpoints from there rather than hard-coding them. The document advertises `none` among the supported token endpoint authentication methods, which is what a public client uses.
