# SSO Connection

URL: /business/nouns/sso-connection · Id: `noun:sso-connection`

> An SSO Connection links a Customer’s own identity system to its Tenant, so the Customer’s people sign in through it.

An SSO Connection links a [Customer](/business/nouns/customer)’s own identity system to its [Tenant](/business/nouns/tenant), so the Customer’s people sign in through it.

## Properties
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `protocol` | `string` | Yes | How sign-in is asserted: `saml` or `oidc`. |
| `state` | `string` | Yes | Where it stands; only `active` signs anyone in. |
| `certificateExpiresAt` | `datetime` | No | When the signing certificate expires; the Customer’s admin hears 30 days before. |
| `testedAt` | `datetime` | No | When the Customer’s admin last completed a test sign-in. |

## Object
```json
{
  "$id": "https://api.sb/tutorloop.com/sso-connections/sso_connection_1",
  "$type": "SSOConnection",
  "protocol": "saml",
  "state": "draft",
  "certificateExpiresAt": "2026-10-22T12:00:00Z",
  "testedAt": "2026-10-22T12:00:00Z"
}
```

## Relationships
- <a id="rel-serves"></a>A SSO Connection **serves** one [Tenant](/business/nouns/tenant): a [Tenant](/business/nouns/tenant)’s people sign in through its SSO Connections; the [Customer](/business/nouns/customer)’s admin owns each one. `serves`, inverse [`signsInVia`](/business/nouns/tenant#rel-signs-in-via).

## Lifecycle

| State | Meaning | Moves to |
| --- | --- | --- |
| `draft` (first) | Its configuration is entered, and no test sign-in has passed. | `active` by [SSOConnection.activate](/business/nouns/sso-connection/activate) |
| `active` | Everyone in the Tenant signs in through it. | `failing` on [`SSOConnection.failed`](/business/nouns/sso-connection#ssoconnectionfailed); `inactive` by [SSOConnection.deactivate](/business/nouns/sso-connection/deactivate) |
| `failing` | Sign-ins through it fail, and the Customer’s admin has been told why. | `active` by [SSOConnection.activate](/business/nouns/sso-connection/activate); `inactive` by [SSOConnection.deactivate](/business/nouns/sso-connection/deactivate) |
| `inactive` | Turned off: nobody signs in through it, and its configuration is kept. | `active` by [SSOConnection.activate](/business/nouns/sso-connection/activate) |

## Verbs
| Verb | Description | Subject | Event | Class |
| --- | --- | --- | --- | --- |
| [SSOConnection.activate](/business/nouns/sso-connection/activate) | Activate turns an SSO Connection on after a test sign-in passes, so the Tenant’s people sign in through it. | [Security Engineer](/agents/engineering/security-engineer) | [`SSOConnection.activated`](/business/events/SSOConnection.activated) | Two-way: `try`, `send`, `do` |
| [SSOConnection.deactivate](/business/nouns/sso-connection/deactivate) | Deactivate turns an SSO Connection off, so nobody signs in through it; its configuration is kept. | [Security Engineer](/agents/engineering/security-engineer) | [`SSOConnection.deactivated`](/business/events/SSOConnection.deactivated) | Two-way: `try`, `send`, `do` |
| <a id="create"></a>[SSOConnection.create](/business/nouns/sso-connection/create) | Creates a SSO Connection. Inherited from [Noun](/business/nouns/noun). | None. | `SSOConnection.created` | Two-way: `try`, `send`, `do` |
| <a id="get"></a>[SSOConnection.get](/business/nouns/sso-connection/get) | Returns one SSO Connection by id. Inherited from [Noun](/business/nouns/noun). | None. | None. | Read: `try`, `do` |
| <a id="list"></a>[SSOConnection.list](/business/nouns/sso-connection/list) | Returns SSO Connections, newest first, one page at a time. Inherited from [Noun](/business/nouns/noun). | None. | None. | Read: `try`, `do` |
| <a id="update"></a>[SSOConnection.update](/business/nouns/sso-connection/update) | Changes the given properties of a SSO Connection. Inherited from [Noun](/business/nouns/noun). | None. | `SSOConnection.updated` | Two-way: `try`, `send`, `do` |

## Events
Events no Verb emits. Each Verb’s Event is in the Verbs table above; every Event is delivered in the same shape.

### SSOConnection.failed
Sign-ins through an SSO Connection started failing, from an expired certificate or a change on the Customer’s side. Raised by the rail.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | `string` | Yes | `SSOConnection.failed`. |
| `at` | `datetime` | Yes | When it happened. |
| `seq` | `integer` | No | Its position in the namespace, one more than the Event before; any seq is a cursor into the history. |
| `data` | `json` | Yes | The Thing as the Action left it. |
| `data.ssoConnection` | [SSO Connection](/business/nouns/sso-connection) | Yes | The SSO Connection as the change left it. |
| `data.ssoConnection.protocol` | `string` | Yes | How sign-in is asserted: `saml` or `oidc`. |
| `data.ssoConnection.state` | `string` | Yes | Where it stands; only `active` signs anyone in. |
| `data.ssoConnection.certificateExpiresAt` | `datetime` | No | When the signing certificate expires; the Customer’s admin hears 30 days before. |
| `data.ssoConnection.testedAt` | `datetime` | No | When the Customer’s admin last completed a test sign-in. |
| `data.cause` | `string` | Yes | What failed: `certificateExpired`, `signatureInvalid`, or `providerUnreachable`. |
| `data.previous.state` | `string` | Yes | The `state` before this Event: `active`. |

```json
{
  "$id": "https://api.sb/tutorloop.com/events/event_1",
  "$type": "Event",
  "name": "SSOConnection.failed",
  "at": "2026-10-22T12:00:00Z",
  "data": {
    "ssoConnection": {
      "$id": "https://api.sb/tutorloop.com/sso-connections/sso_connection_1",
      "$type": "SSOConnection",
      "protocol": "saml",
      "state": "failing",
      "certificateExpiresAt": "2026-10-22T12:00:00Z",
      "testedAt": "2026-10-22T12:00:00Z"
    },
    "cause": "certificateExpired",
    "previous": {
      "state": "active"
    }
  },
  "seq": 1
}
```

## Permissions
Which Access level may call each Verb. One-way Verbs wait their delay window.

| Operation | `owner` | `admin` | `member` | `viewer` |
| --- | --- | --- | --- | --- |
| `SSOConnection.create` | Yes | Yes | No | No |
| `SSOConnection.get` | Yes | Yes | Yes | Yes |
| `SSOConnection.list` | Yes | Yes | Yes | Yes |
| `SSOConnection.update` | Yes | Yes | No | No |
| `SSOConnection.activate` | Yes | Yes | No | No |
| `SSOConnection.deactivate` | Yes | Yes | No | No |

## Call it

**Ask**

```text
Activate the latest SSO connection.
```

**API**

```bash
curl -X POST https://api.sb/quietcopy.com/sso-connections/sso_connection_1/activate \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
```

```json
{
  "api": "https://api.sb/quietcopy.com",
  "connection": {
    "$id": "https://api.sb/quietcopy.com/sso-connections/sso_connection_1",
    "$type": "SSOConnection",
    "protocol": "saml",
    "state": "draft"
  },
  "action": {
    "$id": "https://api.sb/quietcopy.com/actions/action_1",
    "operation": "SSOConnection.activate",
    "status": "done"
  },
  "links": {
    "self": "https://api.sb/quietcopy.com/actions/action_1"
  }
}
```

**SDK**

```ts
import { $ } from 'startup-builder'

const connection = await $.SSOConnection.activate({ connection: 'sso_connection_1' })
```

**CLI**

```bash
npx startup-builder do SSOConnection.activate --json '{"connection":"sso_connection_1"}'
```

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "code": "return await $.SSOConnection.activate({ connection: 'sso_connection_1' })"
  }
}
```

## Example
A 2,000-person [Customer](/business/nouns/customer) connects its identity system to its [Tenant](/business/nouns/tenant) over `saml`. Its admin completes a test sign-in, the connection goes `active`, and passwords stop working for that domain.