# Admin Portal

## Introduction

The Admin Portal provides an out-of-the-box UI for IT contacts to verify domains, configure SSO and Directory Sync connections, and more. Designed to remove friction, custom walk-through documentation for each identity provider means that IT contacts can onboard their organizations without high-touch support from your team. Easy to integrate and fully maintained and hosted by WorkOS, the Admin Portal makes Domain Verification, SSO, and Directory Sync setup simple, fast, and secure.

![A screenshot showing the IdP selection in the WorkOS Admin Portal.](https://images.workoscdn.com/images/dd00d92d-2810-484a-a3c7-4e0fdb8703a7.png?auto=format\&fit=clip\&q=50)

## Workflow Options

There are two main workflows for initiating an Admin Portal session for IT contacts. You can either share a link to the Admin Portal from the WorkOS dashboard, or you can seamlessly integrate Admin Portal into your application through WorkOS SDKs or APIs.

![A screenshot showing the different workflows for creating an Admin Portal shareable link.](https://images.workoscdn.com/images/33851982-5baf-4ffe-8b41-71054b95948b.png?auto=format\&fit=clip\&q=50)

If you want to provide IT contacts with a link to the Admin Portal, in an email for example, then you would need to create that link in the WorkOS dashboard.

However, if you are adding a button to open the Admin Portal from within your application, then you would need to use the API.

| Workflow                        | Use cases                    | Security                                                                         | Return URL and Success URLs                                                                                                                |
| ------------------------------- | :--------------------------- | :------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------- |
| Share a link from the dashboard | Setup only                   | Can be revoked; Automatically revoked on setup completion; Expires after 30 days | Not applicable                                                                                                                             |
| Generate a link via the API     | Setup and post-configuration | Cannot be revoked; Expires after 5 minutes                                       | Can be configured on the [Redirects](https://dashboard.workos.com/redirects) page in the dashboard or specified as a parameter for the API |

***

## Before getting started

To get the most out of these guides, you’ll need:

- A [WorkOS account](https://dashboard.workos.com/)

## API object definitions

[Connection](https://workos.com/docs/reference/sso/connection)
: Represents the method by which users of an organization sign in to your application.

[Organization](https://workos.com/docs/reference/organization)
: Describes an organization whose users sign in with an SSO connection, or whose users are synced with a Directory Sync connection.

[Portal link](https://workos.com/docs/reference/admin-portal/portal-link)
: A temporary link to initiate an Admin Portal session.

## (A) Setup link from WorkOS dashboard

The Admin Portal setup link gives your customer access to a guided configuration experience through our Admin Portal. It instructs them how to verify a domain, configure their identity or directory provider, and more. If successfully configured, no other action is required and you’ll see verified domains and/or active connections appear under the organization.

First decide whether your customer will be configuring Domain Verification, Single Sign-On, Directory Sync, Log Streams, or all of the above. Once you generate a link, the customer will have access for 30 days or until configured.

You’ll need a [WorkOS dashboard account](https://dashboard.workos.com/) to create an organization that will represent the enterprise you are onboarding.

### Create organization

Sign in to your WorkOS dashboard account and create a new organization.

![WorkOS dashboard UI showing organization creation](https://images.workoscdn.com/images/1c69fd98-01be-491d-9255-58363bc6a983.png?auto=format\&fit=clip\&q=50)

### Generate a setup link

Click the “Invite IT contact” button, select the features to include and then click “Next." Enter the emails of the IT contacts for the organization to automatically send them a setup link, or click "Copy setup link." Only one link can be active at a time. After creating the initial link, you can click the “Manage” button to revoke the existing link before creating a new one.

### Sharing a setup link

If you chose to copy the setup link you can share it over email, Slack or direct message. We also recommend including details on what the link does and how long the link is active.

## (B) Integrate with your app

In this guide, we’ll walk you through the full end-to-end integration of the Admin Portal into your application.

> [Sign in](https://dashboard.workos.com/) to your WorkOS dashboard account to see code examples pre-filled with your test API keys and resource IDs.

### Configure Admin Portal redirect links

In order to integrate, you must configure your app's default return URI in the production environment. A button in the Admin Portal will use this value to allow users to return to your app unless otherwise specified when generating the Admin Portal link.

![A screenshot showing the Admin Portal Redirect Links tab to set redirect URIs in the WorkOS dashboard.](https://images.workoscdn.com/images/9df0f214-0350-466c-b54b-8a1e0be6b678.png?auto=format\&fit=clip\&q=50)

Additionally, you can configure success URIs to redirect users upon successfully setting up Single Sign-On, Directory Sync, or Log Streams.

![A screenshot showing the Admin Portal redirect URI variations in the WorkOS dashboard.](https://images.workoscdn.com/images/3d75975c-b36a-4bfc-b05d-d745b8ba916b.png?auto=format\&fit=clip\&q=50)

> All redirect links must use HTTPS.

You can configure these links in the [dashboard](https://dashboard.workos.com/).

### Install the WorkOS SDK

WorkOS offers native SDKs in several popular programming languages. Choose a language below to see instructions in your application’s language.

### Set secrets

To make calls to WorkOS, provide the API key and, in some cases, the client ID. Store these values as managed secrets, such as `WORKOS_API_KEY` and `WORKOS_CLIENT_ID`, and pass them to the SDKs either as environment variables or directly in your app's configuration based on your preferences.

```plain title="Environment variables"
WORKOS_API_KEY='sk_example_123456789'
WORKOS_CLIENT_ID='client_123456789'
```

### Create a new organization

Each Admin Portal session is scoped to a specific organization resource, meaning a session is only capable of managing a connection that belongs to its associated organization. Organizations may only have one connection.

For every customer in your application that would like access to the Admin Portal, you must create an organization and maintain a reference to its ID.

> Create an organization when onboarding a new customer.

#### Create an organization

```go
package main

import (
	"net/http"
	"os"

	"github.com/workos/workos-go/v3/pkg/organizations"
)

func main() {
	apiKey := os.Getenv("WORKOS_API_KEY")

	organizations.SetAPIKey(apiKey)

	http.HandleFunc("/provision-enterprise", func(w http.ResponseWriter, r *http.Request) {
		organizationName := "Example Organization"
		organizationDomains := []OrganizationDomainDataOptions{
			{
				Domain: "foo-corp.com",
				State:  "pending",
			},
		}

		organization, err := organizations.CreateOrganization(r.Context(), organizations.CreateOrganizationOpts{
			Name:       organizationName,
			DomainData: organizationDomains,
		})

		if err != nil {
			// Handle the error...
		}

		// You should persist `organization.ID` since it will be needed
		// to generate a Portal Link.

		// Provision additional Enterprise-tier resources.
	})
}
```

```php
<?php

require __DIR__ . "/vendor/autoload.php";

$organizations = new WorkOS\Organizations();

switch (strtok($_SERVER["REQUEST_URI"], "?")) {
    case "/provision-enterprise":
        $organizationName = "Example Organization";
        $organizationDomains = [
            [
                "domain" => "foo-corp.com",
                "state" => "pending",
            ],
        ];

        $organization = $organizations->createOrganization(
            name: $organizationName,
            domain_data: $organizationDomains
        );

    // You should persist `organization["id"]` since it will be needed
    // to generate a Portal Link.

    // Provision additional Enterprise-tier resources.
}
```

```php
<?php

use Illuminate\Support\Facades\Route;

$organizations = new WorkOS\Organizations();

Route::post("/provision-enterprise", function () {
    $organizationName = "Example Organization";
    $organizationDomains = [
        [
            "domain" => "foo-corp.com",
            "state" => "pending",
        ],
    ];

    $organization = $organizations->createOrganization(
        name: $organizationName,
        domain_data: $organizationDomains
    );

    // You should persist `organization["id"]` since it will be needed
    // to generate a Portal Link.

    // Provision additional Enterprise-tier resources.
});
```

```java
import com.workos.WorkOS;
import com.workos.organizations.OrganizationsApi.CreateOrganizationOptions;
import com.workos.organizations.OrganizationsApi.OrganizationDomainDataOptions;
import com.workos.organizations.OrganizationsApi.OrganizationDomainDataState;
import com.workos.organizations.models.Organization;
import io.javalin.Javalin;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class Application {
  public static void main(String[] args) {
    Map<String, String> env = System.getenv();
    Javalin app = Javalin.create().start(7001);
    WorkOS workos = new WorkOS(env.get("WORKOS_API_KEY"));
    String clientId = env.get("WORKOS_CLIENT_ID");

    app.post("/provision-enterprise", ctx -> {
      String organizationName = "Example Organization";
      List<OrganizationDomainDataOptions> organizationDomains =
          new ArrayList<OrganizationDomainDataOptions>(new OrganizationDomainDataOptions(
              "foo-corp.com", OrganizationDomainDataState.Pending));

      CreateOrganizationOptions options = CreateOrganizationOptions.builder()
                                              .name(organizationName)
                                              .domainData(organizationDomains)
                                              .build();

      Organization organization = workos.organizations.createOrganization(options);

      // You should persist `organization.Id` since it will be needed
      // to generate a Portal Link.
    });
  }
}
```

```cs
using System;
using Microsoft.AspNetCore.Mvc;
using WorkOS;

namespace MyApplication.Controllers
{
    public class ProvisionEnterpriseController : Controller
    {
        [HttpPost("provision-enterprise")]
        public async Task<IActionResult> Index()
        {
            var organizationsService = new OrganizationsService();

            // The name of the organization to provision
            string organizationName = "Example Organization";

            // The list of domains the organization uses
            OrganizationDomDataOptions[] organizationDomains = {
                new OrganizationDomainDataOptions {
                    Domain = "example.com",
                    State = OrganizationDomainDataState.Pending,
                },
            };

            var options = new CreateOrganizationOptions {
                Name = organizationName,
                DomainData = organizationDomains,
            };

            var organization = await organizationsService.CreateOrganization(options);

            // You should persist `organization.Id` since it will be needed
            // to generate a Portal Link.
        }
    }
}
```

### Redirect IT contacts to the Admin Portal

A Portal link is your enterprise user's gateway to accessing the Admin Portal, where they can set up and manage resources scoped to their organization. To generate a Portal link using the API, you must provide the organization ID and specify one of the following intents: [`sso`](https://workos.com/docs/admin-portal/c-using-admin-portal/managing-sso-connections), [`dsync`](https://workos.com/docs/admin-portal/c-using-admin-portal/managing-directories), [`audit_logs`](https://workos.com/docs/audit-logs/admin-portal/creating-admin-portal-link), [`log_streams`](https://workos.com/docs/audit-logs/log-streams/admin-portal), [`domain_verification`](https://workos.com/docs/domain-verification/admin-portal-domain-verification), or [`certificate_renewal`](https://workos.com/docs/sso/signing-certificates/saml-response-signing-certificate/renewing-certificates).

For security reasons, Portal links expire 5 minutes after they’re created, so we recommend redirecting users immediately (i.e. don’t email the user Portal links).

> The endpoint that redirects a user to the Admin Portal should be guarded by auth in your application and only available to IT contacts.

#### Redirect to Admin Portal

```go
package main

import (
	"net/http"
	"os"

	"github.com/workos/workos-go/v3/pkg/portal"
)

func main() {
	apiKey := os.Getenv("WORKOS_API_KEY")

	portal.SetAPIKey(apiKey)

	http.HandleFunc("/admin-portal", func(w http.ResponseWriter, r *http.Request) {
		// The ID of the organization to start an Admin Portal session for
		organizationID := "org_123"

		link, err := portal.GenerateLink(r.Context(), portal.GenerateLinkOpts{
			Organization: organizationID,
			Intent:       portal.SSO,
		})

		if err != nil {
			// Handle the error...
		}

		http.Redirect(w, r, link.Link, http.StatusSeeOther)
	})
}
```

```php
<?php

require __DIR__ . "/vendor/autoload.php";

$portal = new WorkOS\Portal();

switch (strtok($_SERVER["REQUEST_URI"], "?")) {
    case "/admin-portal":
        // The ID of the organization to start an Admin Portal session for
        $organizationId = "org_123";

        $portalLink = $portal->generateLink(
            organization: $organizationId,
            intent: "sso"
        );

        $url = $portalLink["link"];

        header("Location: $url", true, 302);
        return true;
}
```

```php
<?php

use Illuminate\Support\Facades\Route;

$portal = new WorkOS\Portal();

Route::get("/admin-portal", function () {
    // The ID of the organization to start an Admin Portal session for
    $organizationId = "org_123";

    $portalLink = $portal->generateLink(
        organization: $organizationId,
        intent: "sso"
    );

    return redirect($portalLink["link"]);
});
```

```java
import com.workos.WorkOS;
import com.workos.portal.PortalApi.GeneratePortalLinkOptions;
import com.workos.portal.models.Intent;
import com.workos.portal.models.Link;
import io.javalin.Javalin;
import java.util.Map;

public class Application {
  public static void main(String[] args) {
    Map<String, String> env = System.getenv();
    Javalin app = Javalin.create().start(7001);
    WorkOS workos = new WorkOS(env.get("WORKOS_API_KEY"));
    String clientId = env.get("WORKOS_CLIENT_ID");

    // The ID of the organization to start an Admin Portal session for
    String organizationId = "org_123";

    app.get("/admin-portal", ctx -> {
      Link url = workos.portal.generateLink(GeneratePortalLinkOptions.builder()
                                                .organization(organizationId)
                                                .intent(Intent.Sso)
                                                .build());

      ctx.redirect(url.link);
    });
  }
}
```

```cs
using System;
using Microsoft.AspNetCore.Mvc;
using WorkOS;

namespace MyApplication.Controllers
{
    public class AdminPortalController : Controller
    {
        [HttpGet("admin-portal")]
        public async Task<IActionResult> Index()
        {
            var portalService = new PortalService();

            // The ID of the organization to start an Admin Portal session for
            string organizationId = "org_123";

            var options = new GenerateLinkOptions {
                Intent = Intent.SSO,
                Organization = organizationId,
            };

            var link = await portalService.GenerateLink(options);

            return Redirect(link);
        }
    }
}
```

An [optional return\_url parameter](https://workos.com/docs/reference/admin-portal/portal-link/generate) can be used to describe exactly where a user should be sent when they are finished in the Admin Portal. If one is not provided, the success URL configured on the [Redirects](https://dashboard.workos.com/redirects) page of the dashboard will be used.

### Add IT contacts programmatically

When generating a Portal link via the API, emails can be added as IT contacts for the organization at the same time by passing the optional `it_contact_emails` parameter. This mirrors the behavior of the "Invite IT contact" flow in the WorkOS dashboard. IT contacts receive future organization notifications, such as alerts for expiring SSO certificates.

```bash title="Generate a Portal link and add IT contacts"
curl --request POST \
  --url "https://api.workos.com/portal/generate_link" \
  --header "Authorization: Bearer sk_example_123456789" \
  --header "Content-Type: application/json" \
  -d '{
    "organization": "org_01EHZNVPK3SFK441A1RGBFSHRT",
    "intent": "sso",
    "it_contact_emails": ["it-contact@example.com", "it-lead@example.com"]
  }'
```

Any email that isn't already an IT contact for the organization will be added; existing IT contacts remain unchanged, so the same call can be made repeatedly with the full list of contacts without creating duplicates. Email matching is case-insensitive.

An organization can have up to 20 IT contacts at a time. A request that would exceed this limit is rejected and no IT contacts from the request are created. Invalid email addresses are rejected without creating any IT contacts.

Adding an email via `it_contact_emails` does not restrict who can use the Portal link, and it does not send a notification to the IT contact. The Portal link must still be delivered by the calling application—typically by redirecting the current authenticated user immediately.

See the [Generate a Portal Link reference](https://workos.com/docs/reference/admin-portal/portal-link/generate) for the full request schema.

## (C) Using Admin Portal

In this guide, we’ll review some of the features of Admin Portal from an IT contact's perspective.

### Verifying a domain

In the Admin Portal [Domain Verification](https://workos.com/docs/domain-verification/admin-portal-domain-verification) flow, you can view instructions on adding a DNS TXT record to prove ownership of your organization's domain(s).

> Unless an organization [allows any domain](https://workos.com/docs/sso/domains/allowing-any-domain), a verified domain is required in order to activate SSO. Domains can also be [manually verified](https://workos.com/docs/authkit/domain-verification/self-serve-domain-verification) outside of the self-serve Admin Portal flow if the IT contact has already proven domain ownership in another context.

### Managing SSO connections

On the Admin Portal SSO screen, you can view the identity provider details and connection status, metadata configuration details, and a list of recent connection sessions. You may test your SSO connection from the Admin Portal by using the “Test sign-in” button.

![A screenshot showing the Admin Portal SSO screen and where to click the “Test Single Sign-On” button.](https://images.workoscdn.com/images/b9ab3cc1-c524-4eae-9a25-f4c6a4059683.png?auto=format\&fit=clip\&q=50)

You may also edit your metadata configuration from the Admin Portal.

![A screenshot showing the Admin Portal SSO screen and where to "Edit Metadata Configuration".](https://images.workoscdn.com/images/2ada73aa-dfa4-45c8-afc8-2e69a3a9b4fc.png?auto=format\&fit=clip\&q=50)

The Sessions section displays a list of recent sessions by timestamp, and can be sorted by `state`.

![A screenshot showing the Admin Portal SSO screen and how to sort "Sessions" by "state".](https://images.workoscdn.com/images/0b1b05a0-8c18-4c99-8d02-22f2b4f2bf46.png?auto=format\&fit=clip\&q=50)

Click on a session in the list to see session details, such as the request made to the IdP, and the response.

![A screenshot showing the "Session Details" within the Admin Portal SSO screen.](https://images.workoscdn.com/images/6655ae75-c0c2-4b0c-be7b-b21dbfb6aadd.png?auto=format\&fit=clip\&q=50)

If you wish to reset your SSO connection and set it up from scratch, select “Reset Connection” and follow the prompts.

![A screenshot showing how to "Reset Connection" within the Admin Portal SSO screen.](https://images.workoscdn.com/images/b0f2919d-07f2-457d-8499-1b617235c485.png?auto=format\&fit=clip\&q=50)

### Managing directories

On the Admin Portal Directory Sync screen, you can view the directory provider details and connection status, user and group counts, and last sync time. There is also an option to reset the directory.

![A screenshot showing Admin Portal Directory Sync screen details.](https://images.workoscdn.com/images/13be17b8-46da-4c2c-801f-029be3686101.png?auto=format\&fit=clip\&q=50)

You may also view and edit the attribute map from the synced directory by clicking "Edit Attribute Map".

![A screenshot showing editing the attribute map in the Admin Portal Directory Sync screen.](https://images.workoscdn.com/images/c6dd347f-15dc-4918-9820-946f38361c2e.png?auto=format\&fit=clip\&q=50)

If you wish to update the groups that are being synced, select "Edit groups sync” and follow the prompts on the next page.

![A screenshot showing editing user groups in the Admin Portal Directory Sync screen.](https://images.workoscdn.com/images/05fc69da-3688-4c90-873b-505be64779b9.png?auto=format\&fit=clip\&q=50)

You can also view a complete list of users from all selected groups in your synced directory.

![A screenshot showing the user list in the Admin Portal Directory Sync screen.](https://images.workoscdn.com/images/f581e97e-2052-4635-805c-fad3c47f792e.png?auto=format\&fit=clip\&q=50)
