Skip to content

Welcome new users

Send a welcome email and write signup-context data to app_metadata for every new user account created on your tenant.

Every product benefits from a clean post-signup ritual. The welcome email signals to the user that their account exists and that you are paying attention. The app_metadata write — country, device type, acquisition path — gives your product analytics something to work with from day one, without a separate event-tracking pipeline.

This recipe is a natural companion to the tag corporate vs self-serve recipe. Running both together means a new account arrives already tagged by segment and already welcomed.

A custom action attached to the Registration flow's Post-Create stage. The action runs after the identity has been created, so event.user.id and event.user.email are populated and stable. It writes signup context to app_metadata, then delivers a welcome email through one of two paths:

  • Path A — the platform's built-in notification block handles delivery. Use this when you have configured an email template in Branding > Email Templates.
  • Path B — the custom action calls api.fetch to your own transactional mailer (Postmark, Resend, Amazon SES, etc.). Use this when your mailer owns the template and your brand team manages it there.

Pick one path. Running both would send two emails.

Open Custom Actions and click + New Action.

Set:

  • Name: welcome-new-users
  • Trigger: Registration

Click Save.

Replace the default function body with the code from whichever path you have chosen, in the Code section below. Click Save, then toggle the action to Enabled.

Step 3 — Add the block to the Registration flow

Section titled “Step 3 — Add the block to the Registration flow”

Navigate to Flows → Registration → Edit. Expand the Post-Create stage.

Drag a Run Custom Action block into the Post-Create stage. In the configuration panel, select welcome-new-users. Click Save.

Post-Create runs after the identity is committed — event.user.id is stable here.

Path A only: Also add a Send Email block to the Post-Create stage, placed immediately after the Run Custom Action block. Configure the block with your welcome template, subject line, and sender address. The custom action does not send the email in Path A — it only writes the metadata.

Click Publish. All new registrations from this point forward will run the action.

Path A — metadata only (pair with the Send Email block)

Section titled “Path A — metadata only (pair with the Send Email block)”
async function welcomeNewUser(event, api) {
const signupCountry = event.request.geo.country ?? 'unknown';
const signupDevice = event.request.user_agent.device_type ?? 'unknown';
// Write context to the admin-controlled metadata bag.
// These values persist across all future logins and can be read back
// as event.user.app_metadata.signup_country in any later flow.
api.user.setAppMetadata({
signup_country: signupCountry,
signup_device: signupDevice,
signup_at: new Date().toISOString(),
});
api.log('info', `New user tagged — country: ${signupCountry}, device: ${signupDevice}`);
}

The Send Email block in the flow handles delivery. No network call is made from the action itself, which keeps the action fast and failure-free.

Path B — metadata + your transactional mailer

Section titled “Path B — metadata + your transactional mailer”
async function welcomeNewUser(event, api) {
const email = event.user.email ?? '';
const userId = event.user.id;
const signupCountry = event.request.geo.country ?? 'unknown';
const signupDevice = event.request.user_agent.device_type ?? 'unknown';
const createdAt = event.user.created_at;
// 1. Write signup context before the outbound call so the data
// is persisted even if the mailer call fails.
api.user.setAppMetadata({
signup_country: signupCountry,
signup_device: signupDevice,
signup_at: createdAt,
});
// 2. POST to your mailer.
// Replace the URL with your production endpoint.
// Add the hostname to Tenant Settings > Custom Actions > Fetch Allowlist
// before deploying to production.
try {
const res = await api.fetch('https://mailer.example.com/v1/welcome', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// Include a pre-shared secret in the header so your mailer
// can verify the request came from the platform.
// Store secrets in Tenant Settings > Custom Actions > Secrets,
// then reference them from your block configuration.
'X-Internal-Secret': 'replace-with-secret-reference',
},
body: JSON.stringify({
email,
user_id: userId,
signup_country: signupCountry,
signup_device: signupDevice,
}),
});
if (res.status >= 200 && res.status < 300) {
api.log('info', `Welcome email dispatched for ${email}`);
} else {
// Log the failure but do not deny — a failed welcome email
// should not block the user from completing registration.
api.log('warn', `Welcome email delivery failed: HTTP ${res.status}`);
}
} catch (err) {
api.log('error', `Welcome email error: ${String(err)}`);
}
}

Open the welcome-new-users action in Custom Actions and click Test. Select the New registration preset.

Click Run. In the API calls tab:

  • You should see api.user.setAppMetadata called with signup_country, signup_device, and signup_at populated.
  • For Path B, you should also see api.fetch called with your mailer endpoint. Because the test pane runs in sandbox mode, the fetch will be intercepted — check that the request shape in the API calls tab matches what your mailer expects.
  • You should NOT see api.deny.

Edit the Event JSON to simulate a mobile user by changing request.user_agent.device_type to mobile. Run again and confirm that signup_device in the setAppMetadata call reflects the change.

Register a new account at your tenant's registration URL with a fresh email address.

In the admin console, navigate to Users and open the new user's profile. Under App Metadata, you should see signup_country, signup_device, and signup_at present with the values from the registration request.

Check your mailer's dashboard (Path B only) to confirm the welcome message was delivered.

Navigate to Flows → Registration → Recent Runs and open the latest run. Confirm the action ran in the Post-Create stage with status Completed.

Blocking calls slow down registration. The action runs synchronously in the registration path. If your mailer endpoint responds slowly, the user waits. Two practical mitigations: use a queue (your action POSTs to an internal queue endpoint; the queue handles delivery asynchronously) or use Path A with the built-in Send Email block, which the platform dispatches asynchronously.

Write metadata before the outbound call. The code above writes app_metadata before calling api.fetch. If the mailer call throws, the metadata is already persisted. This ordering is intentional — do not swap it.

Do not log event.user.email in production at info level. The examples page logs the email for visibility during development. For production, prefer logging event.user.id only. The audit trail already records the email; you do not need to duplicate it.