Skip to content

Integrations

Integrate Admin Portal

Admin Portal replaces the setup call. You generate a signed, short-lived link, your customer's IT admin opens it and configures their own SSO connection or directory, and you find out it worked from a webhook. The whole integration is one endpoint.

Before you begin

  • An organization for the customer. The portal session is scoped to it, and that scoping is what keeps one customer out of another's configuration.
  • An admin surface of your own where a link can be generated. Anyone who can create a portal link can configure that tenant's identity, so protect the route.
  • A webhook endpoint if you want to know when setup finishes without asking.
  • Your logo and brand colour, if the portal should look like part of your product.

Install

Every example on this page uses the Node.js client. The same calls exist in the Python, Go, Ruby, PHP and Java SDKs, following the naming conventions of each language.

terminal
1npm i @paycux/node

Configure

The portal has no separate credentials. It uses your API key, and the return URL you pass decides where the admin lands when they are finished.

.env.local
1PAYCUX_API_KEY=sk_example_7f4c1b9ad2e84c6f
2PAYCUX_PORTAL_RETURN_URL=https://yourapp.example/settings/sso
3PAYCUX_WEBHOOK_SECRET=whsec_example_2b6d9f31

Then, in the dashboard:

  1. 1Open Admin Portal and upload your logo, set the brand colour and write the short intro line the admin reads first.
  2. 2Choose which intents to allow: SSO, directory sync, domain verification, or a combination.
  3. 3Set the link lifetime. A few minutes is right for a link you generate on demand; longer only if you email it.
  4. 4Set the default return URL, then override it per link when you need to send someone back to a specific screen.
  5. 5Subscribe to connection and directory events so setup completion reaches your app on its own.

Implement

Generate the link at the moment the admin clicks, never in advance, and never store it. Portal links are bearer credentials with an expiry, and the shorter their life the less there is to leak.

Generate a portal link

Check the caller's permission first, then request a link for the intent you want. Redirect straight to it rather than rendering it into the page, so it does not end up in a screenshot or a browser history.

app/api/portal/route.ts
1import { Paycux } from "@paycux/node";
2import { requirePermission } from "@/lib/authz";
3
4const paycux = new Paycux(process.env.PAYCUX_API_KEY);
5
6export async function POST(request: Request) {
7 const { organizationId } = await requirePermission("organization:manage");
8 const { intent } = await request.json();
9
10 const link = await paycux.portal.generateLink({
11 organization: organizationId,
12 intent,
13 returnUrl: process.env.PAYCUX_PORTAL_RETURN_URL,
14 successUrl: process.env.PAYCUX_PORTAL_RETURN_URL + "?setup=done",
15 });
16
17 return Response.redirect(link.url, 303);
18}

Show the current setup state

Your settings screen should say what is connected before it offers to connect anything. Read the organization's connections and directories and render one row each.

app/settings/sso/state.ts
1import { Paycux } from "@paycux/node";
2
3const paycux = new Paycux(process.env.PAYCUX_API_KEY);
4
5export async function ssoState(organizationId: string) {
6 const [connections, directories] = await Promise.all([
7 paycux.sso.listConnections({ organizationId }),
8 paycux.directorySync.listDirectories({ organizationId }),
9 ]);
10
11 return {
12 sso: connections.data.map((c) => ({ id: c.id, type: c.connectionType, state: c.state })),
13 directory: directories.data.map((d) => ({ id: d.id, type: d.type, state: d.state })),
14 };
15}

React to a finished setup

The admin will not always tell you they are done. Listen for the activation events and update your own onboarding checklist from them.

app/api/webhooks/route.ts
1import { Paycux } from "@paycux/node";
2
3const paycux = new Paycux(process.env.PAYCUX_API_KEY);
4
5export async function POST(request: Request) {
6 const payload = await request.text();
7 const signature = request.headers.get("paycux-signature");
8
9 const event = paycux.webhooks.verify({
10 payload,
11 signature,
12 secret: process.env.PAYCUX_WEBHOOK_SECRET,
13 });
14
15 if (event.event === "connection.activated" || event.event === "dsync.activated") {
16 await completeOnboardingStep(event.data.organizationId, event.event);
17 }
18
19 return new Response("ok", { status: 200 });
20}

Verify

Walk the portal as your customer will, from your own settings screen rather than from a link you pasted into the address bar.

  1. 1Open your settings screen as an organization admin and confirm the setup button is hidden for a member without the permission.
  2. 2Click through to the portal and confirm it shows your logo, your colour and only the intents you enabled.
  3. 3Complete an SSO setup with a provider tenant you control and confirm you are returned to your success URL.
  4. 4Confirm your app received the activation event and that the settings screen now shows the connection as active.
  5. 5Reuse the same portal link after it expires and confirm it is refused rather than reopened.

Reference

Request and response shapes for each of these live in the API reference. Paths are relative to https://api.paycux.com.

MethodPathWhat it does
POST/portal/generate_linkCreate a short-lived setup link for one organization.
GET/organizations/:idRead the organization the link will be scoped to.
GET/connectionsList SSO connections for an organization.
GET/directoriesList directories for an organization.
GET/organization_domainsList claimed domains and their verification state.
POST/organization_domainsClaim a domain so it can be verified in the portal.

Next steps