Skip to main content

accountAdded

The accountAdded webhook event is triggered when a new email account is registered with EmailEngine. It is the first event in the account lifecycle and means the account configuration has been accepted and stored. Nothing has been connected yet.

When This Event is Triggered

The accountAdded event fires once, when an account ID that EmailEngine has not seen before is registered:

The event is sent by the main process after the account has been assigned to a worker and before that worker attempts a connection. Authentication has not been verified at this point.

Re-registering an account ID that already exists does not fire it again. A POST /v1/account for an existing account, or a hosted-form re-authorization of one, updates the stored account and reconnects it instead. The lifecycle events that follow a fresh registration are described under Event Sequence.

Like every event, it is delivered only if accountAdded or * is in webhookEvents. See Webhooks Overview for the allowlist.

Common Use Cases

  • Account registration tracking - Log when new accounts are added to your system
  • Onboarding workflows - Create the local record that later lifecycle events update
  • Billing integration - Start billing cycles when accounts are registered
  • User notifications - Inform users their account is being set up
  • Audit logging - Track all account additions for compliance purposes
  • Dashboard updates - Show newly added accounts in a pending or connecting state

Payload Schema

Top-Level Fields

FieldTypeRequiredDescription
serviceUrlstring or nullYesThe configured EmailEngine service URL. null when the serviceUrl setting is empty
accountstringYesThe account ID that was registered
datestringYesISO 8601 timestamp when the webhook was generated
eventstringYesAlways accountAdded
dataobjectYesEvent data object

Event Data Fields (data object)

FieldTypeRequiredDescription
accountstringYesThe account ID, the same value as the top-level account field

There is no event ID in the body. EmailEngine sends it in the X-EE-Wh-Event-Id request header, which is what to deduplicate on. See Delivery and Retries.

Example Payload

{
"serviceUrl": "https://emailengine.example.com",
"account": "user123",
"date": "2025-10-17T06:49:22.157Z",
"event": "accountAdded",
"data": {
"account": "user123"
}
}

Example Payload (Without Service URL)

When no service URL is configured:

{
"serviceUrl": null,
"account": "gmail-user456",
"date": "2025-10-17T08:15:30.000Z",
"event": "accountAdded",
"data": {
"account": "gmail-user456"
}
}

Handling the Event

Basic Handler

async function handleAccountAdded(event, headers) {
const { account, date } = event;
const eventId = headers['x-ee-wh-event-id'];

console.log(`New account registered: ${account}`);
console.log(` Time: ${date}`);
console.log(` Event ID: ${eventId}`);

// Record the new account in your system
await createAccountRecord(account);
}

Creating Account Records in Database

async function handleAccountAdded(event, headers) {
const { account, date } = event;
const eventId = headers['x-ee-wh-event-id'];

// Create initial account record
await db.accounts.create({
data: {
emailEngineId: account,
status: 'connecting',
createdAt: new Date(date),
lastEventId: eventId
}
});

// Log the account addition
await auditLog.create({
event: 'account_added',
account,
timestamp: date,
eventId
});
}

Triggering Onboarding Workflows

async function handleAccountAdded(event) {
const { account, date } = event;

// Create account record with pending status
await db.accounts.create({
data: {
emailEngineId: account,
status: 'pending_authentication',
createdAt: new Date(date)
}
});

// Send a notification to the user who owns the account
const user = await getUserByAccount(account);
if (user) {
await sendNotification({
userId: user.id,
type: 'account_connecting',
message: 'Your email account is being connected'
});
}

// Check back if neither authenticationSuccess nor an error event has arrived
await scheduleJob('check_account_connection', {
account,
checkAfterMinutes: 5
});
}

Billing Integration

async function handleAccountAdded(event) {
const { account, date } = event;

// Get user associated with this account
const user = await getUserByAccount(account);

if (user) {
// Update billing records
await billing.addAccount({
userId: user.id,
accountId: account,
startDate: new Date(date)
});

// Check account limits
const accountCount = await getAccountCount(user.id);
const plan = await getUserPlan(user.id);

if (accountCount > plan.maxAccounts) {
await billing.upgradeRequired(user.id, 'account_limit_exceeded');
}
}
}

Event Sequence

When a new account is added and connects, you receive:

  1. accountAdded - Account is registered (this event)
  2. authenticationSuccess - The mail server or provider accepted the credentials
  3. accountInitialized - The account reached the connected state for the first time

For an IMAP account the last two arrive in that order: authenticationSuccess is sent as soon as the login succeeds, and accountInitialized after the first pass over the folders. For a Gmail API or Microsoft Graph account both are sent during initialization and accountInitialized comes first. Do not depend on the order between them.

If authentication fails:

  1. accountAdded - Account is registered (this event)
  2. authenticationError or connectError - The credentials were rejected, or the server could not be reached

Differences from Other Account Events

EventWhen TriggeredWhat It Means
accountAddedImmediately after account creationAccount config is stored, connection not yet attempted
authenticationSuccessAfter successful authenticationAccount can connect to mail server
accountInitializedAfter the first successful syncMailboxes and messages are available
accountDeletedWhen account is removedAccount has been deleted from EmailEngine

See Also