Frictionless Sign-up Flow

In the frictionless flow, your existing customers are automatically signed up to Coinify in the background — no Coinify sign-up screen, no password creation, no email verification code. The transition from your platform to the Trade Widget is invisible to the user.

📘

Before diving in:

The complete frictionless flow for both individual and corporate customers is visually mapped here — look for the Frictionless Flow sections in the diagram.

The setup involves three steps: generating a key pair, creating a signed JWT token, and passing it in the sign-up request.

Please find the steps for setting up the Frictionless Sign-up flow below:

1. Generate Private/Public key pair

The following code samples contain file names which you can adjust according to your needs. Of course, you are welcome to use your own code for the same use case.

You'll use RSA-SHA256 asymmetric cryptography to sign sign-up requests. Generate your key pair using OpenSSL:

# Generate a 2048-bit RSA private key
openssl genrsa -out key.pem 2048

# Extract the public key
openssl rsa -in key.pem -outform PEM -pubout -out public.pem
📘

Get the Public key and deliver it to Conify via your dedicated channel of communication. Once the Public key is set on the Coinify side, you can start the Frictionless Sign-up process by following the next steps.

❗️

Never share your Private Key with anyone!

2. Generate the JWT token

The JWT token is passed in the trustedEmailValidation field on the customer Sign-up API request. It confirms that the request is coming from your trusted system and that customer with the correct email is being signed up.

Below, you can find a couple of code examples on how to generate the JWT token. In this example, the only thing you need to make sure is that you are passing the correct email of the customer you want to sign up and that you are using the correct path to the file with the Private key.


const base64 = require('base64url'); // Import the 'base64url' library to handle Base64Url encoding
const crypto = require('crypto'); // Import the 'crypto' module to perform cryptographic operations
const signatureFunction = crypto.createSign('RSA-SHA256'); // Create a cryptographic signature object using the RSA-SHA256 algorithm
const fs = require('fs'); // Import the 'fs' module to read the private key from a file

// Define the header object for the JWT, specifying the algorithm and token type
const headerObj = {
    alg: 'RS256',
    typ: 'JWT'
};

// Define the payload object with the email of the customer that's being signed-up
const payloadObj = {
    email: '[email protected]'
  //exp: 1467331200  // Optional: Use the "exp" parameter IF you want the JWT token to expire at a specific time. UNIX Timestamp format. 
};

const headerObjString = JSON.stringify(headerObj); // Convert the header object to a JSON string
const payloadObjString = JSON.stringify(payloadObj); // Convert the payload object to a JSON string

// Encode the header JSON as Base64Url
const base64UrlHeader = base64(headerObjString);
// Encode the payload JSON as Base64Url
const base64UrlPayload = base64(payloadObjString);

// Concatenate the Base64Url-encoded header and payload
signatureFunction.write(base64UrlHeader + '.' + base64UrlPayload);
signatureFunction.end();

const PRIV_KEY = fs.readFileSync(__dirname + '/id_rsa_priv.pem', 'utf8');// Read the Private key from a file (in this example file name is 'id_rsa_priv.pem')


const signatureBase64 = signatureFunction.sign(PRIV_KEY, 'base64');// Sign the concatenated data using the private key and get a Base64 signature
const signatureBase64Url = base64.fromBase64(signatureBase64); // Convert the Base64 signature to Base64Url encoding

console.log(base64UrlHeader + '.' + base64UrlPayload + '.' + signatureBase64Url);// Combine the Base64Url-encoded header, payload, and signature to form the JWT
import jwt # Import the 'jwt' library for JSON Web Token operations
import json # Import the 'json' library for JSON data handling

# Define the header with the token type (JWT) and the signing algorithm (RS256 in this case)
header = {
    "typ": "JWT",
    "alg": "RS256"
}

# Define the payload with the email of the customer that's being signed up
payload = {
    "email": "[email protected]"
#   "exp": 1467331200  #Optional: Use the "exp" parameter IF you want the JWT token to expire at a specific time. UNIX Timestamp format. 
}

# Load your RSA private key
# Replace 'your_private_key.pem' with your actual private key file
with open('id_rsa_priv.pem', 'rb') as key_file:
    private_key = key_file.read()

# Generate the JWT token by encoding the payload with the private key and algorithm
token = jwt.encode(payload, private_key, algorithm='RS256', headers=header)

# Print the JWT token after decoding it from bytes to a UTF-8 string
print(token.decode('utf-8'))

As visible in the code examples above, the payload for generating the JWT token is the email address of the customer signing-up:

//Example JWT payload for email address [email protected]
{
  "email": "[email protected]"
}
📘

The output of this code is passed to the trustedEmailValidationToken in the Sign-up request.

👍

Alternatively, if you want to reset or create a new offline_token for an existing end-user, the output of the above code is used in the Authorization header of the Reset Offline Token request.

🧪

Testing

Use the Online JWT tool to verify your token's header, payload, and signature during development.

3. Frictionless Sign-up API request

❗️

Important:

You must ensure that the address country (and state where applicable) are provided from the end-user's direct input, in case you are not already collecting this data point on your end.

Pass the generated JWT in the trustedEmailValidationToken field, along with generateOfflineToken: true to receive an offline token for authenticating the user going forward.

curl --location 'https://app-api.sandbox.coinify.com/signup/trader' \
--header 'Content-Type: application/json' \
--data-raw '{
  "email": "[email protected]",
  "partnerId":"your-partner-id-here",
  "accountType":"individual"
  "profile": {
    "address": {
      "country": "DK"
    }
  },
  "trustedEmailValidationToken": "<your-generated-jwt>"
  "generateOfflineToken": true
}'
📘

country (and state for US users) must come from the end-user's direct input — never pre-filled or inferred on their behalf.

A successful response returns a trader object and an offlineToken. Store the offlineToken securely — it is used to authenticate this user silently on all future sessions without them needing to provide credentials.

📘

Resetting an offline token: If you need to generate a new offlineToken for an existing user, use the same JWT generation from Step 2 in the Authorization header of the Reset Offline Token → endpoint.

For the full endpoint spec, see Frictionless Email Verification →.


Next Steps

Frictionless Sign-in → — use the offlineToken to authenticate the user and load the Trade Widget


Did this page help you?