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

# Enterprise SSO

> Let your users connect their Mihu account to your own application, with their consent and only the permissions you need

**Enterprise SSO** lets your own product work with Mihu on behalf of your users. Your users click **Connect with Mihu**, approve the permissions your application asks for, and your application can then call the Mihu API as that user: list their AI agents, start campaigns, read conversations, whatever the approved permissions allow.

Nobody shares a password or a workspace-wide API key. Every connection belongs to one person, is limited to what they approved, and can be switched off by them or by their workspace at any moment.

<Note>
  Enterprise SSO is part of the **Enterprise** plan and is switched on for your workspace on request. Contact your Mihu account manager or [support@mihu.ai](mailto:support@mihu.ai) to enable it.
</Note>

## When to use it

* You run a platform, a marketplace or an internal tool, and your users also use Mihu.
* You want to show or change Mihu data inside your product without asking each customer for an API key.
* You need every action to be attributed to the person who approved it, with the same permissions they have in Mihu.

If you only need to call the API for your own workspace, a regular API token is simpler. See [Authentication](/authentication).

## How onboarding works

<Steps>
  <Step title="Enterprise SSO is enabled">
    We switch on the SSO layer for your Enterprise workspace.
  </Step>

  <Step title="Your application is registered">
    You send us your application's name, logo, the **redirect URIs** your users should return to, and the **permissions** your application needs, for example `agents`, `contacts` and `campaigns`. We register it and send you a **client ID** and a **client secret**. The secret is shown once; store it on your server.
  </Step>

  <Step title="You add the Connect button">
    Your application sends users to the Mihu consent screen and exchanges the result for tokens, as described below.
  </Step>

  <Step title="Your users connect">
    Each user approves once. From then on your application calls the Mihu API with their token and keeps it fresh in the background.
  </Step>
</Steps>

Redirect URIs must use HTTPS and are matched exactly. Only the permissions you registered can ever be requested.

## 1. Send the user to the consent screen

Open this address, usually in a popup. `https://acme.mihu.ai` stands for your user's workspace address; every request in this guide goes to that address.

```text theme={null}
https://acme.mihu.ai/sso/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&state=RANDOM_VALUE
```

| Parameter | Required | What it is |
| - | - | - |
| `client_id` | Yes | Your client ID |
| `redirect_uri` | Yes | One of your registered redirect URIs |
| `state` | Yes | A random value you generate and check on return, up to 512 characters |
| `scope` | No | A space-separated subset of your registered permissions |

The user signs in to Mihu if needed and sees which permissions your application asks for.

* **Allow:** the browser returns to your redirect URI with a one-time code.
* **Cancel:** the browser returns with `error=access_denied`.
* **Missing permissions:** if the user's own Mihu account lacks any of the requested permissions, Allow is disabled and the missing ones are listed. There are no partial grants.

```text theme={null}
YOUR_REDIRECT_URI?code=VQ3yKL8x...&state=RANDOM_VALUE
```

Check that `state` matches what you sent. The code works **once** and expires after **60 seconds**.

## 2. Exchange the code for tokens

Do this on your server, never in the browser.

```bash theme={null}
curl -X POST https://acme.mihu.ai/api/v1/sso/token \
  -H 'Content-Type: application/json' \
  -d '{
    "grant_type": "authorization_code",
    "code": "VQ3yKL8x...",
    "redirect_uri": "YOUR_REDIRECT_URI",
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET"
  }'
```

```json theme={null}
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "mihu_rt_...",
  "scope": "agents contacts campaigns"
}
```

Store, per connection: the workspace address, the access token, its expiry time and the refresh token. The access token lasts **1 hour** and the refresh token **30 days**, unless agreed otherwise when your application is registered.

## 3. Call the API

Send the access token in the `Authorization` header on every request.

```bash theme={null}
curl https://acme.mihu.ai/api/v1/agents \
  -H "Authorization: Bearer ACCESS_TOKEN"
```

```bash theme={null}
curl -X POST https://acme.mihu.ai/api/v1/agents \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Support Agent", "role": "Customer Support"}'
```

* The token only opens what the user approved. Anything else answers `403`.
* Access always follows the user's current permissions in Mihu. If their role changes, your access changes with it, immediately.
* Every action is recorded under the user who approved the connection.

<Warning>
  Keep tokens on your server. Never put them in a URL, in browser storage or in client-side code.
</Warning>

## 4. Keep the connection alive

Refresh a few minutes before the access token expires, or when a call answers `401`.

```bash theme={null}
curl -X POST https://acme.mihu.ai/api/v1/sso/token \
  -H 'Content-Type: application/json' \
  -d '{
    "grant_type": "refresh_token",
    "refresh_token": "mihu_rt_...",
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET"
  }'
```

The answer has the same shape as step 2, including a **new** refresh token.

1. **Always store the new refresh token.** The old one stops working the moment the refresh succeeds.
2. **Refresh from one place at a time.** Using an old refresh token again is treated as a stolen token and ends the whole connection, so two parallel refreshes for the same connection will break it.
3. **`invalid_grant` means the connection is over.** Mark it as disconnected and ask the user to connect again.

## 5. Disconnect

When a user disconnects inside your application, revoke the connection so it disappears from their Mihu account too.

```bash theme={null}
curl -X POST https://acme.mihu.ai/api/v1/sso/revoke \
  -H 'Content-Type: application/json' \
  -d '{
    "token": "mihu_rt_...",
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET"
  }'
```

## Who can switch a connection off

Connections can end from the Mihu side at any time. Your application should expect that and simply ask the user to connect again.

| Who | How | Effect |
| - | - | - |
| The user | **Profile, Connected apps, Remove** | Their connection ends and the tokens stop working on the next request |
| The workspace | Turns your application off for the whole workspace | Nobody in that workspace can connect, and existing tokens stop working |
| Mihu | Disables your application | All connections stop until it is enabled again |

## When calls fail

| Response | Meaning | What to do |
| - | - | - |
| `401` on an API call | The token expired, or the connection was ended | Refresh once and retry. If the refresh fails, reconnect |
| `403` on an API call | Outside the approved permissions, or the user lost that permission | Do not retry |
| `401 invalid_client` | Wrong client ID or client secret | Check your credentials |
| `400 invalid_grant` | The code expired or was used, the redirect URI differs, or the refresh token is no longer valid | Start again from step 1 |
| `422` | The request body failed validation | Fix the field named in the message |
| `429` | Too many requests to the token endpoint | Back off and retry later |

## What is inside the access token

You do not need to read the token, but you can. It is a signed JWT (RS256), and the public keys are published at `https://acme.mihu.ai/.well-known/jwks.json`.

| Claim | Meaning |
| - | - |
| `iss`, `aud` | The workspace address. The token only works there |
| `sub` | The user who approved the connection |
| `client` | Your client ID |
| `token_use` | Always `client` for connected applications |
| `scopes` | The approved permissions |
| `iat`, `exp`, `jti` | Issued at, expiry time and a unique token ID |

## Checklist before you go live

* The client secret and all tokens are stored on your server only.
* You check `state` on every return from the consent screen.
* You store the new refresh token after every refresh, and refresh one connection at a time.
* You handle `401`, `403` and `invalid_grant` by refreshing, stopping or asking the user to reconnect.
* You offer a **Disconnect** option that calls the revoke endpoint.
