---
title: "Set an organization's federated-login policy (owner)"
method: PUT
path: "/v1/organizations/{organization_id}/sso/policy"
tags: ["organizations"]
---

# Set an organization's federated-login policy (owner)

`PUT /v1/organizations/{organization_id}/sso/policy`

## Request

- Base URL: `https://api.ethiack.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.ethiack.com/v1/organizations/{organization_id}/sso/policy`
- Auth: none declared

Set whether federated login is served and whether it is mandatory.

The body is total, so this replaces the policy rather than patching it. The
write interlocks are pre-flighted by the service and answer 400: enabling a
connection no admin has pointed anywhere, and mandating SSO on one that is
not served -- the latter an organization-wide lockout, because members of an
SSO-mandated organization hold no password. The CHECK constraints behind
them remain the floor, but reaching one would surface as an unmapped 500.

Turning ``require_sso`` on adds one more precondition, enforced by the
workflow because it needs Zitadel: every member must already be linked to
the IdP, or the mandate would lock the unlinked ones out on their next
request. Arming with unlinked members answers 409; link them first.

Args:
    organization_id (int): The parent organization.
    payload (SsoPolicyRequest): The policy fields to change.
    db (AsyncSession): Request-scoped session.
    context (ActiveContext): Resolved caller.

Returns:
    SsoConnectionResponse: The connection as it now stands.

## Path parameters

- `organization_id` integer, required

## Request body

- SsoPolicyRequest: Organization-owner body: the policy applied on top of the template.
  - `enabled` boolean, required: Whether federated login is served for this organization. Refused while no template is set.
  - `require_sso` boolean, required: Whether members must authenticate through the IdP. Enforced on every organization-scoped session: turning it on refuses every member session that did not come through this IdP, on its next request. Link existing members first. Turning it on for a connection that is not served is refused outright rather than merely discouraged, since it would leave nothing to log in with.

## Response `200`

Successful Response

- SsoConnectionResponse: An organization's connection, as an admin or its owner sees it. Wider than ``GET /auth/sso/{login_slug}``, which is unauthenticated and deliberately omits everything about what happens to *accounts*. **Carries the customer-facing SAML endpoints, which the write responses also carry.** They used to come back only from ``POST|PUT .../sso/idp``, both admin-only, so the organization owner -- who can already read this resource -- had no way to see the ACS and metadata URLs they have to paste into their own IdP. Every one of them is derived from ``zitadel_idp_id`` by ``TemplateEndpoints`` and is published, non-secret metadata that Zitadel itself serves unauthenticated, so returning them here grants no authority the reader did not already hold. The descriptions are copied verbatim from ``IdpTemplateResponse`` so the two surfaces cannot drift into describing the same URL differently. Nothing about the *provider* is echoed: no metadata document, no OIDC client id and no secret. Those are inputs, admin-only, and this is a read every owner holds.
  - `login_slug` string, required: Org-scoped entry token in the login URL.
  - `zitadel_idp_id` string, nullable: The Zitadel IdP template, or null if unpointed.
  - `enabled` boolean, required: Whether federated login is served.
  - `require_sso` boolean, required: Whether members must authenticate through the IdP.
  - `provision_on_first_login` boolean, required: Whether a first federated login may mint an account. Armed only through the provisioning workflow, which requires a DNS-proven domain; not settable here.
  - `provisioned_role` 'owner' | 'admin' | 'analyst' | 'viewer', required: Sub-role within a :data:`ActorKind.ORGANIZATION` context. Snake_case wire values are used in the DB, in JWT claims synced from Zitadel, and as components of Cerbos role strings (e.g. ``"org_admin"``). Attributes: OWNER: Full authority over the organization. ADMIN: Administrative authority below the owner. ANALYST: Read and write on engagements, findings, assets. VIEWER: Read-only.
  - `verified_at` string, date-time, nullable: When a configuration test last passed against the template now in service, or null if none has. `require_sso` cannot be set true while this is null: mandating SSO on a connection nobody has proved works locks the organization out, since its members hold no password. Re-pointing the template clears it, so a verification never outlives the thing it verified.
  - `protocol` 'saml' | 'unknown', nullable: Which protocol family the template configures, as far as Phoenix can tell. Null when the connection points at no template. `saml` when `expected_issuer` is stored -- the mint workflow refuses to record a SAML template without one, so its presence is a positive signal, and it is the same discriminator the login path keys on (`sso_jit_login._issuer_verdict`). `unknown` for a connection that is pointed but stores no issuer: that is an OIDC template, or a SAML one pointed at by `PUT .../sso/template` alone, which records no issuer -- and nothing on the row separates the two. Answering `oidc` there would be a guess, and the guess would hide the ACS and metadata URLs a SAML customer cannot finish onboarding without.
  - `acs_url` string, nullable: SAML only. The Assertion Consumer Service URL the customer must configure in their IdP. Unique to this template, which is what keeps one customer's assertions off another's connection.
  - `sp_metadata_url` string, nullable: SAML only. Zitadel's **SP** metadata for this template, for customers who prefer importing it over manual entry. Named for the direction: the request bodies' `metadata_url` is the opposite one, the customer's IdP metadata that Zitadel fetches.
  - `certificate_url` string, nullable: SAML only. Zitadel's signing certificate for this template.
  - `slo_url` string, nullable: SAML only. Single-logout endpoint for this template.

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/ethiack/apis/ethiack-api.md) - [All operations](https://skmtc.dev/ethiack/apis/ethiack-api/llms.txt) - [OpenAPI document](https://skmtc.dev/ethiack/apis/ethiack-api/revisions/4eaba89bfebf?raw)
