Skip to main content

Flow: User Signup

New customer registration from the console web app through Cognito, Stripe, and DynamoDB.

Request Path

Step-by-Step

1. Browser → Console API

Where: API Gateway {env}-ConsoleApi, route POST /api/account/signup (anonymous — no auth required)

Input: email, name, country, organization, password, Stripe token, optional UTM fields.

Inspect: Check API Gateway — see API Gateway.

2. Cognito User Creation

Where: components/identity_service/backends/identity_aws.py

Calls cognito_idp.sign_up() with email + password + secret hash (HMAC-SHA256).

Creates user in UNCONFIRMED state. Cognito sends verification email via SES.

Cognito trigger fired: PreSignUp Lambda (for Google SSO, links identity and auto-confirms).

Inspect: Check Cognito user — see Cognito.

aws cognito-idp admin-get-user --user-pool-id {pool_id} --username {email}

Failure: UsernameExistsExceptionDuplicateRecordError → upsert instead of create.

3. User Record in DynamoDB

Where: components/users_accounts_service/

Writes to UsersAccountsTable (note: no {env}- prefix in staging):

  • pk: USER#{cognito_sub}, sk: DETAILS
  • Fields: email, name, organization, country, email_verified=false, signup_method

Inspect: Query user record — see DynamoDB and Controller.

4. Stripe Customer Registration

Where: components/billing_service/data/stripe_data_service.py

Creates Stripe customer with email, name, country. Attaches payment method from token. Returns stripe_id.

Failure here is critical: User exists in Cognito but no account. Tracked by INCONSISTENT_ACCOUNT_STORES metric.

5. Account + Membership + API Key Creation

Where: components/users_accounts_service/

Multiple DynamoDB writes to UsersAccountsTable:

  1. Account: pk=ACCOUNT#{visible_account_id}, sk=DETAILS — includes system_account_id, stripe_id, cell_id, limits
  2. Membership (dual-record pattern):
    • pk=USER#{cognito_username}, sk=ACCOUNT#{visible_account_id} — role=OWNER, status=ACTIVE, system_account_id, cloud_provider
    • pk=ACCOUNT#{visible_account_id}, sk=USER#{cognito_username} — role=OWNER, status=ACTIVE, email, signup_method
  3. API Key: via api_keys_service.create_api_key() — default key for the account

The dual membership records allow efficient lookups in both directions (user→accounts and account→users).

Returns visible_account_id to the browser.

6. Email Confirmation

Where: Browser POST /api/account/confirm_signupcomponents/identity_service/

  1. cognito_idp.confirm_sign_up() — validates the 6-digit code
  2. cognito_idp.admin_update_user_attributes() — sets email_verified=true

Cognito trigger fired: PostConfirmation Lambda → Slack webhook notification (skips internal domains: marqo.ai, s2search.io, mailinator.com, tempsmtp.com, e2etest.marqo-staging.com).

Inspect: Check PostConfirmation Lambda logs — see Lambda. Note: this Lambda has a CDK-generated name (not {env}- prefixed). Find it with:

aws lambda list-functions --query "Functions[?contains(FunctionName, 'PostConfirmation') && contains(FunctionName, 'staging')].[FunctionName]" --output text
# Then tail its logs:
aws logs tail /aws/lambda/{function-name-from-above} --since 15m

Google SSO Variation

  1. Cognito redirects to Google OAuth (callback URL includes console hostname)
  2. PreSignUp Lambda fires on PreSignUp_ExternalProvider trigger
  3. Links Google identity to existing Cognito user (if exists) or creates new
  4. Sets autoConfirmUser=true, autoVerifyEmail=true — no email confirmation needed
  5. Account creation proceeds as normal

What to Look For

SymptomWhere to Check
Signup failingMonolith logs (ECS). Check ECS.
"User already exists"Cognito user pool. DDB user record. May need upsert.
No verification emailSES delivery metrics. Cognito trigger logs. Check spam folder.
Stripe failureBilling service logs. INCONSISTENT_ACCOUNT_STORES metric in CloudWatch.
Google SSO not workingPreSignUp Lambda logs. Google OAuth config in Cognito.
Slack notification missingPostConfirmation Lambda logs. Webhook URL valid?
Account created but can't loginCheck email_verified flag in Cognito. Check user_status.

Metrics

CloudWatch custom metrics (namespace varies by component):

  • SIGNUP_SUCCESS, SIGNUP_FAILURE, SIGNUP_ERROR
  • EMAIL_PRESIGNUP_SUCCESS, EMAIL_PRESIGNUP_FAILURE
  • INCONSISTENT_ACCOUNT_STORES, INCONSISTENT_USER_STORES
  • SIGNUP_CONFIRM_SUCCESS, SIGNUP_CONFIRM_ERROR