Skip to main content

Authentication

Every Rise Public API request carries an OAuth 2.0 bearer token in the Authorization header. This page walks through registering an application, choosing a grant type, and obtaining + refreshing tokens.

Choosing a grant type

Use caseFlow
Server-to-server, no user interaction (your backend → Rise)Client Credentials
Acting on behalf of a signed-in Rise user (partner app, public client)Authorization Code with PKCE
Refreshing an expired user access tokenRefresh token

Device flow, implicit flow, and password grant are not supported.

Registering an application

  1. Sign in to your Rise account as an account owner or administrator.
  2. Go to Settings → Developer → OAuth applications.
  3. Click Create application and fill in:
    • Name — shown to users on the consent screen
    • Redirect URI(s) — for Authorization Code flow; must be https:// (except http://localhost in development)
    • Scopes — see Scope reference below
  4. Choose client type:
    • Confidential — server-to-server; issues a client_id + client_secret
    • Public — browser or mobile; issues client_id only, requires PKCE

You'll receive the client_id (and client_secret if confidential) immediately. The secret is shown once — store it somewhere safe.

Client Credentials

Use this when your own backend is the caller and no end user is involved.

curl -X POST https://api.risepeople.com/v1/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=$RISE_CLIENT_ID" \
-d "client_secret=$RISE_CLIENT_SECRET" \
-d "scope=employees:read payroll:read"

Response:

{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "employees:read payroll:read"
}

Cache the token for expires_in seconds (currently 1 hour); request a new one before it expires.

Authorization Code with PKCE

Use this when a human is involved — your app acts on behalf of a Rise user who has explicitly granted you permission.

Step 1 — generate a PKCE verifier + challenge

The code verifier is a high-entropy random string; the challenge is its SHA-256 hash, base64url-encoded.

import crypto from "node:crypto";

const verifier = crypto.randomBytes(32).toString("base64url");
const challenge = crypto.createHash("sha256").update(verifier).digest("base64url");

Store the verifier server-side (or in session storage) keyed by a state value you'll send to Rise.

Step 2 — redirect the user to /oauth/authorize

https://api.risepeople.com/v1/oauth/authorize
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https%3A%2F%2Fyour-app.example.com%2Fcallback
&scope=employees%3Aread%20payroll%3Aread
&state=OPAQUE_ANTI_CSRF_VALUE
&code_challenge=CHALLENGE_FROM_STEP_1
&code_challenge_method=S256

Rise shows a consent screen; if the user approves, they're redirected to your redirect_uri with ?code=...&state=....

Step 3 — exchange the code for tokens

curl -X POST https://api.risepeople.com/v1/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=$CODE_FROM_CALLBACK" \
-d "redirect_uri=https://your-app.example.com/callback" \
-d "client_id=$RISE_CLIENT_ID" \
-d "code_verifier=$VERIFIER_FROM_STEP_1"

Response:

{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "def50200...",
"scope": "employees:read payroll:read"
}

Refreshing a token

When an access token expires (401 token_expired), exchange the refresh token for a new one:

curl -X POST https://api.risepeople.com/v1/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "refresh_token=$REFRESH_TOKEN" \
-d "client_id=$RISE_CLIENT_ID"

Refresh tokens rotate: the response contains a new refresh_token. Discard the old one — using it again returns 401.

Using the token

Pass the access token in the Authorization header on every call:

curl https://api.risepeople.com/v1/employees/42 \
-H "Authorization: Bearer $ACCESS_TOKEN"

Scope reference

ScopeGrants
employees:readList and retrieve employee records
employees:writeCreate, update, and terminate employees
payroll:readRead payroll runs, pay stubs, and payroll configuration
payroll:writeSubmit payroll runs, edit deductions
webhooks:manageRegister, update, and delete webhook subscriptions

A valid token without the right scope returns 403 insufficient_scope, not 401 — check the error response to distinguish authentication ("we don't know who you are") from authorization ("we know who you are, but you can't do this") failures.

Token lifetime

TokenLifetime
Access token (Client Credentials)1 hour
Access token (Authorization Code)1 hour
Refresh token30 days, rotated on every use

Revoking a token

curl -X POST https://api.risepeople.com/v1/oauth/revoke \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "token=$ACCESS_OR_REFRESH_TOKEN" \
-d "client_id=$RISE_CLIENT_ID" \
-d "client_secret=$RISE_CLIENT_SECRET" # omit for public clients

Revoking a refresh token also revokes all access tokens derived from it.

Common errors

HTTPtypeMeaning
400invalid_requestMissing or malformed parameter
401invalid_clientclient_id / client_secret wrong
401invalid_grantCode expired, already used, or PKCE verifier mismatch
401token_expiredAccess token past expires_in — refresh it
403insufficient_scopeValid token but missing the scope for this endpoint

See Errors for the full problem-details envelope.