Create a Userv1.2 Only

â„šī¸ Note: This guide covers the flexible user creation features available exclusively in API version v1.2. These features allow you to create user accounts with different authentication methods and optional email validation, without necessarily deploying a wallet immediately.

The IBEX API v1.2 introduces flexible user creation options that go beyond the traditional passkey-only approach. You can now create user accounts in four different ways, depending on your application's needs:

Overview

All user creation scenarios use the GET /v1.2/auth/sign-up endpoint, with different query parameters to control the behavior:

Scenario a) Simple Session Creation

Create a user account without passkey or email

Use case: Quick account creation for users who want immediate access without biometric authentication or email verification.

Endpoint: GET /v1.2/auth/sign-up?passkeys=FALSE

Request Example:

GET /v1.2/auth/sign-up?passkeys=FALSE

Response (200 OK):

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "issuer": "foo.domain",
  "audience": "foo.domain",
  "subject": "<externalUserId>",
  "roles": ["USER"],
  "userId": "<userId>",
  "externalUserId": "<externalUserId>",
  "emailValidationRequired": false,
  "authMethod": "EXTERNAL_USER_ID",
  "hasPasskey": false
}
💾 To Store:
  • access_token: JWT for API authentication (duration: ~1h)
  • refresh_token: JWT to refresh access token (store securely)
  • subject (externalUserId): Unique user identifier for this rpId
  • userId: Internal user ID

Next steps: The user can immediately use the API with the provided tokens. A passkey can be added later using POST /v1.2/auth/sign-up with the externalUserId.

Scenario b) Email-Validated Account (No Wallet)

Create a user account with email validation, without passkey

Use case: Create an account with email verification for security, but defer wallet creation to a later step.

Step 1: Request Sign Up with Email

Endpoint: GET /v1.2/auth/sign-up?passkeys=FALSE&email=user@example.com

Request Example:

GET /v1.2/auth/sign-up?passkeys=FALSE&email=user@example.com

Response (200 OK):

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "issuer": "foo.domain",
  "audience": "foo.domain",
  "subject": "<externalUserId>",
  "roles": ["USER"],
  "userId": "<userId>",
  "externalUserId": "<externalUserId>",
  "emailValidationRequired": true,
  "authMethod": "EMAIL",
  "hasPasskey": false
}
📧 Email Sent: A validation code has been sent to user@example.com. The user must validate their email in the next step.

Step 2: Validate Email

Endpoint: POST /v1.2/auth/sign-up

Request Example:

POST /v1.2/auth/sign-up
Content-Type: application/json

{
  "emailCode": "123456",
  "externalUserId": "<externalUserId-from-step-1>"
}

Response (200 OK):

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "issuer": "foo.domain",
  "audience": "foo.domain",
  "subject": "<externalUserId>",
  "roles": ["USER"],
  "emailValidationStatus": "validated",
  "authMethod": "EMAIL",
  "hasPasskey": false
}
💾 To Store:
  • access_token and refresh_token: JWT tokens for API access
  • subject (externalUserId): Unique user identifier
  • emailValidationStatus: Confirms email is validated

Next steps: The user account is created and email-validated. A passkey (wallet) can be added later using POST /v1.2/auth/sign-up with the externalUserId and a credential.

Scenario c) Wallet + Email Validation

Create a user account with both passkey (wallet) and email validation

Use case: Maximum security: create an account with both biometric authentication (passkey) and email verification, deploying a wallet immediately.

Step 1: Request Sign Up with Passkey and Email

Endpoint: GET /v1.2/auth/sign-up?email=user@example.com (passkeys=TRUE is default)

Request Example:

GET /v1.2/auth/sign-up?email=user@example.com&user.name=user@example.com&user.displayname=John%20Doe

Response (200 OK):

{
  "credentialRequestOptions": {
    "rp": { "id": "foo.domain", "name": "foo.domain" },
    "user": { 
      "id": "<base64url>", 
      "name": "foo.domain <shortId>", 
      "displayName": "foo.domain <shortId>" 
    },
    "challenge": "<base64url>",
    "pubKeyCredParams": [{ "alg": -7, "type": "public-key" }],
    "authenticatorSelection": { 
      "residentKey": "preferred", 
      "userVerification": "preferred" 
    },
    "attestation": "none",
    "timeout": 60000
  },
  "emailValidationRequired": true
}
📧 Email Sent: A validation code has been sent to user@example.com. The email code is optional in the next step (account will be created even if code is invalid, with a warning).

Step 2: Complete Sign Up with Passkey (and optionally Email Code)

Endpoint: POST /v1.2/auth/sign-up

Request Example (with email code):

POST /v1.2/auth/sign-up
Content-Type: application/json

{
  "credential": {
    "id": "AVZs0qRCBSmfThZWu37g...",
    "rawId": "AVZs0qRCBSmfThZWu37g...",
    "type": "public-key",
    "response": {
      "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVh...",
      "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoi..."
    }
  },
  "emailCode": "123456",
  "chainIds": [421614]
}

Request Example (without email code - still works):

POST /v1.2/auth/sign-up
Content-Type: application/json

{
  "credential": {
    "id": "AVZs0qRCBSmfThZWu37g...",
    "rawId": "AVZs0qRCBSmfThZWu37g...",
    "type": "public-key",
    "response": {
      "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVh...",
      "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoi..."
    }
  },
  "chainIds": [421614]
}

Response (200 OK):

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "issuer": "foo.domain",
  "audience": "foo.domain",
  "subject": "<externalUserId>",
  "roles": ["USER"],
  "safeAddress": { 
    "421614": "0xd676c6188195372EC269E9C2cAf815C56436A679"
  },
  "chainId": 421614,
  "emailValidationStatus": "validated",
  "authMethod": "PASSKEY_AND_EMAIL",
  "hasPasskey": true
}
💾 To Store:
  • access_token and refresh_token: JWT tokens
  • subject (externalUserId): Unique user identifier
  • safeAddress: Deployed Safe wallet addresses by chainId
  • emailValidationStatus: Email validation status (if code was provided)

Scenario d) Wallet Only (Traditional)

Create a user account with passkey (wallet) only, without email validation

Use case: Traditional v1/v1.1 behavior: create an account with passkey and deploy wallet immediately, without email verification.

Step 1: Request Sign Up with Passkey

Endpoint: GET /v1.2/auth/sign-up (passkeys=TRUE is default, no email parameter)

Request Example:

GET /v1.2/auth/sign-up?user.name=user@example.com&user.displayname=John%20Doe

Response (200 OK):

{
  "credentialRequestOptions": {
    "rp": { "id": "foo.domain", "name": "foo.domain" },
    "user": { 
      "id": "<base64url>", 
      "name": "foo.domain <shortId>", 
      "displayName": "foo.domain <shortId>" 
    },
    "challenge": "<base64url>",
    "pubKeyCredParams": [{ "alg": -7, "type": "public-key" }],
    "authenticatorSelection": { 
      "residentKey": "preferred", 
      "userVerification": "preferred" 
    },
    "attestation": "none",
    "timeout": 60000
  },
  "emailValidationRequired": false
}

Step 2: Complete Sign Up with Passkey

Endpoint: POST /v1.2/auth/sign-up

Request Example:

POST /v1.2/auth/sign-up
Content-Type: application/json

{
  "credential": {
    "id": "AVZs0qRCBSmfThZWu37g...",
    "rawId": "AVZs0qRCBSmfThZWu37g...",
    "type": "public-key",
    "response": {
      "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVh...",
      "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoi..."
    }
  },
  "chainIds": [421614]
}

Response (200 OK):

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "issuer": "foo.domain",
  "audience": "foo.domain",
  "subject": "<externalUserId>",
  "roles": ["USER"],
  "safeAddress": { 
    "421614": "0xd676c6188195372EC269E9C2cAf815C56436A679"
  },
  "chainId": 421614,
  "authMethod": "PASSKEY",
  "hasPasskey": true
}
💾 To Store:
  • access_token and refresh_token: JWT tokens
  • subject (externalUserId): Unique user identifier
  • safeAddress: Deployed Safe wallet addresses by chainId

Summary

The v1.2 API provides flexible user creation options:

Scenario Parameters Result
a) Simple Session passkeys=FALSE JWT tokens immediately, no wallet
b) Email-Validated Account passkeys=FALSE&email=... JWT tokens + email validation, no wallet
c) Wallet + Email email=... (passkeys=TRUE default) Wallet deployed + email validation
d) Wallet Only No parameters (default) Wallet deployed, no email
🔄 Deferred Passkey Creation: For scenarios a) and b), users can add a passkey (wallet) later by calling POST /v1.2/auth/sign-up with the externalUserId and a credential.

API Reference

For complete API documentation, see: