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.
| Field | Type | Description |
|---|---|---|
| id | string | Unique identifier, prefixed conn_. |
| object | string | Always "connection". |
| organization_id | string | The tenant this connection signs people in to. Always present. |
| name | string | Display name, usually chosen by the IT admin who created it. |
| connection_type | string | The provider family, such as OktaSAML, AzureSAML, GoogleSAML, GenericSAML or GenericOIDC. Informational only — the profile shape does not change with it. |
| state | string | draft, validating, active or inactive. Only an active connection accepts a sign-in. |
| domains | array | Email domains that route to this connection, inherited from the organization. |
| connection_domains | array | Domain records with their own ids, present when a connection covers a subset of the organization's domains. |
| created_at | timestamp | RFC 3339. |
| updated_at | timestamp | RFC 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.
| State | Meaning | What to do |
|---|---|---|
| draft | Created but not yet configured | The admin has opened the portal and not finished. Sign-ins are refused. |
| validating | Configuration submitted, first assertion not yet seen | Test it before you tell the customer it is live. |
| active | Working | Sign-ins are accepted and profiles are returned. |
| inactive | Disabled by you or by the customer | Sign-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.
| Method | Path | What it does |
|---|---|---|
| GET | /connections | List connections, filtered by organization, domain or state. |
| GET | /connections/:id | Retrieve one connection with its provider type and current state. |
| DELETE | /connections/:id | Remove a connection and stop accepting its assertions. |
| GET | /sso/authorize | Redirect a browser into the customer's identity provider. |
| POST | /sso/token | Swap a single-use authorization code for a normalised profile. |
| POST | /portal/generate_link | Create 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.
1curl "https://api.paycux.com/connections?organization_id=org_01HQ8ZK3M4N5P6R7S8T9V0W1X2&state=active" \2 -H "Authorization: Bearer sk_example_123456789"
1HTTP 2002{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.
1curl https://api.paycux.com/connections/conn_01HQ8ZK3M4N5P6R7S8T9V0W1X2 \2 -H "Authorization: Bearer sk_example_123456789"
1HTTP 2002{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.
1curl -X DELETE https://api.paycux.com/connections/conn_01HQ8ZK3M4N5P6R7S8T9V0W1X2 \2 -H "Authorization: Bearer sk_example_123456789"
1HTTP 2042(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.
1https://api.paycux.com/sso/authorize2 ?client_id=client_01HQ8ZK3M4N5P6R7S8T9V0W1X23 &organization_id=org_01HQ8ZK3M4N5P6R7S8T9V0W1X24 &redirect_uri=https://app.yourapp.example/callback/sso5 &response_type=code6 &state=hZ9c1f4a2b
1HTTP 3022Location: https://app.yourapp.example/callback/sso3 ?code=01HQ8ZK3M4N5P6R7S8T9V0W1X24 &state=hZ9c1f4a2b56# on failure the redirect carries an error instead7Location: https://app.yourapp.example/callback/sso8 ?error=connection_inactive9 &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.
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 }'
1HTTP 2002{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}
Generate an Admin Portal link
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.
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 }'
1HTTP 2012{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.
1const { data } = await paycux.sso.listConnections({2 organizationId: 'org_01HQ8ZK3M4N5P6R7S8T9V0W1X2',3 limit: 100,4});56const usesSso = data.some((c) => c.state === 'active');