Integrations
Integrate User Management
This page covers what can be executed today: provisioning the environment's identity realm from the console, running the authorization code flow against it, and reading identities and organisations with the resulting token. Administrative user actions, profile updates and user events are planned and are not described here as available.
Available in part — User Management: Each environment gets its own customer identity realm today, with hosted sign-up and sign-in, a bearer credential for your application's users and a read-only user list in the console; profile and session administration, account deletion and audited user management are planned. Items listed as planned below cannot be enabled yet. What Paycux provides today.
Before you begin
- A Paycux workspace with a project and an environment. Each environment gets its own realm and user pool; development and preview environments may use http loopback URLs, every other environment requires HTTPS.
- An owner or admin role in the workspace. Only owners and admins can save the authentication configuration; every role can read it and the user list.
- An application that can complete an OpenID Connect authorization code flow with PKCE (S256). The realm client is public, so there is no client secret to store.
- Exact redirect, logout and checkout-return URLs. Origins, wildcards, credentials and fragments are refused, and a URL must round-trip unchanged.
- Nothing to install. Every call is plain HTTP, and the identity values you need are shown in the console once setup is ready.
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
Your application holds only public identifiers: the API base URL, the project and environment ids used in query strings and request bodies, the realm id, and the OIDC issuer and client id the console shows once setup is ready. There is no secret key. The dashboard session cookie is set by the console's own sign-in and is not something an integration stores.
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/callback
Then, in the dashboard:
- 1Open the project, choose the environment and open the Authentication tab (Customer authentication).
- 2Enter the display name your users will see, then the exact redirect URLs, logout URLs and checkout return URLs. Save; the API answers 202 and the status shows pending.
- 3Refresh after a moment. When the saved revision has been applied the status is ready and the card shows the OIDC issuer, discovery URL, client ID and realm ID. A failed status explains the cause.
- 4Copy the issuer, client ID and realm ID into your configuration.
- 5Open the Users tab. It stays empty until the first user presents a token; identities are created on first use.
Implement
The flow has three parts. Your application sends people to the realm's authorization endpoint, which serves registration, e-mail verification, sign-in and password reset. The callback exchanges the code for tokens with PKCE and no client secret. From then on the access token authenticates end-user routes on the Paycux API, and the dashboard session cookie authenticates console reads.
Read the environment's authentication setup
A console read. Any workspace role may call it, and the response is the same object the Authentication tab renders. setup is null until the configuration has been saved once.
1curl "https://api.paycux.com/v1/environments/<environment id>/auth?projectId=<project id>" \2 --cookie "__Host-paycux_session=<session cookie>"34HTTP 2005{6 "setup": {7 "workspaceId": "<workspace id>",8 "projectId": "<project id>",9 "environmentId": "<environment id>",10 "realmId": "<realm id>",11 "providerRealmName": "pcx-<uuid>",12 "displayName": "Example App",13 "redirectUris": ["https://app.example.com/auth/callback"],14 "logoutRedirectUris": ["https://app.example.com/"],15 "checkoutReturnUrls": [16 "https://app.example.com/billing/cancel",17 "https://app.example.com/billing/done"18 ],19 "status": "ready",20 "revision": 2,21 "appliedRevision": 2,22 "issuer": "https://<identity host>/realms/pcx-<uuid>",23 "clientId": "<client id>",24 "discoveryUrl": "https://<identity host>/realms/pcx-<uuid>/.well-known/openid-configuration",25 "lastErrorCode": null26 }27}
Start the sign-in from the discovery document
Runs on your server. Read the endpoints from the discovery document instead of hard-coding them, then redirect to the authorization endpoint with your client id, a registered redirect URL, 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());34// PKCE: a random verifier and its S256 challenge.5const base64url = (bytes) =>6 btoa(String.fromCharCode(...bytes))7 .replaceAll("+", "-")8 .replaceAll("/", "_")9 .replace(/=+$/u, "");10const verifier = base64url(crypto.getRandomValues(new Uint8Array(32)));11const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));12const challenge = base64url(new Uint8Array(digest));13const state = base64url(crypto.getRandomValues(new Uint8Array(16)));14// Store verifier and state in a short-lived, HttpOnly cookie or a server session.1516const url = new URL(discovery.authorization_endpoint);17url.searchParams.set("response_type", "code");18url.searchParams.set("client_id", process.env.PAYCUX_OIDC_CLIENT_ID);19url.searchParams.set("redirect_uri", process.env.PAYCUX_OIDC_REDIRECT_URI);20url.searchParams.set("scope", "openid email profile");21url.searchParams.set("state", state);22url.searchParams.set("code_challenge", challenge);23url.searchParams.set("code_challenge_method", "S256");24// Redirect the browser to url.toString(). The realm renders sign-in,25// with links to registration and password reset.
Exchange the code and read the identity
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. The access token authenticates end-user routes; GET /v1/identity/me confirms which identity Paycux sees. A 403 EMAIL_UNVERIFIED means the user has not completed verification yet.
1// Called at PAYCUX_OIDC_REDIRECT_URI with ?code=...&state=...2// Compare state with the stored value first; stop if it differs.3const issuer = process.env.PAYCUX_OIDC_ISSUER;4const discovery = await fetch(issuer + "/.well-known/openid-configuration").then((r) => r.json());56const tokens = await fetch(discovery.token_endpoint, {7 method: "POST",8 headers: { "Content-Type": "application/x-www-form-urlencoded" },9 body: new URLSearchParams({10 grant_type: "authorization_code",11 client_id: process.env.PAYCUX_OIDC_CLIENT_ID,12 redirect_uri: process.env.PAYCUX_OIDC_REDIRECT_URI,13 code,14 code_verifier: verifier,15 }),16}).then((r) => r.json());1718// Keep the access token server-side; it authenticates end-user routes.19const me = await fetch(process.env.PAYCUX_API_BASE_URL + "/v1/identity/me", {20 headers: { Authorization: "Bearer " + tokens.access_token },21});22// 200: the identity record, created on first use.23// 403 EMAIL_UNVERIFIED: ask the user to finish verification first.
Create an organisation and invite a member
End-user routes with the realm access token. The scope in the body must match the token. The invite token is returned once and stored only as a hash; it expires after seven days, and acceptance requires the invited address to be the accepting user's verified e-mail.
1# Create an organisation; the caller becomes its owner2curl --request POST "https://api.paycux.com/v1/billing/organizations" \3 --header "Authorization: Bearer <realm access token>" \4 --header "Content-Type: application/json" \5 --data '{"projectId":"<project id>","environmentId":"<environment id>","realmId":"<realm id>","displayName":"Example Ltd"}'67HTTP 2018{ "billingAccountId": "<billing account id>", "kind": "organization", "role": "owner" }910# Invite a member or admin by e-mail11curl --request POST "https://api.paycux.com/v1/billing/organizations/<billing account id>/invites" \12 --header "Authorization: Bearer <realm access token>" \13 --header "Content-Type: application/json" \14 --data '{"projectId":"<project id>","environmentId":"<environment id>","realmId":"<realm id>","email":"[email protected]","role":"member"}'1516HTTP 20117{ "inviteId": "<invite id>", "token": "<invite token, shown once>", "role": "member", "expiresAt": "2026-10-20T09:12:44.000Z" }1819# The invited user accepts with their own realm access token20curl --request POST "https://api.paycux.com/v1/billing/invites/accept" \21 --header "Authorization: Bearer <invited user realm access token>" \22 --header "Content-Type: application/json" \23 --data '{"projectId":"<project id>","environmentId":"<environment id>","realmId":"<realm id>","token":"<invite token>"}'2425HTTP 20026{ "billingAccountId": "<billing account id>", "role": "member", "status": "active" }
List users from the console session
Read-only. Any workspace role can page through the identities of an environment with limit and cursor; a single record is available at /users/:identityId. There is no write route for users today.
1curl "https://api.paycux.com/v1/environments/<environment id>/users?projectId=<project id>&limit=50" \2 --cookie "__Host-paycux_session=<session cookie>"34HTTP 2005{6 "items": [7 {8 "id": "<identity id>",9 "realmId": "<realm id>",10 "issuer": "https://<identity host>/realms/pcx-<uuid>",11 "subject": "<subject in the realm>",12 "email": "[email protected]",13 "emailVerified": true,14 "createdAt": "2026-10-13T09:12:44.000Z"15 }16 ],17 "nextCursor": null18}1920# Next page: repeat with &cursor=<nextCursor> while nextCursor is not null
Verify
Run the flow once end to end with an address you can receive mail at. Each step leaves a record you can check.
- 1Confirm the Authentication tab shows ready, with an issuer, discovery URL, client ID and realm ID.
- 2Open the registration screen through your application, register, and confirm the verification e-mail arrives and its link completes.
- 3Sign in, complete the callback and call GET /v1/identity/me with the access token. Expect 200 with emailVerified true.
- 4Open the Users tab, or call GET /v1/environments/:environmentId/users, and confirm the identity is listed with its e-mail and first access time.
- 5Use the forgot-password link on the sign-in screen and confirm the reset e-mail arrives.
- 6Sign out through the end-session endpoint and confirm you land on one of the registered logout URLs.
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= | Read the environment's authentication setup, or null before it is configured. Dashboard session cookie. |
| PUT | /v1/environments/:environmentId/auth?projectId= | Save display name, redirect, logout and checkout-return URLs. Owner or admin. 202 pending; 200 when an unchanged ready setup is resubmitted. |
| GET | /v1/environments/:environmentId/users?projectId=&limit=&cursor= | Page through the identities of an environment. Read-only; any workspace role. |
| GET | /v1/environments/:environmentId/users/:identityId?projectId= | Read one identity record. |
| GET | /v1/identity/me | The identity behind a realm access token. Bearer token; verified e-mail required. |
| POST | /v1/billing/organizations | An end user creates an organisation and becomes its owner. Bearer token. |
| GET | /v1/billing/accounts | Billing accounts and organisations the token's identity belongs to. |
| POST | /v1/billing/organizations/:billingAccountId/invites | Invite an e-mail address as member or admin. Owner or admin of the organisation; token returned once. |
| POST | /v1/billing/invites/accept | Accept an invite with its token; the caller's verified e-mail must match the invited address. |
| GET | /v1/workspace/members | Your own workspace's members and roles. Dashboard session cookie. |
| POST | /v1/workspace/members/invites | Invite a colleague into the workspace with a role. Owner or admin; counts against the plan's member limit. |