Skip to content

Reference

Connections

A connection is one customer's identity provider, wired to one organization. SAML and OIDC differences are absorbed before your code runs, so a connection is mostly something you read rather than something you build: it is created by an IT admin through the Admin Portal, and your application asks for a sign-in against it.

The connection object

There is no POST /connections. Connections are created by the customer, either through an Admin Portal link you generate or by an operator pasting provider metadata into the dashboard — because creating one means handling a signing certificate and a set of endpoints that only the customer's IT team can supply.

FieldTypeDescription
idstringUnique identifier, prefixed conn_.
objectstringAlways "connection".
organization_idstringThe tenant this connection signs people in to. Always present.
namestringDisplay name, usually chosen by the IT admin who created it.
connection_typestringThe provider family, such as OktaSAML, AzureSAML, GoogleSAML, GenericSAML or GenericOIDC. Informational only — the profile shape does not change with it.
statestringdraft, validating, active or inactive. Only an active connection accepts a sign-in.
domainsarrayEmail domains that route to this connection, inherited from the organization.
connection_domainsarrayDomain records with their own ids, present when a connection covers a subset of the organization's domains.
created_attimestampRFC 3339.
updated_attimestampRFC 3339. Moves when an admin edits the connection in the portal.

Connection states

State is the field your own admin screen should surface. Most support tickets that begin with SSO is broken end with a connection that never left draft, or one an admin deactivated during a provider migration and forgot to switch back on.

StateMeaningWhat to do
draftCreated but not yet configuredThe admin has opened the portal and not finished. Sign-ins are refused.
validatingConfiguration submitted, first assertion not yet seenTest it before you tell the customer it is live.
activeWorkingSign-ins are accepted and profiles are returned.
inactiveDisabled by you or by the customerSign-ins are refused; the record and its history are kept.

Do not treat a missing connection as an error

Most organizations will never have one. When a lookup returns an empty list, fall back to your ordinary sign-in rather than showing a failure.

Endpoints

Paths are relative to https://api.paycux.com. The reference overview covers authentication.

MethodPathWhat it does
GET/connectionsList connections, filtered by organization, domain or state.
GET/connections/:idRetrieve one connection with its provider type and current state.
DELETE/connections/:idRemove a connection and stop accepting its assertions.
GET/sso/authorizeRedirect a browser into the customer's identity provider.
POST/sso/tokenSwap a single-use authorization code for a normalised profile.
POST/portal/generate_linkCreate a short-lived link that lets a customer configure their own connection.

List connections

The usual call is scoped to one organization, to answer whether this customer has SSO at all. Filtering on state=active additionally excludes the half-finished ones an admin abandoned in the portal last week.

GET /connections
1curl "https://api.paycux.com/connections?organization_id=org_01HQ8ZK3M4N5P6R7S8T9V0W1X2&state=active" \
2 -H "Authorization: Bearer sk_example_123456789"
response
1HTTP 200
2{
3 "object": "list",
4 "data": [
5 {
6 "object": "connection",
7 "id": "conn_01HQ8ZK3M4N5P6R7S8T9V0W1X2",
8 "organization_id": "org_01HQ8ZK3M4N5P6R7S8T9V0W1X2",
9 "connection_type": "OktaSAML",
10 "name": "Foo Corp SAML",
11 "state": "active",
12 "domains": [{ "domain": "foo-corp.example" }],
13 "created_at": "2026-04-12T14:03:55.117Z",
14 "updated_at": "2026-04-12T15:41:09.882Z"
15 }
16 ],
17 "list_metadata": { "before": null, "after": null }
18}

Retrieve a connection

Read a connection when you need to show its status on your own admin screen, or when a support ticket arrives and you want to know whether the customer's certificate has been rotated since the connection last worked.

GET /connections/:id
1curl https://api.paycux.com/connections/conn_01HQ8ZK3M4N5P6R7S8T9V0W1X2 \
2 -H "Authorization: Bearer sk_example_123456789"
response
1HTTP 200
2{
3 "object": "connection",
4 "id": "conn_01HQ8ZK3M4N5P6R7S8T9V0W1X2",
5 "organization_id": "org_01HQ8ZK3M4N5P6R7S8T9V0W1X2",
6 "connection_type": "OktaSAML",
7 "name": "Foo Corp SAML",
8 "state": "active",
9 "domains": [{ "domain": "foo-corp.example" }],
10 "created_at": "2026-04-12T14:03:55.117Z",
11 "updated_at": "2026-04-12T15:41:09.882Z"
12}

Delete a connection

Deleting a connection does not delete the users who signed in through it, and does not end their current sessions. If a customer is offboarding, delete the connection and revoke sessions in the same operation, or people stay signed in until their refresh token expires.

DELETE /connections/:id
1curl -X DELETE https://api.paycux.com/connections/conn_01HQ8ZK3M4N5P6R7S8T9V0W1X2 \
2 -H "Authorization: Bearer sk_example_123456789"
response
1HTTP 204
2(no body)

Start a sign-in

This one is not a JSON call: you build the URL and redirect the browser to it. Pass organization_id to let the platform pick the right connection, or connection_id when you already know which one. The state parameter is yours — sign it, and check it on the way back.

GET /sso/authorize
1https://api.paycux.com/sso/authorize
2 ?client_id=client_01HQ8ZK3M4N5P6R7S8T9V0W1X2
3 &organization_id=org_01HQ8ZK3M4N5P6R7S8T9V0W1X2
4 &redirect_uri=https://app.yourapp.example/callback/sso
5 &response_type=code
6 &state=hZ9c1f4a2b
response
1HTTP 302
2Location: https://app.yourapp.example/callback/sso
3 ?code=01HQ8ZK3M4N5P6R7S8T9V0W1X2
4 &state=hZ9c1f4a2b
5
6# on failure the redirect carries an error instead
7Location: https://app.yourapp.example/callback/sso
8 ?error=connection_inactive
9 &state=hZ9c1f4a2b

Exchange the code for a profile

The profile has the same shape whether the assertion arrived over SAML or OIDC. Key your user records on idp_id rather than email, and trust organization_id from this response rather than anything the browser sent you.

POST /sso/token
1curl -X POST https://api.paycux.com/sso/token \
2 -H "Content-Type: application/json" \
3 -d '{
4 "client_id": "client_01HQ8ZK3M4N5P6R7S8T9V0W1X2",
5 "client_secret": "sk_example_123456789",
6 "grant_type": "authorization_code",
7 "code": "01HQ8ZK3M4N5P6R7S8T9V0W1X2"
8 }'
response
1HTTP 200
2{
3 "access_token": "at_example_123456789",
4 "profile": {
5 "object": "profile",
6 "id": "prof_01HQ8ZK3M4N5P6R7S8T9V0W1X2",
7 "connection_id": "conn_01HQ8ZK3M4N5P6R7S8T9V0W1X2",
8 "connection_type": "OktaSAML",
9 "organization_id": "org_01HQ8ZK3M4N5P6R7S8T9V0W1X2",
10 "idp_id": "00u1a0ufowBJlzPlk357",
11 "email": "avery@foo-corp.example",
12 "first_name": "Avery",
13 "last_name": "Lindqvist",
14 "groups": ["Engineering", "All Staff"]
15 }
16}

The link is single-use and expires quickly, so generate it when the admin clicks rather than emailing one in advance. This is how a connection gets created: the customer's IT team does the provider-side work, and you never handle their certificate.

POST /portal/generate_link
1curl -X POST https://api.paycux.com/portal/generate_link \
2 -H "Authorization: Bearer sk_example_123456789" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "organization": "org_01HQ8ZK3M4N5P6R7S8T9V0W1X2",
6 "intent": "sso",
7 "return_url": "https://app.yourapp.example/settings/sso"
8 }'
response
1HTTP 201
2{
3 "link": "https://portal.paycux.com/setup/pl_01HQ8ZK3M4N5P6R7S8T9V0W1X2",
4 "expires_at": "2026-04-12T14:08:55.117Z"
5}

Errors

Sign-in failures arrive as a redirect carrying an error parameter, not as a JSON body, so your callback has to handle both shapes. An authorization code that has already been exchanged returns 400 rather than a second profile; codes are single-use by design, and a double exchange usually means a browser retried the callback.

Pagination

GET /connections is cursor paginated with limit, before and after. Scoped to a single organization it rarely needs a second page; unscoped, in an environment with many customers, it always does.

has-sso.ts
1const { data } = await paycux.sso.listConnections({
2 organizationId: 'org_01HQ8ZK3M4N5P6R7S8T9V0W1X2',
3 limit: 100,
4});
5
6const usesSso = data.some((c) => c.state === 'active');