Before you start
Generate a signing key pair
Create an RSA key pair for
RS256, the signing algorithm we verify. 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.Register your issuer with Etherfuse
Send your Etherfuse representative your issuer (
iss) and JWKS URL. We link them to your partner organization so JWTs from that issuer resolve to your customers. Register separately for sandbox and production: each environment keeps its own issuer registration and uses its own aud (the sandbox vs production token endpoint). (There is no self-serve endpoint for this yet.)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. Names the flow the user may complete. See Scopes. |
jti / nonce | ✓ | A fresh value, unique per token, for replay protection. You can send it as jti (the standard JWT ID) or nonce (the standard OIDC claim). |
email, name | ✓ | The user’s email and full name. |
exp, iat | ✓ | Keep tokens short-lived (about 5 minutes). There is no enforced maximum lifetime, but verification allows no clock skew, so keep your server clock in sync. |
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; see Targets for the available paths.return_url(optional): where to send the user when they leave or the session can’t resume. It is not the success redirect; on success the user lands ontarget.
/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.Embedding. You can embed the launch page in an iframe or popup on your own site; there’s no origin to register with us. With
postMessage, trust is established by the handshake itself: the page only accepts a reply from the parent or opener that requested it. Flows that use the camera, such as /idv, additionally need allow="camera *; microphone *" on the iframe element (the wildcard origin is required because the scan SDK uses the camera from a nested frame; a bare allow="camera" only covers the iframe’s own origin).Scopes and targets
Whichscope to sign and which target to launch into depends on the flow you’re sending the user into. Every available flow (and what to know about each) is listed in User Launch Flows.
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.