> ## Documentation Index
> Fetch the complete documentation index at: https://upstash.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Auth Provider Webhook

This example demonstrates an authentication provider webhook process using Upstash Workflow.
The workflow handles the user creation, trial management, email reminders and notifications.

## Use Case

Our workflow will:

1. Receive a webhook event from an authentication provider (e.g. Firebase, Auth0, Clerk etc.)
2. Create a new user in our database
3. Create a new user in Stripe
4. Start a trial in Stripe
5. Send a welcome email
6. Send a reminder email if the user hasn't solved any questions in the last 7 days
7. Send a trial warning email if the user hasn't upgraded 2 days before the trial ends
8. Send a trial ended email if the user hasn't upgraded

## Code Example

<CodeGroup>
  ```typescript api/workflow/route.ts theme={"system"}
  import { serve } from "@upstash/workflow/nextjs";
  import { WorkflowContext } from '@upstash/qstash/workflow'

  /**
   * This can be the payload of the user created webhook event coming from your
   * auth provider (e.g. Firebase, Auth0, Clerk etc.)
   */
  type UserCreatedPayload = {
    name: string;
    email: string;
  };

  export const { POST } = serve<UserCreatedPayload>(async (context) => {
    const { name, email } = context.requestPayload;

    const { userid } = await context.run("sync user", async () => {
      return await createUserInDatabase({ name, email });
    });

    await context.run("create new user in stripe", async () => {
      await createNewUserInStripe(email);
    });

    await context.run("start trial in Stripe", async () => {
      await startTrialInStripe(email);
    });

    await context.run("send welcome email", async () => {
      await sendEmail(
        email,
        "Welcome to our platform!, You have 14 days of free trial."
      );
    });

    await context.sleep("wait", 7 * 24 * 60 * 60);

    // get user stats and send email with them
    const stats = await context.run("get user stats", async () => {
      return await getUserStats(userid);
    });
    await sendProblemSolvedEmail({context, email, stats});

    // wait until there are two days to the end of trial period
    // and check upgrade status
    await context.sleep("wait for trial warning", 5 * 24 * 60 * 60);
    const isUpgraded = await context.run("check upgraded plan", async () => {
      return await checkUpgradedPlan(email);
    });

    // end the workflow if upgraded
    if (isUpgraded) return;

    await context.run("send trial warning email", async () => {
      await sendEmail(
        email,
        "Your trial is about to end in 2 days. Please upgrade your plan to keep using our platform."
      );
    });

    await context.sleep("wait for trial end", 2 * 24 * 60 * 60);

    await context.run("send trial end email", async () => {
      await sendEmail(
        email,
        "Your trial has ended. Please upgrade your plan to keep using our platform."
      );
    });
  });

  async function sendProblemSolvedEmail({
    context: WorkflowContext<UserCreatedPayload>
    email: string,
    stats: { totalProblemsSolved: number }
  }) {
    if (stats.totalProblemsSolved === 0) {
      await context.run("send no answers email", async () => {
        await sendEmail(
          email,
          "Hey, you haven't solved any questions in the last 7 days..."
        );
      });
    } else {
      await context.run("send stats email", async () => {
        await sendEmail(
          email,
          `You have solved ${stats.totalProblemsSolved} problems in the last 7 days. Keep it up!`
        );
      });
    }
  }

  async function createUserInDatabase({
    name,
    email,
  }: {
    name: string;
    email: string;
  }) {
    console.log("Creating a user in the database:", name, email);
    return { userid: "12345" };
  }

  async function createNewUserInStripe(email: string) {
    // Implement logic to create a new user in Stripe
    console.log("Creating a user in Stripe for", email);
  }

  async function startTrialInStripe(email: string) {
    // Implement logic to start a trial in Stripe
    console.log("Starting a trial of 14 days in Stripe for", email);
  }

  async function getUserStats(userid: string) {
    // Implement logic to get user stats
    console.log("Getting user stats for", userid);
    return {
      totalProblemsSolved: 10_000,
      mostInterestedTopic: "JavaScript",
    };
  }

  async function checkUpgradedPlan(email: string) {
    // Implement logic to check if the user has upgraded the plan
    console.log("Checking if the user has upgraded the plan", email);
    return false;
  }

  async function sendEmail(email: string, content: string) {
    // Implement logic to send an email
    console.log("Sending email to", email, content);
  }
  ```

  ```python main.py theme={"system"}
  from fastapi import FastAPI
  from typing import Dict, TypedDict
  from upstash_workflow.fastapi import Serve
  from upstash_workflow import AsyncWorkflowContext

  app = FastAPI()
  serve = Serve(app)


  class UserCreatedPayload(TypedDict):
      name: str
      email: str


  class UserStats(TypedDict):
      total_problems_solved: int
      most_interested_topic: str


  async def create_user_in_database(name: str, email: str) -> Dict[str, str]:
      print("Creating a user in the database:", name, email)
      return {"userid": "12345"}


  async def create_new_user_in_stripe(email: str) -> None:
      # Implement logic to create a new user in Stripe
      print("Creating a user in Stripe for", email)


  async def start_trial_in_stripe(email: str) -> None:
      # Implement logic to start a trial in Stripe
      print("Starting a trial of 14 days in Stripe for", email)


  async def get_user_stats(userid: str) -> UserStats:
      # Implement logic to get user stats
      print("Getting user stats for", userid)
      return {"total_problems_solved": 10000, "most_interested_topic": "Python"}


  async def check_upgraded_plan(email: str) -> bool:
      # Implement logic to check if the user has upgraded the plan
      print("Checking if the user has upgraded the plan", email)
      return False


  async def send_email(email: str, content: str) -> None:
      # Implement logic to send an email
      print("Sending email to", email, content)


  async def send_problem_solved_email(
      context: AsyncWorkflowContext[UserCreatedPayload], email: str, stats: UserStats
  ) -> None:
      if stats["total_problems_solved"] == 0:

          async def _send_no_answers_email() -> None:
              await send_email(
                  email, "Hey, you haven't solved any questions in the last 7 days..."
              )

          await context.run("send no answers email", _send_no_answers_email)
      else:

          async def _send_stats_email() -> None:
              await send_email(
                  email,
                  f"You have solved {stats['total_problems_solved']} problems in the last 7 days. Keep it up!",
              )

          await context.run("send stats email", _send_stats_email)


  @serve.post("/auth-provider-webhook")
  async def auth_provider_webhook(
      context: AsyncWorkflowContext[UserCreatedPayload],
  ) -> None:
      payload = context.request_payload
      name = payload["name"]
      email = payload["email"]

      async def _sync_user() -> str:
          return await create_user_in_database(name, email)

      result = await context.run("sync user", _sync_user)
      userid = result["userid"]

      async def _create_new_user_in_stripe() -> None:
          await create_new_user_in_stripe(email)

      await context.run("create new user in stripe", _create_new_user_in_stripe)

      async def _start_trial_in_stripe() -> None:
          await start_trial_in_stripe(email)

      await context.run("start trial in Stripe", _start_trial_in_stripe)

      async def _send_welcome_email() -> None:
          await send_email(
              email, "Welcome to our platform!, You have 14 days of free trial."
          )

      await context.run("send welcome email", _send_welcome_email)

      await context.sleep("wait", 7 * 24 * 60 * 60)

      # get user stats and send email with them

      async def _get_user_stats() -> UserStats:
          return await get_user_stats(userid)

      stats: UserStats = await context.run("get user stats", _get_user_stats)

      await send_problem_solved_email(context, email, stats)

      # wait until there are two days to the end of trial period and check upgrade status
      await context.sleep("wait for trial warning", 5 * 24 * 60 * 60)

      async def _check_upgraded_plan() -> bool:
          return await check_upgraded_plan(email)

      is_upgraded = await context.run("check upgraded plan", _check_upgraded_plan)

      # end the workflow if upgraded
      if is_upgraded:
          return

      async def _send_trial_warning_email() -> None:
          await send_email(
              email,
              "Your trial is about to end in 2 days. Please upgrade your plan to keep using our platform.",
          )

      await context.run("send trial warning email", _send_trial_warning_email)

      await context.sleep("wait for trial end", 2 * 24 * 60 * 60)

      async def _send_trial_end_email() -> None:
          await send_email(
              email,
              "Your trial has ended. Please upgrade your plan to keep using our platform.",
          )

      await context.run("send trial end email", _send_trial_end_email)

  ```
</CodeGroup>

## Code Breakdown

### 1. Sync User

We start by creating a new user in our database:

<CodeGroup>
  ```typescript TypeScript theme={"system"}
  const { userid } = await context.run("sync user", async () => {
    return await createUserInDatabase({ name, email });
  });
  ```

  ```python Python theme={"system"}
  async def _sync_user() -> str:
      return await create_user_in_database(name, email)

  result = await context.run("sync user", _sync_user)
  userid = result["userid"]

  ```
</CodeGroup>

### 2. Create New User in Stripe

Next, we create a new user in Stripe:

<CodeGroup>
  ```typescript TypeScript theme={"system"}
  await context.run("create new user in stripe", async () => {
    await createNewUserInStripe(email);
  });
  ```

  ```python Python theme={"system"}
  async def _create_new_user_in_stripe() -> None:
      await create_new_user_in_stripe(email)

  await context.run("create new user in stripe", _create_new_user_in_stripe)

  ```
</CodeGroup>

### 3. Start Trial in Stripe

We start a trial in Stripe:

<CodeGroup>
  ```typescript TypeScript theme={"system"}
  await context.run("start trial in Stripe", async () => {
    await startTrialInStripe(email);
  });
  ```

  ```python Python theme={"system"}
  async def _start_trial_in_stripe() -> None:
      await start_trial_in_stripe(email)

  await context.run("start trial in Stripe", _start_trial_in_stripe)

  ```
</CodeGroup>

### 4. Send Welcome Email

We send a welcome email to the user:

<CodeGroup>
  ```typescript TypeScript theme={"system"}
  await context.run("send welcome email", async () => {
    await sendEmail(
      email,
      "Welcome to our platform!, You have 14 days of free trial."
    );
  });
  ```

  ```python Python theme={"system"}
  async def _send_welcome_email() -> None:
      await send_email(
          email, "Welcome to our platform!, You have 14 days of free trial."
      )

  await context.run("send welcome email", _send_welcome_email)

  ```
</CodeGroup>

### 5. Send Reminder Email

After 7 days, we check if the user has solved any questions. If not, we send a reminder email:

<CodeGroup>
  ```typescript TypeScript theme={"system"}
  await context.sleep("wait", 7 * 24 * 60 * 60);

  const stats = await context.run("get user stats", async () => {
    return await getUserStats(userid);
  });
  await sendProblemSolvedEmail({context, email, stats});
  ```

  ```python Python theme={"system"}
  await context.sleep("wait", 7 * 24 * 60 * 60)

  async def _get_user_stats() -> UserStats:
      return await get_user_stats(userid)

  stats: UserStats = await context.run("get user stats", _get_user_stats)

  await send_problem_solved_email(context, email, stats)

  ```
</CodeGroup>

The `sendProblemSolvedEmail` method:

<CodeGroup>
  ```typescript TypeScript theme={"system"}
  async function sendProblemSolvedEmail({
    context: WorkflowContext<UserCreatedPayload>
    email: string,
    stats: { totalProblemsSolved: number }
  }) {
    if (stats.totalProblemsSolved === 0) {
      await context.run("send no answers email", async () => {
        await sendEmail(
          email,
          "Hey, you haven't solved any questions in the last 7 days..."
        );
      });
    } else {
      await context.run("send stats email", async () => {
        await sendEmail(
          email,
          `You have solved ${stats.totalProblemsSolved} problems in the last 7 days. Keep it up!`
        );
      });
    }
  }
  ```

  ```python Python theme={"system"}
  async def send_problem_solved_email(
      context: AsyncWorkflowContext[UserCreatedPayload], email: str, stats: UserStats
  ) -> None:
      if stats["total_problems_solved"] == 0:

          async def _send_no_answers_email() -> None:
              await send_email(
                  email, "Hey, you haven't solved any questions in the last 7 days..."
              )

          await context.run("send no answers email", _send_no_answers_email)
      else:

          async def _send_stats_email() -> None:
              await send_email(
                  email,
                  f"You have solved {stats['total_problems_solved']} problems in the last 7 days. Keep it up!",
              )

          await context.run("send stats email", _send_stats_email)

  ```
</CodeGroup>

### 6. Send Trial Warning Email

If the user hasn't upgraded 2 days before the trial ends, we send a trial warning email:

<CodeGroup>
  ```typescript TypeScript theme={"system"}
  await context.sleep("wait for trial warning", 5 * 24 * 60 * 60);

  const isUpgraded = await context.run("check upgraded plan", async () => {
    return await checkUpgradedPlan(email);
  });

  if (isUpgraded) return;

  await context.run("send trial warning email", async () => {
    await sendEmail(
      email,
      "Your trial is about to end in 2 days. Please upgrade your plan to keep using our platform."
    );
  });
  ```

  ```python Python theme={"system"}
  await context.sleep("wait for trial warning", 5 * 24 * 60 * 60)

  async def _check_upgraded_plan() -> bool:
      return await check_upgraded_plan(email)

  is_upgraded = await context.run("check upgraded plan", _check_upgraded_plan)

  # end the workflow if upgraded
  if is_upgraded:
      return

  async def _send_trial_warning_email() -> None:
      await send_email(
          email,
          "Your trial is about to end in 2 days. Please upgrade your plan to keep using our platform.",
      )

  await context.run("send trial warning email", _send_trial_warning_email)

  ```
</CodeGroup>

If they upgraded, we end the workflow by returning.

### 7. Send Trial Ended Email

If the user hasn't upgraded after the trial ends, we send a trial ended email:

<CodeGroup>
  ```typescript TypeScript theme={"system"}
  await context.sleep("wait for trial end", 2 * 24 * 60 * 60);

  await context.run("send trial end email", async () => {
    await sendEmail(
      email,
      "Your trial has ended. Please upgrade your plan to keep using our platform."
    );
  });
  ```

  ```python Python theme={"system"}
  await context.sleep("wait for trial end", 2 * 24 * 60 * 60)

  async def _send_trial_end_email() -> None:
      await send_email(
          email,
          "Your trial has ended. Please upgrade your plan to keep using our platform.",
      )

  await context.run("send trial end email", _send_trial_end_email)

  ```
</CodeGroup>
