An OAuth client lets software obtain **short-lived access tokens** for a SendOps organization, instead of holding a long-lived [API key](/api-integrations/api-keys).

There are two reasons to want one:

- **You are building a product that connects to your customers' SendOps accounts.** Your customer clicks "Connect SendOps", approves what you asked for, and your product acts within their permissions. You never handle their credentials.
- **You are automating your own organization** and prefer a credential that expires in 15 minutes over one that lives until you rotate it.

OAuth clients live under **Workspace → OAuth Clients**. They use the same permissions as API keys — only the **Owner** and **Org Admin** roles can see or manage them — because registering a client and minting a key are the same trust decision.



## Choosing a flow

The first choice when registering a client, and the one everything else follows from.

| | Server-to-server | Connect (on behalf of a user) |
|---|---|---|
| Who does the token act as | Your organization | The user who authorized, in the org they chose |
| Is a person involved | No | Yes — they see a consent screen |
| Standard name | `client_credentials` | `authorization_code` + PKCE |
| Use it when | Automating your own org | Your customers connect your product |

**Server-to-server** is the simpler one: your backend exchanges its client ID and secret for a token. No user, no browser, no redirect.

**Connect** is what you want if you are shipping an integration. Your customer is sent to SendOps, sees exactly what you are asking for, picks which of their organizations to grant, and approves. Your product receives a token bound to that person and that organization.

## Registering a client


<Step title="Open Workspace → OAuth Clients">
  Select **Workspace** in the sidebar, then the **OAuth Clients** tab, then **Register client**.
</Step>
<Step title="Name it and pick a flow">
  The name is shown to **your users on the consent screen** if you use the connect flow, so use your product's name, not an internal codename.

  Pick the flow from the table above. Choosing **Connect** reveals three more fields.
</Step>
<Step title="Choose an environment">
  **Live** or **Test**, exactly as with API keys — same data, different rate limit. Test clients run at 10% of your plan's per-minute ceiling.
</Step>
<Step title="Set the client type (connect flow only)">
  **Confidential** — a server that can keep a secret. Choose this whenever your code runs on a machine you control.

  **Public** — a CLI, desktop, mobile, or browser app, where any secret you ship can be extracted. Public clients get no secret and rely on PKCE alone. A single-page app counts here: it can complete the connect flow from the browser without a backend of your own.

  Server-to-server clients are always confidential: they authenticate with their secret, so a public one could not authenticate at all.
</Step>
<Step title="Add redirect URIs (connect flow only)">
  Where SendOps sends the user back after they approve. Matched **exactly** at authorization time — no wildcards, no path prefixes. `https://` is required, except on `localhost`, where `http://` is allowed for native apps that cannot hold a certificate.

  Add every environment you use, including local development.
</Step>
<Step title="Choose a refresh token lifetime (connect flow only)">
  How long a connection can survive before the user has to re-authorize: **never expires** (recommended), 30 days, 90 days, or 1 year.

  This is measured from **when the user authorized**, not from last use. Refreshing does not extend it — the deadline is absolute, so when it passes the user must approve again.
</Step>
<Step title="Select permissions">
  The scopes your client may ever ask for. You can only grant scopes you hold yourself.
</Step>
<Step title="Save the credentials">
  You get a **client ID** (`oc_live_…` or `oc_test_…`) and, for confidential clients, a **client secret** (`ocs_…`).

  The secret is shown **once** and stored only as a hash. The client ID is not a secret and appears in logs and the dashboard.
</Step>



  The scopes on the client are the most a token can ever carry. When a user connects your app, they can never grant more than their own role allows — and if their role is reduced later, their tokens shrink with it, re-checked on every request. Asking for a broad scope does not give you access to organizations whose users do not have it.


## What your users see

With the connect flow, the person authorizing sees a consent screen showing your client's **name**, the **permissions** you are requesting in plain language, and a picker for which of their organizations to grant.

The screen is always shown — including when the client belongs to their own organization. There is no silent first-party approval.

Once they approve, the connection appears in their [Connected Apps](/api-integrations/connected-apps), where they can review or revoke it at any time.

## Rotating a secret

Rotation issues a new client secret while the old one keeps working for **24 hours**, so you can roll it out without downtime. The **client ID never changes** — your users' existing connections are unaffected, and nobody has to re-authorize.


  The old secret stays valid for a day. If you believe a secret is being abused right now, deactivate the client instead.


## Deactivating a client

Deactivating stops the client immediately: no new tokens are issued, and existing access tokens stop working. Every user's connection to it is severed.

This is the right response to a leaked secret, and it is not reversible — register a new client to start again.

## Token lifetimes at a glance

| | Lifetime |
|---|---|
| Access token | 15 minutes |
| Refresh token | Rotates on every use; expiry as configured above |
| Client secret | Until rotated or the client is deactivated |

Refresh tokens are single-use: each refresh returns a new one and invalidates the old. If an old refresh token is ever replayed, SendOps treats it as theft and revokes the whole chain — so a stolen token cannot be used quietly alongside the real one.

## Audit trail

Registering, updating, rotating, and deactivating a client are recorded in the [Audit Log](/team/audit-log), along with grants issued and revoked. Creating and deactivating additionally require a recent multi-factor check.

## Building against it

The developer documentation covers the wire protocol — the endpoints, the exact request and response shapes, PKCE, and the discovery documents that let standard OAuth libraries configure themselves:

- [OAuth 2.1 guide](https://developers.sendops.dev/api-reference/oauth)
- [Authentication reference](https://developers.sendops.dev/api-reference/authentication)