Skip to main content
You create workflows by describing what you need to the AI in chat. The AI generates the Inngest function, configures the trigger, and wires it into your app.

Creating a workflow with AI

Describe what you need in plain language:
“Create a workflow that syncs our Salesforce contacts every hour.”
“Set up a daily job that sends a Slack summary of open tickets at 9 AM.”
“Add a workflow that sends a welcome email when a new user is added to the database.”
The AI creates the function, sets up the trigger, and registers it. You can view the generated code in the Code tab and test it in the sandbox.

Workflow structure

Workflows are Inngest functions stored in your app’s /inngest/functions directory. Here is a scheduled workflow example:
import { inngest } from './client';

export const syncContacts = inngest.createFunction(
  { id: 'sync-contacts' },
  { cron: '0 * * * *' }, // Every hour
  async ({ step }) => {
    const contacts = await step.run('fetch-contacts', async () => {
      // Fetch contacts from Salesforce
      const response = await fetch('https://api.salesforce.com/contacts', {
        headers: {
          Authorization: `Bearer ${process.env.SALESFORCE_TOKEN}`,
        },
      });
      return response.json();
    });

    await step.run('update-database', async () => {
      // Update local database with fetched contacts
      for (const contact of contacts) {
        await db.contact.upsert({
          where: { externalId: contact.id },
          update: { name: contact.name, email: contact.email },
          create: { externalId: contact.id, name: contact.name, email: contact.email },
        });
      }
    });
  }
);
Each workflow has three parts:
PartPurpose
Function configA unique ID and optional name
TriggerA cron schedule or an event name
HandlerThe logic that executes, broken into steps

Cron scheduling

Scheduled workflows use cron expressions:
ExpressionSchedule
* * * * *Every minute
*/15 * * * *Every 15 minutes
0 * * * *Every hour
0 9 * * *Daily at 9:00 AM (UTC)
0 9 * * 1Every Monday at 9:00 AM (UTC)
0 0 1 * *First day of every month at midnight
You don’t need to memorize cron syntax — just tell the AI when you want it to run:
“Run this every weekday at 8 AM.”
Cron schedules use UTC time. If your team operates in a specific timezone, mention it in your prompt and the AI will calculate the correct UTC offset.

Event-driven workflows

Event-driven workflows run in response to events emitted by your app:
import { inngest } from './client';

export const sendWelcomeEmail = inngest.createFunction(
  { id: 'send-welcome-email' },
  { event: 'user/created' },
  async ({ event, step }) => {
    await step.run('send-email', async () => {
      // event.data contains the payload from the event
      const { email, name } = event.data;
      // Send welcome email logic
    });
  }
);
To trigger an event-driven workflow, your app emits an event from an API route:
import { inngest } from '@/inngest/client';

// In an API route
await inngest.send({
  name: 'user/created',
  data: { email: '[email protected]', name: 'Jane' },
});
The AI sets up both the event emission and the workflow handler when you describe the behavior you want.

Step functions

Workflows are broken into steps — discrete units of work managed independently by Inngest. Steps give you:
  • Automatic retries — Failed steps are retried without re-running earlier steps
  • Durability — Completed steps are not re-executed on retry
  • Observability — Each step is tracked separately in the Workflows tab
async ({ step }) => {
  // Step 1: Fetch data
  const data = await step.run('fetch-data', async () => {
    return await fetchFromExternalApi();
  });

  // Step 2: Process data (only runs if Step 1 succeeds)
  const processed = await step.run('process-data', async () => {
    return transformData(data);
  });

  // Step 3: Save results (only runs if Step 2 succeeds)
  await step.run('save-results', async () => {
    await db.results.createMany({ data: processed });
  });
}
Each step should represent a single logical operation — fetching data, transforming it, or saving results.

Error handling and retries

Inngest automatically retries failed steps with exponential backoff, so transient failures like API outages or network timeouts are handled automatically. If a step continues to fail after all retry attempts, the workflow is marked as failed and error details are visible in the Workflows tab. For custom error handling, catch errors within the step:
await step.run('fetch-with-fallback', async () => {
  try {
    return await fetchFromPrimaryApi();
  } catch (error) {
    // Fall back to cached data
    return await fetchFromCache();
  }
});

What’s next