# Customer sign-up and email verification The Customer sign-up process involves two main steps: 1. **Customer sign-up**: your application collects the user's details and sends a request to the Composable Commerce API to create a Customer record. 2. **Email verification**: the user verifies ownership of their email address by clicking a unique link sent to them. Email verification activates the account and enables features such as password recovery. ## Customer sign-up flow To create a Customer, your application collects the user's information and sends it to the Composable Commerce API. The primary endpoint for creating Customers is `POST /{projectKey}/customers`. The following example shows a request to create a Customer account for a user named Alice, including her address information and a custom field for marketing preferences. ### Endpoint and key request fields - **Endpoint**: `POST /{projectKey}/customers` - **Request summary**: the payload is a [CustomerDraft](/search.md?urn=ctp:api:type:CustomerDraft). In most implementations, the critical fields are `email` and `password` for authentication, `addresses` plus default address indexes for fulfillment, and `stores` when your Project distinguishes between global and Store-specific Customers. Add `custom` only when your business process needs extra profile data at sign-up. - **Outcome**: - After a successful `POST /{projectKey}/customers` request, commercetools creates a Customer record with the `isEmailVerified` flag set as `false`. This initiates the email verification process. ### How Stores affect Customer sign-up For multi-Store operations, the `stores` array in the Customer creation request is critical for managing brand separation and data segregation. ### Store architecture A Project can contain multiple Stores, such as Electronics High Tech for smartphones and Zenith Living for smart home devices. The Customer-to-Store relationship determines authentication, data access, and user experience. For the full reference on how global and Store-specific Customers differ, see [Global versus Store-specific Customers](/api/customers-overview.md#global-versus-store-specific-customers). - **Customer type**: Global Customer - `stores` array value: omitted or empty (`[]`). - Login behavior: can log in via the global `/login` endpoint or any Store-specific `/in-store/key={storeKey}/login` endpoint. Global Customer access is suitable for Customers who shop across all brands. - **Customer type**: Store-specific Customer. - `stores` array value: contains one Store reference. For example, `[{"typeId": "store", "key": "electronics-high-tech-store"}]`. - Login behavior: can log in only via the `/in-store/key={storeKey}/login` endpoint for their assigned Store. Store-specific Customers can't log in via the global `/login` endpoint or an in-Store endpoint for a Store to which they aren't assigned. - **Customer type**: Multi-Store Customer - `stores` array value: contains multiple Store references. For example, `[{"typeId": "store", "key": "electronics-high-tech-store"}, {"typeId": "store", "key": "zenith-living-store"}]`. - Login behavior: can log in via the in-Store endpoint for any of their assigned Stores, but can't use the global `/login` endpoint. Customer-to-Store separation is essential to maintain distinct Customer bases for different brands or regions. ### Why use Stores? Using Stores provides the following advantages: - **Multi-brand management**: operate distinct brands with separate Customer bases, catalogs, and pricing. - **Regional variations**: manage different legal entities, currencies, and languages for international expansion. - **B2B vs. B2C**: run separate storefronts for different Customer segments. - **Data segmentation**: segment Customer data for targeted analysis and marketing. - **Access control**: inherently control authentication against specific storefronts. Proper implementation of the Customer-to-Store relationship enables a flexible, scalable, and secure e-commerce platform. ### Example - Create a global Customer account The following example creates a global Customer account, including address details and a custom field for marketing opt-in. For more information about setting up the TypeScript SDK, see the [Prepare your work environment](/learning-developer-essentials/prepare-your-work-environment/overview.md) module. ```ts import { CustomerDraft, Customer } from "@commercetools/platform-sdk"; import { apiRoot } from "../../client"; // Function to create a new customer async function createCustomer(customerData: CustomerDraft): Promise { try { const customer = await apiRoot .customers() .post({ body: customerData }) .execute(); console.log("Customer created successfully:", customer.body); return customer.body; } catch (error) { console.error("Error creating customer:", error); throw error; } } // Alice's sign-up data const aliceSignUpData: CustomerDraft = { email: "alice.smith@example.com", password: "StrongPassword123!", // Ensure your BFF enforces password compliance e.g. min. password length, usage of special characters, etc. firstName: "Alice", lastName: "Smith", addresses: [ { streetName: "Main St", streetNumber: "123", postalCode: "90210", city: "Beverly Hills", state: "CA", country: "US", }, { streetName: "Oak Ave", streetNumber: "45", postalCode: "10001", city: "New York", state: "NY", country: "US", }, ], defaultShippingAddress: 0, // Index of the first address defaultBillingAddress: 0, // Index of the first address custom: { type: { key: "customer-marketing-opt-in", // Assuming you have a Custom Type defined with this key typeId: "type", }, fields: { marketingOptIn: true, }, }, }; // Execute the customer creation (async () => { try { const newCustomer = (await createCustomer(aliceSignUpData)).customer; // At this point, newCustomer.isEmailVerified will be false console.log( `Customer account created. Email verification status: ${newCustomer.isEmailVerified}` ); } catch (err) { console.error("Failed to create customer account."); } })(); ``` ### Key takeaways - Use `POST /{projectKey}/customers` to create a Customer. - The `isEmailVerified` flag is `false` by default upon creation. - The `stores` array determines if a Customer is global or Store-specific. - Global Customers can log in anywhere; Store-specific Customers are restricted to their assigned Store's login endpoint. - Stores are a powerful feature for managing multiple brands, regions, and business models within a single Project. ## Email verification flow After a Customer record is created, their email address must be verified. This is a critical step for security and user experience. ### Why email verification is crucial - **Data accuracy**: ensures that the email address is valid and owned by the user. - **Spam prevention**: reduces fake or bot accounts, maintaining a clean Customer database. - **Password recovery**: a verified email address is essential for secure password resets. - **Communication channel**: establishes a trusted channel for Order updates, notifications, and marketing. - **Account security**: prevents unauthorized users from registering with an email address they don't own. The email verification process involves creating a verification token and then confirming the email with that token. ### Create the verification token After sign-up, your application must generate a unique token to be sent to the Customer's email. - **Endpoint**: [Create email token for Customer](/search.md?urn=ctp:api:endpoint:/{projectKey}/customers/email-token:POST) (`POST /{projectKey}/customers/email-token`). For Store-specific Customers, use [Create email token for Customer in Store](/search.md?urn=ctp:api:endpoint:/{projectKey}/in-store/key={storeKey}/customers/email-token:POST). - **Request**: send the Customer `id` and a suitable `ttlMinutes` (Time To Live) value in a [CustomerCreateEmailToken](/search.md?urn=ctp:api:type:CustomerCreateEmailToken) payload. A short TTL (for example, 120 minutes) is recommended for security. - **Response**: the API returns a [CustomerToken](/search.md?urn=ctp:api:type:CustomerToken) containing a `value`, which is the token. - **Action**: your backend or email service provider sends the returned token to the Customer's email, typically embedded in a verification link. For example, `https://www.example.com/verify-email?token=TOKEN_VALUE`. When the Customer clicks this link, the Customer is redirected to your application to process the verification. **Important**: the token must be sent via a secure channel, such as email. Never expose it in the client-side response or insecure logs. ### Example - Generate an email verification token This example generates a verification token for a Customer, assuming that you have their `customerId`. ```ts import { apiRoot } from "../../client"; // Assuming newCustomer.id from the previous step const aliceCustomerId = "customer-id-of-alice"; // Replace with actual ID obtained after creation // Function to create an email verification token async function createEmailVerificationToken( customerId: string, ttlMinutes: number = 120 ) { try { const tokenResponse = await apiRoot .customers() .emailToken() .post({ body: { id: customerId, ttlMinutes: ttlMinutes, }, }) .execute(); // Note to learner: Avoid logging tokens in the logs, specially on production env if (process.env.NODE_ENV !== "production") { console.log( "Email verification token generated:", tokenResponse.body.value ); } return tokenResponse.body.value; } catch (error) { // Note to learner: Implement proper error handling console.error("Error generating email verification token:", error); throw error; } } // Execute token generation and simulate sending verification email (async () => { try { const verificationToken = await createEmailVerificationToken( aliceCustomerId ); const verificationLink = `https://www.example.com/verify-email?token=${verificationToken}`; const emailData = { toName: "Alice", toEmail: "alice@example.com", subject: "Verify your Zen Electron Account", body: ` Dear Alice, Thank you for signing up with Zen Electron! Please click the following link to verify your email address: ${verificationLink} This link expires in 2 hours. If you did not create an account, please ignore this email. `.trim(), }; // Note to learner: Implement actual email sending logic inside sendVerificationEmail() sendVerificationEmail(emailData); } catch (err) { console.error("Failed to generate or send verification email."); } })(); ``` ### Confirm the email address When the Customer clicks the verification link, your application's backend extracts the token and sends it to commercetools for confirmation. - **Endpoint**: [Verify email of Customer](/search.md?urn=ctp:api:endpoint:/{projectKey}/customers/email/confirm:POST) (`POST /{projectKey}/customers/email/confirm`). For Store-specific Customers, use [Verify email of Customer in Store](/search.md?urn=ctp:api:endpoint:/{projectKey}/in-store/key={storeKey}/customers/email/confirm:POST). - **Request**: provide the `tokenValue` from the verification link in a [CustomerEmailVerify](/search.md?urn=ctp:api:type:CustomerEmailVerify) payload. - **Outcome**: if the token is valid and not expired, commercetools sets `isEmailVerified` to `true` on the Customer object, which means the Customer account is now fully activated. If the token is invalid or expired, then the API returns an error message. For the full verification flow and token lifecycle details, see [Customer email verification](/api/customers-overview.md#customer-email-verification). ### Example - Confirm a Customer's email address This example shows how your backend confirms the email address by using a token received from the frontend. ```ts import { apiRoot } from "../../client"; // Assuming `tokenValue` is received from the URL parameter (e.g., from the front-end) const receivedTokenValue = "GENERATED_TOKEN_FROM_PREVIOUS_STEP"; // Replace with the actual token // Function to confirm email verification async function confirmEmailVerification(token: string) { try { const customer = await apiRoot .customers() .emailConfirm() .post({ body: { tokenValue: token, }, }) .execute(); console.log( "Email successfully confirmed for customer:", customer.body.email ); console.log("isEmailVerified status:", customer.body.isEmailVerified); return customer.body; } catch (error) { console.error("Error confirming email:", error); throw error; } } // Execute email confirmation (async () => { try { const verifiedCustomer = await confirmEmailVerification(receivedTokenValue); // You can now proceed with logging Alice in or directing her to a welcome page } catch (err) { console.error("Email verification failed."); } })(); ``` ### Considerations for email verification - **Token expiry (TTL)**: set a reasonable TTL for tokens (1-2 hours is secure). Your application should handle expired tokens by prompting the user to request a new one. - **User experience**: provide clear instructions. After sign-up, inform the user to check their email. Offer a resend verification email option, which triggers a new `POST /{projectKey}/customers/email-token` request. - **Email templates**: email templates are managed by an external email service provider (ESP) like SendGrid, not by commercetools. Customize these templates to match your brand. When using Subscriptions for asynchronous flows, the `CustomerEmailTokenCreated` message contains the `tokenValue` only if the `ttlMinutes` is 60 minutes or fewer. For longer token validity, the `tokenValue` is omitted due to security reasons. For more information, see the [`CustomerEmailTokenCreated` message](/api/projects/messages/customer-messages.md#customer-email-token-created) documentation. ### Considerations for migrating verified customers When migrating existing, verified Customers from another system, you can create Customer profiles with the `isEmailVerified` flag set as `true` to bypass the verification flow. If you use an external identity provider (IDP) like Auth0 or Okta, then the IDP typically manages email verification. In this scenario, your application relies on the IDP's verification status and the `isEmailVerified` flag in commercetools might be redundant. ### Key takeaways - Email verification uses a two-step process: token creation and email confirmation. - Use `POST /{projectKey}/customers/email-token` to generate a secure, time-limited token for a Customer. - Your application is responsible for sending the verification link with the token to the Customer's email. - Use `POST /{projectKey}/customers/email/confirm` with the `tokenValue` to verify the email address. - A successful verification sets the `isEmailVerified` flag for the Customer as `true`. ## Sign-up and email verification flow diagram The following diagram illustrates the sign-up and email verification flow: ```mermaid sequenceDiagram participant C as Customer (Alice) participant ZE_UI as Zen Electron UI (Frontend) participant ZE_BFF as Zen Electron BFF (Backend for Frontend) participant CT_API as Composable Commerce API participant ESP as Email Service Provider C->>ZE_UI: 1. Fills out sign-up Form ZE_UI->>ZE_BFF: 2. Sends sign-up Data ZE_BFF->>CT_API: 3. POST /{projectKey}/customers (Create Customer) activate CT_API CT_API-->>ZE_BFF: 4. Returns Customer Record (isEmailVerified: false, customerId) deactivate CT_API ZE_BFF->>CT_API: 5. POST /{projectKey}/customers/email-token (Generate Token) activate CT_API CT_API-->>ZE_BFF: 6. Returns Verification Token deactivate CT_API ZE_BFF->>ESP: 7. Sends Email with Verification Link (containing token) ESP-->>C: 8. Delivers Verification Email C->>ZE_UI: 9. Clicks Verification Link ZE_UI->>ZE_BFF: 10. Sends Token from Link ZE_BFF->>CT_API: 11. POST /{projectKey}/customers/email/confirm (Confirm Email) activate CT_API CT_API-->>ZE_BFF: 12. Returns Confirmed Customer Record (isEmailVerified: true) deactivate CT_API ZE_BFF->>ZE_UI: 13. Redirects to Welcome/Success Page ZE_UI-->>C: 14. Displays Success Message ``` You've now learned how to create Customer accounts and implement email verification. Next, we'll cover how Customers can securely log in to their accounts. ## Related pages - [Area overview page with navigation](/learning-implement-carts-and-shopping-lists.md) - [Previous page: Overview](/learning-implement-carts-and-shopping-lists/manage-signups-and-signins/overview.md) - [Next page: Customer sign-in](/learning-implement-carts-and-shopping-lists/manage-signups-and-signins/customer-signin.md) - [Search documentation and API specs](/search.md)