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

# OAuth apps

> Obtain Engini access tokens with the standard OAuth 2.1 authorization-code flow.

Engini is an OAuth 2.1 authorization server: your application can send a user through a standard authorize → consent → token flow and receive an Engini access token scoped to that user and account. This is how MCP clients authenticate, and how any third-party app can act on behalf of an Engini user without handling their credentials.

<Note>
  For your **own** scripts, servers, and agents, an [API key](/concepts/authentication) is simpler. OAuth is for apps acting on behalf of **other** Engini users.
</Note>

## Discovery

```
GET https://identity.engini.io/.well-known/oauth-authorization-server
```

Standards-compliant clients can configure themselves from this metadata alone. Supported: `authorization_code` + `refresh_token` grants, `code` response type, PKCE `S256`, client auth `none` (public clients) / `client_secret_basic` / `client_secret_post`. Scopes: `openid`, `profile`, `mcp:tools`.

## Register a client

OAuth clients are currently provisioned from your Engini account (or by the Engini team for global/partner clients) - there is **no dynamic registration**. A client pins its exact `redirect_uri` allowlist. Public clients (no secret) are supported and **PKCE is required** by default.

## The flow

1. **Authorize** - send the user to:

```
GET https://identity.engini.io/oauth/authorize
    ?client_id=<your-client-id>
    &redirect_uri=<registered-uri>
    &response_type=code
    &state=<random>
    &code_challenge=<S256(verifier)>
    &code_challenge_method=S256
    &scope=openid profile
```

2. **Consent** - the user signs in to Engini (email/password or Google/Microsoft/GitHub) and approves your app for a specific account they're a member of.

3. **Redirect** - the browser returns to your `redirect_uri` with `?code=...&state=...`. Codes are single-use and expire in 10 minutes.

4. **Token** - exchange the code (form-encoded):

```bash theme={null}
curl -X POST https://identity.engini.io/api/oauth/token \
  -d grant_type=authorization_code \
  -d code=<code> \
  -d redirect_uri=<registered-uri> \
  -d code_verifier=<verifier> \
  -d client_id=<your-client-id>
```

```json theme={null}
{
  "access_token": "<jwt>",
  "token_type": "bearer",
  "expires_in": 3600,
  "refresh_token": "<opaque>"
}
```

## Using and refreshing the token

Send the access token as `Authorization: Bearer <jwt>`. It expires after **1 hour**; the refresh token lasts **30 days**:

```bash theme={null}
curl -X POST https://identity.engini.io/api/oauth/token \
  -d grant_type=refresh_token \
  -d refresh_token=<refresh-token> \
  -d client_id=<your-client-id>
```

Account membership is re-verified on every refresh - a user removed from the account loses access at the next refresh at the latest.

## Where OAuth tokens work

* **MCP** - the primary consumer: see [Connect via MCP](/agents/mcp).
* **DeveloperAPI (`/v1`)** - the token is a standard Engini Bearer JWT.
