Before you start
Generate a signing key pair
Create an asymmetric key pair for an algorithm we support, such as RSA for
RS256 or an EC key for ES256. You sign JWTs with the private key and publish the public key (next step).Host a JWKS endpoint
Publish the public key as a JSON Web Key Set (RFC 7517) at a stable HTTPS URL, e.g. The URL must be reachable from Etherfuse’s servers over HTTPS and return the key set as JSON. We fetch it fresh on every verification (no caching), so rotating keys is straightforward: add the new key as another entry in
https://your-domain.com/.well-known/jwks.json. Give the key a stable id (kid) so we can match it to a token’s kid header. Most JWT libraries can export a public key to JWK form:keys, start signing with its kid, then remove the old key once no unexpired tokens still reference it.Sign a user JWT
Sign a JWT with the key behind your JWKS. The claims:| Claim | Required | Notes |
|---|---|---|
iss | ✓ | Your registered issuer. |
sub | ✓ | The user’s id. A UUID is strongly recommended; see the note below. |
aud | ✓ | The token endpoint URL: https://api.etherfuse.com/auth/token (or https://api.sand.etherfuse.com/auth/token in sandbox). |
scope | ✓ | Required. Use kyb for KYB access. See Scopes. |
nonce | ✓ | A fresh random value, unique per token (replay-protected). |
email, name | ✓ | The user’s email and full name. |
exp, iat | ✓ | Keep tokens short-lived. |
picture | optional | Profile image URL. |
Strongly recommended: make
sub a UUID. sub identifies the person signing in. For an individual customer, that person is the customer: use the same UUID for sub and for the customerId in the Ramp API, so both resolve to one customer. A non-UUID sub still signs in, but can’t be addressed through the Ramp API.Launch the user into the app
This is the recommended path. You hand the user’s browser to Etherfuse with their JWT, and the app signs them in and takes them where you point them (for example, KYB). Every launch carries:grant_typeandassertion: either a raw partner JWT (grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer) or arefresh_tokenfrom a prior server-side exchange (grant_type=refresh_token).target: the app path to land on, e.g./kyb.return_url(optional): where to send the user when they leave.
/auth/launch, either as a top-level navigation or into an iframe you’ve embedded (point the form’s target at the frame). The app exchanges the JWT, establishes the session, and redirects to target. A server-rendered auto-submitting form is the usual way:
Rather not POST a form? Hand the credentials to the launch page over
postMessage instead: open it in an iframe or popup and message the JWT in. See Launch via postMessage for the message contract and a working example.Scopes
Thescope claim is required on every partner JWT. Set it to the scope for what the user needs to do:
| Scope | Grants |
|---|---|
kyb | Access to the Business Onboarding (KYB) flow. |
Scope is strictly enforced: the claim cannot be omitted, and any value other than the ones documented here is rejected with
invalid_scope. Ask only for the scope a user needs.When something fails
Auth failures come back as OAuth errors with anerror, an error_description, and an error_uri pointing at the matching entry in Authentication errors.
A launch renders these on the page rather than returning them. If you want to read them programmatically, exchange the JWT server-side first: POST /auth/token returns the same error as JSON, so you can branch on error before ever handing the user to a launch.