Internal Documentation internal
TalkIDE internal documentation

Authenticated user changes the billing email address used on Stripe invoices and payment receipts. This is independent of the account email (users.email). Requires a valid JWT access token.

  • Billing email is stored on the Stripe Customer object (customer.email) — not in a TalkIDE DB column.
  • On first billing action, Stripe Customer is created with the user’s account email as billing email. The user can then override it independently.
  • The new billing email must be a valid email format. No uniqueness check across users (multiple users may share a billing entity, e.g. company billing email).
  • If the user has no stripe_customer_id yet, this endpoint lazily creates the Stripe Customer (same as UC-10001) and sets the provided email on it.
  • Related: UC-10002billingEmail field in the GET response reflects the current Stripe Customer email.
sequenceDiagram
    actor User

    User->>+FE: edits billing email field and clicks "Save"

    FE->>FE: validate email format
    alt email format invalid
        FE-->>User: show "Please enter a valid email address" error
    end

    FE->>+BE: PUT /api/v1/users/me/billing/email <br> Authorization: Bearer {accessToken} <br> UpdateBillingEmailRequest

    BE->>BE: validate JWT access token
    alt access token invalid or missing
        BE-->>FE: 401 Unauthorized <br> ErrorResponse
    end

    BE->>BE: validate request (email format)
    alt request is invalid
        BE-->>FE: 400 Bad Request <br> ErrorResponse
    end

    BE->>BE: resolve stripe_customer_id (ensureCustomer)
    alt stripe_customer_id is null
        BE->>Stripe: stripe.customers.create({ email: newEmail, metadata: { userId } })
        Stripe-->>BE: Customer { id: "cus_..." }
        BE->>DB: UPDATE users SET stripe_customer_id = "cus_..." WHERE id = userId
    end

    BE->>Stripe: stripe.customers.update(customerId, { email: newEmail })
    Stripe-->>BE: Customer (updated)

    BE->>-FE: 204 No Content

    FE->>-User: show "Billing email updated" success toast, update displayed billing email

PUT /api/v1/users/me/billing/email UpdateBillingEmailRequest:

{
  "billingEmail": "billing@mycompany.com"
}

204 No Content (success, no body)

400 Bad Request (validation) ErrorResponse:

{
  "status": 400,
  "code": "VALIDATION_ERROR",
  "message": "Validation failed",
  "errors": [
    { "field": "billingEmail", "message": "must be a valid email address" }
  ]
}

401 Unauthorized ErrorResponse:

{
  "status": 401,
  "code": "AUTHENTICATION_FAILED",
  "message": "Access token is missing or invalid"
}

502 Bad Gateway (Stripe API unreachable) ErrorResponse:

{
  "status": 502,
  "code": "STRIPE_UNAVAILABLE",
  "message": "Payment provider is temporarily unavailable. Please try again."
}

Frontend

Validations

FieldConstraintsSizePatternNote
billingEmailnot_blank, email5 - 254^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$Validated before submit

UX Guidelines

Billing email row in BillingSection.vue:

  • Displayed as an inline editable field. Default value populated from GET /payment-method response (billingEmail field).
  • “Save” button visible only when the value differs from the loaded value.
  • On 204: toast (green, 3 s): “Billing email updated.”
  • On 502: toast (rose, 5 s): “Could not update billing email. Please try again.”
  • On 400: inline error below field.

Backend

Validations

FieldConstraintsNote
JWT Authorization headernot_blank, valid signature, not expired401 if missing/invalid
billingEmailnot_blank, valid email format400 VALIDATION_ERROR if blank or invalid format

Test Cases

GIVENWHENTHEN
Authenticated user with existing stripe_customer_idPUT /billing/email is called with valid email204 No Content; Stripe Customer email updated; subsequent GET /payment-method returns new billingEmail
Authenticated user with no stripe_customer_idPUT /billing/email is called with valid emailStripe Customer lazily created with new email; stripe_customer_id persisted to users table; 204 No Content
billingEmail is blankPUT /billing/email is called400 VALIDATION_ERROR returned
billingEmail is not a valid email (e.g. “notanemail”)PUT /billing/email is called400 VALIDATION_ERROR returned
No Authorization headerPUT /billing/email is called401 AUTHENTICATION_FAILED returned
Stripe API unreachablePUT /billing/email is called502 STRIPE_UNAVAILABLE returned; no Customer created/updated

Was this page helpful?

Thanks for the feedback.