Skip to main content

Overview

The registration process is an 8-step flow that collects required KYC (Know Your Customer) information from new users and establishes formal consent records. Each step must be completed in order, with the onboardingId passed between steps to maintain session continuity. Steps 1-2: Email and phone verification with onboardingId generation. Step 3: KYC identity verification via Veriff using onboardingId (no authentication required). Steps 4-5: Personal details and consent record creation using onboardingId (consent required for regulatory compliance - GDPR, CCPA, E-Sign Act). Steps 6-7: Address collection that finalizes registration and returns userId and accessToken. Step 8: Link consent set to userId to complete the audit trail.
All registration endpoints require the x-client-key header. For US environment routing, include x-us-env: true header or region=us query parameter.
Consent Must Be Created Before Address Submission: Step 5 creates the formal consent record required for regulatory compliance. The address endpoints (Steps 6-7) finalize registration and return the userId, which is then used to link the consent (Step 8). Do not skip consent creation for production deployments handling user data under GDPR, CCPA, or E-Sign Act requirements. See Consent Management for details.

Registration Flow Diagram

Complete Flow Overview

Step 1: Email Verification (Send)

Initiate registration by sending a verification code to the user’s email address.

Endpoint

POST /v1/auth/register/email/send
API Reference: POST /v1/auth/register/email/send

Request

curl -X POST https://dev.api.baanx.com/v1/auth/register/email/send \
  -H "x-client-key: YOUR_CLIENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]"
  }'

Response

{
  "contactVerificationId": "US_100a99cf-f4d3-4fa1-9be9-2e9828b20ebb"
}
Store the contactVerificationId securely. This ID is required for the email verification step and phone verification step.

Field Descriptions

FieldTypeRequiredDescription
emailstringYesUser’s email address (must be valid email format)

Common Errors

{
  "message": "Email already exists"
}
Resolution: User should login instead, or use password recovery
{
  "message": "Invalid email format"
}
Resolution: Validate email format before submission

Step 2: Email Verification (Verify)

Verify the code received via email and create the user onboarding session.

Endpoint

POST /v1/auth/register/email/verify
API Reference: POST /v1/auth/register/email/verify

Request

curl -X POST https://dev.api.baanx.com/v1/auth/register/email/verify \
  -H "x-client-key: YOUR_CLIENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "password": "SecurePassword123!",
    "verificationCode": "123456",
    "contactVerificationId": "US_100a99cf-f4d3-4fa1-9be9-2e9828b20ebb",
    "countryOfResidence": "GB",
    "allowMarketing": true,
    "allowSms": true
  }'

Response

{
  "hasAccount": false,
  "onboardingId": "US_100a99cf-f4d3-4fa1-c123-2e9833440ebb",
  "user": {
    "id": "US_100a99cf-f4d3-4fa1-c123-2e9833440ebb",
    "email": "[email protected]",
    "verificationState": "UNVERIFIED"
  }
}
The onboardingId returned here must be passed to all subsequent registration steps. Store it securely in your session.

Field Descriptions

FieldTypeRequiredDescription
emailstringYesUser’s email address (must match Step 1)
passwordstringYesUser’s chosen password (8+ characters recommended)
verificationCodestringYes6-digit code received via email
contactVerificationIdstringYesID from Step 1 response
countryOfResidencestringYesISO 3166-1 alpha-2 country code (e.g., “GB”, “US”)
allowMarketingbooleanYesUser consent for marketing communications (see Consent Management for tracking)
allowSmsbooleanYesUser consent for SMS communications (see Consent Management for tracking)
Best Practice: After registration completes, use the Consent Management API to create a formal consent record linking the user to their consent choices. This provides an immutable audit trail for regulatory compliance (GDPR, CCPA, E-Sign Act) and allows users to review or revoke their consent later.

Password Requirements

While specific requirements may vary, follow these best practices:
  • Minimum 8 characters
  • Include uppercase and lowercase letters
  • Include numbers and special characters
  • Avoid common passwords

Common Errors

{
  "message": "Invalid verification code"
}
Resolution: Request a new code via Step 1 and try again
{
  "message": "Verification code has expired"
}
Resolution: Codes typically expire after 10-15 minutes. Request a new code via Step 1
{
  "message": "Invalid or expired contact verification ID"
}
Resolution: Restart from Step 1 to get a new verification ID

Step 3: Phone Verification (Send)

Send SMS verification code to the user’s phone number.

Endpoint

POST /v1/auth/register/phone/send
API Reference: POST /v1/auth/register/phone/send

Request

curl -X POST https://dev.api.baanx.com/v1/auth/register/phone/send \
  -H "x-client-key: YOUR_CLIENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneCountryCode": "+44",
    "phoneNumber": "7400846282",
    "contactVerificationId": "US_100a99cf-f4d3-4fa1-9be9-2e9828b20ebb"
  }'

Response

{
  "success": true
}

Field Descriptions

FieldTypeRequiredDescription
phoneCountryCodestringYesInternational dialing code with + prefix (e.g., “+44”, “+1”)
phoneNumberstringYesPhone number without country code or formatting
contactVerificationIdstringYesID from Step 1 response (same one used in Step 2)
Phone number should be provided without the country code, spaces, or special characters. The country code is sent separately in phoneCountryCode.

Common Errors

{
  "message": "Invalid phone number format"
}
Resolution: Ensure phone number is valid for the specified country code and contains only digits
{
  "message": "Phone number already exists"
}
Resolution: User may already have an account with this number

Step 4: Phone Verification (Verify)

Verify the SMS code and link the phone number to the user’s onboarding session.

Endpoint

POST /v1/auth/register/phone/verify
API Reference: POST /v1/auth/register/phone/verify

Request

curl -X POST https://dev.api.baanx.com/v1/auth/register/phone/verify \
  -H "x-client-key: YOUR_CLIENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneCountryCode": "+44",
    "phoneNumber": "7400846282",
    "verificationCode": "123456",
    "onboardingId": "US_100a99cf-f4d3-4fa1-c123-2e9833440ebb"
  }'

Response

{
  "success": true
}

Field Descriptions

FieldTypeRequiredDescription
phoneCountryCodestringYesMust match Step 3
phoneNumberstringYesMust match Step 3
verificationCodestringYes6-digit code received via SMS
onboardingIdstringYesOnboarding ID from Step 2 response

Common Errors

{
  "message": "Invalid verification code"
}
Resolution: Request a new code via Step 3 and try again
{
  "message": "Phone number does not match verification session"
}
Resolution: Ensure phone number and country code exactly match Step 3

Step 5: KYC Verification (Veriff)

Generate a verification session URL for identity verification. This step initiates the KYC process where users upload their government ID and complete biometric verification.
No Authentication Required: This endpoint uses onboardingId for validation and does not require a bearer token. The user doesn’t have an access token yet at this stage of registration.

Endpoint

POST /v1/auth/register/verification
API Reference: POST /v1/auth/register/verification

Request

curl -X POST https://dev.api.baanx.com/v1/auth/register/verification \
  -H "x-client-key: YOUR_CLIENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "onboardingId": "100a99cf-f4d3-4fa1-9be9-2e9828b20ebb"
  }'

Response

{
  "sessionUrl": "https://magic.veriff.me/v/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Field Descriptions

FieldTypeRequiredDescription
onboardingIdstringYesOnboarding ID from Step 2

What Happens Next

After receiving the sessionUrl:
  1. Redirect the user to the Veriff session URL
  2. User completes verification by:
    • Uploading a government-issued ID (passport, driver’s license, etc.)
    • Taking a selfie for biometric matching
    • Following on-screen instructions
  3. Verification processing (typically 5-30 minutes)
  4. User returns to your application
  5. Poll for status using GET /v1/auth/register?onboardingId={id} to check verification state

Verification States

The user’s verificationState will be updated to:
  • PENDING: Verification submitted and under review
  • VERIFIED: Verification approved (ready to proceed)
  • REJECTED: Verification rejected (can retry later)

Polling for Completion

Implement polling to detect when verification completes:
async function waitForVerification(onboardingId: string): Promise<boolean> {
  const maxAttempts = 60; // 5 minutes

  for (let i = 0; i < maxAttempts; i++) {
    const response = await fetch(
      `https://dev.api.baanx.com/v1/auth/register?onboardingId=${onboardingId}`,
      {
        headers: { 'x-client-key': 'YOUR_CLIENT_KEY' }
      }
    );

    const user = await response.json();

    if (user.verificationState === 'VERIFIED' || user.verificationState === 'PENDING') {
      return true;
    } else if (user.verificationState === 'REJECTED') {
      throw new Error('Verification rejected');
    }

    await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds
  }

  throw new Error('Verification timeout');
}

Common Errors

{
  "message": "Invalid onboarding ID"
}
Resolution: Verify the onboardingId from Step 2 is correct and hasn’t expired
{
  "message": "Failed to generate verification session"
}
Resolution: Verification provider may be temporarily unavailable. Retry after a brief delay
User Experience Tip: Consider opening the Veriff session in a modal or in-app browser rather than a full redirect. This provides a better user experience and makes it easier to detect when the user returns.

Step 6: Personal Details

Collect the user’s personal information required for KYC compliance.

Endpoint

POST /v1/auth/register/personal-details
API Reference: POST /v1/auth/register/personal-details

Request

curl -X POST https://dev.api.baanx.com/v1/auth/register/personal-details \
  -H "x-client-key: YOUR_CLIENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "onboardingId": "US_100a99cf-f4d3-4fa1-c123-2e9833440ebb",
    "firstName": "John",
    "lastName": "Doe",
    "countryOfNationality": "GB",
    "dateOfBirth": "1990-01-01"
  }'

Request (US Users)

For users with countryOfResidence = "US", SSN is required:
{
  "onboardingId": "US_100a99cf-f4d3-4fa1-c123-2e9833440ebb",
  "firstName": "John",
  "lastName": "Doe",
  "countryOfNationality": "US",
  "ssn": "123456789",
  "dateOfBirth": "1990-01-01"
}

Response

{
  "onboardingId": "US_100a99cf-f4d3-4fa1-c123-2e9833440ebb",
  "user": {
    "id": "US_100a99cf-f4d3-4fa1-c123-2e9833440ebb",
    "firstName": "John",
    "lastName": "Doe",
    "dateOfBirth": "1990-01-01",
    "email": "[email protected]",
    "verificationState": "UNVERIFIED",
    "phoneNumber": "7400846282",
    "phoneCountryCode": "+44",
    "countryOfNationality": "GB"
  }
}

Field Descriptions

FieldTypeRequiredDescription
onboardingIdstringYesOnboarding ID from Step 2
firstNamestringYesUser’s legal first name
lastNamestringYesUser’s legal last name
countryOfNationalitystringYesISO 3166-1 alpha-2 country code
dateOfBirthstringYesFormat: YYYY-MM-DD (must be 18+ years old)
ssnstringConditionalRequired only if countryOfResidence = "US"
US Users: The ssn field is mandatory when countryOfResidence = "US". Requests will fail validation without it.

Age Validation

Users must meet minimum age requirements (typically 18+). The API will validate the dateOfBirth against this requirement.

Common Errors

{
  "message": "User must be at least 18 years old"
}
Resolution: Verify date of birth is correct. Users under minimum age cannot register
{
  "message": "SSN is required for US residents"
}
Resolution: Include ssn field when countryOfResidence = "US"

Step 7: Physical Address

Collect the user’s residential address information.

Endpoint

POST /v1/auth/register/address
API Reference: POST /v1/auth/register/address

Request (Non-US Users)

curl -X POST https://dev.api.baanx.com/v1/auth/register/address \
  -H "x-client-key: YOUR_CLIENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "onboardingId": "US_100a99cf-f4d3-4fa1-c123-2e9833440ebb",
    "addressLine1": "23 Werrington Bridge Rd",
    "addressLine2": "Milking Nook",
    "city": "Peterborough",
    "zip": "PE6 7PP",
    "isSameMailingAddress": true
  }'

Request (US Users)

For US users, include the usState field:
{
  "onboardingId": "US_100a99cf-f4d3-4fa1-c123-2e9833440ebb",
  "addressLine1": "123 Main Street",
  "addressLine2": "Apt 4B",
  "city": "San Francisco",
  "zip": "94102",
  "usState": "CA",
  "isSameMailingAddress": true
}

Response (Registration Complete)

When isSameMailingAddress = true OR countryOfResidence != "US":
{
  "accessToken": "US_100a99cf-f4d3-4fa1-c321-2e9833440ebb",
  "onboardingId": "US_100a99cf-f4d3-4fa1-c123-2e9833440ebb",
  "user": {
    "id": "US_100a99cf-f4d3-4fa1-c123-2e9833440ebb",
    "firstName": "John",
    "lastName": "Doe",
    "dateOfBirth": "1990-01-01",
    "email": "[email protected]",
    "verificationState": "UNVERIFIED",
    "phoneNumber": "7400846282",
    "phoneCountryCode": "+44",
    "addressLine1": "23 Werrington Bridge Rd",
    "addressLine2": "Milking Nook",
    "city": "Peterborough",
    "zip": "PE6 7PP",
    "countryOfResidence": "GB"
  }
}
Registration Complete: When accessToken is returned, registration is complete. Store the token for authenticated API calls.

Response (Mailing Address Required)

When isSameMailingAddress = false AND countryOfResidence = "US":
{
  "accessToken": null,
  "onboardingId": "US_100a99cf-f4d3-4fa1-c123-2e9833440ebb",
  "user": {
    // ... user details
  }
}
If accessToken is null, proceed to Step 8 to collect mailing address (US users only).

Field Descriptions

FieldTypeRequiredDescription
onboardingIdstringYesOnboarding ID from Step 2
addressLine1stringYesPrimary address line (street address)
addressLine2stringNoSecondary address line (apartment, suite, etc.)
citystringYesCity or town
zipstringYesPostal/ZIP code
usStatestringConditionalRequired only if countryOfResidence = "US". Two-letter state code (e.g., “CA”, “NY”)
isSameMailingAddressbooleanYesMust be true for non-US users. For US users, set to false to collect separate mailing address
US Users: The usState field is mandatory when countryOfResidence = "US". Use two-letter state abbreviations (e.g., “CA”, “NY”, “TX”).

Common Errors

{
  "message": "US state is required for US residents"
}
Resolution: Include usState field when countryOfResidence = "US"
{
  "message": "Invalid ZIP code format"
}
Resolution: Ensure ZIP code matches the expected format for the country

Step 8: Mailing Address (Optional - US Only)

For US users who have a different mailing address than their physical address, collect the mailing address information.
This step is only required when:
  • countryOfResidence = "US", AND
  • isSameMailingAddress = false in Step 7

Endpoint

POST /v1/auth/register/mailing-address
API Reference: POST /v1/auth/register/mailing-address

Request

curl -X POST https://dev.api.baanx.com/v1/auth/register/mailing-address \
  -H "x-client-key: YOUR_CLIENT_KEY" \
  -H "x-us-env: true" \
  -H "Content-Type: application/json" \
  -d '{
    "onboardingId": "US_100a99cf-f4d3-4fa1-c123-2e9833440ebb",
    "mailingAddressLine1": "PO Box 12345",
    "mailingAddressLine2": "",
    "mailingCity": "Los Angeles",
    "mailingZip": "90001",
    "mailingUsState": "CA"
  }'

Response

{
  "accessToken": "US_100a99cf-f4d3-4fa1-c321-2e9833440ebb",
  "user": {
    "id": "US_100a99cf-f4d3-4fa1-c123-2e9833440ebb",
    "email": "[email protected]",
    "firstName": "John",
    "lastName": "Doe",
    "verificationState": "UNVERIFIED",
    "addressLine1": "123 Main Street",
    "city": "San Francisco",
    "zip": "94102",
    "usState": "CA",
    "mailingAddressLine1": "PO Box 12345",
    "mailingCity": "Los Angeles",
    "mailingZip": "90001",
    "mailingUsState": "CA"
  }
}
Registration Complete: The accessToken returned here completes the registration process. Store it for authenticated API calls.

Field Descriptions

FieldTypeRequiredDescription
onboardingIdstringYesOnboarding ID from Step 2
mailingAddressLine1stringYesPrimary mailing address line
mailingAddressLine2stringNoSecondary mailing address line
mailingCitystringYesMailing address city
mailingZipstringYesMailing address ZIP code
mailingUsStatestringYesTwo-letter state code for mailing address
After core registration completes (Steps 1-8), create a formal consent record for regulatory compliance. This step is essential for production deployments handling user data under GDPR, CCPA, or E-Sign Act.
Why This Step Matters: Step 9 creates a formal, immutable audit trail required for regulatory compliance. This provides:
  • Legal proof of consent with timestamps
  • IP address and user agent tracking
  • Complete consent change history
  • Region-specific policy enforcement (US vs Global)
  • Revocation tracking for “right to be forgotten” requests
E-Sign Act Legal Requirement (US Users): For US users, the E-Sign Act legally requires that you present the E-Sign Act disclosure document to users and provide an opportunity to review it before they can consent to eSignAct. This is not optional - it’s a federal legal requirement.Your client application must:
  • Display the full E-Sign Act disclosure document
  • Allow users to read and review it before proceeding
  • Obtain affirmative consent after presentation
Simply showing a checkbox labeled “I agree to E-Sign Act” without providing access to the disclosure document does not meet legal requirements and could invalidate the consent.Best Practice for Other Consents: While not legally mandated in the same way, it’s also good practice to provide access to Terms of Service, Privacy Policy, and Marketing Communications policies before collecting those consents.

Workflow

The consent flow requires two API calls:
  1. Create onboarding consent - Create consent set using the onboardingId from registration
  2. Link user to consent - Associate the permanent userId with the consent set
POST /v2/consent/onboarding
API Reference: POST /v2/consent/onboarding

Request

curl -X POST https://prod.com/v2/consent/onboarding \
  -H "x-client-key: YOUR_CLIENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "onboardingId": "US_100a99cf-f4d3-4fa1-c123-2e9833440ebb",
    "policy": "us",
    "consents": {
      "eSignAct": "granted",
      "termsAndPrivacy": "granted",
      "marketingNotifications": "granted",
      "smsNotifications": "granted",
      "emailNotifications": "granted"
    },
    "metadata": {
      "ipAddress": "192.168.1.1",
      "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X)",
      "timestamp": "2024-01-15T10:30:00Z"
    }
  }'

Response

{
  "id": "consent_set_123abc",
  "consentSetId": "consent_set_123abc",
  "onboardingId": "US_100a99cf-f4d3-4fa1-c123-2e9833440ebb",
  "policy": "us",
  "consents": [
    {
      "id": "consent_456def",
      "type": "eSignAct",
      "status": "granted",
      "metadata": {
        "ipAddress": "192.168.1.1",
        "userAgent": "Mozilla/5.0...",
        "timestamp": "2024-01-15T10:30:00Z"
      },
      "createdAt": "2024-01-15T10:30:00Z"
    }
  ]
}
Store the consentSetId returned from this call. You’ll need it for the linking step.
PATCH /v2/consent/onboarding/{consentSetId}
API Reference: PATCH /v2/consent/onboarding/{consentSetId}

Request

curl -X PATCH https://prod.com/v2/consent/onboarding/consent_set_123abc \
  -H "x-client-key: YOUR_CLIENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "US_100a99cf-f4d3-4fa1-c123-2e9833440ebb"
  }'

Response

{
  "id": "consent_set_123abc",
  "consentSetId": "consent_set_123abc",
  "userId": "US_100a99cf-f4d3-4fa1-c123-2e9833440ebb",
  "onboardingId": "US_100a99cf-f4d3-4fa1-c123-2e9833440ebb",
  "policy": "us",
  "consents": [
    {
      "id": "consent_456def",
      "type": "eSignAct",
      "status": "granted",
      "createdAt": "2024-01-15T10:30:00Z"
    }
  ]
}
Registration Complete with Audit Trail: Once the user is linked to the consent set, your registration flow is complete with full regulatory compliance. The consent record includes immutable audit trails for all future changes.

Policy Selection

Choose the correct policy based on the user’s country of residence:
User LocationPolicyRequired Consents
United StatesuseSignAct, termsAndPrivacy, marketingNotifications, smsNotifications, emailNotifications
InternationalglobaltermsAndPrivacy, marketingNotifications, smsNotifications, emailNotifications
E-Sign Act Disclosure for US Users: The eSignAct consent is required for US users under the federal Electronic Signatures in Global and National Commerce Act. Your client application must present the E-Sign Act disclosure document to users and provide an opportunity to review it before they can consent. The disclosure typically explains:
  • What it means to conduct business electronically
  • How to withdraw consent
  • How to obtain paper copies of documents
  • Hardware and software requirements
  • How to contact the company
This is a legal requirement - users cannot legally consent to something they haven’t been given the opportunity to read.
Mapping Registration to Consent: The consent choices from Step 2 (allowMarketing, allowSms) should be used to set the status values in Step 9:
  • allowMarketing: truemarketingNotifications: "granted", emailNotifications: "granted"
  • allowMarketing: falsemarketingNotifications: "denied", emailNotifications: "denied"
  • allowSms: truesmsNotifications: "granted"
  • allowSms: falsesmsNotifications: "denied"
  • termsAndPrivacy should always be "granted" (required to register)
  • eSignAct (US only) should always be "granted" (required for US users)

Common Errors

{
  "message": "Policy must be 'us' or 'global'"
}
Resolution: Ensure policy matches user’s country (us for US, global for others)
{
  "message": "Onboarding ID not found or already used"
}
Resolution: Verify the onboardingId from Step 2 and ensure it hasn’t been used before
{
  "message": "Consent set already linked to a user"
}
Resolution: Cannot link the same consent set to multiple users. Create a new consent set for each user.
Once consent is established, you can: See the complete Consent Management Guide for details on audit trails, revocation, and compliance best practices.

Complete Registration Example

Here’s a complete end-to-end example implementing all registration steps:
class RegistrationFlow {
  private apiBase = 'https://dev.api.baanx.com';
  private clientKey = 'YOUR_CLIENT_KEY';

  async sendEmailCode(email: string): Promise<string> {
    const response = await fetch(`${this.apiBase}/v1/auth/register/email/send`, {
      method: 'POST',
      headers: {
        'x-client-key': this.clientKey,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ email })
    });

    if (!response.ok) {
      throw new Error(`Email send failed: ${response.statusText}`);
    }

    const data = await response.json();
    return data.contactVerificationId;
  }

  async verifyEmail(
    email: string,
    password: string,
    code: string,
    contactVerificationId: string,
    countryOfResidence: string
  ): Promise<string> {
    const response = await fetch(`${this.apiBase}/v1/auth/register/email/verify`, {
      method: 'POST',
      headers: {
        'x-client-key': this.clientKey,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        email,
        password,
        verificationCode: code,
        contactVerificationId,
        countryOfResidence,
        allowMarketing: true,
        allowSms: true
      })
    });

    if (!response.ok) {
      throw new Error(`Email verification failed: ${response.statusText}`);
    }

    const data = await response.json();
    return data.onboardingId;
  }

  async sendPhoneCode(
    phoneCountryCode: string,
    phoneNumber: string,
    contactVerificationId: string
  ): Promise<void> {
    const response = await fetch(`${this.apiBase}/v1/auth/register/phone/send`, {
      method: 'POST',
      headers: {
        'x-client-key': this.clientKey,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        phoneCountryCode,
        phoneNumber,
        contactVerificationId
      })
    });

    if (!response.ok) {
      throw new Error(`Phone send failed: ${response.statusText}`);
    }
  }

  async verifyPhone(
    phoneCountryCode: string,
    phoneNumber: string,
    code: string,
    onboardingId: string
  ): Promise<void> {
    const response = await fetch(`${this.apiBase}/v1/auth/register/phone/verify`, {
      method: 'POST',
      headers: {
        'x-client-key': this.clientKey,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        phoneCountryCode,
        phoneNumber,
        verificationCode: code,
        onboardingId
      })
    });

    if (!response.ok) {
      throw new Error(`Phone verification failed: ${response.statusText}`);
    }
  }

  async submitPersonalDetails(
    onboardingId: string,
    firstName: string,
    lastName: string,
    dateOfBirth: string,
    countryOfNationality: string,
    ssn?: string
  ): Promise<void> {
    const body: any = {
      onboardingId,
      firstName,
      lastName,
      dateOfBirth,
      countryOfNationality
    };

    if (ssn) {
      body.ssn = ssn;
    }

    const response = await fetch(`${this.apiBase}/v1/auth/register/personal-details`, {
      method: 'POST',
      headers: {
        'x-client-key': this.clientKey,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(body)
    });

    if (!response.ok) {
      throw new Error(`Personal details failed: ${response.statusText}`);
    }
  }

  async submitAddress(
    onboardingId: string,
    addressLine1: string,
    city: string,
    zip: string,
    isSameMailingAddress: boolean,
    addressLine2?: string,
    usState?: string
  ): Promise<string | null> {
    const body: any = {
      onboardingId,
      addressLine1,
      city,
      zip,
      isSameMailingAddress
    };

    if (addressLine2) body.addressLine2 = addressLine2;
    if (usState) body.usState = usState;

    const response = await fetch(`${this.apiBase}/v1/auth/register/address`, {
      method: 'POST',
      headers: {
        'x-client-key': this.clientKey,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(body)
    });

    if (!response.ok) {
      throw new Error(`Address submission failed: ${response.statusText}`);
    }

    const data = await response.json();
    return data.accessToken;
  }

  async completeRegistration() {
    try {
      const contactVerificationId = await this.sendEmailCode('[email protected]');
      console.log('Email code sent');

      const emailCode = '123456';
      const onboardingId = await this.verifyEmail(
        '[email protected]',
        'SecurePassword123!',
        emailCode,
        contactVerificationId,
        'GB'
      );
      console.log('Email verified, onboardingId:', onboardingId);

      await this.sendPhoneCode('+44', '7400846282', contactVerificationId);
      console.log('Phone code sent');

      const phoneCode = '123456';
      await this.verifyPhone('+44', '7400846282', phoneCode, onboardingId);
      console.log('Phone verified');

      await this.submitPersonalDetails(
        onboardingId,
        'John',
        'Doe',
        '1990-01-01',
        'GB'
      );
      console.log('Personal details submitted');

      const accessToken = await this.submitAddress(
        onboardingId,
        '23 Werrington Bridge Rd',
        'Peterborough',
        'PE6 7PP',
        true,
        'Milking Nook'
      );
      console.log('Registration complete! Access token:', accessToken);

      return accessToken;
    } catch (error) {
      console.error('Registration failed:', error);
      throw error;
    }
  }
}

const registration = new RegistrationFlow();
registration.completeRegistration();

Troubleshooting

Session Expiration

Onboarding sessions may expire if too much time passes between steps. If you receive an “invalid onboarding ID” error:
  1. Check if the session has expired (typically 30-60 minutes)
  2. Restart the registration flow from Step 1
  3. Complete all steps within the session timeout window

Verification Code Issues

If users report not receiving codes:
  1. Email: Check spam/junk folders, verify email address is correct
  2. SMS: Verify phone number format, ensure number can receive SMS, check for carrier blocks

Data Validation Failures

Common validation errors and solutions:
  • Invalid email format: Ensure proper email validation on client side
  • Weak password: Implement password strength checker matching server requirements
  • Invalid date format: Use YYYY-MM-DD format consistently
  • Invalid country code: Use ISO 3166-1 alpha-2 codes (2-letter codes)
  • Phone number format: Remove spaces, dashes, and formatting characters

Best Practices

Client-Side Validation

Validate all fields on the client side before API submission to reduce errors and improve UX

Progress Tracking

Show users clear progress indicators through the 8-step flow so they know where they are

Error Messages

Provide clear, actionable error messages. Don’t just show API errors—explain what the user should do

Session Management

Store onboardingId securely and implement timeout warnings to prevent session expiration

Code Resend

Implement “resend code” functionality for both email and phone verification with rate limiting

Field Persistence

Save form progress locally so users don’t lose data if they navigate away or refresh

Next Steps

After successful registration (including Step 9 consent):
  1. ✅ Store Access Token: Securely store the returned accessToken for authenticated API calls (6-hour validity)
  2. ✅ Consent Established: Your consent record with audit trail is now active. Users can manage consent via their profile
  3. Identity Verification: Direct users to complete identity verification via GET /v1/user/verification
  4. Profile Management: Allow users to view and update their profile via Profile Management
Production Checklist: Ensure Step 9 (Consent Management) is implemented before deploying to production. Missing consent records can result in regulatory compliance issues.