Let your product request access to a mutual customer's rzilient data through
a standard authorization-code flow with PKCE. The customer consents once;
you receive a bearer token scoped to that customer's company, usable against
every endpoint below under /api/external/v1.
client_id and a PKCE challenge.Registration is set up directly on the rzilient platform — either by an admin at your organization who already manages mutual customer companies on rzilient (self-service, from their own account), or by the rzilient team on your behalf if your integration has no rzilient login of its own. Either way, we'll need:
| Item | Notes |
|---|---|
| App name | Shown to customers on the consent screen — keep it recognizable to your own users. |
| Logo URL | HTTPS, square, ≥112×112px. Displayed next to your app name at consent time. |
| Redirect URI(s) | HTTPS only, exact match enforced — no wildcards. Send both staging and production if applicable. |
| Contact email | Where your one-time credential link will be sent. |
You'll receive a secure link — generated on our platform, nothing third-party
involved — that shows your client_id and client_secret
exactly once. Copy both into your secret manager immediately: the
link cannot be reopened, and we cannot retrieve the secret afterward (we store only
a hash, the same way a password manager would).
| Purpose | URL |
|---|---|
| Authorize (browser) | https://join.rzilient.club/oauth/authorize |
| Token exchange (server) | https://api.rzilient.tech/oauth/token |
| Revocation (server) | https://api.rzilient.tech/oauth/revoke |
| External API base | https://api.rzilient.tech/api/external/v1 |
code_challenge_method=S256 is mandatory. Never reuse a verifier across customers or retries.
import { randomBytes, createHash } from 'node:crypto'; const codeVerifier = randomBytes(48).toString('base64url'); const codeChallenge = createHash('sha256').update(codeVerifier).digest('base64url');
require 'securerandom' require 'openssl' require 'base64' code_verifier = SecureRandom.urlsafe_base64(48) code_challenge = Base64.urlsafe_encode64( OpenSSL::Digest::SHA256.digest(code_verifier), padding: false )
import secrets, hashlib, base64 code_verifier = secrets.token_urlsafe(48) code_challenge = base64.urlsafe_b64encode( hashlib.sha256(code_verifier.encode()).digest() ).rstrip(b'=').decode()
https://join.rzilient.club/oauth/authorize ?client_id=YOUR_CLIENT_ID &response_type=code &redirect_uri=https://yourapp.example.com/integrations/rzilient/callback &scope=external_api &state=RANDOM_OPAQUE_VALUE &code_challenge=CODE_CHALLENGE &code_challenge_method=S256
redirect_uri must byte-for-byte match a registered URI.state must be unguessable (≥128 bits) and verified on callback — your CSRF defense.Success:
https://yourapp.example.com/integrations/rzilient/callback?code=AUTH_CODE&state=RANDOM_OPAQUE_VALUE
Denial or error:
...?error=access_denied&error_description=...&state=RANDOM_OPAQUE_VALUE
Verify state on every callback. The code is single-use and expires in 10 minutes — exchange it immediately.
curl -s https://api.rzilient.tech/oauth/token \ -X POST \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d grant_type=authorization_code \ -d code=AUTH_CODE \ -d redirect_uri=https://yourapp.example.com/integrations/rzilient/callback \ -d client_id=YOUR_CLIENT_ID \ -d client_secret=YOUR_CLIENT_SECRET \ -d code_verifier=CODE_VERIFIER
{
"access_token": "9iHIhNKsrBpYQQr6JwjiLu1ZZz2wVNZtI3SCjmyc7Es=",
"token_type": "Bearer",
"scope": "external_api",
"created_at": 1753257600
}
expires_in, no refresh_token — the token is long-lived by design.+, /, =). Use only in the Authorization header, never in a URL.curl -s https://api.rzilient.tech/api/external/v1/clients \ -H 'Authorization: Bearer 9iHIhNKsrBpYQQr6JwjiLu1ZZz2wVNZtI3SCjmyc7Es='
Every request is automatically scoped to the customer that consented — there's no tenant parameter, and no way to reach another company with this token. Full endpoint reference below.
One authorization = one token = one customer company. Key stored tokens by your own customer/workspace identifier, and trigger the flow from a per-customer "Connect rzilient" action in your product.
To revoke programmatically (e.g. a customer disconnects from your side):
curl -s https://api.rzilient.tech/oauth/revoke \ -X POST \ -u 'YOUR_CLIENT_ID:YOUR_CLIENT_SECRET' \ -d token=ACCESS_TOKEN
Returns 200 regardless of whether the token was valid. After revocation from either side, the token stops working immediately.
| Where | Error | Meaning | What to do |
|---|---|---|---|
| Callback / consent | application_not_active | Your application hasn't cleared internal review yet | Wait for the confirmation from your rzilient contact; ask about a sandbox app to keep testing meanwhile |
| Callback | access_denied | User cancelled or lacks manager role | Show "connection cancelled"; offer retry |
| /oauth/token | invalid_grant | Code expired, already used, or verifier mismatch | Restart from step 1 with fresh PKCE values |
| /oauth/token | invalid_client | Bad client_id / client_secret | Check credentials; contact us if rotated |
| External API | 401 | Token revoked or invalid | Mark customer as disconnected; do not retry in a loop |
| External API | 429 | Rate limited | Back off with jitter; respect Retry-After |
state generated with a CSPRNG and verified on every callback/oauth/revoke and deletes the stored tokenclient_id pointing at your staging redirect URI, plus
a demo company with seeded data.