# Sending events from functions

In some workflows or pipeline functions, you may want to broadcast events from within your function to trigger _other_ functions. This pattern is useful when:

* You want to decouple logic into separate functions that can be re-used across your system
* You want to send an event to [fan-out](/docs/guides/fan-out-jobs) to multiple other functions
* Your function is handling many items that you want to process in parallel functions
* You want to [cancel](/docs/guides/cancel-running-functions) another function
* You want to send data to another function [waiting for an event](/docs/reference/functions/step-wait-for-event)

If your function needs to handle the result of another function, or wait until that other function has completed, you should use [direct function invocation](/docs/guides/invoking-functions-directly) instead.

## How to send events from functions

<GuideSelector
  options={[
    { key: 

typescript", title: "TypeScript" },
    { key: "go", title: "Go" },
    { key: "python", title: "Python" },
  ]}>

<GuideSection show="typescript">

To send events from within functions, you will use [`step.sendEvent()`](/docs/reference/functions/step-send-event). This method takes a single event, or an array of events. The example below uses an array of events.

This is an example of a [scheduled function](/docs/guides/scheduled-functions) that sends a weekly activity email to all users.

First, the function fetches all users, then it maps over all users to create a `"app/weekly-email-activity.send"` event for each user, and finally it sends all events to Inngest.

```ts
new Inngest({ id: "signup-flow" });
type Events = GetEvents<typeof inngest>;

loadCron = inngest.createFunction(
  { id: "weekly-activity-load-users" },
  { cron: "0 12 * * 5" },
  async ({ event, step }) => {
    // Fetch all users
    await step.run("fetch-users", async () => {
      return fetchUsers();
    });

    // For each user, send us an event.  Inngest supports batches of events
    // as long as the entire payload is less than 512KB.
    users.map<Events["app/weekly-email-activity.send"]>(
      (user) => {
        return {
          name: "app/weekly-email-activity.send",
          data: {
            ...user,
          },
          user,
        };
      }
    );

    // Send all events to Inngest, which triggers any functions listening to
    // the given event names.
    await step.sendEvent("fan-out-weekly-emails", events);

    // Return the number of users triggered.
    return { count: users.length };
  }
);
```

Next, create a function that listens for the `"app/weekly-email-activity.send"` event. This function will be triggered for each user that was sent an event in the previous function.

```ts
sendReminder = inngest.createFunction(
  { id: "weekly-activity-send-email" },
  { event: "app/weekly-email-activity.send" },
  async ({ event, step }) => {
    await step.run("load-user-data", async () => {
      return loadUserData(event.data.user.id);
    });

    await step.run("email-user", async () => {
      return sendEmail(event.data.user, data);
    });
  }
);
```

Each of these functions will run in parallel and individually retry on error, resulting in a faster, more reliable system.

<Callout>
  💡 **Tip**: When triggering lots of functions to run in parallel, you will likely want to configure `concurrency` limits to prevent overloading your system. See our [concurrency guide](/docs/guides/concurrency) for more information.
</Callout>

##
By using [`step.sendEvent()`](/docs/reference/functions/step-send-event) Inngest's SDK can automatically add context and tracing which ties events to the current function run. If you use [`inngest.send()`](/docs/reference/events/send), the context around the function run is not present.

</GuideSection>
<GuideSection show="go">

To send events from within functions, you  will use [`step.Send()`](https://pkg.go.dev/github.com/inngest/inngestgo/step#Send) or [`step.SendMany()`](https://pkg.go.dev/github.com/inngest/inngestgo/step#SendMany)

This is an example of a [scheduled function](/docs/guides/scheduled-functions) that sends a weekly activity email to all users.

First, the function fetches all users, then it maps over all users to create a `"app/weekly-email-activity.send"` event for each user, and finally it sends all events to Inngest.

```go
!snippet:path=snippets/go/docs/functions/sending_events_from_functions_part1.go
```

Next, create a function that listens for the `"app/weekly-email-activity.send"` event. This function will be triggered for each user that was sent an event in the previous function.

```go
!snippet:path=snippets/go/docs/functions/sending_events_from_functions_part2.go
```

Each of these functions will run in parallel and individually retry on error, resulting in a faster, more reliable system.

<Callout>
  💡 **Tip**: When triggering lots of functions to run in parallel, you will likely want to configure `concurrency` limits to prevent overloading your system. See our [concurrency guide](/docs/guides/concurrency) for more information.
</Callout>

</GuideSection>
<GuideSection show="python">

To send events from within functions, you will use [`step.send_event()`](/docs/reference/python/steps/send-event). This method takes a single event, or an array of events. The example below uses an array of events.

This is an example of a [scheduled function](/docs/guides/scheduled-functions) that sends a weekly activity email to all users.

First, the function fetches all users, then it maps over all users to create a `"app/weekly-email-activity.send"` event for each user, and finally it sends all events to Inngest.

```py
import inngest
from src.inngest.client import inngest_client

@inngest_client.create_function(
    fn_id="weekly-activity-load-users",
    trigger=inngest.TriggerCron(cron="0 12 * * 5")
)
async def load_cron(ctx: inngest.Context):
    # Fetch all users
    async def fetch():
        return await fetch_users()
    
    users = await ctx.step.run("fetch-users", fetch)

    # For each user, send us an event. Inngest supports batches of events
    # as long as the entire payload is less than 512KB.
    events = []
    for user in users:
        events.append(
            inngest.Event(
                name="app/weekly-email-activity.send",
                data={
                    **user,
                    "user": user
                }
            )
        )

    # Send all events to Inngest, which triggers any functions listening to
    # the given event names.
    await ctx.step.send_event("fan-out-weekly-emails", events)

    # Return the number of users triggered.
    return {"count": len(users)}
```

Next, create a function that listens for the `"app/weekly-email-activity.send"` event. This function will be triggered for each user that was sent an event in the previous function.

```py
@inngest_client.create_function(
    fn_id="weekly-activity-send-email",
    trigger=inngest.TriggerEvent(event="app/weekly-email-activity.send")
)
async def send_reminder(ctx: inngest.Context):
    async def load_data():
        return await load_user_data(ctx.event.data["user"]["id"])
    
    data = await ctx.step.run("load-user-data", load_data)

    async def send():
        return await send_email(ctx.event.data["user"], data)
    
    await ctx.step.run("email-user", send)
```

Each of these functions will run in parallel and individually retry on error, resulting in a faster, more reliable system.

<Callout>
  💡 **Tip**: When triggering lots of functions to run in parallel, you will likely want to configure `concurrency` limits to prevent overloading your system. See our [concurrency guide](/docs/guides/concurrency) for more information.
</Callout>

### Why `step.send_event()` vs. `inngest.send()`?

By using [`step.send_event()`](/docs/reference/python/steps/send-event) Inngest's SDK can automatically add context and tracing which ties events to the current function run. If you use [`inngest.send()`](/docs/reference/python/client/send), the context around the function run is not present.

</GuideSection>

</GuideSelector>

## Parallel functions vs. parallel steps

Another technique similar is running multiple steps in parallel (read the [step parallelism guide](/docs/guides/step-parallelism)). Here are the key differences:

* Both patterns run code in parallel
* With parallel steps, you can access the output of each step, whereas with the above example, you cannot
* Parallel steps have limit of 1,000 steps, though you can trigger as many functions as you'd like using the send event pattern
* Decoupled functions can be tested and [replayed](/docs/platform/replay) separately, whereas parallel steps can only be tested as a whole
* You can retry individual functions easily if they permanently fail, whereas if a step permanently fails (after retrying) the function itself will fail and terminate.

## Sending events vs. invoking

A related pattern is invoking external functions directly instead of just triggering them with an event. See the [Invoking functions directly](/docs/guides/invoking-functions-directly) guide. Here are some key differences:

* Sending events from functions is better suited for parallel processing of independent tasks and invocation is better for coordinated, interdependent functions
* Sending events can be done in bulk, whereas invoke can only invoke one function at a time.
* Sending events can be combined with [fan-out](/docs/guides/fan-out-jobs) to trigger multiple functions from a single event
* Unlike invocation, sending events will not receive the result of the invoked function