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

# Auth Provider Webhook

> Build an authentication provider webhook with Upstash Workflow to handle user creation, Stripe trials, and automated email reminders.

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>
