Client registration

How to follow this chapter

Inputs: Client inputs and lookup IDs; start with the GUEST token.

Steps and expected result: Complete all verification steps in order. The returned client identity and token pair become the context for subsequent client calls.

Alternative or later actions: Self-registration is the main path. Use administrator-assisted creation only when your integration creates clients on their behalf.

DTO source: CivilStatusOutputModel, ClientOutputModel, CountryOutputModel, GenderOutputModel, LanguageOutputModel, LegalAcceptanceOutputModel, NationalityOutputModel, PensionSituationOutputModel, RegistrationFirstFactorOutputModel, RegistrationSecondFactorOutputModel, RegistrationThirdFactorOutputModel, TaxLiabilityOutputModel

Variables assigned in an earlier block are reused by later blocks. Client-entered values come from your UI; keep identifiers and lookup results in the current client context. Do not create a fresh context between steps.

The snippets use shared helper classes. See Shared helper classes: complete C# and Java source before copying an example.
Before you start: Client contact details, consentTimestamp collected when consent is given, phoneNumberPrefix, phoneNumberNumber and a password chosen by the client. Choose self-registration with GUEST or administrative registration with ADMIN.
Keep for the next step: The userId. Self-registration also returns accessToken, refreshToken and validUntil. For administrative registration, retain the existing ADMIN token.

Note

During client registration, the user's e-mail address is requested. The e-mail address is unique within the WMS database for the target deployment. Any attempt to register a customer with an existing e-mail address will fail.
There are two options for user registration: client self-registration or administrative client registration.

Client self-registration

During self-registration, the user can complete the registration process independently by collecting all the information step by step. This is a three-factor registration for which a GUEST authorization token is required. During this process, the client first receives a code via e-mail, then a code via SMS, and finally a CLIENT token, which is used for all further HTTP calls. Use the returned ValidUntil to determine when this token expires.

First-factor registration

Inputs: Client name, surname, email, password, consent timestamp and gender lookup.
Action: Start self-registration and request email verification.
Keep / expected result: Keep the registration inputs; collect emailVerificationCode for the final step.
During initial registration, the client's gender, first name, last name, and e-mail address are requested. Set the HTTP client to the GUEST token for this registration path. The client receives an e-mail verification code. Keep this code for the final registration request; the next request starts mobile-number verification.

httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", guestToken);
// clientEmail, clientName and clientSurname are entered by the client.
var gender = await httpClient.GetFromJsonAsync<GenderOutputModel>("/api/v1/genders/code/MALE");
var payload = JsonConvert.SerializeObject(new
{
    GenderId = gender.Id,
    Name = clientName,
    Surname = clientSurname,
    EmailAddress = clientEmail
});
var sContent = new StringContent(payload, Encoding.UTF8, "application/json");
var httpResponseMessage = await httpClient.PostAsync("api/v1/users/registration/first-factor", sContent);
httpResponseMessage.EnsureSuccessStatusCode();
var resultAsString = await httpResponseMessage.Content.ReadAsStringAsync();
var firstFactorRegistrationResult = JsonConvert.DeserializeObject<RegistrationFirstFactorOutputModel>(resultAsString);

								
// clientEmail, clientName and clientSurname are entered by the client.

// First-factor registration
HttpRequest genderRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("genders/code/MALE"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
    .header("Content-Type", "application/json")
    .build();

HttpResponse<String> response = httpClient.send(genderRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
GenderOutputModel genderObj = new Gson().fromJson(response.body(), GenderOutputModel.class);
var genderId = genderObj.Id;

JsonObject firstFactorPayload = new JsonObject();
firstFactorPayload.addProperty("GenderId", genderId);
firstFactorPayload.addProperty("Name", clientName);
firstFactorPayload.addProperty("Surname", clientSurname);
firstFactorPayload.addProperty("EmailAddress", clientEmail);

HttpRequest firstFactorRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("users/registration/first-factor"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(firstFactorPayload.toString()))
    .build();

response = httpClient.send(firstFactorRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
RegistrationFirstFactorOutputModel firstFactorRegistrationResult = new Gson().fromJson(response.body(), RegistrationFirstFactorOutputModel.class);

Second-factor registration

Inputs: The same email and the phone number entered by the client.
Action: Request phone verification.
Keep / expected result: Collect smsVerificationCode; retain both verification codes.
This request sends a verification code to the supplied mobile number. Collect both the e-mail and SMS verification codes from the client before the third step:

payload = JsonConvert.SerializeObject(new { PhoneNumberPrefix = phoneNumberPrefix, PhoneNumberNumber = phoneNumberNumber });
sContent = new StringContent(payload, Encoding.UTF8, "application/json");
httpResponseMessage = await httpClient.PostAsync("api/v1/users/registration/second-factor", sContent);
httpResponseMessage.EnsureSuccessStatusCode();
resultAsString = await httpResponseMessage.Content.ReadAsStringAsync();
var secondFactorRegistrationResult = JsonConvert.DeserializeObject<RegistrationSecondFactorOutputModel>(resultAsString);

								
// Second-factor registration
JsonObject secondFactorPayload = new JsonObject();
secondFactorPayload.addProperty("PhoneNumberPrefix", phoneNumberPrefix);
secondFactorPayload.addProperty("PhoneNumberNumber", phoneNumberNumber);

HttpRequest secondFactorRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("users/registration/second-factor"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(secondFactorPayload.toString()))
    .build();

response = httpClient.send(secondFactorRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
RegistrationSecondFactorOutputModel registrationSecondFactorOutputModel = new Gson().fromJson(response.body(), RegistrationSecondFactorOutputModel.class);

Third-factor registration

Inputs: Both verification codes, language and consent choices.
Action: Complete self-registration.
Keep / expected result: Keep userId, accessToken, refreshToken and validUntil.
Collect emailVerificationCode and smsVerificationCode from the client. The Token response fields may be empty when the environment does not return verification codes in responses. This step completes the registration process. The response contains a CLIENT token that must be used for subsequent HTTP calls:

var language = await httpClient.GetFromJsonAsync<LanguageOutputModel>("/api/v1/languages/code/DE");
var legalAcceptance = await httpClient.GetFromJsonAsync<LegalAcceptanceOutputModel>("/api/v1/legal-acceptances/active");
payload = JsonConvert.SerializeObject(new
{
    ManagementPersonalDataConsent = consentTimestamp,
    SecondFactorToken = smsVerificationCode,
    FirstFactorToken = emailVerificationCode,
    GenderId = gender.Id,
    Name = clientName,
    Surname = clientSurname,
    EmailAddress = clientEmail,
    PhoneNumberPrefix = phoneNumberPrefix,
    PhoneNumberNumber = phoneNumberNumber,
    Password = clientPassword,
    LanguageId = language.Id,
    LegalAcceptanceId = legalAcceptance.Id, });
sContent = new StringContent(payload, Encoding.UTF8, "application/json");
httpResponseMessage = await httpClient.PostAsync("api/v1/users/registration/third-factor", sContent);
httpResponseMessage.EnsureSuccessStatusCode();
resultAsString = await httpResponseMessage.Content.ReadAsStringAsync();
var thirdFactorRegistrationResult = JsonConvert.DeserializeObject<RegistrationThirdFactorOutputModel>(resultAsString);
var userId = thirdFactorRegistrationResult.UserId;
var accessToken = thirdFactorRegistrationResult.AccessToken;
var refreshToken = thirdFactorRegistrationResult.RefreshToken;
var validUntil = thirdFactorRegistrationResult.ValidUntil;
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

								
// Third-factor registration
HttpRequest languageRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("languages/code/DE"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
    .header("Content-Type", "application/json")
    .build();

response = httpClient.send(languageRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
LanguageOutputModel languageObj = new Gson().fromJson(response.body(), LanguageOutputModel.class);
var languageId = languageObj.Id;

HttpRequest legalAcceptanceRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("legal-acceptances/active"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
    .header("Content-Type", "application/json")
    .build();

response = httpClient.send(legalAcceptanceRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
LegalAcceptanceOutputModel legalAcceptanceObj = new Gson().fromJson(response.body(), LegalAcceptanceOutputModel.class);
var legalAcceptanceId = legalAcceptanceObj.Id;

JsonObject thirdFactorPayload = new JsonObject();
thirdFactorPayload.addProperty("ManagementPersonalDataConsent", consentTimestamp);
thirdFactorPayload.addProperty("FirstFactorToken", emailVerificationCode);
thirdFactorPayload.addProperty("SecondFactorToken", smsVerificationCode);
thirdFactorPayload.addProperty("GenderId", genderId);
thirdFactorPayload.addProperty("Name", clientName);
thirdFactorPayload.addProperty("Surname", clientSurname);
thirdFactorPayload.addProperty("EmailAddress", clientEmail);
thirdFactorPayload.addProperty("PhoneNumberPrefix", phoneNumberPrefix);
thirdFactorPayload.addProperty("PhoneNumberNumber", phoneNumberNumber);
thirdFactorPayload.addProperty("Password", clientPassword);
thirdFactorPayload.addProperty("LanguageId", languageId);
thirdFactorPayload.addProperty("LegalAcceptanceId", legalAcceptanceId);

HttpRequest thirdFactorRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("users/registration/third-factor"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(thirdFactorPayload.toString()))
    .build();

response = httpClient.send(thirdFactorRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
RegistrationThirdFactorOutputModel registrationThirdFactorOutputModelObj = new Gson().fromJson(response.body(), RegistrationThirdFactorOutputModel.class);

var userId = registrationThirdFactorOutputModelObj.UserId;
var accessToken = registrationThirdFactorOutputModelObj.AccessToken;
var refreshToken = registrationThirdFactorOutputModelObj.RefreshToken;
var validUntil = registrationThirdFactorOutputModelObj.ValidUntil;

Note

Read ValidUntil from the registration result. Token lifetime is configured by the service. If the refresh-token pair is still valid, obtain a new CLIENT token using the refreshing token procedure.

Recognize the result

After completed self-registration: store the client ID and both tokens. These values are illustrative.

{"userId":123,"accessToken":"<access-token>","refreshToken":"<refresh-token>","validUntil":"2026-10-13T12:00:00Z"}

The DTO catalog contains the remaining response properties. A failed request must be handled before advancing to the next step; see Error handling.

Alternative: administrative client registration

The snippets use shared helper classes. See Shared helper classes: complete C# and Java source before copying an example.
Administrative registration creates a client in one call using an existing ADMIN token. The response contains the client data and identifier; it does not issue a new ADMIN token. Use this path for an authorized server-to-server integration that collects the required client data. It requires an ADMIN token authorized to create clients. Its lifetime is determined by the integration configuration. Please keep it secret.

Single call

Inputs: ADMIN token, complete client data and lookup IDs.
Action: Create the client on their behalf.
Keep / expected result: Keep the returned userId; this operation does not return a new administrative token.
The following example creates a client with the shown fields. Provide the information required by the selected product and follow the API validation rules. clientBirthDate comes from the client (C#: DateTime; Java: an ISO yyyy-MM-dd string). In this case, the HTTP proxy must use an ADMIN token.

httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", adminToken);
var language = await httpClient.GetFromJsonAsync<LanguageOutputModel>("/api/v1/languages/code/DE");
var legalAcceptance = await httpClient.GetFromJsonAsync<LegalAcceptanceOutputModel>("/api/v1/legal-acceptances/active");
var nationality = await httpClient.GetFromJsonAsync<NationalityOutputModel>("api/v1/nationalities/code/CH");
var country = await httpClient.GetFromJsonAsync<CountryOutputModel>("api/v1/countries/code/CH");
var pensionSituation = await httpClient.GetFromJsonAsync<PensionSituationOutputModel>("api/v1/pension-situations/code/PENSION-FUND");
var taxLiability = await httpClient.GetFromJsonAsync<TaxLiabilityOutputModel>("api/v1/tax-liabilities/code/CH");
var civilStatus = await httpClient.GetFromJsonAsync<CivilStatusOutputModel>("api/v1/civil-statuses/code/SINGLE");
var gender = await httpClient.GetFromJsonAsync<GenderOutputModel>("/api/v1/genders/code/MALE");
var payload = JsonConvert.SerializeObject(new
{
    ManagementPersonalDataConsent = consentTimestamp,
    Name = clientName,
    Surname = clientSurname,
    Email = clientEmail,
    PhoneNumberPrefix = phoneNumberPrefix,
    PhoneNumberNumber = phoneNumberNumber,
    PensionSituationId = pensionSituation.Id,
    CivilStatusId = civilStatus.Id,
    LanguageId = language.Id,
    LegalAcceptanceId = legalAcceptance.Id,
    Address = new
    {
        City = "Baar",
        Zip = "6340",
        Street = "Neugasse",
        StreetNr = "28A",
        CountryId = country.Id,
        CoName = "Zoo",
        CoSurname = "Zurich"
    },
    TaxLiabilityId = taxLiability.Id,
    GenderId = gender.Id,
    Nationality1Id = nationality.Id,
    BirthDate = clientBirthDate
});
var httpResponseMessage = await httpClient.PostAsync("api/v1/users", new StringContent(payload, Encoding.UTF8, "application/json"));
httpResponseMessage.EnsureSuccessStatusCode();
var userId = JsonConvert.DeserializeObject<ClientOutputModel>(await httpResponseMessage.Content.ReadAsStringAsync()).Id;
var accessToken = adminToken;

								
// clientEmail, clientName and clientSurname are entered by the client.

HttpRequest languageRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("languages/code/DE"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
    .header("Content-Type", "application/json")
    .build();

var response = httpClient.send(languageRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
LanguageOutputModel languageObj = new Gson().fromJson(response.body(), LanguageOutputModel.class);
var languageId = languageObj.Id;

HttpRequest legalAcceptanceRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("legal-acceptances/active"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
    .header("Content-Type", "application/json")
    .build();

response = httpClient.send(legalAcceptanceRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
LegalAcceptanceOutputModel legalAcceptanceObj = new Gson().fromJson(response.body(), LegalAcceptanceOutputModel.class);
var legalAcceptanceId = legalAcceptanceObj.Id;

HttpRequest nationalityRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("nationalities/code/CH"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
    .header("Content-Type", "application/json")
    .build();

response = httpClient.send(nationalityRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
NationalityOutputModel nationalityObj = new Gson().fromJson(response.body(), NationalityOutputModel.class);
var nationalityId = nationalityObj.Id;

HttpRequest countryRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("countries/code/CH"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
    .header("Content-Type", "application/json")
    .build();

response = httpClient.send(countryRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
CountryOutputModel countryObj = new Gson().fromJson(response.body(), CountryOutputModel.class);
var countryId = countryObj.Id;

HttpRequest pensionRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("pension-situations/code/PENSION-FUND"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
    .header("Content-Type", "application/json")
    .build();

response = httpClient.send(pensionRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
PensionSituationOutputModel pensionObj = new Gson().fromJson(response.body(), PensionSituationOutputModel.class);
var pensionId = pensionObj.Id;

HttpRequest taxLiabilityRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("tax-liabilities/code/CH"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
    .header("Content-Type", "application/json")
    .build();

response = httpClient.send(taxLiabilityRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
TaxLiabilityOutputModel taxLiabilityObj = new Gson().fromJson(response.body(), TaxLiabilityOutputModel.class);
var taxLiabilityId = taxLiabilityObj.Id;

HttpRequest civilStatusRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("civil-statuses/code/SINGLE"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
    .header("Content-Type", "application/json")
    .build();

response = httpClient.send(civilStatusRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
CivilStatusOutputModel civilStatusObj = new Gson().fromJson(response.body(), CivilStatusOutputModel.class);
var civilStatusId = civilStatusObj.Id;

HttpRequest genderRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("genders/code/MALE"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
    .header("Content-Type", "application/json")
    .build();

response = httpClient.send(genderRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
GenderOutputModel genderObj = new Gson().fromJson(response.body(), GenderOutputModel.class);
var genderId = genderObj.Id;

// Preparing payload
JsonObject registrationPayload = new JsonObject();
registrationPayload.addProperty("ManagementPersonalDataConsent", consentTimestamp);
registrationPayload.addProperty("CivilStatusId", civilStatusId);
registrationPayload.addProperty("Name", clientName);
registrationPayload.addProperty("Surname", clientSurname);
registrationPayload.addProperty("Email", clientEmail);
registrationPayload.addProperty("PhoneNumberPrefix", phoneNumberPrefix);
registrationPayload.addProperty("PhoneNumberNumber", phoneNumberNumber);
registrationPayload.addProperty("PensionSituationId", pensionId);
registrationPayload.addProperty("LanguageId", languageId);
registrationPayload.addProperty("LegalAcceptanceId", legalAcceptanceId);
registrationPayload.addProperty("GenderId", genderId);
registrationPayload.addProperty("BirthDate", clientBirthDate);
registrationPayload.addProperty("Nationality1Id", nationalityId);
registrationPayload.addProperty("TaxLiabilityId", taxLiabilityId);
JsonObject address = new JsonObject();
address.addProperty("City", "Baar");
address.addProperty("Zip", "6340");
address.addProperty("Street", "Neugasse");
address.addProperty("StreetNr", "1");
address.addProperty("CountryId", countryId);
registrationPayload.add("Address", address);

HttpRequest createRequest = HttpRequest.newBuilder()
    .timeout(Duration.ofMinutes(1))
    .uri(Settings.wmsUri("users"))
    .header("Accept-Language", "de-DE")
    .header("Authorization", "Bearer " + Settings.ADMIN_TOKEN)
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(registrationPayload.toString()))
    .build();

response = httpClient.send(createRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
ClientOutputModel clientObj = new Gson().fromJson(response.body(), ClientOutputModel.class);

var userId = clientObj.Id;
// Continue using the existing administrative access token.
var accessToken = Settings.ADMIN_TOKEN;