diff --git a/framework/bigcommerce/api/index.ts b/framework/bigcommerce/api/index.ts
index e30517171..425759df5 100644
--- a/framework/bigcommerce/api/index.ts
+++ b/framework/bigcommerce/api/index.ts
@@ -11,6 +11,7 @@ import fetchStoreApi from './utils/fetch-store-api'
 
 import type { CartAPI } from './cart'
 import type { CustomerAPI } from './customer'
+import type { LoginAPI } from './login'
 import login from './operations/login'
 
 export interface BigcommerceConfig extends CommerceAPIConfig {
@@ -112,7 +113,7 @@ export const provider = {
 
 export type Provider = typeof provider
 
-export type APIs = CartAPI | CustomerAPI
+export type APIs = CartAPI | CustomerAPI | LoginAPI
 
 export type BigcommerceAPI<P extends Provider = Provider> = CommerceAPI<P>
 
diff --git a/framework/bigcommerce/api/login/index.ts b/framework/bigcommerce/api/login/index.ts
new file mode 100644
index 000000000..8b9543c29
--- /dev/null
+++ b/framework/bigcommerce/api/login/index.ts
@@ -0,0 +1,10 @@
+import type { GetAPISchema } from '@commerce/api'
+import type { LoginSchema } from '../../types/login'
+import type { BigcommerceAPI } from '..'
+import login from './login'
+
+export type LoginAPI = GetAPISchema<BigcommerceAPI, LoginSchema>
+
+export type LoginEndpoint = LoginAPI['endpoint']
+
+export const operations = { login }
diff --git a/framework/bigcommerce/api/login/login.ts b/framework/bigcommerce/api/login/login.ts
new file mode 100644
index 000000000..670734963
--- /dev/null
+++ b/framework/bigcommerce/api/login/login.ts
@@ -0,0 +1,49 @@
+import { FetcherError } from '@commerce/utils/errors'
+import type { LoginEndpoint } from '.'
+
+const invalidCredentials = /invalid credentials/i
+
+const login: LoginEndpoint['operations']['login'] = async ({
+  res,
+  body: { email, password },
+  config,
+  commerce,
+}) => {
+  // TODO: Add proper validations with something like Ajv
+  if (!(email && password)) {
+    return res.status(400).json({
+      data: null,
+      errors: [{ message: 'Invalid request' }],
+    })
+  }
+  // TODO: validate the password and email
+  // Passwords must be at least 7 characters and contain both alphabetic
+  // and numeric characters.
+
+  try {
+    await commerce.login({ variables: { email, password }, config, res })
+  } catch (error) {
+    // Check if the email and password didn't match an existing account
+    if (
+      error instanceof FetcherError &&
+      invalidCredentials.test(error.message)
+    ) {
+      return res.status(401).json({
+        data: null,
+        errors: [
+          {
+            message:
+              'Cannot find an account that matches the provided credentials',
+            code: 'invalid_credentials',
+          },
+        ],
+      })
+    }
+
+    throw error
+  }
+
+  res.status(200).json({ data: null })
+}
+
+export default login
diff --git a/framework/bigcommerce/types/login.ts b/framework/bigcommerce/types/login.ts
new file mode 100644
index 000000000..3313c60d8
--- /dev/null
+++ b/framework/bigcommerce/types/login.ts
@@ -0,0 +1 @@
+export * from '@commerce/types/login'
diff --git a/framework/commerce/api/endpoints/login.ts b/framework/commerce/api/endpoints/login.ts
new file mode 100644
index 000000000..b5b70a1da
--- /dev/null
+++ b/framework/commerce/api/endpoints/login.ts
@@ -0,0 +1,35 @@
+import type { LoginSchema } from '../../types/login'
+import { CommerceAPIError } from '../utils/errors'
+import isAllowedOperation from '../utils/is-allowed-operation'
+import type { GetAPISchema } from '..'
+
+const loginEndpoint: GetAPISchema<
+  any,
+  LoginSchema
+>['endpoint']['handler'] = async (ctx) => {
+  const { req, res, operations } = ctx
+
+  if (
+    !isAllowedOperation(req, res, {
+      POST: operations['login'],
+    })
+  ) {
+    return
+  }
+
+  try {
+    const body = req.body ?? {}
+    return await operations['login']({ ...ctx, body })
+  } catch (error) {
+    console.error(error)
+
+    const message =
+      error instanceof CommerceAPIError
+        ? 'An unexpected error ocurred with the Commerce API'
+        : 'An unexpected error ocurred'
+
+    res.status(500).json({ data: null, errors: [{ message }] })
+  }
+}
+
+export default loginEndpoint
diff --git a/framework/commerce/api/index.ts b/framework/commerce/api/index.ts
index f076657c0..86ac17e43 100644
--- a/framework/commerce/api/index.ts
+++ b/framework/commerce/api/index.ts
@@ -3,6 +3,7 @@ import type { RequestInit, Response } from '@vercel/fetch'
 import type { APIEndpoint, APIHandler } from './utils/types'
 import type { CartSchema } from '../types/cart'
 import type { CustomerSchema } from '../types/customer'
+import type { LoginSchema } from '../types/login'
 import {
   defaultOperations,
   OPERATIONS,
@@ -10,7 +11,7 @@ import {
   APIOperations,
 } from './operations'
 
-export type APISchemas = CartSchema | CustomerSchema
+export type APISchemas = CartSchema | CustomerSchema | LoginSchema
 
 export type GetAPISchema<
   C extends CommerceAPI<any>,
diff --git a/framework/commerce/types/login.ts b/framework/commerce/types/login.ts
new file mode 100644
index 000000000..5974ed2db
--- /dev/null
+++ b/framework/commerce/types/login.ts
@@ -0,0 +1,20 @@
+export type LoginBody = {
+  email: string
+  password: string
+}
+
+export type LoginTypes = {
+  body: LoginBody
+}
+
+export type LoginSchema<T extends LoginTypes = LoginTypes> = {
+  endpoint: {
+    options: {}
+    operations: {
+      login: {
+        data: null
+        body: T['body']
+      }
+    }
+  }
+}
diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts
index 6ef673d77..93fd8153c 100644
--- a/framework/commerce/utils/types.ts
+++ b/framework/commerce/utils/types.ts
@@ -71,7 +71,7 @@ export type HookSchemaBase = {
   data: any
   // Input expected by the hook
   input: {}
-  // Input expected before doing a fetch operation
+  // Input expected before doing a fetch operation (aka fetch handler)
   fetchInput?: {}
   // Data expected by the fetch operation
   body?: {}
diff --git a/pages/api/login.ts b/pages/api/login.ts
new file mode 100644
index 000000000..5f87a24e8
--- /dev/null
+++ b/pages/api/login.ts
@@ -0,0 +1,8 @@
+import login from '@commerce/api/endpoints/login'
+import { LoginAPI, operations } from '@framework/api/login'
+import commerce from '@lib/api/commerce'
+
+export default commerce.endpoint({
+  handler: login as LoginAPI['endpoint']['handler'],
+  operations,
+})