Skip to main content
Event functions run automatically when Nango reaches a connection lifecycle event. They are defined with createOnEvent(). Use them to validate credentials, register provider webhooks, seed connection-specific metadata, or clean up external resources before a connection is deleted.

Choose the lifecycle event

Supported events:
EventWhen it runsCommon use
post-connection-creationImmediately after a connection is createdRegister provider webhooks, seed metadata, run setup checks
validate-connectionDuring connection creation and reconnectReject invalid credentials, missing provider permissions, or policy violations (e.g. same-account enforcement)
pre-connection-deletionBefore a connection is deletedDelete provider webhook subscriptions or external resources
Before generating an event function, identify the lifecycle event, provider endpoint, required metadata, and desired failure behavior.For validate-connection, throwing rejects the auth attempt. On first connect, the connection is deleted. On reconnect, the connection is marked as an auth error (refresh_exhausted) and the user must reconnect explicitly. For cleanup functions, make the provider call idempotent because the external resource may already be gone.For same-account enforcement on reconnect, store the provider account identifier in connection metadata during post-connection-creation, then compare it to a live identity API call in validate-connection. Do not compare connection_config fields: reconnect upserts new credentials before validation runs, so config values already reflect the newly authenticated account.

Create a setup function

Add a file under the integration’s on-events/ folder:
salesforce/on-events/post-connection-setup.ts
import { createOnEvent } from 'nango';
import * as z from 'zod';

const Metadata = z.object({
    organization_id: z.string().optional(),
    webhookId: z.string().optional()
});

export default createOnEvent({
    description: 'Register a Salesforce webhook after a successful connection',
    event: 'post-connection-creation',
    metadata: Metadata,
    exec: async (nango) => {
        const response = await nango.post({
            endpoint: '/webhooks',
            data: {
                url: 'https://api.myapp.com/webhooks/salesforce',
                events: ['contact.updated']
            }
        });

        await nango.setMetadata({
            webhookId: response.data.id
        });
    }
});
Import it from index.ts:
index.ts
import './salesforce/on-events/post-connection-setup';

Create a connection validation function

Add a file under the integration’s on-events/ folder:
salesforce/on-events/validate-connection.ts
import { createOnEvent } from 'nango';
import * as z from 'zod';

const Metadata = z.object({
    organizationId: z.string().optional(),
    webhookId: z.string().optional()
});

export default createOnEvent({
    description: 'Validate connection against saved organization id to prevent changing account on reconnect',
    event: 'validate-connection',
    metadata: Metadata,
    exec: async (nango) => {
        const metadata = await nango.getMetadata();
        const lockedOrganizationId = metadata?.organizationId;

        const response = await nango.get<{organization_id: string}>({
            endpoint: '/services/oauth2/userinfo'
        });
        const organizationId = response.data?.organization_id;
        if (!organizationId) {
            throw new nango.ActionError('Salesforce connection missing organization_id from /services/oauth2/userinfo');
        }

        // On initial connection, save organization id to connection metadata
        if (!lockedOrganizationId) {
            await nango.setMetadata({organizationId});
            return;
        }

        // On subsequent connections, check if new organization id matches stored value
        if (lockedOrganizationId !== organizationId) {
            throw new nango.ActionError(
                `Salesforce org mismatch: expected ${lockedOrganizationId}, got ${organizationId}`
            );
        }
    }
});
Import it from index.ts:
index.ts
import './salesforce/on-events/validate-connection';
On first connect, validation passes when metadata has no locked org yet; the setup function records organization_id after auth succeeds. On reconnect, validation compares the new credentials against the saved org ID and rejects the attempt if they differ.
Compare the locked value in metadata to a live value from the provider API. Do not compare connection_config fields: on reconnect, Nango upserts the new credentials and overwrites connection_config before validate-connection runs, so config already reflects the account the user just authenticated with.

Create a cleanup function

If the provider webhook is registered per connection, clean it up before the Nango connection is deleted:
salesforce/on-events/pre-connection-cleanup.ts
import { createOnEvent } from 'nango';
import * as z from 'zod';

const Metadata = z.object({
    organization_id: z.string().optional(),
    webhookId: z.string().optional()
});

export default createOnEvent({
    description: 'Delete the Salesforce webhook before connection deletion',
    event: 'pre-connection-deletion',
    metadata: Metadata,
    exec: async (nango) => {
        const metadata = await nango.getMetadata();
        if (!metadata?.webhookId) {
            return;
        }

        await nango.delete({
            endpoint: `/webhooks/${metadata.webhookId}`
        });
    }
});
Import it from index.ts:
index.ts
import './salesforce/on-events/pre-connection-cleanup';

Test and deploy

Dry run an event function against an existing connection:
nango dryrun post-connection-setup '<CONNECTION-ID>' -e dev
Deploy your functions:
nango deploy
Event functions run automatically when their configured event occurs. Your app does not trigger them directly.