Integrations
Integrate Sign-in UI
The hosted screens are part of every environment's identity realm, so integrating them is the same authorization code flow described for User Management. This page focuses on the screens themselves: which exist, what they require, and how logout returns to your application. Branding, passwordless and social sign-in are planned, and nothing on this page configures them.
Available in part — Sign-in UI: Hosted sign-up and sign-in screens exist today for each environment's identity realm, with redirect and logout addresses registered in the console; branding settings, additional sign-in methods and validated reset and verification states are planned. Items listed as planned below cannot be enabled yet. What Paycux provides today.
Before you begin
- An environment whose Authentication tab shows ready. The issuer, discovery URL, client ID and realm ID on that card are the only values you need.
- A redirect URL and a logout URL registered exactly as your application uses them. http loopback is accepted only in development and preview environments.
- A client library or your own code for the authorization code flow with PKCE (S256). The realm client is public; it has no secret.
- An inbox you control for the verification and password reset messages the realm sends.
Install
There is nothing to install. Paycux is a plain HTTPS JSON API, so every example on this page uses curl. Console routes send the dashboard session cookie, end-user routes send the application's realm access token as a bearer credential, and agent routes send the short-lived agent token. The routes that exist today are listed on What exists today.
1# Console routes carry the dashboard session cookie set by sign-in2curl https://api.paycux.com/v1/auth/session \3 -H "Cookie: __Host-paycux_session=<session>"45# End-user routes carry the application's realm access token6curl https://api.paycux.com/v1/<end-user route> \7 -H "Authorization: Bearer <realm access token>"
Configure
Nothing is installed and no secret is stored. Your application holds the identifiers the console shows; the realm's client is public and the flow uses PKCE.
1PAYCUX_API_BASE_URL=https://api.paycux.com2PAYCUX_PROJECT_ID=<project id>3PAYCUX_ENVIRONMENT_ID=<environment id>4PAYCUX_REALM_ID=<realm id>5PAYCUX_OIDC_ISSUER=<issuer shown in the console>6PAYCUX_OIDC_CLIENT_ID=<client id shown in the console>7PAYCUX_OIDC_REDIRECT_URI=https://app.example.com/auth/callback8PAYCUX_OIDC_LOGOUT_URI=https://app.example.com/
Then, in the dashboard:
- 1Open the project, choose the environment and open the Authentication tab.
- 2Set the display name; it becomes the realm's display name.
- 3Add every redirect URL your application uses, one per line, exactly as it will be sent in the authorization request.
- 4Add the logout URLs the end-session endpoint may return to.
- 5Save and refresh until the status reads ready. Copy the issuer, client ID and realm ID.
Implement
The realm renders the screens; your code only starts the flow, finishes it and ends it. Read the endpoints from the discovery document rather than hard-coding them.
Build the authorization URL
Fetch the discovery document, then redirect to the authorization endpoint with your client id, the registered redirect URL, the openid, email and profile scopes, a state value and a PKCE S256 challenge. The sign-in screen links to registration and password reset itself.
1const issuer = process.env.PAYCUX_OIDC_ISSUER;2const discovery = await fetch(issuer + "/.well-known/openid-configuration").then((r) => r.json());34const base64url = (bytes) =>5 btoa(String.fromCharCode(...bytes))6 .replaceAll("+", "-")7 .replaceAll("/", "_")8 .replace(/=+$/u, "");9const verifier = base64url(crypto.getRandomValues(new Uint8Array(32)));10const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));11const challenge = base64url(new Uint8Array(digest));12const state = base64url(crypto.getRandomValues(new Uint8Array(16)));13// Store verifier and state in a short-lived, HttpOnly cookie or a server session.1415const url = new URL(discovery.authorization_endpoint);16url.searchParams.set("response_type", "code");17url.searchParams.set("client_id", process.env.PAYCUX_OIDC_CLIENT_ID);18url.searchParams.set("redirect_uri", process.env.PAYCUX_OIDC_REDIRECT_URI);19url.searchParams.set("scope", "openid email profile");20url.searchParams.set("state", state);21url.searchParams.set("code_challenge", challenge);22url.searchParams.set("code_challenge_method", "S256");23// Redirect the browser to url.toString()
Exchange the code
On the callback, compare the returned state with the stored one, then post the code and the PKCE verifier to the token endpoint. There is no client secret. Keep the access token server-side; it is what authenticates end-user calls to the Paycux API.
1// Called at PAYCUX_OIDC_REDIRECT_URI with ?code=...&state=...2const issuer = process.env.PAYCUX_OIDC_ISSUER;3const discovery = await fetch(issuer + "/.well-known/openid-configuration").then((r) => r.json());45const tokens = await fetch(discovery.token_endpoint, {6 method: "POST",7 headers: { "Content-Type": "application/x-www-form-urlencoded" },8 body: new URLSearchParams({9 grant_type: "authorization_code",10 client_id: process.env.PAYCUX_OIDC_CLIENT_ID,11 redirect_uri: process.env.PAYCUX_OIDC_REDIRECT_URI,12 code,13 code_verifier: verifier,14 }),15}).then((r) => r.json());16// tokens.access_token: present it as a bearer credential to the Paycux API.
Confirm the identity Paycux sees
Call GET /v1/identity/me with the access token. The record is created on first use. A 403 EMAIL_UNVERIFIED means the user has not completed verification yet; a 401 CREDENTIAL_INVALID means the token is not from this environment's realm or has expired.
1curl "https://api.paycux.com/v1/identity/me" \2 --header "Authorization: Bearer <realm access token>"34HTTP 2005{6 "id": "<identity id>",7 "realmId": "<realm id>",8 "issuer": "https://<identity host>/realms/pcx-<uuid>",9 "subject": "<subject in the realm>",10 "email": "ada@example.com",11 "emailVerified": true,12 "createdAt": "2026-10-13T09:12:44.000Z"13}
Sign out
Clear your own session first, then send the browser to the end-session endpoint with client_id and a post_logout_redirect_uri that matches one of the registered logout URLs. Without an ID token hint the realm asks the user to confirm before returning.
1const issuer = process.env.PAYCUX_OIDC_ISSUER;2const discovery = await fetch(issuer + "/.well-known/openid-configuration").then((r) => r.json());34const url = new URL(discovery.end_session_endpoint);5url.searchParams.set("client_id", process.env.PAYCUX_OIDC_CLIENT_ID);6url.searchParams.set("post_logout_redirect_uri", process.env.PAYCUX_OIDC_LOGOUT_URI);7// Redirect the browser to url.toString(); the realm returns to the8// registered logout URL afterwards.
Verify
Check each screen once with a real inbox.
- 1Open your sign-in route and confirm the realm's screen loads with the display name you saved.
- 2Register with an address you control, confirm the verification e-mail arrives and its link completes.
- 3Sign in and confirm GET /v1/identity/me answers 200 with emailVerified true.
- 4Use the forgot-password link and confirm the reset e-mail arrives.
- 5Sign out and confirm the realm returns to a registered logout URL.
- 6Send an authorization request with an unregistered redirect URL and confirm the realm refuses it.
Reference
Request and response shapes for each of these live in the API reference. Paths are relative to https://api.paycux.com.
| Method | Path | What it does |
|---|---|---|
| GET | /v1/environments/:environmentId/auth?projectId= | Setup status plus issuer, discovery URL, client ID and realm ID. Dashboard session cookie. |
| PUT | /v1/environments/:environmentId/auth?projectId= | Save display name, redirect, logout and checkout-return URLs. Owner or admin. |
| GET | /v1/identity/me | Identity behind the realm access token. Bearer token; verified e-mail required. |
| GET | /v1/environments/:environmentId/users?projectId= | Read-only list of identities that have presented a token. Dashboard session cookie. |